多Agent协作-Orchestrator-Worker与Supervisor模式
*两种多 Agent 模式对比——Orchestrator-Worker(集中控制)与 Supervisor 分层(层级管理)*
多 Agent 协作:Orchestrator-Worker 与 Supervisor 模式
1.1 多 Agent 的动机:单 Agent 能力上限在哪里
两种多 Agent 模式对比——Orchestrator-Worker(集中控制)与 Supervisor 分层(层级管理)
在引入多 Agent 架构之前,需要先认清单 Agent 的能力上限在哪里。不是所有场景都需要多 Agent——过早引入多 Agent 只会增加复杂度而不带来收益。
单 Agent 的三道天花板
天花板一:上下文窗口限制。再大的上下文窗口也有上限,复杂任务(分析大型代码库、处理数百页文档)很容易超出限制。
天花板二:专业化深度与广度的矛盾。要求一个 Agent 同时具备"数据分析专家"、"代码工程师"、"法律顾问"的水平,等于要求一个人能精通所有领域——理论上可以,实践中每个角色都会做得平庸。
天花板三:串行处理的时间成本。10 个独立的子任务,单 Agent 必须串行处理,时间是并行处理的 10 倍。
多 Agent 如何突破这些上限
多 Agent 系统的核心思路是分治:把一个超出单 Agent 能力范围的任务,分解成多个可以被单 Agent 处理的子任务,分配给专门化的 Agent 并行执行,最后合并结果。
每个 Worker Agent 只处理自己专长的子任务,上下文窗口不会被无关内容占用;多个 Worker 并行执行,总时间大幅缩短;Supervisor Agent 负责协调,不承担执行工作,也不需要"懂所有领域",只需要能判断"这个任务该给谁"。
引入多 Agent 的代价
多 Agent 带来的复杂度是真实的:更多的 LLM 调用意味着更高的成本和延迟;Agent 之间的通信需要定义接口和格式;某个 Agent 失败需要有错误传播机制;调试变得更难,因为问题可能出在任何一个 Agent 或它们的交互中。
引入多 Agent 之前,先问一个问题:单 Agent 加上更好的工具、更长的上下文是否已经够用?如果够用,保持简单。
第 17 篇讲了让单个 Agent 在关键节点暂停等待人工确认。但还有另一类问题是人工确认解决不了的:任务本身超出了单个 Agent 的能力边界。
这时候,需要让多个 Agent 分工协作。
1.2 为什么需要多 Agent
单个 Agent 面临三道天花板,无论怎么调优都难以突破。
天花板一:上下文长度限制
任何 LLM 都有上下文窗口(Context Window,模型每次推理时能处理的最大文本量,超出部分会被截断忽略)——可以同时"看到"的文字数量上限。Claude 系列是 200k token,GPT-5 系列是 128k token(GPT-4.1 已扩展至 1M token)。
听起来很大,但分析一个中型代码库(几十个文件、几万行代码)时,把所有代码塞进一个上下文已经不够用。分析一份完整的法律合同集(几百页文件)时,更是远超上限。
单个 Agent 的记忆是有上限的房间——房间再大,也放不下一个仓库的东西。
天花板二:专业化分工的效率差异
一个"通才"Agent 被要求同时扮演:数据分析师(写 SQL、分析趋势)、后端工程师(写 API 代码)、技术文档作者(写用户手册)。
每个角色都有完全不同的专业知识、写作风格、思维框架。一个 Agent 在同一个上下文里同时扮演多个角色,要么每个角色都做得平庸,要么在角色之间的切换中产生混乱。
就像一个公司用一名员工同时承担 CTO、产品经理、销售总监三个职位——理论上可行,实际效果远不如各有专才。
天花板三:串行处理复杂任务太慢
分析 10 个竞争对手的产品,每个需要 3 分钟,串行处理要 30 分钟。如果 10 个 Agent 并行工作,3 分钟就能完成。
任务之间没有依赖关系时,串行是资源的浪费。
1.3 三种多 Agent 拓扑结构
根据任务的依赖关系和协作方式,多 Agent 系统有三种基本拓扑结构:
三种结构对比:
| 维度 | 串行管道 | 并行分工 | Supervisor-Worker |
|---|---|---|---|
| 任务依赖 | 强依赖(前一步的输出是下一步的输入) | 无依赖(子任务相互独立) | 动态分配(Supervisor 决定) |
| 执行速度 | 慢(必须按顺序等待) | 快(并行执行) | 灵活(可并行也可串行) |
| 适用场景 | 研究→写作→审核;数据→分析→报告 | 同时分析多个文档;并发爬取多个网页 | 任务类型多样;需要动态判断下一步 |
| 实现难度 | 简单(A 完成后传给 B) | 中等(需要合并多个结果) | 较复杂(Supervisor 需要路由逻辑) |
| 错误隔离 | 差(前一步出错,后续全部失败) | 好(某个 Agent 出错不影响其他) | 中等(取决于 Supervisor 的错误处理) |
| 典型框架实现 | LangGraph 线性图 | LangGraph 并行分支 | LangGraph + 路由节点 |
1.4 Supervisor 模式详解
Supervisor-Worker 是三种结构中最灵活、最复杂、也最适合"真实世界复杂任务"的模式。以下重点拆解它的设计。
1.4.1 Supervisor 的四个核心职责
职责一:任务分解
用户说"帮我研究一下竞争对手并写一份报告",Supervisor 要把这个模糊的目标分解成具体的子任务:
- 先交给研究员 Agent 收集信息
- 再交给分析 Agent 整理发现
- 最后交给写作 Agent 生成报告
职责二:Agent 选择
Supervisor 维护一份可用 Worker 清单,根据任务类型路由到合适的 Worker。这需要 Supervisor 知道"每个 Worker 擅长什么"。
职责三:结果整合
多个 Worker 返回的结果,格式、风格、详细程度可能各不相同。Supervisor 负责把碎片化的结果整合成连贯的最终输出。
职责四:质量把控
如果某个 Worker 返回了低质量的结果(答案太短、格式错误、没有回答问题),Supervisor 可以重新分配任务或者要求该 Worker 重试。
1.4.2 Worker Agent 的设计原则
Worker 的设计应遵循三个原则:
- 专业化:每个 Worker 只做一件事,做精做深,而不是通才
- 接口统一:所有 Worker 接受相同格式的输入(任务描述字符串),返回相同格式的输出(结果字符串)
- 可独立测试:Worker 应该可以脱离整个多 Agent 系统单独测试,输入一个任务,验证输出是否符合预期
1.4.3 通信方式:共享 State vs 消息传递
共享 State(共享状态):所有 Agent 共用一个状态对象,任何 Agent 都可以读取和修改状态。LangGraph(LangChain 旗下专门用于构建有状态 Agent 工作流的框架,通过"节点"表示执行步骤、"边"表示流转逻辑来描述复杂的多步骤 Agent 流程)默认使用这种方式。优点是简单直观;缺点是状态对象可能变得复杂,Agent 之间可能意外地修改彼此的数据。
消息传递:每个 Agent 只接收发给它的消息,处理完毕后发送消息给下一个 Agent。优点是耦合度低;缺点是需要明确定义消息格式,实现更复杂。
对于大多数场景,LangGraph 的共享 State 方式已经足够,本篇代码示例也采用这种方式。
1.5 LangGraph 实现 Supervisor 模式
以下为代码示例,非程序员可跳过代码,重点看文字说明。
这个示例实现了一个由三个 Worker 组成的内容生产系统:研究员(收集信息)、写作者(生成内容)、审核者(检查质量)。Supervisor 负责协调这三个 Worker 的工作流程。
验证目的:通过一个研究员-写作者-审核者的三 Agent 协作系统,验证 Supervisor 模式的核心机制——集中式路由决策、Worker 专业化分工、审核反馈循环、防死循环保护——并观察 Agent 之间通过共享状态传递信息的方式。
from typing import TypedDict, Literal, Annotated
import operator
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, SystemMessage
from langgraph.graph import StateGraph, START, END
# ---- 定义系统状态 ----
class MultiAgentState(TypedDict):
"""
共享状态:所有 Agent 都可以读写这个状态对象。
每个字段存储一类信息,Agent 通过读写对应字段来协作。
"""
task: str # 原始任务描述
research_result: str # 研究员的调研结果
draft_content: str # 写作者的初稿
review_feedback: str # 审核者的反馈意见
final_output: str # 最终输出
next_agent: str # Supervisor 决定的下一个执行者
iteration_count: int # 迭代次数(防止无限循环)
messages: Annotated[list, operator.add] # 完整消息历史
MAX_ITERATIONS = 10 # 最大迭代次数,超过则强制结束
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
# ---- 定义三个 Worker Agent ----
def researcher_agent(state: MultiAgentState) -> MultiAgentState:
"""
研究员 Agent:负责收集和整理与任务相关的背景信息。
输入:任务描述(state["task"])
输出:调研结果(写入 state["research_result"])
"""
response = llm.invoke([
SystemMessage(content=(
"你是一名专业研究员。请针对给定的任务,收集相关背景信息、"
"关键数据和重要观点。输出结构化的调研摘要,300字以内。"
)),
HumanMessage(content=f"任务:{state['task']}"),
])
print(f"[研究员] 完成调研,字数:{len(response.content)}")
return {
"research_result": response.content,
"messages": [response],
}
def writer_agent(state: MultiAgentState) -> MultiAgentState:
"""
写作者 Agent:基于调研结果撰写内容初稿。
输入:任务描述 + 调研结果
输出:内容初稿(写入 state["draft_content"])
"""
# 如果有审核反馈,则参考反馈修改;否则基于调研结果写初稿
feedback_section = ""
if state.get("review_feedback"):
feedback_section = f"\n\n审核反馈(请据此修改):\n{state['review_feedback']}"
response = llm.invoke([
SystemMessage(content=(
"你是一名专业内容写作者。请基于提供的调研结果,撰写高质量的文章内容。"
"要求:逻辑清晰、语言流畅、有实质内容,500字以内。"
)),
HumanMessage(content=(
f"任务:{state['task']}\n\n"
f"调研结果:{state['research_result']}"
f"{feedback_section}"
)),
])
print(f"[写作者] 完成初稿,第 {state['iteration_count']+1} 次写作")
return {
"draft_content": response.content,
"messages": [response],
}
def reviewer_agent(state: MultiAgentState) -> MultiAgentState:
"""
审核者 Agent:审查初稿质量,给出通过/修改意见。
输入:任务描述 + 初稿内容
输出:审核反馈(写入 state["review_feedback"])
特殊约定:如果质量通过,反馈开头写 "APPROVED"
"""
response = llm.invoke([
SystemMessage(content=(
"你是一名严格的内容审核者。请评估文章初稿的质量。\n"
"评估标准:内容准确性、逻辑连贯性、实用价值。\n"
"如果质量合格,回复以 'APPROVED' 开头,后接简短评语。\n"
"如果需要修改,直接说明具体的修改建议,不要写 APPROVED。"
)),
HumanMessage(content=(
f"任务要求:{state['task']}\n\n"
f"文章初稿:{state['draft_content']}"
)),
])
print(f"[审核者] 完成审核:{'通过' if 'APPROVED' in response.content else '需修改'}")
return {
"review_feedback": response.content,
"messages": [response],
}
# ---- 定义 Supervisor Agent ----
def supervisor_agent(state: MultiAgentState) -> MultiAgentState:
"""
Supervisor:根据当前状态决定下一步派谁去工作。
路由逻辑:
- 没有调研结果 → 派研究员
- 有调研结果但没有初稿 → 派写作者
- 有初稿但没有审核反馈 → 派审核者
- 审核通过(APPROVED) → 结束
- 审核不通过 → 派写作者修改(检查迭代次数防止死循环)
"""
# 超过最大迭代次数,强制结束(防止无限循环)
if state["iteration_count"] >= MAX_ITERATIONS:
print(f"[Supervisor] 已达最大迭代次数 {MAX_ITERATIONS},强制结束")
return {
"next_agent": "end",
"final_output": state.get("draft_content", "任务超时,未能完成"),
}
# 路由决策逻辑
if not state.get("research_result"):
print("[Supervisor] → 派遣研究员")
next_step = "researcher"
elif not state.get("draft_content"):
print("[Supervisor] → 派遣写作者")
next_step = "writer"
elif not state.get("review_feedback"):
print("[Supervisor] → 派遣审核者")
next_step = "reviewer"
elif "APPROVED" in state.get("review_feedback", ""):
print("[Supervisor] → 内容已通过审核,任务完成")
next_step = "end"
else:
# 审核未通过,让写作者根据反馈修改
print(f"[Supervisor] → 审核未通过,第 {state['iteration_count']+1} 次修改")
# 清空旧的审核反馈,让写作者重写后再次审核
next_step = "writer"
return {
"next_agent": next_step,
"iteration_count": state["iteration_count"] + 1,
}
# ---- 路由函数:Supervisor 的决策映射到图的边 ----
def route_by_supervisor(state: MultiAgentState) -> Literal[
"researcher", "writer", "reviewer", "__end__"
]:
"""
根据 Supervisor 的决策,返回下一个要执行的节点名称。
LangGraph 用这个函数的返回值决定走哪条边。
"""
next_agent = state.get("next_agent", "researcher")
if next_agent == "end":
return "__end__"
return next_agent # 返回 "researcher"、"writer" 或 "reviewer"
# ---- 构建 LangGraph 图 ----
def build_supervisor_graph():
builder = StateGraph(MultiAgentState)
# 注册所有节点
builder.add_node("supervisor", supervisor_agent)
builder.add_node("researcher", researcher_agent)
builder.add_node("writer", writer_agent)
builder.add_node("reviewer", reviewer_agent)
# 入口:从 Supervisor 开始
builder.add_edge(START, "supervisor")
# Supervisor 根据路由函数决定下一步
builder.add_conditional_edges(
"supervisor",
route_by_supervisor,
{
"researcher": "researcher",
"writer": "writer",
"reviewer": "reviewer",
"__end__": END,
}
)
# 所有 Worker 完成后,回到 Supervisor 汇报
builder.add_edge("researcher", "supervisor")
builder.add_edge("writer", "supervisor")
builder.add_edge("reviewer", "supervisor")
return builder.compile()
# ---- 运行示例 ----
def run_multi_agent_task(task: str) -> str:
graph = build_supervisor_graph()
initial_state = {
"task": task,
"research_result": "",
"draft_content": "",
"review_feedback": "",
"final_output": "",
"next_agent": "",
"iteration_count": 0,
"messages": [],
}
print(f"=== 开始多 Agent 协作任务 ===\n任务:{task}\n")
final_state = graph.invoke(initial_state)
print("\n=== 任务完成 ===")
print(f"总迭代次数:{final_state['iteration_count']}")
print(f"\n最终内容:\n{final_state['draft_content']}")
return final_state["draft_content"]
# 测试
result = run_multi_agent_task("写一篇面向Java程序员的AI Agent入门介绍,500字")
文字说明(代码之外的关键设计):
APPROVED约定:审核者 Agent 通过在输出开头写APPROVED来表示"通过",Supervisor 通过字符串检测来识别。这是 Agent 间通信的一种简单约定,实际生产中建议使用结构化输出(JSON格式)替代字符串匹配,更可靠。iteration_count是防止死循环的关键。如果写作者和审核者陷入"写了改、改了还不过"的死循环,MAX_ITERATIONS会强制终止。- 每个 Worker 完成后都回到 Supervisor,由 Supervisor 决定下一步。这是"中心化协调"的核心思想:没有 Worker 知道自己之后谁上场,只有 Supervisor 掌握全局。
1.6 防止多 Agent 失控的三个关键设计
多 Agent 系统相比单 Agent,有更多出问题的可能:某个 Agent 卡住、某两个 Agent 互相等待、某个 Agent 反复失败拖慢整个系统……
以下三个设计是避免失控的关键:
1.6.1 设计一:明确的终止条件
每个多 Agent 系统必须有至少一条"结束路径",并且要有兜底的强制结束条件(如最大迭代次数)。否则任何一个环节出现非预期情况,系统就会无限运行消耗资源。
# 好的设计:双保险
if task_is_complete(state): # 正常结束:任务完成
return END
if state["iterations"] > 20: # 兜底:超过最大次数强制结束
return END
1.6.2 设计二:每个 Agent 的权限边界
每个 Worker 只能做自己职责范围内的事,不应该跨界修改其他 Worker 的数据。
# 坏的设计:审核者直接修改初稿
def reviewer_agent(state):
state["draft_content"] = improved_draft # 不应该!审核者应该写反馈,不应该改稿
# 好的设计:审核者只写反馈
def reviewer_agent(state):
return {"review_feedback": "建议增加具体数据支撑..."} # 只修改自己负责的字段
1.6.3 设计三:错误传播的隔离
某个 Worker 报错时,不应该让整个系统崩溃。Supervisor 应该能捕获 Worker 的异常,进行降级处理(跳过、重试、使用备用 Agent)。
def supervisor_with_error_handling(state):
try:
# 正常路由逻辑
return route_to_next_agent(state)
except AgentTimeoutError as e:
# Worker 超时:记录日志,尝试换一个 Worker
log_error(e)
return {"next_agent": "fallback_agent"}
except CriticalError as e:
# 严重错误:终止任务,返回错误信息给用户
return {"next_agent": "end", "final_output": f"任务失败:{e}"}
1.7 实战案例:代码生成与验证协作系统
以下为代码示例,非程序员可跳过代码,重点看文字说明。
这个案例展示了一个三 Agent 协作的代码质量保障系统:代码生成 Agent(写代码)+ 测试 Agent(写测试并运行)+ 代码审查 Agent(检查代码质量)。
import subprocess
import tempfile
import os
# 代码生成 Agent:根据需求写代码
def code_generator(state):
feedback = state.get("review_comments", "")
prompt = f"需求:{state['requirement']}"
if feedback:
prompt += f"\n\n代码审查意见(请据此修改):{feedback}"
response = llm.invoke([
SystemMessage(content=(
"你是一名 Python 专家。请编写符合需求的 Python 代码。"
"只输出代码,不要解释,不要 markdown 代码块格式。"
)),
HumanMessage(content=prompt),
])
print("[代码生成] 完成代码编写")
return {"generated_code": response.content}
# 测试 Agent:为生成的代码编写单元测试并执行
def test_agent(state):
# 请 LLM 为代码生成测试用例
response = llm.invoke([
SystemMessage(content=(
"你是测试工程师。请为以下代码编写 pytest 单元测试。"
"只输出测试代码,不要解释。"
)),
HumanMessage(content=f"待测代码:\n{state['generated_code']}"),
])
test_code = response.content
# 实际执行测试(在临时目录中)
test_result = "PASS" # 默认通过
try:
with tempfile.TemporaryDirectory() as tmpdir:
# 写入代码文件
code_file = os.path.join(tmpdir, "solution.py")
test_file = os.path.join(tmpdir, "test_solution.py")
with open(code_file, "w") as f:
f.write(state["generated_code"])
with open(test_file, "w") as f:
f.write(test_code)
# 运行 pytest
result = subprocess.run(
["python", "-m", "pytest", test_file, "-v", "--tb=short"],
capture_output=True, text=True, timeout=30, cwd=tmpdir
)
test_result = "PASS" if result.returncode == 0 else f"FAIL:\n{result.stdout}"
except Exception as e:
test_result = f"ERROR: {e}"
print(f"[测试] 运行结果:{test_result[:50]}")
return {"test_result": test_result, "test_code": test_code}
# 代码审查 Agent:检查代码质量
def code_reviewer(state):
response = llm.invoke([
SystemMessage(content=(
"你是高级代码审查专家。请审查代码质量:\n"
"检查项:命名规范、错误处理、边界条件、代码简洁性。\n"
"如果代码质量合格且测试通过,回复以 'LGTM'(Looks Good To Me)开头。\n"
"否则,直接给出具体修改意见。"
)),
HumanMessage(content=(
f"代码:\n{state['generated_code']}\n\n"
f"测试结果:{state.get('test_result', '未测试')}"
)),
])
approved = response.content.startswith("LGTM")
print(f"[代码审查] {'通过' if approved else '未通过'}")
return {"review_comments": response.content, "code_approved": approved}
这个系统的关键点:
- 测试 Agent 不只是"写测试代码",还会真正执行测试并返回通过/失败。这是多 Agent 协作的价值所在——不同 Agent 可以使用不同的工具(写代码 vs 执行代码)。
- 审查 Agent 同时看"代码质量"和"测试结果",两者都满足才给出 LGTM。
- 整个流程和前面的 Supervisor 框架一样:三个 Worker 都完成后,Supervisor 决定"是否需要再次修改"。
1.8 小结
多 Agent 协作是突破单 Agent 能力上限的核心方案,选择哪种拓扑取决于任务特性:
- 串行管道:当下一步严格依赖上一步的输出,选串行
- 并行分工:当多个子任务可以独立完成,选并行
- Supervisor-Worker:当任务需要动态判断下一步做什么,选 Supervisor
设计多 Agent 系统时,最容易被忽视的是防失控设计。最大迭代次数、明确的权限边界、错误隔离——这三点比架构选型更能决定系统在生产环境中的稳定性。
下一篇讲 Agent 长期运行时面临的挑战:进程崩溃、服务重启之后,如何从断点继续执行,而不是从头开始。