ontology-harness 实现原理学习笔记

仓库 AgentOS/ontology-harness · 分支 main
依据 commit 3916a98d7a1d3aa6e96a622a69dcd525bfc69a5f(短 SHA 3916a98)· 日期 2026-10-29(Asia/Shanghai)
重点:DeerFlow 能力 vs Harness 自研边界、Run 生命周期、三池 + task_type→agent 分流(以源码为准,不是提交流水账)

一句话:本仓库是站在 DeerFlow 之上的 FastAPI 执行层(Harness)。DeerFlow 负责 Agent 运行时、checkpoint、沙箱工具与 MCP 装载;Harness 把「一轮对话 / 一次主动任务」变成可对客户端推送或轮询的 Run + Message + Blocks,并用自研中间件做本体编排、记忆注入、技能白名单、主动任务与产品协议。

本体推理不在本仓库:通过 .mcp.json 连接平台 MCP copy_ontology。当前 config.yaml 编排面为 none(先跑通对话/技能,MCP 健康项可标 disabled)。

1. 产品定位(设计文档口径)

文档 docs/1030/agent-harness-1030-版本规范.md · Harness 保持纯执行器:

  • 调度、接收范围、频控留在 BFF;Harness 补系统触发的执行能力
  • 无用户消息也能创建并跑完一次 run:POST /runs/proactive
  • 终态后把结果交回 BFF:GET /runs/{id}/result
  • 主动内容写入用户主会话:POST /threads/messages
  • 对话 agent 写 DeerMem,问候读同一桶,并向 BFF 提供记忆读接口
  • 用户对话现有协议保持可运行:SSE、快照、cancel / retry / continue / answer

读文档注意 版本规范正文里仍残留早期数字(如「五枚举」「mes-patrol」「池 6/2/4」)。以当前源码为准:services/proactive.py 的 TASKS 是 6 个 task_type;执行器名是 anomaly-detection / brief-generation / greeting_first / greeting_daily;池容量是 user 12 · batch 4 · per_user 8(见 agent/executors.py 的 POOL_WORKERS)。

2. 整体架构图(DeerFlow vs Harness)

图 A · 能力边界:左侧 Harness 产品化,右侧 DeerFlow 运行时。

flowchart LR
  subgraph C["调用方"]
    BFF["BFF / H5"]
  end
  subgraph H["Harness"]
    API["controllers"]
    SVC["Run / Proactive"]
    MW["中间件 + Blocks"]
    DB["harness 表"]
  end
  subgraph D["DeerFlow"]
    CL["Client x5"]
    G["Agent 图"]
    CP["checkpoint"]
    SB["沙箱 / MCP"]
  end
  subgraph X["外部"]
    ONTO["copy_ontology"]
    MEM["DeerMem"]
  end
  BFF --> API --> SVC --> CL --> G
  G --> CP
  G --> SB --> ONTO
  SVC --> MW --> DB
  MW -.-> MEM
  G -.-> MEM

DeerFlow 给你什么

  • 可 checkpoint 的 Agent 回合与工具循环
  • 沙箱、技能目录约定、内置工具(如 present_files)
  • MCP 插件装载框架
  • Memory 桶存储(写侧仍走其 MemoryMiddleware)

Harness 补什么

  • 产品化 Run / Thread / Blocks / 补发协议
  • 三池并发与 queued(单会话仍不排队)
  • 本体编排面、技能白名单、工具组裁剪
  • 记忆注入快照、主动执行器、军师业务契约

3. DeerFlow vs 自研:边界怎么切

层级谁做落到哪里
LLM Agent / LangGraph 图、工具循环DeerFlowDeerFlowClient、agent 配置目录、checkpointer
对话状态真相(可恢复中断)DeerFlowcheckpoint(Postgres);harness 不另存执行状态
沙箱文件工具、bash、TavilyDeerFlowconfig.yaml 里 deerflow.* / tools
MCP 协议装载DeerFlow + Harness 合同校验Harness agent/mcp_setup/loader.py 只放行 copy_ontology
Run / 会话消息 / 交互 / 分享表Harnessstore/models/ + Alembic(与 DeerFlow alembic_version 隔离)
对外 HTTPHarnesscontrollers/
流式事件 → UI Blocks混合services/agent.py + assembler / 中间件 custom 事件
系统提示词、技能清单拼装Harness 覆盖SystemPromptMiddleware + SkillsMiddleware
记忆注入(读侧)HarnessMemoryContextMiddleware:写成 checkpoint 里的 HumanMessage 快照(前缀缓存);须 memory.injection_enabled: false
本体编排Harnessagent/copy_ontology.py + agent/middlewares/copy_ontology_*
主动任务Harnessservices/proactive.py + 四个执行器;不挂 copy_ontology、不写记忆

4. 一次 Run:创建 → 调度 → 执行 → 落盘 / 事件

核心文件:controllers/run.py → services/run.py(对话)/ services/proactive.py(主动)→ services/agent.py(不走公开 DeerFlowClient.stream,直接调 agent 的 stream / run_once)→ 中间件栈 → DeerFlow checkpointer。

图 B1 · 对话 Run:创建与排队(单会话不排队;忙则 409)。

sequenceDiagram
  autonumber
  participant BFF as BFF
  participant API as API
  participant RS as RunService
  participant Pool as user池
  participant DB as DB

  BFF->>API: POST /runs
  API->>RS: create
  alt 会话忙
    RS-->>BFF: 409 Busy
  else waiting_user
    RS->>RS: 当作回答续跑
  else 空闲
    RS->>DB: 插入 queued
    RS->>Pool: submit
    RS-->>BFF: 202 Run
  end

图 B2 · 对话 Run:执行、流式事件与落盘(事件可丢,快照为准)。

sequenceDiagram
  autonumber
  participant Pool as user池
  participant RS as RunService
  participant AS as Agent
  participant DF as DeerFlow
  participant DB as DB
  participant BFF as BFF

  Pool->>RS: _execute
  RS->>DB: running
  RS->>AS: stream
  AS->>DF: agent.stream
  DF->>DF: checkpoint
  loop 流式事件
    DF-->>AS: token/custom
    AS-->>RS: AgentEvent
    RS->>DB: events+Blocks
    RS-->>BFF: SSE
  end
  alt 提问 interrupt
    RS->>DB: waiting_user
  else 正常结束
    RS->>DB: completed
  end
  Note over BFF,DB: 断线改拉 GET /threads/id

要点 AGENTS.md:DeerFlow checkpoint 是对话真相;harness 表只存 Run、事件日志、会话、给用户看的消息、交互、分享。一个 thread_id 同一时刻只有一个非终态 Run,不排队。跨会话并发靠三池;池满 proactive 留在 queued,对话撞忙会话是 409。

状态机(对话)
queued → running → completed | failed | cancelled
            ⇅
       waiting_user   (等用户回答提问;对外/服务层常用此名)

先写结束事件,再把行标终态 —— 客户端读到终态时日志一定完整。
一个库只有一个 harness 写进程;启动 recover() 把残留 queued/running 标失败(worker_lost)。

5. 三池 + task_type → agent 分流(以代码为准)

历史上曾是「单一 ThreadPoolExecutor(max_workers=max_concurrent_runs)」。现在:

  • RunService 持有 user 池(对话)
  • ProactiveService 持有 batch + per_user 两池
  • harness.max_concurrent_runs 已不再读取
  • 启动断言 database.pool_size >= 24(三池 worker 之和;每个 worker 跑图各占一条 checkpointer 连接)

图 C · 三池分流:对外用 task_type,池按执行器名选择。

flowchart TB
  U["POST /runs 对话"] --> CU["chat-agent"]
  CU --> PU["user池 x12"]

  P["POST /runs/proactive"] --> T{"task_type"}
  T -->|quality / production / inventory| AD["anomaly-detection"]
  T -->|company_brief| BG["brief-generation"]
  T -->|greeting_first / daily| GR["greeting-*"]

  AD --> PB["batch池 x4"]
  BG --> PB
  GR --> PP["per_user池 x8"]
task_type粒度执行器 design_nameDeerFlow agent_name池
(用户对话,无此字段)会话—chat-agentuser · 12
quality_anomaly批次anomaly-detectionanomaly-detectionbatch · 4
production_anomaly批次anomaly-detectionanomaly-detectionbatch · 4
inventory_anomaly批次anomaly-detectionanomaly-detectionbatch · 4
company_brief批次brief-generationbrief-generationbatch · 4
greeting_first用户greeting_firstgreeting-firstper_user · 8
greeting_daily用户greeting_dailygreeting-dailyper_user · 8
# agent/executors.py(摘录)
POOL_WORKERS = {POOL_USER: 12, POOL_BATCH: 4, POOL_PER_USER: 8}

def normalize_agent_name(name: str) -> str:
    """只在构造客户端、找提示词文件时用;task_type 不经过它。"""
    return re.sub(r"[^A-Za-z0-9-]", "-", name)

EXECUTORS = {
    "anomaly-detection": Executor(..., POOL_BATCH),
    "brief-generation":  Executor(..., POOL_BATCH),
    "greeting_first":    Executor(..., POOL_PER_USER),  # → greeting-first
    "greeting_daily":    Executor(..., POOL_PER_USER),  # → greeting-daily
}

# services/proactive.py
TASKS = {
    "quality_anomaly":    Task(EXECUTORS["anomaly-detection"], KIND_ANOMALY),
    "production_anomaly": Task(EXECUTORS["anomaly-detection"], KIND_ANOMALY),
    "inventory_anomaly":  Task(EXECUTORS["anomaly-detection"], KIND_ANOMALY),
    "company_brief":      Task(EXECUTORS["brief-generation"], KIND_BRIEF),
    "greeting_first":     Task(EXECUTORS["greeting_first"], KIND_GREETING),
    "greeting_daily":     Task(EXECUTORS["greeting_daily"], KIND_GREETING),
}

映射要点 对外路由字段是 task_type;池映射键是执行器内部名。创建 proactive Run 时已完成 task_type → Executor,再 self._pools[task.executor.pool].submit(...)。同键不同 task_type → 409;非法 task_type → 422。用户级必须带 user_id,批次不能带。

6. 本体能力:copy_ontology(自研编排 + 平台 MCP)

本体推理引擎不在 harness。三种编排面(harness.copy_ontology_orchestration):

编排面中间件思路适用
platform_runtimeStrategist 上下文循环 + CopyOntologyEvents平台侧主导检索/编排
scoped_toolsBudget + ScopedOntology + Events + Provenance本轮动态挂本体工具,有预算
none(当前 config)不连 MCP;Provenance 兜底;health 可标 mcp.disabled先跑通对话/技能

none / platform_runtime 会拿掉 DeerFlow 内置 ask_clarification(它不在 tools 池,只能在 copy_ontology.py 显式处理)。展示组件由中间件校验平台返回后直接发 custom 事件;files 例外走 present_files + PresentFilesMiddleware。

对话中间件大致顺序:SystemPrompt → Skills? → MemoryContext → ReferenceContext → LocalToolGroups? → ToolCallLog → 编排面相关 → PresentFiles …

7. 记忆:DeerFlow 存桶,Harness 管注入与 HTTP

  • 写:仅 chat-agent 通过 DeerFlow MemoryMiddleware(桶名默认 chat-agent)。
  • 读/注入:自研 MemoryContextMiddleware 把 get_context 写成 checkpoint 里带 harness_user_memory 标记的 HumanMessage 快照(插在当前用户消息前),用 revision 做前缀缓存——facts 不变就不重读,避免把记忆塞进系统提示词导致整段前缀失效。
  • 必须关 DeerFlow 自带注入(memory.injection_enabled: false)。
  • 问候在 compose 阶段读同一桶;BFF 经记忆 HTTP 读/覆盖画像与兴趣称呼。

8. 技能与工具面:配置白名单压过 DeerFlow 默认「全开」

五个 agent(1 对话 + 4 执行器)。启动时:

  1. 补写/校验各 agent 的 agent_dir(name)/config.yaml(执行器强制 memory_enabled=false、mcp_plugins: [])。
  2. agent/skills.py 扫 skills/public/*/SKILL.md,按 config.yaml 白名单校验;未知技能告警跳过。
  3. 本地工具全进程一个池;对话用 LocalToolGroupsMiddleware 藏未选中组;执行器用 ToolsMiddleware。
  4. 技能隔离现用 prompt(保留 local sandbox host bash);接入 aio 沙箱后宜改回 sandbox。
agentskills(当前)tool_groups
chat-agentdeep-research(已移除 markdown-render;成品约定直接 HTML)null(全部)
anomaly-detectionmes-quality-analysis、mes-production-analysis、html-cardfile:read/write + bash
brief-generationhtml-cardfile:read/write + bash
greeting-first无[]
greeting-daily无[web]

9. 主动执行器

主动 Run:trigger_kind=proactive,无会话、无触发消息、不写事件、不进 harness_messages;按全局唯一 idempotency_key 去重;非流式 run_once;结果只在 completed 写 result_payload。

图 D · 主动 Run:创建 → 分池执行 → 取结果 → 可选写入主会话。

flowchart TD
  A["BFF POST /proactive"] --> B{"校验参数"}
  B -->|非法| E1["422"]
  B -->|同键冲突| E2["409"]
  B -->|同键命中| H["202 hit"]
  B -->|新键| Q["queued 入库"]
  Q --> S["按池 submit"]
  S --> R["running + compose"]
  R --> I["run_once"]
  I --> F{"终态"}
  F -->|成功| OK["completed + payload"]
  F -->|失败| FL["failed"]
  F -->|取消| CA["cancelled"]
  OK --> G["GET /result"]
  G --> M["可选写主会话"]
  M --> N["空闲 append / 忙则下轮补写"]

今日相关 greeting_first 必须带 data: {name, junshi_name, interest_tags},落进 input_payload;其它任务带了 data 也会丢掉。Run 归属统一用 harness_runs.user_id(已删除 subject_user_id)。批次任务 user_id 为空。

# 结果信封经 GET /runs/{id}/result
# schema_version 按 kind 选;异常块可带 summary/metadata;
# 周报优先读沙箱固定路径 brief.html,不读模型正文。
# DeerFlow thread 用合成键 proactive-{run_id.hex},不建 harness_threads 行。

10. 客户端协议:Message + Blocks

  • 事件流可丢,以线程快照为准(docs/agent-ui.md)。
  • 交互走 LangGraph interrupt() + harness interaction + POST /runs/{id}/answer。
  • 块类型含 text / html / reasoning / 组件块等;未知 type 客户端应跳过。
  • 近期协议增强:思考增量、本地工具卡、files 插在文本后、用户消息引用、游标分页、主动 message_kind。

11. 今日(2026-10-29)代码要点 · 原理向

#变更实现思路
1首次登录欢迎语按用户资料生成POST /runs/proactive 增加 GreetingFirstData;材料进 input_payload,compose 拼进用户消息
2删除 subject_user_id迁移 0006/0007;对话与用户级 proactive 统一写 user_id;响应直接读该列
3对话成品统一 HTMLchat-agent 提示词约定直接出 HTML;配置去掉 markdown-render 技能
4问候工具面收紧greeting-first 的 tool_groups: [];日常问候保留 web
5产量异常任务production_anomaly 与质量/库存共用 anomaly-detection;技能含 mes-production-analysis
6联网检索尽快收敛chat-agent 提示词侧约束检索步数/收敛,不改运行时协议
7问候文案产品化greeting-first.md / greeting-daily.md 与计划文档同步

12. 近期功能(按原理,不按 commit)

功能实现思路边界
1030 合入 main目录迁到 agent/controllers;只留 copy_ontology;uv/pyproject混合
编排面 none关 MCP;隐藏 ask_clarificationHarness
提示词本地 / LangfuseLANGFUSE_PROMPT_LOCAL 等环境开关;一 agent 一文件Harness
记忆前缀缓存 / 覆盖 memoryrevision 快照;HTTP 写画像给 AppHarness
兴趣与称呼接口给 H5 写 harness 记忆HTTP
引用 / 游标分页 / 自动标题协议与查询层;标题并行主模型Harness
思考增量 + 本地工具卡扩展事件类型混合
工具调用日志最外层 ToolCallLogHarness
部署门禁 / slim 镜像 / JSON 日志DB·模型·Tavily 预检;Loki 友好运维

13. 建议阅读顺序

  1. AGENTS.md(权威约定)
  2. docs/1030/agent-harness-1030-版本规范.md(对照源码纠偏过期表)
  3. config.yaml:tools / memory / agents / orchestration
  4. agent/executors.py → services/proactive.py → services/run.py
  5. agent/copy_ontology.py → agent/middlewares/*
  6. services/agent.py(与 DeerFlow 粘合最深)
  7. controllers/run.py / memory.py / thread.py
  8. docs/agent-ui.md、docs/chat.md、docs/1030/greeting_first.md

14. 设计文档地图(仓库内)

1030 版本规范 · 目录

  • 军师 APP 1030 · Agent Harness 版本规范
  • 修订记录
  • 1. 目标与非目标
  • 2. 已收口的结构裁决
  • 3. 标识(含 task_type)
  • 4. 数据模型
  • 5. 运行时(含三池)
  • 6. 接口
  • 7. 记忆数据流
  • 8. proactive 错误码
  • 9. 验收标准
  • 10. 仍开放

设计骨架里与并发相关的裁决

  • 现状曾是单一进程级 FIFO 线程池
  • 改造:三池;submit 时按执行器所属池分流
  • 对外路由字段:task_type(源码现为六值)
  • 池映射键:内部 agent / executor 名;task_type→agent 在创建时完成
  • 另见:问候规范、strategist-runtime-v2、.cursor/plans 下今日计划文档

学习时怎么判断「该不该改 DeerFlow」:若问题是 checkpoint / 工具循环 / 沙箱生命周期——优先查 DeerFlow 升级与 services/agent.py 粘合;若问题是 Blocks 形态、编排策略、军师业务、记忆注入内容、并发排队——改 Harness 中间件与 controllers,不要把业务逻辑 fork 进 DeerFlow。

返回课程首页