《LangGraph 设计与实现》完整目录
- 前言
- 第1章 为什么需要理解 LangGraph
- 第2章 架构总览
- 第3章 StateGraph 图构建 API
- 第4章 Channel 状态管理与 Reducer
- 第5章 图编译:从 StateGraph 到 CompiledStateGraph
- 第6章 Pregel 执行引擎
- 第7章 任务调度与并行执行
- 第8章 Checkpoint 持久化
- 第9章 中断与人机协作
- 第10章 Command 与高级控制流
- 第11章 子图与嵌套
- 第12章 Send 与动态并行
- 第13章 流式输出与调试
- 第14章 Runtime 与 Context
- 第15章 Store 与长期记忆
- 第16章 预构建 Agent 组件
- 第17章 多 Agent 模式实战(当前)
- 第18章 设计模式与架构决策
第17章 多 Agent 模式实战
17.1 引言
单个 Agent 能力有限——它只有一组工具、一种提示、一个执行循环。当任务复杂度上升时(如"帮我分析这家公司的财报,然后写一份投资建议书"),单个 Agent 要么因为工具太多导致 LLM 选择困难,要么因为系统提示过长影响推理质量。多 Agent 系统通过分工协作解决这个问题:不同的 Agent 负责不同的能力域,通过明确的通信机制协同完成复杂任务。
LangGraph 的底层基础设施——StateGraph、Send、Command、子图——天然支持多 Agent 模式的构建。图的节点可以是子图(即嵌套的 Agent),条件边可以实现动态路由,Send 支持一对多的任务分发,Command 支持跨图的状态更新和导航。
本章将系统介绍基于 LangGraph 实现的四种典型多 Agent 架构:Supervisor(监督者)、Swarm(蜂群)、分层 Agent 和协作 Agent。每种模式都有其适用场景和权衡,我们会结合源码和实战代码深入分析它们的设计原理。
:::tip 本章要点
- Supervisor 模式——中央调度者分配任务给专业 Agent
- Swarm 模式——Agent 之间点对点的控制权交接
- 分层 Agent——多层级的 Supervisor 树形结构
- 协作 Agent——共享状态的对等协作
- 基于 LangGraph 原语实现各种模式的核心技术 :::
17.2 多 Agent 的核心抽象
17.2.1 Agent 作为子图
在 LangGraph 中,一个 Agent 本质上就是一个 CompiledStateGraph。多 Agent 系统就是一个图中嵌套了多个子图:
# 每个 Agent 是一个独立的图
research_agent = create_react_agent(model, research_tools, name="researcher")
writer_agent = create_react_agent(model, writing_tools, name="writer")
# 多 Agent 系统是一个包含子图的父图
builder = StateGraph(TeamState)
builder.add_node("researcher", research_agent)
builder.add_node("writer", writer_agent)
17.2.2 通信机制的三种模式
graph TB
subgraph "模式 1:共享状态"
A1[Agent A] -->|写入状态| S1[共享 State]
S1 -->|读取状态| B1[Agent B]
end
subgraph "模式 2:Command 导航"
A2[Agent A] -->|"Command(goto='B')"| B2[Agent B]
end
subgraph "模式 3:Send 分发"
Router[路由器] -->|"Send('A', task1)"| A3[Agent A]
Router -->|"Send('B', task2)"| B3[Agent B]
end
17.3 Supervisor 模式
17.3.1 架构概述
Supervisor 模式是最直观的多 Agent 架构:一个"主管"Agent 接收用户请求,分析任务需求,然后将子任务分配给专业 Agent。每个专业 Agent 完成后将结果返回给 Supervisor,Supervisor 决定是否需要更多工作。
graph TB
User[用户] --> Sup[Supervisor Agent]
Sup -->|"分配研究任务"| RA[Research Agent]
Sup -->|"分配写作任务"| WA[Writer Agent]
Sup -->|"分配代码任务"| CA[Coder Agent]
RA -->|"研究结果"| Sup
WA -->|"文章草稿"| Sup
CA -->|"代码片段"| Sup
Sup -->|"最终回复"| User
style Sup fill:#ffe6e6,stroke:#333,stroke-width:2px
17.3.2 实现方式
from typing import Annotated, Literal, TypedDict
from langchain_core.messages import BaseMessage, HumanMessage
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langgraph.prebuilt import create_react_agent
from langgraph.types import Command
class SupervisorState(TypedDict):
messages: Annotated[list[BaseMessage], add_messages]
next_agent: str
# 创建专业 Agent
research_agent = create_react_agent(model, research_tools, name="researcher")
writer_agent = create_react_agent(model, writing_tools, name="writer")
def supervisor_node(state: SupervisorState) -> Command:
"""Supervisor 决定下一步交给哪个 Agent"""
response = supervisor_llm.invoke([
SystemMessage(content="""你是一个任务分配主管。
分析用户请求,决定交给哪个 Agent:
- researcher: 负责信息检索和数据分析
- writer: 负责内容创作和文档编写
- FINISH: 任务完成,返回最终结果"""),
*state["messages"]
])
# 解析 LLM 的决策
next_agent = parse_decision(response.content)
if next_agent == "FINISH":
return Command(goto=END, update={"messages": [response]})
else:
return Command(
goto=next_agent,
update={"messages": [response]}
)
# 构建 Supervisor 图
builder = StateGraph(SupervisorState)
builder.add_node("supervisor", supervisor_node)
builder.add_node("researcher", research_agent)
builder.add_node("writer", writer_agent)
builder.add_edge(START, "supervisor")
builder.add_edge("researcher", "supervisor") # 完成后返回 Supervisor
builder.add_edge("writer", "supervisor")
graph = builder.compile()
17.3.3 Supervisor 的关键设计
- 中心化决策:Supervisor 是唯一的决策者,专业 Agent 只负责执行
- 循环结构:Agent 完成后总是返回 Supervisor,由 Supervisor 决定下一步
- Command 导航:使用
Command(goto=next_agent)实现动态路由
17.3.4 适用场景与局限
适用场景:
- 子任务之间有明确的先后依赖
- 需要中央协调来决定任务顺序
- Agent 数量较少(3-5 个)
局限:
- Supervisor 是单点瓶颈
- 每次决策都需要额外的 LLM 调用
- 子任务之间无法直接通信
17.4 Swarm 模式
17.4.1 架构概述
Swarm 模式(蜂群模式)没有中央调度者。每个 Agent 在完成自己的工作后,可以直接将控制权交给另一个 Agent。这类似于客服系统中的"转接"——当前 Agent 判断用户的需求超出自己的能力范围时,主动转接给更合适的 Agent。
graph LR
User[用户] --> A1[接待 Agent]
A1 -->|"转接"| A2[技术支持 Agent]
A2 -->|"转接"| A3[退款处理 Agent]
A3 -->|"完成"| User
A1 -.->|"也可以直接完成"| User
A2 -.->|"也可以转回"| A1
style A1 fill:#e6f3ff,stroke:#333
style A2 fill:#f3ffe6,stroke:#333
style A3 fill:#ffe6f3,stroke:#333
17.4.2 实现方式
Swarm 的核心是"handoff"工具——每个 Agent 有一个特殊工具用于将控制权转交给其他 Agent:
from langchain_core.tools import tool
from langgraph.types import Command
# 创建 handoff 工具
def create_handoff_tool(target_agent: str, description: str):
@tool(name=f"transfer_to_{target_agent}")
def handoff() -> Command:
f"""将对话转交给 {target_agent}。{description}"""
return Command(goto=target_agent)
return handoff
# 定义 Agent 和它们的 handoff 能力
greeting_tools = [
create_handoff_tool("tech_support", "当用户有技术问题时转交"),
create_handoff_tool("billing", "当用户有账单问题时转交"),
]
tech_tools = [
search_docs_tool,
create_handoff_tool("greeting", "当问题解决后转回"),
]
billing_tools = [
check_balance_tool,
process_refund_tool,
create_handoff_tool("greeting", "当问题解决后转回"),
]
# 创建各 Agent
greeting_agent = create_react_agent(model, greeting_tools, name="greeting")
tech_agent = create_react_agent(model, tech_tools, name="tech_support")
billing_agent = create_react_agent(model, billing_tools, name="billing")
# 构建 Swarm 图
builder = StateGraph(SwarmState)
builder.add_node("greeting", greeting_agent)
builder.add_node("tech_support", tech_agent)
builder.add_node("billing", billing_agent)
builder.add_edge(START, "greeting") # 入口始终是接待 Agent
# 每个 Agent 可以通过 Command 跳转到任意其他 Agent
# 或者到达 END 结束对话
graph = builder.compile()
17.4.3 Swarm 的关键设计
- 去中心化:没有 Supervisor,Agent 之间是对等关系
- Handoff 工具:通过返回
Command(goto=...)实现控制权转移 - 工具即路由:LLM 通过选择"转交"工具来决定路由
17.4.4 适用场景与局限
适用场景:
- 客服系统、对话路由
- Agent 之间是专业领域的分工
- 同一时间只有一个 Agent 活跃
局限:
- 难以协调并行任务
- 可能出现循环转交
- 需要每个 Agent 都了解其他 Agent 的能力
17.5 分层 Agent
17.5.1 架构概述
分层 Agent 是 Supervisor 模式的扩展:多个 Supervisor 形成树形结构,每个 Supervisor 管理一组专业 Agent 或下级 Supervisor。这种模式适用于大规模复杂任务的分解。
graph TB
CEO[CEO Agent<br/>总调度] --> PM1[项目经理 Agent<br/>研究方向]
CEO --> PM2[项目经理 Agent<br/>创作方向]
PM1 --> R1[数据分析 Agent]
PM1 --> R2[文献检索 Agent]
PM2 --> W1[文案撰写 Agent]
PM2 --> W2[校对编辑 Agent]
style CEO fill:#ffe6e6,stroke:#333,stroke-width:2px
style PM1 fill:#fff3e6,stroke:#333
style PM2 fill:#fff3e6,stroke:#333
17.5.2 实现方式
# 第一层:专业 Agent
data_agent = create_react_agent(model, data_tools, name="data_analyst")
search_agent = create_react_agent(model, search_tools, name="searcher")
writing_agent = create_react_agent(model, writing_tools, name="writer")
editing_agent = create_react_agent(model, editing_tools, name="editor")
# 第二层:子 Supervisor
def research_supervisor(state):
"""研究方向的中层主管"""
response = llm.invoke([
SystemMessage(content="你管理数据分析和文献检索。决定分配给谁。"),
*state["messages"]
])
next_agent = parse_decision(response.content)
return Command(goto=next_agent, update={"messages": [response]})
research_team = StateGraph(TeamState)
research_team.add_node("supervisor", research_supervisor)
research_team.add_node("data_analyst", data_agent)
research_team.add_node("searcher", search_agent)
research_team.add_edge(START, "supervisor")
research_team.add_edge("data_analyst", "supervisor")
research_team.add_edge("searcher", "supervisor")
research_graph = research_team.compile(name="research_team")
# 类似地创建 writing_team...
# 第三层:顶层 Supervisor
top_builder = StateGraph(TopState)
top_builder.add_node("ceo", ceo_supervisor)
top_builder.add_node("research_team", research_graph) # 子图作为节点
top_builder.add_node("writing_team", writing_graph)
top_builder.add_edge(START, "ceo")
top_builder.add_edge("research_team", "ceo")
top_builder.add_edge("writing_team", "ceo")
top_graph = top_builder.compile()
17.5.3 子图作为节点的运行时行为
当 Pregel 执行到子图节点时:
- 子图的输入从父图状态中提取
- 子图内部按自己的超步循环运行
- 子图的 checkpoint_ns 自动嵌套(如
research_team:supervisor:task_id) - 子图完成后,输出合并回父图状态
sequenceDiagram
participant Parent as 父图 (CEO)
participant Sub as 子图 (Research Team)
participant Agent as 子 Agent (Data Analyst)
Parent->>Sub: 输入子任务
Note over Sub: checkpoint_ns = "research_team"
Sub->>Agent: Supervisor 分配任务
Note over Agent: checkpoint_ns = "research_team:data_analyst:task_id"
Agent-->>Sub: 返回结果
Sub-->>Parent: 合并输出到父图状态
17.6 协作 Agent
17.6.1 架构概述
协作 Agent 模式中,多个 Agent 共享同一个状态空间,按照预定义的顺序或条件轮流执行。每个 Agent 都能读取和修改共享状态,形成一种"接力赛"式的协作。
graph LR
Start[START] --> P[规划 Agent]
P --> R[研究 Agent]
R --> W[写作 Agent]
W --> Rev[审核 Agent]
Rev -->|需要修改| R
Rev -->|通过| End[END]
style P fill:#e6f3ff
style R fill:#f3ffe6
style W fill:#ffe6f3
style Rev fill:#fff3e6
17.6.2 实现方式
class CollaborativeState(TypedDict):
messages: Annotated[list[BaseMessage], add_messages]
plan: str
research_results: list[str]
draft: str
review_feedback: str
iteration: int
def planner(state: CollaborativeState) -> dict:
"""规划 Agent:分析需求,制定计划"""
response = planner_llm.invoke([
SystemMessage(content="你是一个项目规划者。分析用户需求,制定详细的执行计划。"),
*state["messages"]
])
return {"plan": response.content, "messages": [response]}
def researcher(state: CollaborativeState) -> dict:
"""研究 Agent:根据计划收集信息"""
response = research_llm.invoke([
SystemMessage(content=f"执行以下研究计划:{state['plan']}"),
*state["messages"]
])
return {"research_results": [response.content], "messages": [response]}
def writer(state: CollaborativeState) -> dict:
"""写作 Agent:基于研究结果撰写"""
context = "\n".join(state["research_results"])
response = writer_llm.invoke([
SystemMessage(content=f"基于以下研究结果撰写:\n{context}"),
*state["messages"]
])
return {"draft": response.content, "messages": [response]}
def reviewer(state: CollaborativeState) -> dict:
"""审核 Agent:评审草稿质量"""
response = reviewer_llm.invoke([
SystemMessage(content=f"审核以下草稿的质量:\n{state['draft']}"),
*state["messages"]
])
return {
"review_feedback": response.content,
"messages": [response],
"iteration": state.get("iteration", 0) + 1
}
def review_decision(state: CollaborativeState) -> str:
"""决定是否需要修改"""
if "approved" in state["review_feedback"].lower():
return END
if state.get("iteration", 0) >= 3:
return END # 最多迭代 3 次
return "researcher"
builder = StateGraph(CollaborativeState)
builder.add_node("planner", planner)
builder.add_node("researcher", researcher)
builder.add_node("writer", writer)
builder.add_node("reviewer", reviewer)
builder.add_edge(START, "planner")
builder.add_edge("planner", "researcher")
builder.add_edge("researcher", "writer")
builder.add_edge("writer", "reviewer")
builder.add_conditional_edges("reviewer", review_decision)
graph = builder.compile()
17.6.3 协作的关键设计
- 共享状态:所有 Agent 读写同一个
CollaborativeState - 专用字段:每个 Agent 有自己的输出字段(plan、research_results、draft、review_feedback)
- 迭代循环:reviewer 可以触发新一轮的 research -> write -> review
17.7 模式对比与选择
17.7.1 四种模式对比
graph TB
subgraph Supervisor
S_Hub[中央调度者]
S_A[Agent A]
S_B[Agent B]
S_Hub --> S_A
S_Hub --> S_B
S_A --> S_Hub
S_B --> S_Hub
end
subgraph Swarm
SW_A[Agent A]
SW_B[Agent B]
SW_C[Agent C]
SW_A <-->|handoff| SW_B
SW_B <-->|handoff| SW_C
end
subgraph 分层
H_Top[顶层 Sup]
H_Mid1[中层 Sup 1]
H_Mid2[中层 Sup 2]
H_A[Agent A]
H_B[Agent B]
H_Top --> H_Mid1
H_Top --> H_Mid2
H_Mid1 --> H_A
H_Mid2 --> H_B
end
subgraph 协作
C_A[Agent A] --> C_B[Agent B]
C_B --> C_C[Agent C]
C_C -.->|可选循环| C_A
end
| 维度 | Supervisor | Swarm | 分层 | 协作 |
|---|---|---|---|---|
| 决策方式 | 中央决策 | 自主决策 | 层级决策 | 预定义顺序 |
| 通信模式 | 星形(hub-spoke) | 点对点 | 树形 | 链式/环形 |
| 并行能力 | 可通过 Send | 单一活跃 | 层内可并行 | 可通过 Send |
| 适用规模 | 小型(3-5 Agent) | 中型 | 大型 | 任意 |
| 复杂度 | 低 | 中 | 高 | 低 |
| 灵活性 | 中 | 高 | 高 | 低 |
17.7.2 选择建议
flowchart TB
Q1{任务可以分解为<br/>独立子任务?}
Q1 -->|是| Q2{需要动态决策<br/>任务分配?}
Q1 -->|否| Q3{Agent 之间需要<br/>直接交接?}
Q2 -->|是| Sup[Supervisor 模式]
Q2 -->|否| Collab[协作 Agent 模式]
Q3 -->|是| Swarm[Swarm 模式]
Q3 -->|否| Q4{Agent 数量 > 5?}
Q4 -->|是| Hier[分层 Agent 模式]
Q4 -->|否| Sup
17.8 高级技巧
17.8.1 并行 Agent 执行
使用 Send API 让 Supervisor 同时分配多个任务:
def supervisor_with_parallel(state):
"""Supervisor 同时分配多个任务"""
tasks = analyze_subtasks(state["messages"])
return [
Send(task["agent"], {"messages": state["messages"], "task": task["description"]})
for task in tasks
]
17.8.2 Agent 间消息传递的状态设计
class MultiAgentState(TypedDict):
# 公共消息历史
messages: Annotated[list[BaseMessage], add_messages]
# 各 Agent 的中间结果(使用 reducer 合并)
agent_outputs: Annotated[dict[str, Any], merge_dicts]
# 当前活跃的 Agent
active_agent: str
# 全局上下文
shared_context: dict[str, Any]
17.8.3 跨 Agent 的 Store 共享
def research_agent_node(state, runtime: Runtime):
"""研究 Agent:结果保存到 Store"""
results = do_research(state["messages"])
runtime.store.put(
("shared", "research"),
state["task_id"],
{"results": results}
)
return {"messages": [AIMessage(content="研究完成")]}
def writer_agent_node(state, runtime: Runtime):
"""写作 Agent:从 Store 读取研究结果"""
research = runtime.store.search(
("shared", "research"),
limit=10
)
context = "\n".join(r.value["results"] for r in research)
draft = write_with_context(context, state["messages"])
return {"messages": [AIMessage(content=draft)]}
17.9 常见陷阱与最佳实践
17.9.1 避免无限循环
多 Agent 系统最常见的问题是循环转交——Agent A 认为应该交给 Agent B,Agent B 又认为应该交回 Agent A。解决方案:
class SafeState(TypedDict):
messages: Annotated[list[BaseMessage], add_messages]
handoff_count: int
max_handoffs: int
def safe_handoff(state: SafeState, target: str) -> Command:
"""带有次数限制的安全转交"""
if state.get("handoff_count", 0) >= state.get("max_handoffs", 10):
return Command(
goto=END,
update={"messages": [AIMessage(content="已达到最大转交次数,结束对话。")]}
)
return Command(
goto=target,
update={"handoff_count": state.get("handoff_count", 0) + 1}
)
17.9.2 状态 Schema 设计原则
多 Agent 系统的状态设计需要平衡共享与隔离:
class WellDesignedState(TypedDict):
# 公共字段:所有 Agent 共享
messages: Annotated[list[BaseMessage], add_messages]
task_description: str
# Agent 特有字段:使用 NotRequired 避免其他 Agent 被强制提供
research_notes: NotRequired[str]
draft_content: NotRequired[str]
review_score: NotRequired[float]
# 元数据字段:追踪执行过程
current_agent: str
iteration_count: int
17.9.3 消息历史管理
多 Agent 系统中,消息历史会快速膨胀。建议使用 pre_model_hook 或专门的消息管理节点:
from langgraph.graph.message import REMOVE_ALL_MESSAGES, RemoveMessage
def trim_messages_hook(state):
"""保留最近 20 条消息,加上系统消息"""
messages = state["messages"]
if len(messages) > 20:
# 保留系统消息 + 最近 20 条
system_msgs = [m for m in messages if isinstance(m, SystemMessage)]
recent = messages[-20:]
return {
"messages": [RemoveMessage(id=REMOVE_ALL_MESSAGES)] + system_msgs + recent
}
return {"messages": messages}
17.10 设计决策
17.9.1 为什么用子图而不是函数调用?
将 Agent 实现为子图(而非简单的函数调用)的优势:
- 独立 Checkpoint:每个子图有自己的 checkpoint 命名空间,支持独立的中断恢复
- 可视化:子图在图的可视化中自然展示层级关系
- 复用:同一个 Agent 子图可以在不同的多 Agent 系统中复用
- 流式输出:子图的流式事件自动携带命名空间前缀,便于追踪
17.9.2 为什么 Handoff 用 Command 而不是状态更新?
Swarm 模式中使用 Command(goto=target) 而不是 return {"next_agent": target} 的原因:
- 原子性:Command 的 goto 和 update 在同一步中执行
- 类型安全:goto 的目标必须是已注册的节点名
- 跨图能力:Command 可以通过
graph=Command.PARENT导航到父图
17.9.3 状态隔离 vs 状态共享
不同的多 Agent 模式对状态共享有不同的需求:
- Supervisor:子 Agent 的状态可以通过子图隔离
- Swarm:所有 Agent 共享同一个状态(消息历史)
- 协作:共享状态是核心机制,每个 Agent 写入不同的字段
- 分层:每层有自己的状态,通过子图接口传递数据
17.10 小结
本章系统介绍了基于 LangGraph 实现的四种多 Agent 模式。每种模式都是底层原语(StateGraph、Send、Command、子图)的不同组合方式:
- Supervisor 通过
Command(goto=agent)实现中央调度 - Swarm 通过 handoff 工具返回
Command实现点对点交接 - 分层 Agent 通过子图嵌套实现多级管理
- 协作 Agent 通过共享 State 的不同字段实现接力式协作
选择哪种模式取决于具体需求:任务是否可分解、是否需要动态路由、Agent 数量和并行需求。在实践中,这些模式也可以混合使用——例如,顶层使用 Supervisor 模式,底层使用协作模式。
LangGraph 的优势在于它不绑定任何特定的 Agent 框架模式。无论选择哪种多 Agent 架构,底层都是相同的 StateGraph + Pregel 调度器,享有相同的 Checkpoint 持久化、中断恢复、流式输出和类型安全能力。这使得开发者可以根据业务需求自由组合,而不是被框架限制在某个特定的模式中。
下一章是本书的最后一章,我们将从更高的视角审视 LangGraph 的设计模式与架构决策——Pregel 计算模型的选择、Channel 版本追踪的巧妙、Checkpoint 时间旅行的实现、中断/恢复机制的全貌,以及如何基于这些思想构建你自己的工作流引擎。