第 2 章:三层架构 —— 先找到每一层的责任边界
大型仓库最怕按目录逐个读。正确的第一步是识别稳定边界,再沿跨边界的数据结构前进。
总体拓扑
第一层:Harness 是“能力与语义”
backend/packages/harness/deerflow/ 是最值得先读的目录。它定义:
agents/:Lead Agent、通用工厂、ThreadState 与中间件;models/:模型配置到 LangChain ChatModel 的适配;tools/:内置工具、配置工具、MCP 工具和延迟发现;sandbox/:统一抽象、本地/远程 provider、虚拟路径;subagents/:子代理注册、隔离事件循环和执行器;runtime/:RunManager、worker、事件、checkpoint、stream bridge;persistence/:SQL/内存/文件等后端;skills/、mcp/、guardrails/、authz/:能力扩展与治理。
它内部又有一条很重要的分界:agents 决定模型如何思考,runtime 决定一次思考如何可靠地活着。前者偏 LangGraph,后者偏作业系统。
第二层:Gateway 是“运行控制面”
backend/app/gateway/ 不是普通的 CRUD 外壳。app.py 在 lifespan 中组装数据库、checkpointer、RunManager、StreamBridge、memory manager、MCP 缓存和后台服务,并挂载二十余组 router。
Gateway 的职责可以归成四类:
- 入口协议:兼容 LangGraph SDK 的 threads/runs/stream 路由;
- 可靠性:run 准入、互斥、取消、join、断线重放、孤儿运行恢复;
- 产品 API:agents、skills、memory、uploads、artifacts、channels、scheduled tasks;
- 安全边界:Auth、CSRF、CORS、用户域隔离和资源授权。
最重要的设计是:Gateway 内嵌 Agent runtime。生产主路径不再依赖一个独立 LangGraph Server 进程,而是通过 RunManager + run_agent() + StreamBridge 执行同一张图。
第三层:Frontend 是“事件投影器”
前端不是收到 token 就 append 文本。它同时处理三类状态:
- 持久状态:分页历史、线程元数据、已保存 checkpoint;
- 运行状态:当前 SSE 中的
values、messages-tuple、custom; - 乐观状态:刚发送但后端尚未回显的用户消息、编辑重跑替换、面板 UI。
因此,frontend/src/core/ 比页面组件更值得读。threads/ 管运行和历史,tasks/ 折叠子代理事件,artifacts/ 管产物,messages/ 把原始消息转换成用户看到的分组。
跨层契约:比目录更重要
ThreadState
Agent 的共享状态不仅有 messages,还有:
sandbox 当前线程的沙箱引用
thread_data workspace/uploads/outputs 虚拟路径
title 自动生成的线程标题
artifacts 已交付产物路径
todos / goal 计划与长期目标
uploaded_files 本轮可见上传文件
viewed_images 图片元数据,不存 base64
promoted 延迟工具晋升记录
delegations 子代理耐久账本
skill_context 最近加载的技能引用
summary_text 压缩后的上下文摘要2
3
4
5
6
7
8
9
10
11
字段带 reducer,因为同一 graph super-step 可能有多个节点并行写状态。sandbox 只接受同 ID 的幂等写;artifacts 有序去重;delegations 以任务 ID 合并且不允许终态倒退;这些不是数据清洗,而是并发语义。
RunRecord
Thread 表示长期对话,Run 表示一次执行。RunRecord 记录 run_id、thread_id、状态、策略、模型、token、stop reason、所有权租约等。把 Run 独立建模后,系统才有能力处理取消、join、重放、编辑重跑和多 worker 接管。
SSE 事件
Agent 内部产生 LangGraph stream frame,worker 将其标准化后写入 StreamBridge。前端订阅的不是某个进程内生成器,而是一条可恢复事件日志。内存模式适合单进程,Redis Streams 让多 worker 也能 join 同一次运行。
为什么是 Harness / App 分离
这不是为了目录好看,而是为了避免三种耦合:
- Agent SDK 用户不该被迫安装 FastAPI 和 IM SDK;
- Gateway 的认证、数据库与部署选择不应渗入 Agent 业务逻辑;
- 自定义入口(TUI、测试、嵌入式 Client)应该复用同一套 Agent 组装,而不是复制 prompt 和工具表。
源码锚点
backend/packages/harness/deerflow/agents/thread_state.pybackend/app/gateway/app.pyfrontend/src/core/threadsbackend/langgraph.json
下一章不再看静态结构,而是跟着一条消息跑完整条链路。