DeerFlow Lead Agent 与 Middleware 链:决策与横切关注点分离
Harness 提供骨架之后,接用户请求的是 Lead Agent。用户说「查 2026 年最火的 AI 编程工具,写对比报告」时,系统要做的事远不止调一次模型:注入当前日期和用户记忆、解析上传文件、准备沙箱写盘、压缩超长历史、管理计划模式下的 TodoList、自动生成标题、写回长期记忆、限制子代理并发、检测工具死循环、信息不够时停下来澄清……如果全部塞进一个大函数,横切逻辑会淹没「下一步做什么」的决策,也很难单独替换某一环。DeerFlow 的拆法是:Lead Agent 负责思考与调度,杂活拆成中间件流水线,按固定顺序处理每一次对话。
Lead Agent 是工厂组装的
入口在 backend/packages/harness/deerflow/agents/lead_agent/agent.py。make_lead_agent(config: RunnableConfig) 保持与 LangGraph Server 兼容的图工厂签名,内部进入 _make_lead_agent。Lead Agent 是工厂按配置装配出来的结果。
组装大致四步。第一步从 RunnableConfig 读运行时意图:thinking_enabled、is_plan_mode、subagent_enabled、agent_name(经 validate_agent_name)等,决定本轮开哪些能力。第二步 _resolve_model_name:用户指定则用指定模型,配置不存在则回退默认。第三步 build_middlewares 按固定顺序拼出约十四个中间件。第四步调用 LangChain create_agent,打包 model、tools、middleware、apply_prompt_template 得到的 system prompt,以及 ThreadState。
1 | # 简化自 build_middlewares |
你告诉工厂要什么能力,它把对应中间件排进链里。Lead Agent 能专注决策,是因为横切关注点外置到了 Middleware。
为什么顺序固定
源码注释把顺序钉死了:ThreadDataMiddleware 必须在 SandboxMiddleware 之前,保证 thread_id 可用;SummarizationMiddleware 靠前,先压缩再往后传;TitleMiddleware 在首次对话后起标题;MemoryMiddleware 跟在 Title 之后;ClarificationMiddleware 永远垫底。实务上可以按阶段理解:先准备上下文并压缩,计划(Todo)居中管理,澄清在模型输出之后最后拦截。顺序错了,轻则缓存失效、上下文膨胀,重则澄清拦截不到、沙箱拿不到线程数据。
几个典型中间件:
DynamicContextMiddleware 在首条用户消息前插入隐藏的 <system-reminder>(含 <current_date> 等),不改系统提示词。系统提示保持静态,才能稳定命中 prefix cache,省延迟和费用。把日期塞进 system prompt 每次都变,等于主动放弃前缀缓存。
SummarizationMiddleware 用 token 计数判断是否超阈值,未超则直接返回;超了就把早期消息压成摘要,避免整条链被超长上下文拖垮。
LoopDetectionMiddleware 对一组工具调用做确定性哈希(名字 + 稳定参数排序后摘要),同一哈希出现过频就注入警告或强制停止,防止 agent 卡在重复 tool call。
ClarificationMiddleware 在 wrap_tool_call 里识别 ask_clarification:不是澄清则交给默认 handler;是澄清则转成友好消息并中断执行,让用户补信息后再继续。
一次请求怎么过链
消息进入 Lead Agent 后,中间件在不同钩子介入:before_agent 注入日期与记忆,before_model 决定是否摘要,模型返回工具调用后走沙箱执行,wrap_tool_call 上 Clarification 拦截,收尾再走 Title / Memory 等。Lead 不直接调工具,模型决定下一步;中间件各管一道工序,可单独替换。例如不想用默认记忆模块,换掉 MemoryMiddleware 即可。
自定义中间件可用 @Next(MemoryMiddleware) / @Prev(...) 声明相对位置,不必手算整条链下标。同一条链上,有的中间件改输入消息,有的包住工具调用,有的在模型返回后写副作用(标题、记忆)。把「何时介入」和「排在第几」一起看,才不会把 Clarification 误插到链头。
读代码建议顺序:agent.py 的 _make_lead_agent 与 build_middlewares,再抽读 dynamic_context_middleware.py、summarization_middleware.py、loop_detection_middleware.py、clarification_middleware.py。比先啃 prompt 模板更能看清「谁在决策、谁在做横切」。加能力插一段、换实现换一段;顺序规则写在组装函数和注释里,比散落在字符串里可靠,也更容易做回归。
DeerFlow Lead Agent 与 Middleware 链:决策与横切关注点分离
https://simonsu.net/2026/09/15/deerflow-02-lead-agent-middleware/