Skip to content

Lead Agent 设计与图构建

本章目标:

  1. 看懂 make_lead_agent(config) 工厂如何在一次请求里动态拼出一个 Agent
  2. 掌握 ThreadState 状态 schema 与它的两个自定义 reducer
  3. 理解 config.configurable 这组运行时开关如何驱动模型/中间件/工具的选择

TL;DR

Lead Agent 不是一个固定对象,而是 make_lead_agent(config) 在每次请求时按 config.configurable 动态装配的:解析 model_name(请求 → custom agent 配置 → 全局默认)、按 thinking_enabled/is_plan_mode/subagent_enabled 选模型与中间件、get_available_tools() 装工具、apply_prompt_template() 注入技能/记忆/子代理指令,最后 create_agent(model, tools, middleware, system_prompt, state_schema=ThreadState)ThreadState 在 LangChain AgentState 上扩展 sandbox/thread_data/artifacts/todos/viewed_images,artifacts 与 viewed_images 用自定义 reducer 合并。

Overview(为什么 Agent 是"工厂"而不是单例)

一个对话可能要求:这次用 doubao 开 thinking、下次用 kimi、A 用户走默认 agent、B 用户走自定义"产品专家"agent、有的开 plan mode 有的开子代理。如果 Agent 是启动时建好的单例,这些组合就得提前枚举或运行时改全局状态——既不可扩展也不线程安全。

deer-flow 的解法是 per-request factory:make_lead_agent(config) 注册在 langgraph.json/aegra.json,Aegra/LangGraph 每次 run 调一次,从 config.configurable 读出这次请求的所有开关,当场拼一个专属 Agent。模型、工具集、中间件链、系统提示全是这次请求的快照,请求之间互不影响。这也是为什么前端切模型、开 plan mode 这些操作能即时生效——它们只是改 config.configurable

Architecture:make_lead_agent 装配流程

agents/lead_agent/agent.py:290-398 的核心逻辑:

步骤代码说明Source
读 configurable/contextcfg = config.get("configurable",{})所有运行时开关入口agent.py:295-305
直读 user_id/thread_idcfg.get("user_id") / cfg.get("thread_id")factory 阶段 langgraph.config.get_config() 不可用,前端 hooks 显式注入agent.py:307-315
加载 custom agent 配置load_agent_config(agent_name)非 bootstrap 时按 agent_name 取定制配置agent.py:325-327
解析最终模型_resolve_model_name(requested or agent_model)请求 → agent 配置 → 全局默认,未知名 fallbackagent.py:329-336
thinking 兜底model 不支持 thinking 时降级发 warning 不报错agent.py:337-339
注入 trace metadataconfig["metadata"].update({...})LangSmith trace 打标agent.py:352-365
组装并返回create_agent(model, tools, middleware, system_prompt, state_schema=ThreadState)bootstrap 与默认两条路径agent.py:367-398

_resolve_model_name(agent.py:30)三级优先级:请求 model_name/model > custom agent 配置的 model > 全局默认(config.yaml models[0])。is_bootstrap(agent.py:367)是创建自定义 agent 的特殊流程,用最小 prompt + setup_agent 工具,可用技能只 {"bootstrap"}

Components:ThreadState 状态 schema

agents/thread_state.py:48-55 在 LangChain AgentState 上扩展:

字段类型含义reducerSource
sandboxSandboxState当前 sandbox_id默认覆盖thread_state.py:6-7,49
thread_dataThreadDataStateworkspace/uploads/outputs 路径默认覆盖thread_state.py:10-13,50
titlestr自动生成的对话标题默认覆盖thread_state.py:51
artifactslist[str]产出文件merge_artifacts(去重合并)thread_state.py:21-28,52
todoslistplan mode 任务默认覆盖thread_state.py:53
uploaded_fileslist[dict]已上传文件默认覆盖thread_state.py:54
viewed_imagesdictimage_path →merge_viewed_images(空 dict 清空)thread_state.py:31-46,55

两个自定义 reducer 是关键:merge_artifacts(thread_state.py:21-28)合并并去重——因为多个中间件/工具都可能往 artifacts 追加;merge_viewed_images(thread_state.py:31-46)有特例:传空 dict {} 表示"清空所有已看图片"(用于 vision 内存管理)。LangGraph 用 reducer 决定状态怎么合并,选对 reducer 是状态机正确性的核心。

Data Flow:configurable 驱动的一次请求

config.configurable 的关键开关(agent.py:298-305):thinking_enabled(默认 True)、reasoning_effortmodel_name/modelis_plan_mode(开 TodoMiddleware)、subagent_enabled(开 task 工具)、max_concurrent_subagents(默认 3)、agent_name(custom agent)、is_bootstrapuser_id/thread_id 由前端 core/threads/hooks.ts 显式注入到 configurable——因为 graph factory 阶段 langgraph.config.get_config() 不可用(agent.py:307-315 注释,2026-05-09 D' read gate)。

扩展指南:自定义 Agent

a-cdm 支持 custom agent(前端"智能专家"创建流程)。最小约束:

yaml
# agents/<agent-name>/config.yaml(由 load_agent_config 读)
model: kimi-k2.6          # 可选,不写则用全局默认
tool_groups: [web, file:read]   # 可选,限制工具组
skills: ["weekly-report"]       # 可选,限制可用技能(空=全禁,省略=全开)

约束(从代码读出):

  • agent_name 经 validate_agent_name(agent.py:305)校验;红线规定 user-/project- 前缀是平台专用(前端创 agent UI 拦双前缀 422)。
  • custom agent 的 model 仍走 _resolve_model_name 三级 fallback,不存在的模型名会 fallback 到默认而非报错。
  • bootstrap agent 是创建流程专用,用 setup_agent 工具 + {"bootstrap"} 技能,不要直接复用其路径。

Common Pitfalls / 实战 Tips

  • Agent 是 per-request 拼的:别期望"改一次全局生效"。改模型/技能要走 configurable 或 custom agent config。
  • user_id 必须前端显式注入 configurable:factory 阶段拿不到 langgraph config,靠 cfg.get("user_id")(agent.py:311)。前端 hooks 不注入则记忆/项目上下文 prompt 注入失效。
  • thinking 不支持会静默降级:model 不支持 thinking 时只 warning + 关 thinking(agent.py:337-339),不是报错。
  • artifacts 用了去重 reducer:多次 append 同一 artifact 不会重复,但依赖"覆盖"语义的代码会出错——它是合并不是替换。

References

  • deer-flow/backend/packages/harness/deerflow/agents/lead_agent/agent.py:290-398 — make_lead_agent 工厂(本章主源)
  • deer-flow/backend/packages/harness/deerflow/agents/lead_agent/agent.py:30-91 — 模型解析/摘要/todo 中间件构造
  • deer-flow/backend/packages/harness/deerflow/agents/thread_state.py:1-55 — ThreadState + 两个 reducer
  • deer-flow/backend/packages/harness/deerflow/agents/lead_agent/prompt.py — apply_prompt_template(技能/记忆/子代理注入)
  • deer-flow/backend/packages/harness/deerflow/agents/__init__.py:1-18 — agents 包导出
  • deer-flow/backend/packages/harness/deerflow/agents/lead_agent/agent.py:216-289_build_middlewares 装配
PageRelationship
Agent 中间件链机制本章 _build_middlewares 在该章逐个展开
MCP 集成与工具装配本章 get_available_tools 在该章详解
长期记忆机制本章 apply_prompt_template 注入的记忆在该章
deer-flow 引擎配置体系本章模型解析依赖该章 config.yaml models
AI 消息流与 SSE 基础设施本章 configurable 由该章前端 hooks 注入

公司内部参考 · 由 claude-wiki-gen 基于源码自动生成的二次分析