主题
Lead Agent 与 Agent 工厂
本章目标:
- 讲清
make_lead_agent应用级入口与create_deerflow_agent纯参数 SDK 工厂这两层工厂为何分层、各自边界在哪里。- 拆解一次
make_lead_agent(config)调用如何把 model / tools / middleware / system_prompt 解析装配进langchain.agents.create_agent。- 给出用
create_deerflow_agent+RuntimeFeatures构建自定义 agent 的最小模板与从源码校验逻辑读出的约束清单。
TL;DR
DeerFlow 的 agent 装配分两层:make_lead_agent 是 langgraph.json 注册的图工厂,从 RunnableConfig 解析运行时配置并查全局 AppConfig,产出生产用 lead agent;create_deerflow_agent 是无 YAML、无全局单例的纯参数 SDK 工厂,通过 RuntimeFeatures 声明式开关装配中间件链。两者最终都调用同一个 langchain.agents.create_agent 原语,装配的四要素是 model、tools、middleware、system_prompt,外加 state_schema=ThreadState。make_lead_agent 的中间件链由 build_lead_runtime_middlewares(共享基座)+ _build_middlewares(lead 专属)两段顺序追加而成。features 与 middleware 互斥,后者是「完全接管」。
Overview
为什么需要两层工厂?核心矛盾是「生产入口要懂配置」与「SDK 入口要可测、可嵌入」。
make_lead_agent(config: RunnableConfig) 是 LangGraph Server 注册的图,签名必须兼容 LangGraph Server agent.py:343-347。它从 config.configurable / config.context 合并运行时上下文,读全局 AppConfig,解析模型名、加载 agent 配置与技能、注入 LangSmith trace 元数据 agent.py:350-409。它「重」——耦合 YAML 配置、全局单例、技能存储、子代理注册表。
create_deerflow_agent 则刻意「轻」:它接受纯 Python 参数,工厂装配本身不读任何配置文件 factory.py:1-11,用 RuntimeFeatures 数据类声明要开哪些中间件 features.py:14-34。它定位于「create_agent 原语」与「make_lead_agent 应用工厂」之间的 SDK 层,便于单测、嵌入式客户端复用和构造定制 agent。
两层共享同一终点 langchain.agents.create_agent,装配骨架一致,差别只在「谁来决定 model/tools/mw/prompt」。
Architecture
Source 文件清单:
| 角色 | 文件 | 关键符号 (Source) |
|---|---|---|
| 应用级图工厂 | backend/packages/harness/deerflow/agents/lead_agent/agent.py | make_lead_agent L343-347、_make_lead_agent L350-446、_build_middlewares L240-318 |
| 纯参数 SDK 工厂 | backend/packages/harness/deerflow/agents/factory.py | create_deerflow_agent L61-147、_assemble_from_features L155-298 |
| 特性声明数据类 | backend/packages/harness/deerflow/agents/features.py | RuntimeFeatures L14-34、Next/Prev L42-63 |
| 系统提示词组装 | backend/packages/harness/deerflow/agents/lead_agent/prompt.py | apply_prompt_template L768-823 |
| 共享中间件基座 | backend/packages/harness/deerflow/agents/middlewares/tool_error_handling_middleware.py | build_lead_runtime_middlewares L129-136 |
| 公共导出 | backend/packages/harness/deerflow/agents/__init__.py | create_deerflow_agent / make_lead_agent 导出 L1-20 |
| 图注册 | backend/langgraph.json | graphs.lead_agent L8-10 |
langgraph.json 把图名 lead_agent 指向 deerflow.agents:make_lead_agent langgraph.json:8-10,而 deerflow.agents.__init__ 在导入时即调用 prime_enabled_skills_cache() 预热技能缓存 init.py:6-10。
Components / Subsystems
make_lead_agent — 应用级图入口
职责:作为 langgraph.json 注册的图工厂,从 LangGraph 传入的 RunnableConfig 出发,产出完整生产 lead agent。
关键函数:
make_lead_agent(config)agent.py:343-347:薄包装,取runtime_config["app_config"],否则get_app_config(),转入_make_lead_agent。_get_runtime_config(config)agent.py:29-35:把configurable与 LangGraphcontext合并成一个 dict。_make_lead_agent(config, *, app_config)agent.py:350-446:核心装配体。_resolve_model_name(...)agent.py:38-50:模型名安全解析,无效时回退默认模型。
要点:
- runtime config 读取的关键键:
thinking_enabled(默认True)、reasoning_effort、model_name/model、is_plan_mode、subagent_enabled、max_concurrent_subagents(默认 3)、is_bootstrap、agent_nameagent.py:358-365。 - 模型名解析优先级:请求 → agent 配置 → 全局默认 agent.py:370-373。模型不支持 thinking 时自动降级 agent.py:379-381。
- 两条产出分支:
is_bootstrap=True走最小提示词 +setup_agent工具 agent.py:413-427;否则默认/自定义 agent 分支,自定义 agent 额外注入update_agent工具 agent.py:429-446。 - 两分支最终都调用
create_agent(model=..., tools=..., middleware=..., system_prompt=..., state_schema=ThreadState)。
create_deerflow_agent — 纯参数 SDK 工厂
职责:在 create_agent 原语与 make_lead_agent 应用工厂之间提供无 YAML、无全局单例的 SDK 入口。
关键函数:
create_deerflow_agent(model, tools, *, system_prompt, middleware, features, extra_middleware, plan_mode, state_schema, checkpointer, name)factory.py:61-147。_assemble_from_features(feat, ...)factory.py:155-298:按RuntimeFeatures顺序构建 14 个中间件 + 注入的 extra_tools。_insert_extra(chain, extras)factory.py:306-379:用@Next/@Prev锚点把额外中间件插入链中。
要点:
middleware是「完全接管」:一旦提供,该列表原样使用,且不能与features或extra_middleware共存 factory.py:110-117。- 走 features 分支时,feature 注入的
extra_tools按名去重追加,用户提供的工具优先 factory.py:132-137。 state_schema默认ThreadStatefactory.py:120。
RuntimeFeatures — 声明式特性开关
职责:用纯数据类声明 create_deerflow_agent 要开启哪些中间件,无 I/O、无副作用 features.py:1-4。
要点:
- 多数特性接受三态:
True(用内置默认中间件)、False(禁用)、AgentMiddleware实例(自定义替换)features.py:18-25。 summarization与guardrail无内置默认,只接受False或自定义实例;在_assemble_from_features中传True会raise ValueErrorfactory.py:209-223。- 默认值:
sandbox=True、loop_detection=True,其余特性默认Falsefeatures.py:27-34。 @Next(anchor)/@Prev(anchor)装饰器给中间件类打_next_anchor/_prev_anchor标记,供_insert_extra定位 features.py:42-63。
prompt 组装 — apply_prompt_template
职责:把角色、SOUL、技能、延迟工具、子代理指令拼成完全静态的系统提示词。
关键函数:apply_prompt_template(subagent_enabled, max_concurrent_subagents, *, agent_name, available_skills, app_config) prompt.py:768-823。
要点:
- 用
SYSTEM_PROMPT_TEMPLATE.format(...)填入agent_name、soul、self_update_section、skills_section、deferred_tools_section、subagent_section、subagent_reminder/subagent_thinking、acp_sectionprompt.py:813-823。 - 仅当
subagent_enabled=True时才拼接子代理编排段(含并发上限n)prompt.py:776-796。 - 提示词刻意保持「完全静态」:当前日期与 memory 由
DynamicContextMiddleware以<system-reminder>注入首个 HumanMessage,以最大化前缀缓存复用 prompt.py:809-812,与_build_middlewares中追加DynamicContextMiddleware对应 agent.py:262-265。
Data Flow
Implementation Details
create_deerflow_agent 入口的三段互斥校验,是理解「middleware 完全接管」语义的关键 factory.py:110-131:
python
if middleware is not None and features is not None:
raise ValueError("Cannot specify both 'middleware' and 'features'...")
if middleware is not None and extra_middleware:
raise ValueError("Cannot use 'extra_middleware' with 'middleware' (full takeover).")
if extra_middleware:
for mw in extra_middleware:
if not isinstance(mw, AgentMiddleware):
raise TypeError(...)
if middleware is not None:
effective_middleware = list(middleware) # 完全接管,原样使用
else:
feat = features or RuntimeFeatures() # 默认特性集
effective_middleware, extra_tools = _assemble_from_features(feat, ...)解读:middleware 与 features/extra_middleware 三者两两互斥。给定 middleware 时跳过一切自动装配;否则用 features(缺省退化为默认 RuntimeFeatures())逐项构建。
_assemble_from_features 内对每个特性的三态处理统一遵循「AgentMiddleware 实例直接用 / 否则建内置默认」的模式,而 summarization、guardrail 因缺内置默认会显式报错 factory.py:218-223:
python
if feat.summarization is not False:
if isinstance(feat.summarization, AgentMiddleware):
chain.append(feat.summarization)
else:
raise ValueError("summarization=True requires a custom AgentMiddleware instance ...")链尾不变量:ClarificationMiddleware 必须在最后。_insert_extra 用 @Next 可能把它顶离链尾,因此插入后会强制把它挪回末位 factory.py:289-298。make_lead_agent 侧同样把 ClarificationMiddleware() 作为最后一个 append agent.py:316-318。
make_lead_agent 的中间件链分两段:build_lead_runtime_middlewares 提供共享基座(ThreadData → Sandbox → Uploads → DanglingToolCall → LLMError → Guardrail → SandboxAudit → ToolError)tool_error_handling_middleware.py:82-136,_build_middlewares 在其后顺序追加 lead 专属中间件并以 ClarificationMiddleware 收尾 agent.py:258-318。中间件清单细节见第 12 章。
扩展指南
用 create_deerflow_agent + RuntimeFeatures 构建自定义 agent 的最小模板:
python
from deerflow.agents import create_deerflow_agent, RuntimeFeatures
from deerflow.models import create_chat_model
model = create_chat_model(name="my-model", thinking_enabled=False)
agent = create_deerflow_agent(
model=model,
tools=[my_tool], # 用户工具,feature 注入工具会去重追加
system_prompt="You are a helper.", # None 则用最小默认
features=RuntimeFeatures(
sandbox=True, # 默认 True
memory=True, # True=用内置 MemoryMiddleware
loop_detection=True, # 默认 True
vision=False,
subagent=False,
),
plan_mode=False, # True 则插入 TodoMiddleware
name="my-agent",
)约束清单(从 create_deerflow_agent / _assemble_from_features 校验逻辑读出):
middleware与features互斥;同时传 →ValueErrorfactory.py:110-111。middleware与extra_middleware互斥(middleware即完全接管)→ValueErrorfactory.py:112-113。extra_middleware每项必须是AgentMiddleware实例,否则TypeErrorfactory.py:114-117。features.summarization=True/features.guardrail=True不被支持,必须传自定义AgentMiddleware实例或保持False→ValueErrorfactory.py:209-223。- 同一中间件不能同时带
@Next和@Prev;两个 extra 锚到同一 anchor(同向或反向)会冲突报错 factory.py:322-342。 - 锚点无法解析或 extra 之间循环依赖 →
ValueErrorfactory.py:371-378。 vision开启且sandbox不为False时,会自动追加view_image_tool;subagent开启会追加task_tool;ClarificationMiddleware总会追加ask_clarification_toolfactory.py:249-287。
Common Pitfalls / Tips
- 不要在
create_deerflow_agent里既传middleware又传features:middleware是完全接管语义,会直接报错而非合并。需要在自动链上加东西时用extra_middleware+@Next/@Prev。 summarization/guardrail没有内置默认:在 SDK 工厂里要这两个能力必须自己构造中间件实例传入;make_lead_agent侧的 summarization 由_create_summarization_middleware从配置创建 agent.py:53-112,guardrail 由_build_runtime_middlewares从app_config.guardrails创建 tool_error_handling_middleware.py:99-120。thinking_enabled默认True但会被模型能力降级:若model_config.supports_thinking为假,_make_lead_agent会打 warning 并把thinking_enabled置Falseagent.py:379-381。make_lead_agent签名固定:它是 LangGraph Server 注册的图工厂,改签名会破坏langgraph.json注册 agent.py:343-344。- 系统提示词要保持静态:别把 per-turn 变化的内容塞进
apply_prompt_template,会破坏前缀缓存;动态内容走DynamicContextMiddlewareprompt.py:809-812。
References
backend/packages/harness/deerflow/agents/lead_agent/agent.py— make_lead_agent / _make_lead_agent / _build_middlewares L240-446backend/packages/harness/deerflow/agents/factory.py— create_deerflow_agent / _assemble_from_features L61-379backend/packages/harness/deerflow/agents/features.py— RuntimeFeatures / Next / Prev L14-63backend/packages/harness/deerflow/agents/lead_agent/prompt.py— apply_prompt_template L768-823backend/packages/harness/deerflow/agents/middlewares/tool_error_handling_middleware.py— build_lead_runtime_middlewares L70-136backend/packages/harness/deerflow/agents/__init__.py— 公共导出与缓存预热 L1-20backend/langgraph.json— graphs.lead_agent 注册 L8-10
Related Pages
| 页面 | 关系 |
|---|---|
| 09-Harness与App分层边界.md | 上游:解释 harness/app 分层,本章工厂位于 harness 一侧 |
| 11-ThreadState与状态管理.md | 下游:两层工厂均以 state_schema=ThreadState 调 create_agent,状态结构详见此页 |
| 12-中间件链机制.md | 下游:本章只讲装配骨架,18 个中间件清单与顺序细节见此页 |
| 05-模型配置与Model工厂.md | 关联:_resolve_model_name + create_chat_model 的模型解析与构造细节 |
| 20-工具系统与内置工具.md | 关联:get_available_tools 如何聚合 sandbox/built-in/MCP/subagent 工具 |