主题
10.12 子图与多智能体全谱
07 节用状态栈演示了控制权交接。16 节旅行助手则是混合结构:机票、酒店、游览助理平铺在父图里,租车助理与并行比价是真子图。下面先讲清子图,再按当前官方分类重新认识多智能体。
1. 真子图:图中有图
平铺大图像一张贴满全公司工位的平面图——电话、工位、门牌全在一张纸上,人一多就眼花。真子图像组织架构图:总公司只画到“部门”,每个部门内部有自己的小流程,公司层面只关心“找哪个部门、结果如何”。
子图是一个独立编译的图,直接作为父图的一个节点:
python
flight_builder = StateGraph(FlightState)
# ... add_node / add_edge ...
flight_graph = flight_builder.compile()
parent_builder = StateGraph(ParentState)
parent_builder.add_node("flight_department", flight_graph)
parent_builder.add_edge(START, "flight_department")
parent_graph = parent_builder.compile()两条协作规矩:
- 共享状态键会自动透传。 父图与子图都有
messages,数据会流过去;父图没有的键(例如子图内部的fare_rules)留在子图里,互不打扰。这正是封装的价值。 - 跨层跳转用
Command。 子图节点想直接跳回父图某个节点,而不是原路返回:Command(goto=..., graph=Command.PARENT)。共享键在父图里要有对应的 reducer,否则合并会失败。
怎么选:航班助理要独立测试、要给另一个项目复用、内部状态想对外隐藏——拆成子图。几十个节点的小项目,平铺往往更直观。05 节的 get_graph(xray=True) 就是用来透视子图内部的。
一个子图最好对应能独立说明和测试的业务能力,例如“租车预订”或“退款审核”。若只是把几个相邻节点包起来,却仍然依赖父图大量内部字段,封装并没有降低理解成本。字段名不同时,在边界节点显式转换,不要让内部实现泄漏到父图所有节点。
子图里的 interrupt() 恢复时,父节点和子节点都会从包含该中断的那个节点开头重跑,不是从函数中间恢复。副作用仍然要幂等。
2. 三个重点实现:放回当前官方分类里理解
配套 demo 保留三张可运行图,但它们不是三种互相平级的“官方编队”:Router 是官方模式;Supervisor 应放到 Subagents 下理解;Planner-Executor-Reviewer 是用 LangGraph 画出的 Custom workflow。
2.1 Router:先分类,再进专科
医院分诊或银行叫号屏:先打标签,再进对应窗口。分诊台自己不办事,只负责把人送到对的地方。
适用: 需求类型清楚、各分支互不重叠。三种实现里它最便宜、最可控——一次廉价分类,就能把大部分流量分给轻量分支。
text
┌-> SQL 专员
用户 -> 分诊台 ├-> RAG 专员
└-> Code 专员2.2 Subagents(Supervisor):主 Agent 调用专家
项目经理接下“写一份行业报告”,把调研发给研究员、把初稿发给写作者。专员干完交回经理,经理再派下一单,直到汇总交差。专员之间不直接对话,沟通都经过经理。
适用: 任务需要拆分,专家各有独立 Prompt、工具和上下文窗口,而且专家不必长期直接接待用户。当前官方推荐的基本实现:把每个子 Agent 包成一个工具,主 Agent 决定调用谁、给多少上下文、如何汇总。
text
┌-> 调研专员 ─┐
用户 -> 主管 ├-> 写作专员 ─┼-> 主管 -> 汇总
└-> …… ─┘配套 demo 为了让控制流看得见,使用了“主管节点 → 专家节点 → 主管节点”的显式循环。真实项目更常把专家封装成工具。两者的共同点:主 Agent 集中调度。它与 09 节 Orchestrator-Worker 的差别是:后者通常一次拆完并行收活;Supervisor 可以根据中间结果跨多轮继续派。
2.3 Custom workflow:Planner-Executor-Reviewer
出书三部曲:编辑定大纲,写手照大纲写,主编审稿——不满意就带批注打回,直到通过。主编手里必须有“最多改 N 版”的死规矩,否则写手会被无限打回。
适用: 对质量有硬要求、允许重试(代码生成、报告撰写、数据处理管线)。Reviewer 的“打回重做”是一条显式循环边——控制逻辑写在图里,不必全塞进 Prompt。
text
需求 -> Planner -> Executor -> Reviewer
↑ │
└── needs_fix ┘
│ pass
v
交付2.4 Handoffs 与 Skills:另外两条主线
工作台没有再复制两张大图,选型时却必须认识它们:
- Handoffs(接力): 当前活跃 Agent 写入状态,把控制权交给另一个 Agent。像客服工单转接,接手者直接继续和用户说话。07 节状态栈就是入门版。
- Skills(技能包): 仍由一个 Agent 接待用户,只在需要时加载某领域的提示词与知识。像医生留在同一诊室,遇到眼科问题才翻开眼科手册。领域多但工具不复杂时,比再造一群 Agent 更省上下文。
- 并行多 Agent 通常落在 Router 或 Subagents 内,通过一次多工具调用或
Send并发,不必另造一个编队名字。
3. 当前官方五种模式
LangChain 多智能体文档 以五种模式组织内容:
| 模式 | 控制权怎么走 | 直观说明 | 适合场景 |
|---|---|---|---|
| Subagents | 主 Agent 把专家当工具调用,结果回到主 Agent | 项目经理打专家分机 | 多领域、要上下文隔离、专家不直接长期接客 |
| Handoffs | 工具修改活跃 Agent / 状态,控制权交给接手者 | 客服工单转接 | 分阶段、多轮、接手者需要直接和用户交流 |
| Skills | 单 Agent 按需加载专业提示词和知识 | 一个医生按病种翻手册 | 专业知识多,但没必要维护多个独立 Agent |
| Router | 分类后派给零个、一个或多个专家,再统一合成 | 医院分诊 | 输入类别清楚,可并行查询多个垂直领域 |
| Custom workflow | 用 LangGraph 明确画节点、循环和并行 | 自建流水线 | 确定性步骤、复杂拓扑、或混合多种模式 |
07 / 16 节的“状态栈 + 转交工具”在用户体验上更接近 Handoffs:某个专业助理会成为当前活跃角色。12b 的主管循环图则是在教学层面展示 Subagents / Supervisor 的集中调度。不要只看“都用了转交工具”就把两者混成同一种架构。
多智能体也不是越分散越好。只有当角色拥有不同工具、规则或独立上下文时,拆分才有明显价值;单纯换一段提示词,用一个节点按需加载说明就够了。
上下文工程:真正难的不是画箭头
经理派活时,不会把公司十年聊天记录整车倒给专家,也不会只说“你看着办”。每次交接至少回答三个问题:
- 专家为什么会被选中: 工具名和描述是否把能力边界写清;
- 专家收到什么: 原始用户问题、筛选后的消息、结构化状态,还是一段摘要;
- 专家交回什么: 完整消息历史、最终答案,还是给主管的一份短报告。
传得太少,专家缺背景;传得太多,Token 暴涨、隐私外泄、工具历史还可能配对失败。官方把这件事称为 context engineering,它往往比“选哪种编队名字”更影响质量。第九章 9.10 的三类上下文、三数据源,在这里变成了多智能体交接协议。
4. 生态库:先看维护者现在怎么建议
| 库 | 给你什么 |
|---|---|
| langgraph-supervisor-py | 兼容已有 Supervisor 项目;维护者目前建议多数新项目直接用手写“子 Agent 作为工具”,以便控制上下文 |
| langgraph-swarm-py | Handoffs / Swarm 风格的参考实现 |
| langchain-mcp-adapters | 把 MCP 服务器上的工具挂给 Agent |
| agentevals | 多智能体轨迹评测:对比实际执行轨迹与期望轨迹 |
现成库能少写样板代码,却不会替你解决上下文裁剪、权限边界、工具回执配对和评测。新项目先按官方五种模式选型,只有库确实匹配需求时再引入。
📁 本节示例代码:
- code/examples/12_subgraphs_demo.py —— 真子图嵌套 + xray 透视;
- code/examples/12b_multiagent_paradigms_demo.py —— Router / Subagents / Custom workflow 三张可运行图。
扩展阅读
- Subgraphs:use-subgraphs
- Workflows and agents:workflows-agents
- Command 跨父图跳转:graph-api
- 多智能体总览:multi-agent
- Subagents / Handoffs / Skills: subagents · handoffs · skills
下一节: 06 节的 interrupt_before 是编译期写死的刹车点。官方更推荐节点内动态中断 interrupt()——想踩就踩,还能带回一份待审批数据包。