第 4 章:Agent 工厂与 ThreadState —— LangGraph 外面的真正内核
DeerFlow 没有手写一个
while tool_calls循环,而是用create_agent()生成图。它自己的核心价值在“如何给这张图选择模型、工具、状态和中间件”。
两个工厂,两种使用方式
create_deerflow_agent():SDK 工厂
通用工厂面向二次开发。调用者可以:
- 完全接管 middleware;
- 用 feature flags 让 DeerFlow 自动组装;
- 通过
@Next/@Prev把自定义中间件插入稳定锚点; - 指定 full 或 delta checkpoint channel mode。
显式 middleware 与 feature/extra middleware 互斥,这是刻意避免“半接管”带来的顺序不确定性。
make_lead_agent():产品主 Agent
这是 langgraph.json 注册的 graph factory,也是 Gateway 主路径使用的 Agent。它会从运行 config 和 AppConfig 中解析用户、模型、custom agent、技能 allowlist、工具组、子代理开关和 tracing,再创建完整 Agent。
组装算法
_make_lead_agent() 的主流程可以压缩为:
模型优先级是请求覆盖 → custom agent 默认 → 全局默认。thinking/reasoning 也有类似覆盖规则;若配置请求思考但模型不支持,会明确降级而不是把无效参数传下去。
ThreadState 为什么不是普通对象
LangGraph 图中多个节点可能在一个 super-step 里写同一字段。没有 reducer 时,两个工具并行写 artifacts 会冲突;随便“最后写入胜出”又会丢数据。因此 DeerFlow 为共享字段定义业务 reducer。
| 字段 | 合并规则 | 保护的系统性质 |
|---|---|---|
sandbox | 只接受相同 sandbox_id | 每线程沙箱唯一性 |
artifacts | 有序拼接并去重 | 并行产物不丢失 |
todos | 新的非 None 整体替换 | Todo 是一份当前视图 |
goal | 未触碰时保留 | 中间件不能意外清空目标 |
viewed_images | 字典合并,空字典显式清空 | 图片按需注入后可释放 |
promoted | catalog hash 变更时整体替换 | 防止同名工具目录漂移 |
delegations | ID 合并、终态不可倒退、最多 50 条 | 子代理账本可恢复 |
skill_context | 路径去重、只存引用、最近 8 条 | 摘要后仍知技能来源 |
消息通道的 full 与 delta
ThreadState 默认用 LangGraph 的消息 reducer。DeltaThreadState 则把 messages 换成 DeltaChannel(merge_message_writes),按设定频率写快照、其余写增量,减轻长对话 checkpoint 的写放大。
自定义 reducer 仍保持公共 add_messages 的语义:
- 自动补消息 ID;
- 相同 ID 替换;
RemoveMessage精确删除;REMOVE_ALL_MESSAGES清空后重建;- 不存在 ID 的删除失败关闭。
中间件可能声明自己的 state schema。进入 delta 模式时,normalize_middleware_state_schemas() 会复制中间件并适配其 messages 注解,避免主 state 是 delta、middleware state 却悄悄回到 full。
Bootstrap Agent 与普通 Agent
创建自定义 Agent 时系统先运行一个 bootstrap Agent。它只开放狭窄技能集和 setup_agent,避免一个尚未存在的 Agent 配置反过来影响自己的创建过程。
普通 custom agent 可获得 update_agent 自修改工具,但 webhook 渠道会移除它。理由非常实际:GitHub 评论等外部输入不应有机会永久修改 Agent 的 SOUL、模型或工具组。这是“同一个模型,在不同信任入口能力不同”的典型例子。
System Prompt 静态,动态上下文进消息
Lead Agent 的 system prompt 尽量保持静态,从而复用供应商 prefix cache。当前日期、记忆等每轮变化内容由 DynamicContextMiddleware 注入首个 HumanMessage 附近的隐藏 reminder,而不是重建 system prompt。
这也解释了为什么摘要中间件要特殊保护这些 reminder:它们在消息列表里,却承担系统指令职责,不能当普通旧对话压掉。
中间件顺序是程序语义
LangChain 的 before hooks 正序执行,after hooks 常以反序包裹返回。DeerFlow 明确要求 ClarificationMiddleware 最后注册、Safety middleware 在特定位置注册,就是为了让返回路径上的先后次序正确。
改变列表顺序可能导致:授权发生在技能激活前、摘要看不到 delegation、Safety 清除工具调用太晚、严格模型收到多个 SystemMessage。因此自定义扩展应使用稳定插入锚点,而不是在列表里随意 append。
源码锚点
下一章看 Agent 的两类输入:模型对象与系统提示词。