LangGraph-Multi-Agent多智能体协作架构
单个 Agent 的能力有天花板。当任务足够复杂、足够专业,或者足够长时,单 Agent 开始暴露它的结构性缺陷。Multi-Agent 架构(多智能体架构:多个 AI Agent 各司其职,协同完成单个 Agent 无法独立胜任的复杂任务)不是技术炫技,而是在特定场景下解决单 Agent 无法解决的问题的工程方案。
LangGraph Multi-Agent:多智能体协作架构
单个 Agent 的能力有天花板。当任务足够复杂、足够专业,或者足够长时,单 Agent 开始暴露它的结构性缺陷。Multi-Agent 架构(多智能体架构:多个 AI Agent 各司其职,协同完成单个 Agent 无法独立胜任的复杂任务)不是技术炫技,而是在特定场景下解决单 Agent 无法解决的问题的工程方案。
1.1 为什么单 Agent 不够用
LangGraph Supervisor 模式 — Supervisor 节点动态调度多个 Worker Agent,通过共享 State 通信
1.1.1 上下文长度限制
LLM 的上下文窗口是有限的(各模型具体数字以官方文档为准,通常在 128K 到 200K token 之间)。看起来很大,但一个中等规模的代码库,或者一份几十页的报告,轻松就能打爆这个限制。
单 Agent 处理长任务时,会遭遇两个问题:一是物理上超出 context window(上下文窗口:LLM 一次能处理的文本长度上限,超过则无法运行)无法运行,二是即使没超出,在超长 context 里 LLM 的注意力会分散,"迷失在中间"(Lost in the Middle 现象:LLM 对长文本开头和结尾的关注度远高于中间部分),中间部分的信息容易被忽略。
1.1.2 专业化分工
通用 LLM 什么都会,但什么都不精。一个任务如果需要"先搜索资料,再写代码,再写报告",让同一个 Agent 用同一个 system prompt 做这三件事,效果通常不如让三个各自优化过 system prompt 的专业 Agent 分工协作。
就像公司里不会用一个人既做销售、又做研发、又做财务——术业有专攻,专业化带来效率。
1.1.3 并行执行
某些任务天然可以并行。研究一个课题时,同时搜索"历史背景"和"最新进展",比串行搜索节省一半时间。单 Agent 本质上是串行的,Multi-Agent 架构可以让多个 Agent 并发执行,大幅缩短总耗时。
1.2 LangGraph 的 Multi-Agent 模式
LangGraph 实现 Multi-Agent 主要有两种模式:
| 模式 | 结构 | 适用场景 | 控制复杂度 |
|---|---|---|---|
| Supervisor(监督者)模式 | 一个 Supervisor Agent 动态调度多个 Worker Agent | 任务类型多样、需要动态决策 | 中等 |
| Hierarchical(层级)模式 | Supervisor 管理 Sub-Supervisor,Sub-Supervisor 再管理 Worker | 超大规模、多层级任务分解 | 高 |
| Network(网状)模式 | Agent 之间可以相互调用,无中心协调者 | 去中心化协作,各 Agent 自治 | 很高 |
本文重点介绍最常用的 Supervisor 模式,它覆盖了绝大多数实际场景。
1.3 Supervisor 模式架构
Supervisor 模式的核心思路:有一个 Supervisor Agent 负责理解任务、决定当前应该让哪个 Worker Agent 来处理,Worker 执行完把结果写回共享 State,Supervisor 再决定下一步。
Supervisor 本身也是一个节点,它的职责不是执行任务,而是决策:下一步应该调用哪个 Worker,或者任务是否已经完成。
1.4 完整代码示例:研究员 + 写作员协作系统
下面是一个完整可运行的示例,模拟"给定主题,自动完成资料研究和文章撰写"的协作系统。
# multi_agent_demo.py
# 演示 Supervisor 模式的 Multi-Agent 协作
# 包含:Supervisor Agent、Researcher Agent、Writer Agent
import operator
from typing import TypedDict, List, Annotated, Literal
from langgraph.graph import StateGraph, START, END
# ============================================================
# 1. 定义共享 State
# 所有 Agent 读写同一个 State,通过 State 实现通信
# ============================================================
class MultiAgentState(TypedDict):
topic: str # 研究主题
search_results: List[str] # 研究员收集的资料
draft: str # 写作员的草稿
final_output: str # 最终输出
# operator.add 保证日志是追加而非覆盖,避免节点间相互覆盖历史记录
messages: Annotated[List[str], operator.add]
# 下一步执行哪个节点,由 Supervisor 设置
next_worker: str
# 记录迭代次数,防止死循环
iteration_count: int
# ============================================================
# 2. Researcher Agent
# 职责:收集信息。实际场景这里会调用搜索工具或爬虫。
# ============================================================
def researcher_agent(state: MultiAgentState) -> dict:
"""
研究员 Agent:负责信息收集。
这里用模拟数据代替真实搜索,实际使用时接入 Tavily、Bing 等搜索 API。
"""
topic = state["topic"]
# 模拟搜索结果(实际场景替换为真实搜索调用)
simulated_results = [
f"【基础知识】{topic} 的定义:指通过...方式实现...的技术方法。",
f"【应用案例】{topic} 在工业界的主要应用包括:自动化流程、智能决策支持等。",
f"【最新进展】截至 2026 年,{topic} 领域的主要突破在于...研究方向取得了显著进展。",
f"【常见问题】实践中 {topic} 面临的挑战主要有:数据质量、模型可解释性、部署成本。",
]
return {
"search_results": simulated_results,
# messages 是 Annotated[List, operator.add],返回列表会被追加,不会覆盖
"messages": [f"[Researcher] 完成资料收集,找到 {len(simulated_results)} 条相关信息"],
}
# ============================================================
# 3. Writer Agent
# 职责:基于研究结果生成文章草稿。
# ============================================================
def writer_agent(state: MultiAgentState) -> dict:
"""
写作员 Agent:基于 Researcher 收集的资料撰写文章。
实际场景这里会调用 LLM,传入 search_results 作为上下文。
"""
topic = state["topic"]
search_results = state.get("search_results", [])
if not search_results:
# 防御性处理:如果没有搜索结果,不应该被调用到,但加上保护更稳健
return {
"draft": f"[错误] 没有可用的研究资料,无法生成关于「{topic}」的文章。",
"messages": ["[Writer] 错误:没有研究资料,无法写作"],
}
# 模拟基于资料生成文章(实际替换为 LLM 调用)
results_text = "\n".join(f"- {r}" for r in search_results)
draft = f"""# {topic} 综述
## 概述
{topic} 是当前技术领域的重要研究方向。
## 核心内容
基于最新研究资料整理如下:
{results_text}
## 总结
综合以上资料,{topic} 在实际应用中具有重要价值,未来发展空间广阔。
"""
return {
"draft": draft,
"messages": [f"[Writer] 完成文章草稿,共 {len(draft)} 字符"],
}
# ============================================================
# 4. Finalizer Agent
# 职责:对草稿做最终润色和输出
# ============================================================
def finalizer_agent(state: MultiAgentState) -> dict:
"""
最终整合节点:检查草稿质量,生成最终输出。
实际场景这里可以做格式检查、事实核对、排版优化等。
"""
draft = state.get("draft", "")
if not draft or draft.startswith("[错误]"):
final_output = "任务未能完成,请检查错误信息后重试。"
else:
# 模拟最终润色(添加元信息)
final_output = draft + f"\n\n---\n*本文由 Multi-Agent 系统自动生成,共经历 {state['iteration_count']} 次协作迭代。*"
return {
"final_output": final_output,
"messages": ["[Finalizer] 最终报告生成完成"],
}
# ============================================================
# 5. Supervisor Agent
# 这是整个系统的核心:决定下一步调用哪个 Worker
# ============================================================
def supervisor_agent(state: MultiAgentState) -> dict:
"""
Supervisor:任务调度中枢。
根据当前 State 决定下一步该做什么。
决策逻辑:
1. 没有搜索结果 → 先去搜索
2. 有搜索结果但没有草稿 → 去写作
3. 有草稿 → 进入最终化
4. 迭代次数超限 → 强制结束,防止死循环
"""
iteration = state.get("iteration_count", 0) + 1
# 安全阀:超过最大迭代次数强制结束
# 这是 Multi-Agent 系统中防止死循环的关键机制
MAX_ITERATIONS = 5
if iteration > MAX_ITERATIONS:
return {
"next_worker": "finalizer",
"iteration_count": iteration,
"messages": [f"[Supervisor] 达到最大迭代次数 {MAX_ITERATIONS},强制结束"],
}
has_research = bool(state.get("search_results"))
has_draft = bool(state.get("draft"))
if not has_research:
next_step = "researcher"
reason = "尚未收集资料,分配给研究员"
elif not has_draft:
next_step = "writer"
reason = "资料已收集,分配给写作员"
else:
next_step = "finalizer"
reason = "草稿已完成,进入最终化"
return {
"next_worker": next_step,
"iteration_count": iteration,
"messages": [f"[Supervisor] 第 {iteration} 次决策:{reason} → {next_step}"],
}
# ============================================================
# 6. 条件路由函数
# 根据 Supervisor 设置的 next_worker 字段决定跳转到哪个节点
# ============================================================
def route_by_supervisor(state: MultiAgentState) -> Literal["researcher", "writer", "finalizer"]:
"""
条件边的路由函数。
读取 Supervisor 写入的 next_worker 字段,返回下一个节点名称。
这是 Supervisor 模式的关键连接点。
"""
return state["next_worker"]
# ============================================================
# 7. 构建图
# ============================================================
def build_multi_agent_graph():
graph = StateGraph(MultiAgentState)
# 注册所有节点
graph.add_node("supervisor", supervisor_agent)
graph.add_node("researcher", researcher_agent)
graph.add_node("writer", writer_agent)
graph.add_node("finalizer", finalizer_agent)
# 入口:从 START 进入 Supervisor
graph.add_edge(START, "supervisor")
# Supervisor 出发的条件边:根据 next_worker 字段决定去哪里
graph.add_conditional_edges(
"supervisor",
route_by_supervisor,
{
"researcher": "researcher",
"writer": "writer",
"finalizer": "finalizer",
}
)
# Worker 完成后,回到 Supervisor 进行下一轮决策
# 这形成了 Worker → Supervisor → Worker 的循环结构
graph.add_edge("researcher", "supervisor")
graph.add_edge("writer", "supervisor")
# Finalizer 完成后直接结束
graph.add_edge("finalizer", END)
return graph.compile()
# ============================================================
# 8. 运行演示
# ============================================================
def run_demo():
app = build_multi_agent_graph()
initial_state = {
"topic": "大型语言模型的 Agent 应用",
"search_results": [],
"draft": "",
"final_output": "",
"messages": [],
"next_worker": "",
"iteration_count": 0,
}
print("=" * 60)
print(f"研究主题:{initial_state['topic']}")
print("=" * 60)
# 使用 stream 模式观察每个节点的执行过程
for event in app.stream(initial_state, stream_mode="updates"):
for node_name, updates in event.items():
if node_name.startswith("__"):
continue
# 打印每个节点产生的日志消息
new_messages = updates.get("messages", [])
for msg in new_messages:
print(msg)
# 获取最终结果
final_state = app.invoke(initial_state)
print("\n" + "=" * 60)
print("最终输出:")
print("=" * 60)
print(final_state["final_output"])
print("\n执行日志:")
for msg in final_state["messages"]:
print(f" {msg}")
if __name__ == "__main__":
run_demo()
1.5 Agent 间通信的两种方式
1.5.1 方式一:通过共享 State 通信(推荐)
上面的示例就是这种方式。所有 Agent 读写同一个 State 对象,通过字段传递信息:Researcher 把结果写入 search_results,Writer 从 search_results 读取,再把结果写入 draft。
优点:简单直接,State 天然具有可观测性(可以随时查看当前状态),也支持持久化和断点续跑。
1.5.2 方式二:通过子图直接调用
对于复杂的子系统,可以把一个完整的 Agent 封装成子图,在父图中作为单个节点调用。
from langgraph.graph import StateGraph, START, END
# 子图:独立的研究 Agent
def build_research_subgraph():
sub = StateGraph(ResearchState)
sub.add_node("fetch", fetch_node)
sub.add_node("summarize", summarize_node)
sub.add_edge(START, "fetch")
sub.add_edge("fetch", "summarize")
sub.add_edge("summarize", END)
# 编译子图
return sub.compile()
# 父图中把子图作为一个节点注册
research_subgraph = build_research_subgraph()
parent_graph = StateGraph(MainState)
# 子图可以直接作为节点传入,LangGraph 会自动处理 State 的映射
parent_graph.add_node("research", research_subgraph)
# ...
子图的好处是封装性强,子图内部可以有自己的 State 类型,父图只关心子图的输入和输出,不需要了解内部细节。适合把复杂子系统模块化。
1.6 防止死循环的三种机制
Multi-Agent 系统最容易出的生产事故之一:Agent 陷入死循环,无限互相调用,直到 token 费用爆炸或触发超时。
机制一:迭代计数器(最常用)
在 State 里维护一个 iteration_count 字段,Supervisor 每次决策时递增,超过阈值强制路由到结束节点。
MAX_ITERATIONS = 10
def supervisor(state):
count = state.get("iteration_count", 0) + 1
if count >= MAX_ITERATIONS:
return {"next_worker": "end", "iteration_count": count}
# 正常决策逻辑...
机制二:LangGraph 内置的 recursion_limit
编译图时或调用时设置最大递归深度:
# 在 invoke 时设置,超过后抛出 GraphRecursionError
app.invoke(initial_state, config={"recursion_limit": 25})
机制三:任务完成标志位
在 State 里设置明确的完成标志,Supervisor 每次检查是否已完成:
class State(TypedDict):
is_complete: bool # 明确的完成标志
def supervisor(state):
if state.get("is_complete"):
return {"next_worker": "end"}
# ...
推荐同时使用机制一和机制二,形成双重保险。
1.7 Supervisor 模式的局限
Supervisor 模式不是万能的,有几个场景要注意:
Supervisor 成为瓶颈:所有决策都经过 Supervisor,如果 Supervisor 本身调用了 LLM 来决策(而非基于规则),每轮调度都有 LLM 延迟。任务链长的时候,调度开销显著。
调试困难:Agent 的输出影响 Supervisor 的决策,Supervisor 的决策影响下一个 Agent,出现错误时定位原因需要逐步追踪 State 的变化。
Worker 之间无法直接通信:在 Supervisor 模式下,Worker 之间不能直接对话,必须通过 State 或经由 Supervisor 中转。如果需要 Worker 之间频繁交互,可以考虑 Network 模式。
1.8 小结
Supervisor 模式的设计要点:
- State 里设好通信字段(哪些字段是
Annotated[list, operator.add]的追加型(Annotated是 Python 类型工具,用于给字段附加合并规则;operator.add表示新旧列表相加而非覆盖),哪些是直接覆盖型) - Supervisor 的路由逻辑尽量基于规则而非 LLM,降低延迟和不确定性
- 总是加上最大迭代次数限制
下一篇讲错误处理与容错机制。Multi-Agent 系统里,Worker 随时可能失败,这不是异常情况,是常态。