概述
前面几篇我们已经把单 Agent 的核心能力串起来了:
create_agent()负责把模型、工具、提示词和状态组装成 Agent。- Middleware 负责在 Agent 执行过程中做摘要、审批、fallback、限流等治理。
- LangGraph 负责把复杂流程表达成带状态、分支和循环的图。
到这里,一个自然的问题会出现:
如果一个任务太复杂,能不能让多个 Agent 分工协作?
答案是可以,但要先澄清一个误区:
多 Agent 不是"Agent 越多越智能",而是"用更清晰的边界管理复杂上下文和复杂职责"。
一个 Agent 通常会在这几类情况下变得吃力:
- 工具太多,模型不知道该选哪个。
- Prompt 太长,角色和规则互相干扰。
- 任务跨多个专业领域,比如研发、测试、产品、运营。
- 需要多个步骤顺序执行,并且每一步都有不同判断标准。
- 某些子任务可以并行做,但最后要合并结果。
- 不同团队希望独立维护自己的 Agent 能力。
LangChain 官方多 Agent 文档里也强调:多 Agent 的核心价值通常来自上下文管理、分布式开发和并行化,而不是单纯增加模型调用次数。
本文重点讲两种最常见的多 Agent 协作模式:
- Supervisor 模式:一个主管 Agent 统一调度多个专家 Agent。
- Swarm 模式:多个 Agent 之间可以动态交接控制权。
多 Agent 的本质不是"堆模型",而是把复杂任务拆成边界清晰、上下文可控、可观测的协作单元。
先看全局:Supervisor 和 Swarm 的核心差异
先用一张表建立直觉。
| 维度 | Supervisor 模式 | Swarm 模式 |
|---|---|---|
| 控制中心 | 有中心调度者 | 无固定中心,当前 Agent 可交接 |
| 决策方式 | Supervisor 决定调用哪个专家 | Agent 自己决定是否 handoff |
| 用户交互 | 通常由 Supervisor 对外响应 | 当前活跃 Agent 可直接对外响应 |
| 上下文隔离 | 强,专家可只看到分配给自己的任务 | 弱到中等,取决于 handoff 传递什么 |
| 流程可控性 | 强,适合稳定业务流程 | 更灵活,适合开放式协作 |
| 并行能力 | 更容易并行调用多个专家 | 通常偏顺序交接 |
| 调试难度 | 相对低,所有路由经过主管 | 相对高,控制权会转移 |
| 适用任务 | 汇总报告、代码审查、研究分析、审批流 | 多角色对话、交接式客服、开放式任务推进 |
可以这样理解:
text
Supervisor 像项目经理:
任务进来 -> 拆给专家 -> 收集结果 -> 统一输出
Swarm 像接力协作:
当前专家处理 -> 发现不属于自己 -> 交给下一个专家 -> 下一个继续处理
Supervisor 强在集中调度和结果合成,Swarm 强在动态交接和角色连续性。
Supervisor 模式:中心化调度
Supervisor 模式里通常有一个主 Agent,它负责:
- 理解用户任务。
- 判断需要哪些专家 Agent。
- 把子任务分派给专家。
- 控制执行顺序或并行关系。
- 汇总专家结果。
- 给用户返回最终答案。
专家 Agent 则只负责自己的专业能力。
#mermaid-svg-180nIlGVvlNNKMrY{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-180nIlGVvlNNKMrY .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-180nIlGVvlNNKMrY .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-180nIlGVvlNNKMrY .error-icon{fill:#552222;}#mermaid-svg-180nIlGVvlNNKMrY .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-180nIlGVvlNNKMrY .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-180nIlGVvlNNKMrY .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-180nIlGVvlNNKMrY .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-180nIlGVvlNNKMrY .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-180nIlGVvlNNKMrY .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-180nIlGVvlNNKMrY .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-180nIlGVvlNNKMrY .marker{fill:#333333;stroke:#333333;}#mermaid-svg-180nIlGVvlNNKMrY .marker.cross{stroke:#333333;}#mermaid-svg-180nIlGVvlNNKMrY svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-180nIlGVvlNNKMrY p{margin:0;}#mermaid-svg-180nIlGVvlNNKMrY .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-180nIlGVvlNNKMrY .cluster-label text{fill:#333;}#mermaid-svg-180nIlGVvlNNKMrY .cluster-label span{color:#333;}#mermaid-svg-180nIlGVvlNNKMrY .cluster-label span p{background-color:transparent;}#mermaid-svg-180nIlGVvlNNKMrY .label text,#mermaid-svg-180nIlGVvlNNKMrY span{fill:#333;color:#333;}#mermaid-svg-180nIlGVvlNNKMrY .node rect,#mermaid-svg-180nIlGVvlNNKMrY .node circle,#mermaid-svg-180nIlGVvlNNKMrY .node ellipse,#mermaid-svg-180nIlGVvlNNKMrY .node polygon,#mermaid-svg-180nIlGVvlNNKMrY .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-180nIlGVvlNNKMrY .rough-node .label text,#mermaid-svg-180nIlGVvlNNKMrY .node .label text,#mermaid-svg-180nIlGVvlNNKMrY .image-shape .label,#mermaid-svg-180nIlGVvlNNKMrY .icon-shape .label{text-anchor:middle;}#mermaid-svg-180nIlGVvlNNKMrY .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-180nIlGVvlNNKMrY .rough-node .label,#mermaid-svg-180nIlGVvlNNKMrY .node .label,#mermaid-svg-180nIlGVvlNNKMrY .image-shape .label,#mermaid-svg-180nIlGVvlNNKMrY .icon-shape .label{text-align:center;}#mermaid-svg-180nIlGVvlNNKMrY .node.clickable{cursor:pointer;}#mermaid-svg-180nIlGVvlNNKMrY .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-180nIlGVvlNNKMrY .arrowheadPath{fill:#333333;}#mermaid-svg-180nIlGVvlNNKMrY .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-180nIlGVvlNNKMrY .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-180nIlGVvlNNKMrY .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-180nIlGVvlNNKMrY .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-180nIlGVvlNNKMrY .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-180nIlGVvlNNKMrY .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-180nIlGVvlNNKMrY .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-180nIlGVvlNNKMrY .cluster text{fill:#333;}#mermaid-svg-180nIlGVvlNNKMrY .cluster span{color:#333;}#mermaid-svg-180nIlGVvlNNKMrY div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-180nIlGVvlNNKMrY .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-180nIlGVvlNNKMrY rect.text{fill:none;stroke-width:0;}#mermaid-svg-180nIlGVvlNNKMrY .icon-shape,#mermaid-svg-180nIlGVvlNNKMrY .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-180nIlGVvlNNKMrY .icon-shape p,#mermaid-svg-180nIlGVvlNNKMrY .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-180nIlGVvlNNKMrY .icon-shape .label rect,#mermaid-svg-180nIlGVvlNNKMrY .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-180nIlGVvlNNKMrY .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-180nIlGVvlNNKMrY .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-180nIlGVvlNNKMrY :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 用户任务
Supervisor Agent
Researcher Agent
Writer Agent
Reviewer Agent
最终答案
这个结构的关键是:
所有任务分发和结果合成都经过 Supervisor。
专家之间通常不直接对话。
例如一个"写日报"系统可以拆成:
Researcher Agent:从 Jira、TAPD、Git 提交记录里收集当天工作材料。Writer Agent:把材料写成日报草稿。Reviewer Agent:检查是否遗漏关键事项、是否夸大、是否格式统一。Supervisor Agent:决定调用顺序,整合最终日报。
Supervisor 模式用一个中心 Agent 管住复杂协作,让专家 Agent 保持单职责。
Supervisor 的两种实现方式
在 LangChain / LangGraph 生态里,Supervisor 可以有两种常见实现方式。
方式一:把子 Agent 暴露成工具
这是当前更推荐的思路。
主 Agent 不需要知道子 Agent 的内部细节,只把它们当成工具调用。
text
Supervisor Agent
tools:
call_researcher(task)
call_writer(materials)
call_reviewer(draft)
这样做的好处是:
- Supervisor 的控制权清晰。
- 每个子 Agent 可以独立维护。
- 子 Agent 的上下文可以隔离。
- 工具描述可以明确告诉 Supervisor 什么时候调用谁。
方式二:使用 langgraph-supervisor
langgraph-supervisor 提供了 create_supervisor(),可以更快创建层级化多 Agent 系统。
不过它的 README 里已经提示:多数场景更推荐直接用 tool-calling 的方式实现 Supervisor,因为这样对上下文工程有更强控制力。
所以本文后面的代码会优先使用"子 Agent 作为工具"的写法。
Supervisor 最稳的落地方式,是把专家 Agent 包装成 Supervisor 可调用的工具。
Supervisor 示例:多 Agent 协作写日报
先定义三个专家 Agent。
为了让示例聚焦结构,工具里先用模拟数据。真实项目里你可以把这些函数换成 Jira、GitLab、飞书、TAPD、数据库 API。
python
from langchain.agents import create_agent
from langchain.tools import tool
@tool
def fetch_today_tasks(user_id: str) -> str:
"""获取用户今天完成的任务、提交记录和阻塞事项。"""
return (
"任务:完成 LangGraph 多 Agent 文章大纲;"
"提交:feat(agent): add daily report workflow;"
"阻塞:等待测试环境接口权限。"
)
researcher_agent = create_agent(
model="openai:gpt-4o-mini",
tools=[fetch_today_tasks],
system_prompt=(
"你是日报材料收集助手。"
"你的任务是根据用户和日期收集事实材料。"
"只输出事实,不要润色,不要编造。"
),
name="researcher_agent",
)
writer_agent = create_agent(
model="openai:gpt-4o-mini",
tools=[],
system_prompt=(
"你是日报写作助手。"
"你需要把事实材料整理成结构清晰、表达简洁的工作日报。"
"格式包含:今日完成、遇到问题、明日计划。"
),
name="writer_agent",
)
reviewer_agent = create_agent(
model="openai:gpt-4o-mini",
tools=[],
system_prompt=(
"你是日报审查助手。"
"你需要检查日报是否基于事实、是否遗漏阻塞事项、是否格式统一。"
"如果发现问题,给出修改建议;如果没有问题,返回通过。"
),
name="reviewer_agent",
)
接着把这些 Agent 包成工具,交给 Supervisor。
python
def last_text(result: dict) -> str:
return result["messages"][-1].content
@tool
def collect_daily_material(user_id: str, date: str) -> str:
"""收集指定用户在指定日期的日报事实材料。"""
result = researcher_agent.invoke({
"messages": [
{
"role": "user",
"content": f"请收集用户 {user_id} 在 {date} 的工作材料。",
}
]
})
return last_text(result)
@tool
def write_daily_report(materials: str) -> str:
"""根据事实材料生成日报草稿。"""
result = writer_agent.invoke({
"messages": [
{
"role": "user",
"content": f"请基于以下材料写日报:\n{materials}",
}
]
})
return last_text(result)
@tool
def review_daily_report(draft: str, materials: str) -> str:
"""审查日报草稿是否准确、完整、格式统一。"""
result = reviewer_agent.invoke({
"messages": [
{
"role": "user",
"content": (
f"事实材料:\n{materials}\n\n"
f"日报草稿:\n{draft}\n\n"
"请审查草稿。"
),
}
]
})
return last_text(result)
最后创建 Supervisor。
python
supervisor_agent = create_agent(
model="openai:gpt-4o-mini",
tools=[
collect_daily_material,
write_daily_report,
review_daily_report,
],
system_prompt=(
"你是日报生成系统的 Supervisor。"
"你必须先调用 collect_daily_material 收集事实,"
"再调用 write_daily_report 生成草稿,"
"最后调用 review_daily_report 审查。"
"如果审查不通过,需要根据审查意见修订后再输出。"
"最终只输出日报正文。"
),
name="daily_report_supervisor",
)
result = supervisor_agent.invoke({
"messages": [
{
"role": "user",
"content": "请生成 user_123 在 2026-06-28 的日报。",
}
]
})
print(result["messages"][-1].content)
这段代码背后的执行逻辑是:
Reviewer Writer Researcher Supervisor User Reviewer Writer Researcher Supervisor User #mermaid-svg-RSeqyEIssC0DNIcZ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-RSeqyEIssC0DNIcZ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-RSeqyEIssC0DNIcZ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-RSeqyEIssC0DNIcZ .error-icon{fill:#552222;}#mermaid-svg-RSeqyEIssC0DNIcZ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-RSeqyEIssC0DNIcZ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-RSeqyEIssC0DNIcZ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-RSeqyEIssC0DNIcZ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-RSeqyEIssC0DNIcZ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-RSeqyEIssC0DNIcZ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-RSeqyEIssC0DNIcZ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-RSeqyEIssC0DNIcZ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-RSeqyEIssC0DNIcZ .marker.cross{stroke:#333333;}#mermaid-svg-RSeqyEIssC0DNIcZ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-RSeqyEIssC0DNIcZ p{margin:0;}#mermaid-svg-RSeqyEIssC0DNIcZ .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-RSeqyEIssC0DNIcZ text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-RSeqyEIssC0DNIcZ .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-RSeqyEIssC0DNIcZ .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-RSeqyEIssC0DNIcZ .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-RSeqyEIssC0DNIcZ .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-RSeqyEIssC0DNIcZ #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-RSeqyEIssC0DNIcZ .sequenceNumber{fill:white;}#mermaid-svg-RSeqyEIssC0DNIcZ #sequencenumber{fill:#333;}#mermaid-svg-RSeqyEIssC0DNIcZ #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-RSeqyEIssC0DNIcZ .messageText{fill:#333;stroke:none;}#mermaid-svg-RSeqyEIssC0DNIcZ .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-RSeqyEIssC0DNIcZ .labelText,#mermaid-svg-RSeqyEIssC0DNIcZ .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-RSeqyEIssC0DNIcZ .loopText,#mermaid-svg-RSeqyEIssC0DNIcZ .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-RSeqyEIssC0DNIcZ .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-RSeqyEIssC0DNIcZ .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-RSeqyEIssC0DNIcZ .noteText,#mermaid-svg-RSeqyEIssC0DNIcZ .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-RSeqyEIssC0DNIcZ .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-RSeqyEIssC0DNIcZ .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-RSeqyEIssC0DNIcZ .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-RSeqyEIssC0DNIcZ .actorPopupMenu{position:absolute;}#mermaid-svg-RSeqyEIssC0DNIcZ .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-RSeqyEIssC0DNIcZ .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-RSeqyEIssC0DNIcZ .actor-man circle,#mermaid-svg-RSeqyEIssC0DNIcZ line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-RSeqyEIssC0DNIcZ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 生成日报收集事实材料返回任务、提交、阻塞生成日报草稿返回草稿审查草稿返回审查意见输出最终日报
Supervisor 负责"先后顺序"和"最终合成",专家 Agent 只负责自己的专业步骤。
Supervisor 的上下文隔离
Supervisor 模式最大的优势之一是上下文隔离。
在上面的例子里:
- Researcher 只需要看到用户、日期和数据源。
- Writer 只需要看到事实材料。
- Reviewer 只需要看到事实材料和草稿。
- Supervisor 只需要看到每个专家的最终结果。
这比把所有工具、所有规则、所有中间结果都塞给一个 Agent 更稳。
错误做法:
text
一个超级 Agent:
你会查 Jira
你会看 Git
你会写日报
你会审查
你会判断是否重写
你会处理异常
你会发飞书
你会归档数据库
这种 Agent 会很快变成一个难以调试的黑盒。
更好的拆法:
text
Supervisor:
负责流程和决策
Researcher:
负责事实收集
Writer:
负责表达组织
Reviewer:
负责质量检查
Publisher:
负责发送和归档
多 Agent 的第一价值是上下文隔离,不是让多个 Agent 互相聊天。
Swarm 模式:动态交接控制权
Swarm 模式和 Supervisor 最大的不同是:
没有固定中心调度者,当前活跃 Agent 可以把控制权交给另一个 Agent。
它的结构更像这样:
#mermaid-svg-nbDKx62NtOqxyr5s{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-nbDKx62NtOqxyr5s .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-nbDKx62NtOqxyr5s .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-nbDKx62NtOqxyr5s .error-icon{fill:#552222;}#mermaid-svg-nbDKx62NtOqxyr5s .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-nbDKx62NtOqxyr5s .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-nbDKx62NtOqxyr5s .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-nbDKx62NtOqxyr5s .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-nbDKx62NtOqxyr5s .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-nbDKx62NtOqxyr5s .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-nbDKx62NtOqxyr5s .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-nbDKx62NtOqxyr5s .marker{fill:#333333;stroke:#333333;}#mermaid-svg-nbDKx62NtOqxyr5s .marker.cross{stroke:#333333;}#mermaid-svg-nbDKx62NtOqxyr5s svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-nbDKx62NtOqxyr5s p{margin:0;}#mermaid-svg-nbDKx62NtOqxyr5s .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-nbDKx62NtOqxyr5s .cluster-label text{fill:#333;}#mermaid-svg-nbDKx62NtOqxyr5s .cluster-label span{color:#333;}#mermaid-svg-nbDKx62NtOqxyr5s .cluster-label span p{background-color:transparent;}#mermaid-svg-nbDKx62NtOqxyr5s .label text,#mermaid-svg-nbDKx62NtOqxyr5s span{fill:#333;color:#333;}#mermaid-svg-nbDKx62NtOqxyr5s .node rect,#mermaid-svg-nbDKx62NtOqxyr5s .node circle,#mermaid-svg-nbDKx62NtOqxyr5s .node ellipse,#mermaid-svg-nbDKx62NtOqxyr5s .node polygon,#mermaid-svg-nbDKx62NtOqxyr5s .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-nbDKx62NtOqxyr5s .rough-node .label text,#mermaid-svg-nbDKx62NtOqxyr5s .node .label text,#mermaid-svg-nbDKx62NtOqxyr5s .image-shape .label,#mermaid-svg-nbDKx62NtOqxyr5s .icon-shape .label{text-anchor:middle;}#mermaid-svg-nbDKx62NtOqxyr5s .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-nbDKx62NtOqxyr5s .rough-node .label,#mermaid-svg-nbDKx62NtOqxyr5s .node .label,#mermaid-svg-nbDKx62NtOqxyr5s .image-shape .label,#mermaid-svg-nbDKx62NtOqxyr5s .icon-shape .label{text-align:center;}#mermaid-svg-nbDKx62NtOqxyr5s .node.clickable{cursor:pointer;}#mermaid-svg-nbDKx62NtOqxyr5s .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-nbDKx62NtOqxyr5s .arrowheadPath{fill:#333333;}#mermaid-svg-nbDKx62NtOqxyr5s .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-nbDKx62NtOqxyr5s .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-nbDKx62NtOqxyr5s .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-nbDKx62NtOqxyr5s .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-nbDKx62NtOqxyr5s .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-nbDKx62NtOqxyr5s .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-nbDKx62NtOqxyr5s .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-nbDKx62NtOqxyr5s .cluster text{fill:#333;}#mermaid-svg-nbDKx62NtOqxyr5s .cluster span{color:#333;}#mermaid-svg-nbDKx62NtOqxyr5s div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-nbDKx62NtOqxyr5s .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-nbDKx62NtOqxyr5s rect.text{fill:none;stroke-width:0;}#mermaid-svg-nbDKx62NtOqxyr5s .icon-shape,#mermaid-svg-nbDKx62NtOqxyr5s .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-nbDKx62NtOqxyr5s .icon-shape p,#mermaid-svg-nbDKx62NtOqxyr5s .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-nbDKx62NtOqxyr5s .icon-shape .label rect,#mermaid-svg-nbDKx62NtOqxyr5s .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-nbDKx62NtOqxyr5s .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-nbDKx62NtOqxyr5s .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-nbDKx62NtOqxyr5s :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} handoff
handoff
handoff
用户
Researcher Agent
Writer Agent
Reviewer Agent
每个 Agent 都可以有自己的工具和提示词,也可以持有交接工具:
text
Researcher:
tools:
fetch_today_tasks
handoff_to_writer
Writer:
tools:
handoff_to_reviewer
Reviewer:
tools:
handoff_to_writer
handoff_to_user
langgraph-swarm 的核心思路就是这种动态 handoff:
- 当前 Agent 根据自己的专业判断是否继续处理。
- 如果任务不属于自己,就调用 handoff tool 交给其他 Agent。
- 系统记录当前 active agent。
- 多轮对话时,可以从上一次活跃 Agent 继续。
Swarm 的重点不是"谁调度谁",而是"当前 Agent 处理到合适位置后,把控制权交给更合适的 Agent"。
Swarm 示例:日报写作的接力协作
使用 langgraph-swarm 可以这样组织。
python
from langchain.agents import create_agent
from langchain_openai import ChatOpenAI
from langgraph.checkpoint.memory import InMemorySaver
from langgraph_swarm import create_handoff_tool, create_swarm
model = ChatOpenAI(model="gpt-4o-mini")
researcher = create_agent(
model=model,
tools=[
fetch_today_tasks,
create_handoff_tool(
agent_name="Writer",
description="当事实材料已经收集完成后,转交给 Writer 写日报草稿。",
),
],
system_prompt=(
"你是 Researcher,只负责收集日报事实材料。"
"材料收集完成后,必须转交给 Writer。"
),
name="Researcher",
)
writer = create_agent(
model=model,
tools=[
create_handoff_tool(
agent_name="Reviewer",
description="当日报草稿写完后,转交给 Reviewer 审查。",
),
],
system_prompt=(
"你是 Writer,只负责根据事实材料写日报草稿。"
"写完草稿后,必须转交给 Reviewer。"
),
name="Writer",
)
reviewer = create_agent(
model=model,
tools=[
create_handoff_tool(
agent_name="Writer",
description="如果草稿需要修改,转回 Writer。",
),
],
system_prompt=(
"你是 Reviewer,负责审查日报是否准确、完整、格式统一。"
"如果需要修改,转回 Writer;如果通过,直接给用户最终日报。"
),
name="Reviewer",
)
workflow = create_swarm(
[researcher, writer, reviewer],
default_active_agent="Researcher",
)
app = workflow.compile(checkpointer=InMemorySaver())
config = {"configurable": {"thread_id": "daily-report-001"}}
result = app.invoke(
{
"messages": [
{
"role": "user",
"content": "请生成 user_123 在 2026-06-28 的日报。",
}
]
},
config=config,
)
这里有一个关键点:
Swarm 做多轮对话时,一定要使用 checkpointer,否则系统无法稳定记住上一次活跃的是哪个 Agent。
例如第一轮用户说:
text
请帮我写今天的日报。
系统可能从 Researcher 开始,最后交给 Reviewer。
第二轮用户说:
text
把"明日计划"写得再具体一点。
如果有 checkpointer,系统可以知道当前更适合从上一次活跃 Agent 或相关 Agent 继续处理。
如果没有 checkpointer,Swarm 很可能重新从默认 Agent 开始,导致上下文断裂。
Swarm 依赖 active agent 状态,多轮场景必须重视 checkpoint。
Handoff:Swarm 的核心机制
Swarm 里最关键的是 handoff tool。
handoff 本质上做三件事:
- 告诉图下一步跳到哪个 Agent。
- 更新父图状态,比如
active_agent。 - 决定把哪些上下文传给下一个 Agent。
一个自定义 handoff 的核心形态类似这样:
python
from typing import Annotated
from langchain.tools import tool
from langchain.messages import ToolMessage
from langgraph.types import Command
from langgraph.prebuilt import InjectedState
from langchain.tools import InjectedToolCallId
def create_report_handoff_tool(agent_name: str):
@tool
def handoff(
task_description: Annotated[
str,
"描述下一个 Agent 需要完成的任务和必要上下文。",
],
state: Annotated[dict, InjectedState],
tool_call_id: Annotated[str, InjectedToolCallId],
):
tool_message = ToolMessage(
content=f"Transferred to {agent_name}",
tool_call_id=tool_call_id,
)
return Command(
goto=agent_name,
graph=Command.PARENT,
update={
"messages": state["messages"] + [tool_message],
"active_agent": agent_name,
"task_description": task_description,
},
)
return handoff
这个例子里,Command 同时表达了:
goto: 下一个 Agent。graph=Command.PARENT: 跳转发生在父图。update: 更新父图状态。
这和第 12 篇讲过的 Command 是同一个思想:节点或工具既能更新状态,也能决定下一步去哪里。
Swarm 的 handoff 不是普通函数调用,而是一次带状态更新的控制权转移。
写日报案例:Supervisor 版本 vs Swarm 版本
现在回到"多 Agent 协作写日报"这个案例。
同样是三个角色:
- Researcher:收集事实。
- Writer:写日报。
- Reviewer:审查日报。
Supervisor 版本的流程是:
text
用户 -> Supervisor
Supervisor -> Researcher
Supervisor -> Writer
Supervisor -> Reviewer
Supervisor -> 用户
Swarm 版本的流程是:
text
用户 -> Researcher
Researcher -> Writer
Writer -> Reviewer
Reviewer -> Writer 或 用户
二者区别非常明显。
Supervisor 更像流水线
#mermaid-svg-HC41NPIHdJ2s6Xwq{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-HC41NPIHdJ2s6Xwq .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-HC41NPIHdJ2s6Xwq .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-HC41NPIHdJ2s6Xwq .error-icon{fill:#552222;}#mermaid-svg-HC41NPIHdJ2s6Xwq .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-HC41NPIHdJ2s6Xwq .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-HC41NPIHdJ2s6Xwq .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-HC41NPIHdJ2s6Xwq .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-HC41NPIHdJ2s6Xwq .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-HC41NPIHdJ2s6Xwq .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-HC41NPIHdJ2s6Xwq .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-HC41NPIHdJ2s6Xwq .marker{fill:#333333;stroke:#333333;}#mermaid-svg-HC41NPIHdJ2s6Xwq .marker.cross{stroke:#333333;}#mermaid-svg-HC41NPIHdJ2s6Xwq svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-HC41NPIHdJ2s6Xwq p{margin:0;}#mermaid-svg-HC41NPIHdJ2s6Xwq .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-HC41NPIHdJ2s6Xwq .cluster-label text{fill:#333;}#mermaid-svg-HC41NPIHdJ2s6Xwq .cluster-label span{color:#333;}#mermaid-svg-HC41NPIHdJ2s6Xwq .cluster-label span p{background-color:transparent;}#mermaid-svg-HC41NPIHdJ2s6Xwq .label text,#mermaid-svg-HC41NPIHdJ2s6Xwq span{fill:#333;color:#333;}#mermaid-svg-HC41NPIHdJ2s6Xwq .node rect,#mermaid-svg-HC41NPIHdJ2s6Xwq .node circle,#mermaid-svg-HC41NPIHdJ2s6Xwq .node ellipse,#mermaid-svg-HC41NPIHdJ2s6Xwq .node polygon,#mermaid-svg-HC41NPIHdJ2s6Xwq .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-HC41NPIHdJ2s6Xwq .rough-node .label text,#mermaid-svg-HC41NPIHdJ2s6Xwq .node .label text,#mermaid-svg-HC41NPIHdJ2s6Xwq .image-shape .label,#mermaid-svg-HC41NPIHdJ2s6Xwq .icon-shape .label{text-anchor:middle;}#mermaid-svg-HC41NPIHdJ2s6Xwq .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-HC41NPIHdJ2s6Xwq .rough-node .label,#mermaid-svg-HC41NPIHdJ2s6Xwq .node .label,#mermaid-svg-HC41NPIHdJ2s6Xwq .image-shape .label,#mermaid-svg-HC41NPIHdJ2s6Xwq .icon-shape .label{text-align:center;}#mermaid-svg-HC41NPIHdJ2s6Xwq .node.clickable{cursor:pointer;}#mermaid-svg-HC41NPIHdJ2s6Xwq .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-HC41NPIHdJ2s6Xwq .arrowheadPath{fill:#333333;}#mermaid-svg-HC41NPIHdJ2s6Xwq .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-HC41NPIHdJ2s6Xwq .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-HC41NPIHdJ2s6Xwq .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HC41NPIHdJ2s6Xwq .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-HC41NPIHdJ2s6Xwq .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HC41NPIHdJ2s6Xwq .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-HC41NPIHdJ2s6Xwq .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-HC41NPIHdJ2s6Xwq .cluster text{fill:#333;}#mermaid-svg-HC41NPIHdJ2s6Xwq .cluster span{color:#333;}#mermaid-svg-HC41NPIHdJ2s6Xwq div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-HC41NPIHdJ2s6Xwq .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-HC41NPIHdJ2s6Xwq rect.text{fill:none;stroke-width:0;}#mermaid-svg-HC41NPIHdJ2s6Xwq .icon-shape,#mermaid-svg-HC41NPIHdJ2s6Xwq .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HC41NPIHdJ2s6Xwq .icon-shape p,#mermaid-svg-HC41NPIHdJ2s6Xwq .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-HC41NPIHdJ2s6Xwq .icon-shape .label rect,#mermaid-svg-HC41NPIHdJ2s6Xwq .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HC41NPIHdJ2s6Xwq .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-HC41NPIHdJ2s6Xwq .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-HC41NPIHdJ2s6Xwq :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 用户
Supervisor
Researcher
Writer
Reviewer
最终日报
优点:
- 流程稳定。
- 易观测。
- 易加审批。
- 易做结构化输出。
- 易限制专家 Agent 的上下文。
缺点:
- 每次都要回到 Supervisor。
- 对连续对话来说,可能有额外模型调用。
- Supervisor 的提示词质量会影响整体调度。
Swarm 更像接力
#mermaid-svg-6ZRa8T7ps1708IVf{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-6ZRa8T7ps1708IVf .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-6ZRa8T7ps1708IVf .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-6ZRa8T7ps1708IVf .error-icon{fill:#552222;}#mermaid-svg-6ZRa8T7ps1708IVf .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-6ZRa8T7ps1708IVf .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-6ZRa8T7ps1708IVf .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-6ZRa8T7ps1708IVf .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-6ZRa8T7ps1708IVf .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-6ZRa8T7ps1708IVf .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-6ZRa8T7ps1708IVf .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-6ZRa8T7ps1708IVf .marker{fill:#333333;stroke:#333333;}#mermaid-svg-6ZRa8T7ps1708IVf .marker.cross{stroke:#333333;}#mermaid-svg-6ZRa8T7ps1708IVf svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-6ZRa8T7ps1708IVf p{margin:0;}#mermaid-svg-6ZRa8T7ps1708IVf .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-6ZRa8T7ps1708IVf .cluster-label text{fill:#333;}#mermaid-svg-6ZRa8T7ps1708IVf .cluster-label span{color:#333;}#mermaid-svg-6ZRa8T7ps1708IVf .cluster-label span p{background-color:transparent;}#mermaid-svg-6ZRa8T7ps1708IVf .label text,#mermaid-svg-6ZRa8T7ps1708IVf span{fill:#333;color:#333;}#mermaid-svg-6ZRa8T7ps1708IVf .node rect,#mermaid-svg-6ZRa8T7ps1708IVf .node circle,#mermaid-svg-6ZRa8T7ps1708IVf .node ellipse,#mermaid-svg-6ZRa8T7ps1708IVf .node polygon,#mermaid-svg-6ZRa8T7ps1708IVf .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-6ZRa8T7ps1708IVf .rough-node .label text,#mermaid-svg-6ZRa8T7ps1708IVf .node .label text,#mermaid-svg-6ZRa8T7ps1708IVf .image-shape .label,#mermaid-svg-6ZRa8T7ps1708IVf .icon-shape .label{text-anchor:middle;}#mermaid-svg-6ZRa8T7ps1708IVf .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-6ZRa8T7ps1708IVf .rough-node .label,#mermaid-svg-6ZRa8T7ps1708IVf .node .label,#mermaid-svg-6ZRa8T7ps1708IVf .image-shape .label,#mermaid-svg-6ZRa8T7ps1708IVf .icon-shape .label{text-align:center;}#mermaid-svg-6ZRa8T7ps1708IVf .node.clickable{cursor:pointer;}#mermaid-svg-6ZRa8T7ps1708IVf .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-6ZRa8T7ps1708IVf .arrowheadPath{fill:#333333;}#mermaid-svg-6ZRa8T7ps1708IVf .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-6ZRa8T7ps1708IVf .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-6ZRa8T7ps1708IVf .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6ZRa8T7ps1708IVf .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-6ZRa8T7ps1708IVf .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6ZRa8T7ps1708IVf .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-6ZRa8T7ps1708IVf .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-6ZRa8T7ps1708IVf .cluster text{fill:#333;}#mermaid-svg-6ZRa8T7ps1708IVf .cluster span{color:#333;}#mermaid-svg-6ZRa8T7ps1708IVf div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-6ZRa8T7ps1708IVf .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-6ZRa8T7ps1708IVf rect.text{fill:none;stroke-width:0;}#mermaid-svg-6ZRa8T7ps1708IVf .icon-shape,#mermaid-svg-6ZRa8T7ps1708IVf .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6ZRa8T7ps1708IVf .icon-shape p,#mermaid-svg-6ZRa8T7ps1708IVf .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-6ZRa8T7ps1708IVf .icon-shape .label rect,#mermaid-svg-6ZRa8T7ps1708IVf .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6ZRa8T7ps1708IVf .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-6ZRa8T7ps1708IVf .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-6ZRa8T7ps1708IVf :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 需要修改
通过
用户
Researcher
Writer
Reviewer
最终日报
优点:
- 角色交接自然。
- 连续对话更顺。
- 当前 Agent 可以直接响应用户。
- 适合开放式、多轮协作任务。
缺点:
- 控制流更难预测。
- 容易反复 handoff。
- 对 checkpoint 依赖更强。
- 上下文传递策略不当时会污染后续 Agent。
日报这种稳定流程更适合 Supervisor;如果用户会不断和不同角色来回互动,Swarm 才更有优势。
什么时候选 Supervisor?
优先选择 Supervisor 的场景:
- 任务有明确入口和出口。
- 流程顺序相对固定。
- 最终答案需要统一口径。
- 需要聚合多个专家结果。
- 需要强审计、强观测。
- 子任务可以并行。
- 专家 Agent 不应该直接和用户对话。
典型场景:
| 场景 | 为什么适合 Supervisor |
|---|---|
| 写日报 | 收集、写作、审查、发布流程固定 |
| 代码审查 | 规范、安全、影响范围可以分专家并行检查 |
| 研究报告 | 多个 Researcher 查不同方向,最后统一汇总 |
| 企业客服分流 | 主 Agent 判断走 FAQ、订单、退款、人审 |
| SQL 查询助手 | Schema、SQL 生成、安全校验、执行解释要顺序受控 |
例如代码审查可以这样拆:
text
CodeReview Supervisor
-> Style Agent
-> Security Agent
-> Test Impact Agent
-> Summary Agent
其中前三个 Agent 可以并行执行,最后由 Summary Agent 或 Supervisor 汇总。
只要你希望"流程可控、结果统一、专家隔离",Supervisor 通常是默认选择。
什么时候选 Swarm?
优先选择 Swarm 的场景:
- 多个 Agent 都可能直接面向用户。
- 当前角色需要保持连续性。
- 用户会在多个角色之间来回切换。
- 任务路径不固定。
- Agent 之间的交接本身就是业务流程。
- 不希望所有动作都回到中心调度者。
典型场景:
| 场景 | 为什么适合 Swarm |
|---|---|
| 多角色客服 | 售前、售后、技术支持之间动态转接 |
| 教学助手 | 讲解、出题、批改、提示之间来回切换 |
| 游戏 NPC | 不同角色根据剧情交接对话 |
| 结对编程助手 | 架构师、实现者、测试者之间接力 |
| 开放式工作台 | 用户随时改变任务方向,需要当前 Agent 判断转交 |
例如客服系统:
text
售前 Agent:
介绍套餐
用户问故障 -> handoff 技术支持
技术支持 Agent:
排查问题
用户问退款 -> handoff 售后
售后 Agent:
处理退款和工单
用户问新套餐 -> handoff 售前
这种场景里,如果每一步都回到 Supervisor,反而可能显得绕。
当"谁继续接待用户"本身会随对话动态变化时,Swarm 更自然。
上下文工程:多 Agent 成败的关键
多 Agent 最容易翻车的地方不是代码,而是上下文。
你必须决定:
- 每个 Agent 能看到哪些消息?
- 每个 Agent 能使用哪些工具?
- handoff 时传完整历史还是任务摘要?
- 专家 Agent 的中间推理是否暴露给其他 Agent?
- 最终结果由谁合成?
Supervisor 的上下文建议
Supervisor 模式里,建议默认只让专家看到必要输入。
例如 Reviewer 不需要看到用户和 Supervisor 的所有对话,它只需要:
text
事实材料
日报草稿
审查标准
这样可以减少干扰。
如果专家 Agent 需要保留自己的长期经验,可以通过 memory 或 store 管,而不是把所有消息都丢进本次上下文。
Swarm 的上下文建议
Swarm 模式里,handoff 时要特别谨慎。
最简单的做法是传完整 messages,但这可能导致:
- 后续 Agent 看到太多无关内容。
- 某个 Agent 的内部过程污染其他 Agent。
- token 成本快速上涨。
- 角色边界变模糊。
更稳的做法是 handoff 时让当前 Agent 生成一个 task_description:
text
请 Writer 基于以下事实写日报:
1. 今天完成 LangGraph 多 Agent 文章大纲。
2. 提交 feat(agent): add daily report workflow。
3. 当前阻塞是测试环境接口权限未开。
要求:
使用"今日完成 / 遇到问题 / 明日计划"格式。
把任务摘要传给下一个 Agent,而不是无脑传整段对话。
多 Agent 不是共享越多越好,而是每个 Agent 只看到完成任务所需的最小上下文。
结构化输出:让协作结果可判断
多 Agent 系统里,最好不要只靠自然语言判断下一步。
例如 Reviewer 如果返回:
text
整体还可以,但明日计划可以再具体一点。
Supervisor 很难稳定判断到底是通过还是不通过。
更好的做法是让 Reviewer 返回结构化结果。
python
from pydantic import BaseModel, Field
class ReviewResult(BaseModel):
approved: bool = Field(description="日报是否通过审查")
issues: list[str] = Field(description="发现的问题")
revised_suggestions: list[str] = Field(description="修改建议")
然后创建 Reviewer:
python
reviewer_agent = create_agent(
model="openai:gpt-4o-mini",
tools=[],
system_prompt="你是严格的日报审查助手。",
response_format=ReviewResult,
name="reviewer_agent",
)
Supervisor 拿到结果后可以稳定决策:
python
if review_result.approved:
return final_report
else:
rewrite_with_feedback(review_result.issues)
这和第 09 篇结构化输出、第 12 篇条件边是连起来的。
多 Agent 协作中的路由和审批,最好基于结构化字段,而不是解析自然语言。
常见问题一:为了多 Agent 而多 Agent
很多任务并不需要多 Agent。
如果你的流程只是:
text
用户问题 -> 检索 -> 生成答案
用 RAG Chain 就够了。
如果你的流程只是:
text
用户问题 -> 模型决定是否调用工具 -> 最终回答
用单个 create_agent() 就够了。
不要因为"多 Agent 听起来高级"就拆成:
text
理解 Agent
规划 Agent
执行 Agent
总结 Agent
润色 Agent
这会带来额外问题:
- 更多模型调用。
- 更高延迟。
- 更高 token 成本。
- 更多中间错误。
- 更难调试。
多 Agent 是复杂度管理工具,不是默认架构。
常见问题二:Supervisor 变成超级大脑
Supervisor 不应该什么都做。
错误做法:
text
Supervisor:
理解任务
查数据
写日报
审查日报
修改日报
发消息
这和单 Agent 没区别,只是名字叫 Supervisor。
更好的边界是:
text
Supervisor:
拆任务
选专家
控顺序
合结果
Expert:
完成具体专业任务
Supervisor 的提示词也应该强调调度职责:
text
你是调度者,不直接完成专家任务。
需要事实材料时调用 Researcher。
需要写作时调用 Writer。
需要审查时调用 Reviewer。
Supervisor 负责协调,不负责替所有专家干活。
常见问题三:Swarm 交接没有退出条件
Swarm 里很容易出现循环:
text
Writer -> Reviewer -> Writer -> Reviewer -> Writer ...
这通常来自两个问题:
- Reviewer 的通过标准太模糊。
- Writer 不知道如何根据意见收敛。
要给系统加退出条件:
text
最多修改 2 轮。
如果仍不通过,输出当前最佳版本,并附上未解决问题。
也可以在状态里记录:
python
revision_count: int
每次从 Reviewer 转回 Writer 时加 1。
超过阈值就不再 handoff。
任何 Agent 交接循环都必须有明确的业务退出条件。
常见问题四:所有 Agent 共用一大段 messages
很多人为了省事,让所有 Agent 共用同一个 messages。
这在小 demo 里没问题,但生产里会带来风险:
- Agent 看到不该看的内部信息。
- 子任务上下文越来越长。
- 专家角色被其他 Agent 的输出干扰。
- 难以做权限隔离。
更稳的方式是:
- Supervisor 调专家时只传任务输入。
- Swarm handoff 时传任务摘要。
- 对敏感 Agent 使用独立 state key。
- 对最终输出做单独合成。
例如:
python
class DailyReportState(TypedDict):
messages: list
researcher_notes: str
draft_report: str
review_result: dict
不要所有东西都堆进 messages。
共享消息历史方便,但不是上下文工程的最终答案。
常见问题五:忽略成本和延迟
多 Agent 会增加调用次数。
例如日报系统:
text
单 Agent:
1~3 次模型调用
Supervisor:
Supervisor 调度
Researcher 调用
Supervisor 接收
Writer 调用
Supervisor 接收
Reviewer 调用
Supervisor 合成
Swarm:
Researcher
handoff Writer
Writer
handoff Reviewer
Reviewer
可能再 handoff Writer
如果每一步都是大模型,延迟和成本会很快上升。
优化方向:
- 简单分类用小模型。
- 固定规则用代码,不用 Agent。
- 可并行的专家并行执行。
- 专家只接收必要上下文。
- 对重复材料做缓存。
- 对长历史做摘要。
多 Agent 的架构收益必须大于额外调用成本。
生产建议:从 Supervisor 开始
如果你不知道选哪个,建议先从 Supervisor 开始。
原因很简单:
- 更接近传统工作流。
- 更容易画流程图。
- 更容易加日志和 trace。
- 更容易做权限控制。
- 更容易定位是哪一步错了。
- 更容易把专家替换成普通函数、Chain 或 Agent。
一个实用落地路径:
text
第 1 步:先写成普通 LangGraph 流程
第 2 步:把复杂节点替换成专家 Agent
第 3 步:让 Supervisor 通过工具调用专家
第 4 步:给关键节点加结构化输出
第 5 步:接入 checkpoint 和 LangSmith 观测
第 6 步:只有在需要动态角色交接时,再引入 Swarm
这样不会一上来就陷入"多个 Agent 互相转来转去"的复杂性。
业务流程明确时,Supervisor 是更稳的默认架构;Swarm 应该在确实需要动态交接时再引入。
一张图总结:如何选择多 Agent 模式
#mermaid-svg-TFUnDuWARnjej76C{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-TFUnDuWARnjej76C .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-TFUnDuWARnjej76C .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-TFUnDuWARnjej76C .error-icon{fill:#552222;}#mermaid-svg-TFUnDuWARnjej76C .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-TFUnDuWARnjej76C .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-TFUnDuWARnjej76C .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-TFUnDuWARnjej76C .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-TFUnDuWARnjej76C .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-TFUnDuWARnjej76C .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-TFUnDuWARnjej76C .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-TFUnDuWARnjej76C .marker{fill:#333333;stroke:#333333;}#mermaid-svg-TFUnDuWARnjej76C .marker.cross{stroke:#333333;}#mermaid-svg-TFUnDuWARnjej76C svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-TFUnDuWARnjej76C p{margin:0;}#mermaid-svg-TFUnDuWARnjej76C .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-TFUnDuWARnjej76C .cluster-label text{fill:#333;}#mermaid-svg-TFUnDuWARnjej76C .cluster-label span{color:#333;}#mermaid-svg-TFUnDuWARnjej76C .cluster-label span p{background-color:transparent;}#mermaid-svg-TFUnDuWARnjej76C .label text,#mermaid-svg-TFUnDuWARnjej76C span{fill:#333;color:#333;}#mermaid-svg-TFUnDuWARnjej76C .node rect,#mermaid-svg-TFUnDuWARnjej76C .node circle,#mermaid-svg-TFUnDuWARnjej76C .node ellipse,#mermaid-svg-TFUnDuWARnjej76C .node polygon,#mermaid-svg-TFUnDuWARnjej76C .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-TFUnDuWARnjej76C .rough-node .label text,#mermaid-svg-TFUnDuWARnjej76C .node .label text,#mermaid-svg-TFUnDuWARnjej76C .image-shape .label,#mermaid-svg-TFUnDuWARnjej76C .icon-shape .label{text-anchor:middle;}#mermaid-svg-TFUnDuWARnjej76C .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-TFUnDuWARnjej76C .rough-node .label,#mermaid-svg-TFUnDuWARnjej76C .node .label,#mermaid-svg-TFUnDuWARnjej76C .image-shape .label,#mermaid-svg-TFUnDuWARnjej76C .icon-shape .label{text-align:center;}#mermaid-svg-TFUnDuWARnjej76C .node.clickable{cursor:pointer;}#mermaid-svg-TFUnDuWARnjej76C .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-TFUnDuWARnjej76C .arrowheadPath{fill:#333333;}#mermaid-svg-TFUnDuWARnjej76C .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-TFUnDuWARnjej76C .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-TFUnDuWARnjej76C .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TFUnDuWARnjej76C .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-TFUnDuWARnjej76C .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TFUnDuWARnjej76C .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-TFUnDuWARnjej76C .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-TFUnDuWARnjej76C .cluster text{fill:#333;}#mermaid-svg-TFUnDuWARnjej76C .cluster span{color:#333;}#mermaid-svg-TFUnDuWARnjej76C div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-TFUnDuWARnjej76C .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-TFUnDuWARnjej76C rect.text{fill:none;stroke-width:0;}#mermaid-svg-TFUnDuWARnjej76C .icon-shape,#mermaid-svg-TFUnDuWARnjej76C .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TFUnDuWARnjej76C .icon-shape p,#mermaid-svg-TFUnDuWARnjej76C .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-TFUnDuWARnjej76C .icon-shape .label rect,#mermaid-svg-TFUnDuWARnjej76C .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TFUnDuWARnjej76C .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-TFUnDuWARnjej76C .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-TFUnDuWARnjej76C :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否
是
是
否
是
否
是
否
任务是否真的需要多 Agent?
使用 Chain 或单 Agent
流程是否稳定可描述?
优先 Supervisor
是否需要角色动态交接?
考虑 Swarm
考虑 Router / Custom LangGraph
是否需要并行专家?
Supervisor + 并行子任务
Supervisor + 顺序工具调用
Swarm + checkpointer + handoff 摘要
这张图背后的判断标准是:
- 不复杂就不要多 Agent。
- 流程稳定先用 Supervisor。
- 路径开放、角色动态交接再用 Swarm。
- 多 Agent 里的路由、审批、合成尽量结构化。
- 上下文越复杂,越要严格控制每个 Agent 能看到什么。
总结
本文对比了两种多 Agent 协作模式。
需要记住这些结论:
- 多 Agent 的核心价值是上下文管理、职责边界、并行化和团队协作。
- Supervisor 是中心化调度模式,适合稳定流程、结果合成、强观测场景。
- Swarm 是动态 handoff 模式,适合多角色连续对话和开放式任务推进。
- Supervisor 可以把专家 Agent 包装成工具,由主 Agent 统一调用。
- Swarm 的关键是 handoff tool,它会更新状态并转移控制权。
- 日报、报告、代码审查这类稳定流程,通常优先选 Supervisor。
- 客服转接、教学角色切换、开放式工作台这类动态场景,可以考虑 Swarm。
- 不要为了多 Agent 而多 Agent,能用 Chain 或单 Agent 解决就不要拆。
- 多 Agent 的质量取决于上下文工程,而不是 Agent 数量。
- 所有循环交接都要有退出条件。