第九章:跨框架对比——多 Agent 协作模式
本章目标:理解多 Agent 协作的五种基本拓扑结构,掌握各框架提供的协作机制、handoff 语义和人机协同设计,建立”协作模式选择决定系统上限”的认知。
9.1 为什么需要多 Agent?
日常类比
你一个人搬家:打包、搬运、开车、整理,全部自己来。虽然能做,但效率低、出错多。如果你找几个朋友帮忙,就有了分工:一个人打包、一个人搬运、一个人开车、一个人在新家整理。
多 Agent 协作就是”找朋友帮忙”的 AI 版本。单个 Agent 可以做很多事,但:
- 一个 Agent 的 system prompt 不可能覆盖所有专业领域——让一个 Agent 既懂法律又懂编程又懂市场分析,它每个方面都不够深入
- Context window 有限——把所有任务的上下文塞给一个 Agent,信噪比极低
- 不同任务需要不同的”人设”和工具——写代码的 Agent 需要代码执行工具,写文案的 Agent 需要搜索和风格检查工具
技术动机
多 Agent 系统的核心价值:
- 专业化:每个 Agent 有聚焦的 system prompt + 专用工具集,深度 > 广度
- 信噪比:每个 Agent 只接收与自己相关的信息,减少无关上下文干扰
- 可维护性:修改搜索策略只需改搜索 Agent,不影响其他
- 可扩展性:新增能力 = 新增一个专门的 Agent,而不是修改已有复杂 Agent
- 并行化:独立的 Agent 可以同时工作,缩短总耗时
但多 Agent 也带来了新问题:谁来协调?怎么通信?信息怎么传递?失败了怎么恢复?
9.2 多 Agent 协作的五种基本拓扑
拓扑概览
1. 线性链(Chain/Sequential)
A → B → C → D
2. 星型(中心调度 / Hub-and-Spoke)
↗ B ↘
A → Manager → D
↘ C ↗
3. 网状(去中心化 / Mesh)
A ↔ B ↔ C
↕ ↕
D ↔ E ↔ F
4. 层级树(Hierarchical)
CEO
/ \
VP-Eng VP-Mkt
/ \ / \
Dev QA SEO PR
5. 动态图(Runtime-determined)
A → ? → ? → ? → END
(下一步去哪由运行时的条件/LLM决策决定)
详细对比
| 拓扑 | 日常类比 | 优点 | 缺点 | 适合场景 |
|---|---|---|---|---|
| 线性链 | 流水线生产 | 简单可预测 | 无并行、瓶颈在最慢的 | 有严格顺序依赖的任务 |
| 星型 | 经理+下属 | 清晰控制、可分配/召回 | 中心是瓶颈+单点故障 | 复杂任务分解 |
| 网状 | 同事间直接协作 | 灵活高效 | 混乱难控、难以调试 | 自主Agent群体 |
| 层级树 | 公司组织架构 | 关注点分离、可深度嵌套 | 通信成本高、层级延迟 | 大规模多团队 |
| 动态图 | “走一步看一步” | 最灵活、适应不确定性 | 不可预测、难以保证终止 | 开放式探索任务 |
9.3 各框架的协作模式映射
tRPC-Agent:三种内建模式
tRPC-Agent 提供了三种开箱即用的多 Agent 协作模式:
1. Coordinator(星型调度)
// Coordinator = 中心调度者 + 多个子 Agent
coordinator := NewCoordinator(
WithLLM(gpt4),
WithSubAgents(
researchAgent, // 搜索和信息收集
codingAgent, // 代码编写
reviewAgent, // 代码审查
),
WithInstruction(`你是项目经理。根据用户需求,选择合适的下属Agent执行任务。
每次只指派一个Agent,拿到结果后决定下一步。`),
)
// 执行流程:
// 1. 用户请求 → Coordinator 分析需求
// 2. Coordinator 选择子 Agent → 发送子任务
// 3. 子 Agent 执行 → 返回结果给 Coordinator
// 4. Coordinator 判断:完成了?还是需要另一个 Agent 继续?
// 5. 最终 Coordinator 综合结果回复用户
核心特点:
- Coordinator 有全局视野(看到用户需求 + 所有子 Agent 的返回结果)
- 子 Agent 之间不直接通信——全部通过 Coordinator 中转
- Coordinator 是串行分配的(一次一个子 Agent)
- 天然适合”需要综合判断”的场景
2. Swarm(网状/接力交接)
// Swarm = Agent 之间可以直接"交接"
// 灵感来自 OpenAI 的 Swarm 设计
salesAgent := NewAgent(
WithName("sales"),
WithTools(productSearch, priceCheck),
WithHandoffs("support", "billing"), // 我可以交接给这两个
)
supportAgent := NewAgent(
WithName("support"),
WithTools(ticketCreate, knowledgeBase),
WithHandoffs("sales", "engineering"), // 我可以交接给这两个
)
billingAgent := NewAgent(
WithName("billing"),
WithTools(invoiceLookup, refundProcess),
WithHandoffs("sales", "support"),
)
swarm := NewSwarm(
WithAgents(salesAgent, supportAgent, billingAgent),
WithEntryAgent("sales"), // 初始入口
)
// 执行流程:
// 1. 用户消息 → salesAgent 处理
// 2. salesAgent 发现是技术问题 → 调用 transfer_to_agent("support")
// 3. 控制权交给 supportAgent,继续与用户对话
// 4. supportAgent 解决后直接回复用户
// 没有中心调度者——Agent 自主决定交给谁
核心特点:
- 去中心化——没有 Manager
- Agent 自主决策交接(通过
transfer_to_agent工具) - 上下文随交接传递(对话历史带给下一个 Agent)
- 适合”客服轮转”“多角色对话”场景
3. Chain(线性链)
// Chain = 严格顺序执行
chain := NewChain(
researchAgent, // Step 1: 搜索
outlineAgent, // Step 2: 大纲
writingAgent, // Step 3: 写作
editAgent, // Step 4: 编辑
)
// 执行流程:
// 用户输入 → researchAgent → 输出传给 outlineAgent → ... → 最终结果
// 类似 Unix pipe: agent1 | agent2 | agent3
LangGraph:子图嵌套 + 条件路由
LangGraph 不提供”预制”的协作模式,而是通过图的组合来表达任意拓扑:
1. 子图嵌套(层级树)
from langgraph.graph import StateGraph, START, END
# 子图1:研究团队
research_graph = StateGraph(ResearchState)
research_graph.add_node("web_search", web_search_node)
research_graph.add_node("doc_search", doc_search_node)
research_graph.add_node("synthesize", synthesize_node)
research_graph.add_edge(START, "web_search")
research_graph.add_edge(START, "doc_search") # 并行
research_graph.add_edge("web_search", "synthesize")
research_graph.add_edge("doc_search", "synthesize")
research_graph.add_edge("synthesize", END)
# 子图2:写作团队
writing_graph = StateGraph(WritingState)
writing_graph.add_node("draft", draft_node)
writing_graph.add_node("review", review_node)
writing_graph.add_node("revise", revise_node)
writing_graph.add_edge(START, "draft")
writing_graph.add_edge("draft", "review")
# 条件边:审核通过→结束,不通过→修改
writing_graph.add_conditional_edges("review", quality_check, {
"pass": END,
"fail": "revise"
})
writing_graph.add_edge("revise", "review") # 修改后重新审核
# 主图:嵌套子图
main_graph = StateGraph(MainState)
main_graph.add_node("research_team", research_graph.compile()) # 子图作为节点
main_graph.add_node("writing_team", writing_graph.compile())
main_graph.add_edge(START, "research_team")
main_graph.add_edge("research_team", "writing_team")
main_graph.add_edge("writing_team", END)
2. 条件路由(动态图)
def router(state: AgentState) -> str:
"""根据状态动态决定下一步"""
last_message = state["messages"][-1]
if "需要搜索" in last_message.content:
return "search_agent"
elif "需要代码" in last_message.content:
return "coding_agent"
elif "需要分析" in last_message.content:
return "analysis_agent"
else:
return "final_answer"
graph.add_conditional_edges("dispatcher", router, {
"search_agent": "search_agent",
"coding_agent": "coding_agent",
"analysis_agent": "analysis_agent",
"final_answer": "final_answer",
})
3. 星型调度(用节点+条件边模拟)
# LangGraph 中实现 Coordinator 模式
def coordinator_node(state):
"""中心调度节点——决定下一步谁来"""
# 读取当前状态,用 LLM 决策
response = llm.invoke(f"""
当前任务: {state['current_task']}
已完成: {state['completed_steps']}
请决定下一步由谁执行: research / code / review / done
""")
return {"next_agent": response.content}
def route_to_agent(state):
return state["next_agent"]
graph.add_node("coordinator", coordinator_node)
graph.add_node("research", research_node)
graph.add_node("code", code_node)
graph.add_node("review", review_node)
graph.add_conditional_edges("coordinator", route_to_agent, {
"research": "research",
"code": "code",
"review": "review",
"done": END,
})
# 每个 Agent 执行完回到 Coordinator
graph.add_edge("research", "coordinator")
graph.add_edge("code", "coordinator")
graph.add_edge("review", "coordinator")
CrewAI:三种 Process + Flow
1. Sequential(线性链)
crew = Crew(
agents=[researcher, writer, editor],
tasks=[research_task, write_task, edit_task],
process=Process.sequential
)
# 严格按 tasks 列表顺序执行
2. Hierarchical(星型/层级)
crew = Crew(
agents=[researcher, writer, editor],
tasks=[complex_task],
process=Process.hierarchical,
manager_llm=ChatOpenAI(model="gpt-4"),
manager_agent=None # 自动创建 Manager
)
# Manager Agent 接收任务后,选择子 Agent 分配
# 子 Agent 完成后汇报给 Manager
# Manager 综合判断是否完成
3. Flow + @router(动态图)
from crewai.flow.flow import Flow, start, listen, router
class DynamicFlow(Flow):
@start()
def classify_request(self):
"""分类用户请求"""
return classifier_crew.kickoff()
@router(classify_request)
def route(self, classification):
"""根据分类结果路由"""
if classification.category == "technical":
return "tech_flow"
elif classification.category == "creative":
return "creative_flow"
return "general_flow"
@listen("tech_flow")
def handle_tech(self):
return tech_crew.kickoff()
@listen("creative_flow")
def handle_creative(self):
return creative_crew.kickoff()
@listen("general_flow")
def handle_general(self):
return general_crew.kickoff()
Agno:四种 Team 模式
Agno 提供了最丰富的预制协作模式:
from agno.team import Team
# 1. Coordinate(协调模式 = 星型)
team = Team(
agents=[researcher, writer, editor],
mode="coordinate",
# Leader 协调成员工作,综合结果
)
# 2. Route(路由模式 = 条件分发)
team = Team(
agents=[tech_agent, creative_agent, data_agent],
mode="route",
# 根据输入自动路由到最合适的 Agent
)
# 3. Collaborate(协作模式 = 网状)
team = Team(
agents=[analyst, designer, developer],
mode="collaborate",
# 所有 Agent 都看到输入,各自贡献,最后合并
)
# 4. Broadcast(广播模式 = 并行独立)
team = Team(
agents=[reviewer_1, reviewer_2, reviewer_3],
mode="broadcast",
# 同一个输入发给所有 Agent,收集所有回复
# 适合"多审稿人独立评审"场景
)
框架协作模式映射表
| 拓扑 | tRPC-Agent | LangGraph | CrewAI | Agno |
|---|---|---|---|---|
| 线性链 | Chain | 顺序边 | Sequential | - |
| 星型 | Coordinator | 节点+条件边 | Hierarchical | Coordinate |
| 网状 | Swarm | 复杂条件边 | - | Collaborate |
| 层级树 | - | 子图嵌套 | - | - |
| 动态图 | Swarm(部分) | 条件路由 | Flow @router | Route |
| 广播并行 | - | 并行边 | - | Broadcast |
9.4 Agent 间通信机制对比
四种通信范式
多 Agent 之间如何传递信息?不同框架采用了截然不同的方式:
1. RPC 调用(tRPC-Agent Coordinator)
// Coordinator "调用" 子 Agent,像函数调用一样
// 传入:子任务描述
// 返回:子任务结果
result := coordinator.CallSubAgent("research", "搜索关于X的最新论文")
// 同步等待结果,像调用一个远程过程
特点:同步、阻塞、请求-响应模式。调用者明确知道自己在等谁。
2. State 传递(LangGraph)
# 节点不"调用"其他节点——它们通过共享 State 间接通信
def research_node(state):
results = search(state["query"])
return {"findings": results} # 写入 State
def analyze_node(state):
data = state["findings"] # 从 State 读取前一个节点的输出
analysis = analyze(data)
return {"analysis": analysis}
特点:间接通信、解耦、通过”共享白板”交换信息。节点不知道”谁”会读自己的输出。
3. Task 输出传递(CrewAI)
# 上游 Task 的输出文本自动注入下游 Task 的上下文
research_task = Task(description="研究X", agent=researcher)
writing_task = Task(description="基于研究写文章", context=[research_task], agent=writer)
# writer 的 prompt 中会出现:"前置任务输出: [research结果]"
特点:隐式传递、文本形式、由框架自动注入 prompt。
4. 消息对话(tRPC-Agent Swarm / Agno Collaborate)
// Swarm 模式中,Agent 交接时带上对话历史
// 就像"把客户转接给同事时,顺带说一下之前聊了什么"
transferToAgent("support", withContext(conversationHistory))
特点:对话历史随交接传递,下一个 Agent 能看到”之前说了什么”。
通信机制对比表
| 维度 | RPC调用 | State传递 | Task输出 | 消息对话 |
|---|---|---|---|---|
| 耦合度 | 高(知道具体 Agent) | 低(只知道 State key) | 中(知道前置 Task) | 中(知道可交接对象) |
| 信息量 | 可控(函数参数) | 可控(Channel 内容) | 固定(raw text) | 大(完整对话历史) |
| 灵活性 | 中 | 高 | 低 | 中 |
| 调试难度 | 低(call stack 清晰) | 中(需追溯 State 变化) | 低(线性追溯) | 中(需看转接历史) |
| 适合规模 | 小(3-5 Agent) | 大(可任意扩展) | 中(线性链) | 小-中(轮转场景) |
9.5 Handoff(交接)语义深度对比
什么是 Handoff?
日常类比:你打银行客服电话,一线客服发现你的问题需要专家处理,说”我帮您转接到VIP专线”——这就是 handoff。关键问题是:转接时带不带之前的通话记录?转接后能不能再转回来?
在多 Agent 系统中,handoff 是指一个 Agent 把控制权和上下文移交给另一个 Agent。
tRPC-Agent Swarm 的 transfer_to_agent
// 在 Swarm 模式中,Agent 可以调用 transfer_to_agent 工具
salesAgent := NewAgent(
WithName("sales"),
WithTools(
// transfer_to_agent 是框架提供的特殊工具
TransferTool("support", "当用户有技术问题时转接到support"),
TransferTool("billing", "当用户有账单问题时转接到billing"),
),
WithSystemPrompt("你是销售客服..."),
)
// 执行流程:
// 1. 用户说:"我的订单出了问题"
// 2. salesAgent 的 LLM 决定调用 transfer_to_agent("support")
// 3. 框架执行交接:
// - 当前 Agent 停止执行
// - 对话历史传递给 supportAgent
// - supportAgent 从这里继续与用户对话
// 4. supportAgent 有自己的 system prompt 和工具集
Handoff 语义:
- 单向交接:控制权完全移交,原 Agent 不再参与
- 上下文传递:完整对话历史跟着走
- 不可撤回:一旦转交,不能”转回来”(除非目标 Agent 又主动转回)
- LLM 决策:由 Agent 的 LLM 自主决定何时交接
OpenAI Swarm 的原始设计
tRPC-Agent 的 Swarm 受 OpenAI Swarm 启发。OpenAI Swarm 的核心理念:
# OpenAI Swarm 的设计哲学(Python 参考实现)
class Agent:
name: str
instructions: str
functions: list # 包括 transfer_to_xxx 函数
def transfer_to_support():
"""Transfer the conversation to the support agent."""
return support_agent # 返回 Agent 对象 = 执行交接
# 特点:
# 1. 轻量——没有框架级的"编排器"
# 2. Agent 自治——自己决定交给谁
# 3. 对话历史 = 状态——交接就是"换一个 Agent 来读同一段历史"
# 4. 无持久化——纯内存,适合单次对话
tRPC-Agent vs OpenAI Swarm 的差异:
| 维度 | OpenAI Swarm | tRPC-Agent Swarm |
|---|---|---|
| 定位 | 教学参考实现 | 生产级框架 |
| 持久化 | 无 | 9种 Session 后端 |
| Context 管理 | 无(历史无限增长) | ContextWindowProcessor |
| 与其他模式组合 | 独立 | 可与 Coordinator/Chain 组合 |
| 部署 | 单进程 | tRPC 服务化 |
LangGraph 的”图结束 = 子图返回”
LangGraph 没有显式的 “handoff” 概念——它用子图的进入和退出来表达协作边界:
# 在 LangGraph 中,"交接"就是图的边
# Agent A 执行完 → 沿着边 → Agent B 开始执行
# 子图模式的"交接":
def should_handoff(state):
"""条件边:决定是否交接给子图"""
if state["needs_specialist"]:
return "specialist_subgraph"
return "continue"
graph.add_conditional_edges("generalist", should_handoff, {
"specialist_subgraph": "specialist",
"continue": "generalist_next_step",
})
# 子图执行完毕后,控制权自动返回父图
# 这种"交接"是结构化的——由图的拓扑决定,不是 Agent 自主决定
与 Swarm handoff 的关键差异:
| 维度 | Swarm Handoff | LangGraph 图边 |
|---|---|---|
| 决策者 | Agent 的 LLM(自主决定) | 图定义(编码时决定) + 条件函数(运行时路由) |
| 可预测性 | 低(LLM 可能意外交接) | 高(路由逻辑明确) |
| 灵活性 | 高(运行时任意交接) | 中(受限于预定义的边) |
| 上下文传递 | 对话历史(全量) | State Channel(精确选择) |
| 回退能力 | 不支持 | 支持(Checkpoint + rollback) |
CrewAI 的委派机制
CrewAI 在 Hierarchical 模式中有隐式的”委派”:
# Manager Agent 决定把任务委派给谁
crew = Crew(
agents=[researcher, writer, editor],
tasks=[complex_task],
process=Process.hierarchical,
manager_llm=ChatOpenAI(model="gpt-4")
)
# 内部执行:
# 1. Manager 分析 complex_task
# 2. Manager 决定:"先让 researcher 去收集信息"
# 3. researcher 执行,结果返回给 Manager
# 4. Manager 决定:"现在让 writer 写文章"
# 5. writer 执行,结果返回给 Manager
# 6. Manager 判断是否完成
# 这不是"交接"——是"委派+回收"
# 控制权始终在 Manager 手里,子 Agent 只是"被调用"
委派 vs 交接的本质区别:
- 交接(Handoff):A 把球传给 B,A 退场。B 全权负责。
- 委派(Delegation):Manager 给 Worker 一个子任务,Worker 完成后向 Manager 汇报。Manager 仍然掌控全局。
9.6 人机协同(Human-in-the-Loop)对比
为什么需要人类介入?
即使是最先进的 Agent,也有需要人类确认或干预的场景:
- 高风险操作:删除数据库、发送邮件、支付转账——需要人类确认
- 模糊决策:Agent 不确定怎么做,需要人类指导
- 质量把关:自动生成的内容需要人类审核后才能发布
- 合规要求:某些行业(金融/医疗)要求人类参与关键决策
LangGraph GraphInterrupt:最强人机协同
LangGraph 的 Human-in-the-Loop 设计是三个框架中最成熟的:
from langgraph.graph import StateGraph
from langgraph.checkpoint.sqlite import SqliteSaver
from langgraph.types import interrupt
# 方式1:interrupt() 函数——在任何地方暂停等待人类输入
def sensitive_action_node(state):
"""执行敏感操作前,暂停等待人类确认"""
action = state["pending_action"]
# 暂停执行,等待人类确认
human_response = interrupt(
f"即将执行: {action}\n请确认(yes/no/modify): "
)
if human_response == "no":
return {"action_result": "用户取消了操作"}
elif human_response.startswith("modify:"):
action = human_response.replace("modify:", "")
result = execute_action(action)
return {"action_result": result}
# 方式2:编译时声明"在某些节点前中断"
app = graph.compile(
checkpointer=SqliteSaver.from_conn_string("db"),
interrupt_before=["dangerous_node"], # 在这些节点执行前暂停
interrupt_after=["review_node"], # 在这些节点执行后暂停
)
# 使用流程:
config = {"configurable": {"thread_id": "task-001"}}
# 执行到中断点会暂停
result = app.invoke(input_data, config)
# result.next = ["dangerous_node"] ← 告诉你"接下来要执行这个,等你确认"
# 人类审查 State,可以修改后再继续
current_state = app.get_state(config)
print(current_state.values) # 看当前状态
# 方式A:继续执行(确认)
app.invoke(None, config) # 传 None = "继续"
# 方式B:修改 State 后继续
app.update_state(config, {"pending_action": "修改后的操作"})
app.invoke(None, config)
# 方式C:跳过这个节点
app.update_state(config, {"skip_dangerous": True}, as_node="dangerous_node")
核心优势:
- 修改后恢复:人类不只是”确认/取消”,还可以修改 State 中的任何值后再继续
- 精确定位:可以指定在哪个节点前/后中断
- 与 Checkpoint 集成:中断后 State 自动保存,人类可以过几个小时再回来继续
- 可编程:
interrupt()可以在任何节点的任何位置调用
tRPC-Agent WithInterruptBeforeNodes
tRPC-Agent 提供了类似但更轻量的中断机制:
// 在指定节点执行前中断
agent := NewAgent(
WithLLM(gpt4),
WithTools(emailSend, dbDelete),
WithInterruptBeforeTools("email_send", "db_delete"),
// 当 LLM 决定调用这些工具时,先暂停等人确认
)
// 执行流程:
// 1. LLM 决定调用 email_send
// 2. 框架暂停执行,返回 InterruptEvent
// 3. 应用层展示给用户:"Agent 想发送邮件 To: xxx, Content: yyy"
// 4. 用户选择:确认/取消/修改
// 5. 应用层调用 agent.Resume(userDecision) 继续
result, interrupt := agent.Run(userMessage)
if interrupt != nil {
// 有中断事件
fmt.Printf("Agent 想执行: %s\n", interrupt.ToolCall)
fmt.Printf("参数: %v\n", interrupt.Args)
// 等待用户决策
decision := getUserDecision() // UI层面
if decision.Approved {
result = agent.Resume(interrupt.ID, decision.ModifiedArgs)
} else {
result = agent.Cancel(interrupt.ID, "用户取消")
}
}
特点:
- 粒度是”工具调用级”——在 Agent 要调用特定工具时中断
- 比 LangGraph 简单(不需要理解图/节点/Checkpoint)
- 但灵活性不如 LangGraph(不能修改任意 State)
CrewAI Flow @human_feedback
CrewAI 的 Human-in-the-Loop 能力最基础:
from crewai.flow.flow import Flow, start, listen, human_input
class ReviewFlow(Flow):
@start()
def generate_content(self):
return content_crew.kickoff()
@human_input(generate_content)
def review_content(self, content, human_feedback):
"""
generate_content 执行后暂停等待人类反馈
human_feedback 是人类输入的文本
"""
if "approved" in human_feedback.lower():
return content
else:
# 把人类反馈传给修改 crew
return revision_crew.kickoff(
inputs={"original": content, "feedback": human_feedback}
)
局限性:
- 只支持文本形式的反馈(不像 LangGraph 可以修改结构化 State)
- 没有与 Checkpoint 集成(中断期间如果进程挂了,状态丢失)
- 粒度是”方法级”(不能在 Task 执行中间插入人类确认)
Human-in-the-Loop 对比总结
| 能力 | LangGraph | tRPC-Agent | CrewAI |
|---|---|---|---|
| 中断粒度 | 任意位置 (interrupt()) | 工具调用级 | 方法级 |
| 人类可做什么 | 确认/取消/修改State/跳过/fork | 确认/取消/修改工具参数 | 文本反馈 |
| 断点持久化 | 自动(Checkpoint) | 需手动保存Session | 不持久化 |
| 恢复灵活性 | 极高(可从任意checkpoint恢复) | 中等(从中断点继续) | 低 |
| 实现复杂度 | 中(需理解State/Checkpoint) | 低(事件驱动) | 低 |
9.7 动态 Agent 创建
问题定义
有时你不知道需要多少个 Agent——这取决于运行时的输入:
- 用户上传了 5 个文件 → 需要 5 个并行的”文件分析 Agent”
- 一篇论文有 8 个章节 → 需要 8 个”章节审核 Agent”
- 一个复杂问题可以分解为 N 个子问题 → 需要 N 个”子问题解决 Agent”
LangGraph:Send API + Map-Reduce
LangGraph 通过 Send 实现动态并行:
from langgraph.constants import Send
from langgraph.graph import StateGraph
class State(TypedDict):
documents: list[str] # 输入的文档列表
summaries: Annotated[list[str], operator.add] # 汇总结果
def fan_out(state):
"""根据文档数量动态创建并行分支"""
return [
Send("summarize", {"doc": doc, "index": i})
for i, doc in enumerate(state["documents"])
]
# 如果有 5 个文档,就并行启动 5 个 summarize 节点实例
def summarize_node(state):
"""每个实例处理一个文档"""
summary = llm.summarize(state["doc"])
return {"summaries": [summary]} # reducer 会合并所有实例的结果
graph = StateGraph(State)
graph.add_node("summarize", summarize_node)
graph.add_conditional_edges("start", fan_out)
graph.add_edge("summarize", "aggregate")
tRPC-Agent:应用层循环
tRPC-Agent 没有内建的动态创建机制,但可以在应用层实现:
// 应用层动态创建 Agent
func processDocuments(docs []Document) []string {
var results []string
for _, doc := range docs {
// 为每个文档创建专用 Agent
agent := NewAgent(
WithLLM(gpt4),
WithSystemPrompt(fmt.Sprintf("分析以下文档: %s", doc.Name)),
WithTools(analysisTool),
)
result := agent.Run(doc.Content)
results = append(results, result)
}
return results
}
// 如果需要并行,用 goroutine
func processDocumentsParallel(docs []Document) []string {
results := make([]string, len(docs))
var wg sync.WaitGroup
for i, doc := range docs {
wg.Add(1)
go func(idx int, d Document) {
defer wg.Done()
agent := NewAgent(WithLLM(gpt4), /* ... */)
results[idx] = agent.Run(d.Content)
}(i, doc)
}
wg.Wait()
return results
}
CrewAI:动态 Task 列表
# CrewAI 可以在运行时构造 Task 列表
def create_review_crew(chapters: list[str]) -> Crew:
"""根据章节数量动态创建 Task"""
tasks = []
for i, chapter in enumerate(chapters):
task = Task(
description=f"审核第{i+1}章: {chapter[:100]}...",
agent=reviewer,
expected_output="审核意见",
)
tasks.append(task)
# 最后汇总
summary_task = Task(
description="汇总所有章节审核意见",
context=tasks, # 依赖所有审核 Task
agent=editor,
expected_output="综合审核报告",
)
tasks.append(summary_task)
return Crew(agents=[reviewer, editor], tasks=tasks, process=Process.sequential)
动态创建能力对比
| 维度 | LangGraph | tRPC-Agent | CrewAI |
|---|---|---|---|
| 内建支持 | Send API(原生) | 无(应用层实现) | 动态Task列表 |
| 并行执行 | 自动并行 | 手动 goroutine | 不支持 |
| 结果合并 | Reducer 自动 | 手动收集 | context 引用 |
| 复杂度 | 低(声明式) | 中(需写并发代码) | 低(动态列表) |
9.8 容错与重试
问题定义
多 Agent 系统中,某个 Agent 执行失败怎么办?
- LLM API 超时
- 工具调用返回错误
- Agent 陷入无限循环
- Agent 产出不符合预期格式的结果
LangGraph:Checkpoint + retry_policy
from langgraph.graph import StateGraph
from langgraph.pregel import RetryPolicy
# 节点级重试策略
graph.add_node("risky_node", risky_function, retry=RetryPolicy(
max_attempts=3,
initial_interval=1.0, # 初始等待1秒
backoff_factor=2.0, # 指数退避
retry_on=(TimeoutError, APIError), # 只对这些错误重试
))
# 结合 Checkpoint 的恢复:
# 1. 节点失败 → 先按 retry_policy 重试
# 2. 重试全部失败 → 抛出异常
# 3. 但 Checkpoint 已保存了最后一个成功节点的状态
# 4. 修复问题后,从 Checkpoint 恢复继续
# 高级:自定义错误处理节点
def error_handler(state):
"""当某个节点失败时,走到这个错误处理节点"""
error = state["last_error"]
if "rate_limit" in str(error):
return {"action": "wait_and_retry", "wait_seconds": 60}
elif "invalid_format" in str(error):
return {"action": "retry_with_different_prompt"}
return {"action": "abort"}
graph.add_node("error_handler", error_handler)
# 可以用条件边把"失败状态"路由到 error_handler
tRPC-Agent:框架级熔断 + Plugin 重试
// tRPC-Agent 继承了 tRPC 的熔断限流能力
agent := NewAgent(
WithLLM(gpt4),
WithRetryPolicy(RetryPolicy{
MaxAttempts: 3,
InitialBackoff: time.Second,
MaxBackoff: time.Minute,
Retryable: func(err error) bool {
// 只对临时性错误重试
return isTransient(err)
},
}),
WithCircuitBreaker(CircuitBreaker{
FailureThreshold: 5, // 连续5次失败
ResetTimeout: 30*time.Second, // 30秒后重试
}),
WithRateLimit(RateLimit{
RequestsPerSecond: 10,
}),
)
// Plugin 级别的重试
type RetryPlugin struct {
MaxAttempts int
}
func (p *RetryPlugin) OnToolError(ctx context.Context, err error, toolCall ToolCall) Decision {
if ctx.Value("attempt").(int) < p.MaxAttempts {
return Retry // 重试这次工具调用
}
return SkipAndContinue // 跳过,让 LLM 决定下一步
}
CrewAI:基础重试
# CrewAI 的重试相对简单
from crewai import Agent
agent = Agent(
role="Researcher",
max_retry_limit=3, # 最多重试3次
max_execution_time=300, # 超时限制(秒)
allow_delegation=True, # 允许委派给其他Agent(如果自己搞不定)
)
# Task 级别
task = Task(
description="...",
agent=agent,
max_retries=2, # Task 级重试
)
# 没有:
# - 指数退避
# - 熔断机制
# - 自定义重试条件
# - 从中间状态恢复
容错能力对比
| 能力 | LangGraph | tRPC-Agent | CrewAI |
|---|---|---|---|
| 重试策略 | 声明式 + 可自定义 | 框架级(继承tRPC) | 基础(次数限制) |
| 指数退避 | 支持 | 支持 | 不支持 |
| 熔断器 | 无内建 | 内建(tRPC能力) | 无 |
| 限流 | 无内建 | 内建(tRPC能力) | 无 |
| 状态恢复 | Checkpoint 自动恢复 | Session 手动恢复 | 不支持 |
| 错误路由 | 可(条件边→错误处理节点) | Plugin 钩子 | 不支持 |
| 超时控制 | 节点超时 | 框架级超时 | Agent/Task 超时 |
9.9 协作模式实战选型
场景1:智能客服系统
需求:用户打进来可能是咨询产品、技术问题、退款投诉。不同类型需要不同专业的客服。
最佳选择:tRPC-Agent Swarm
理由:
- 自然的"转接"语义——和真实客服系统一模一样
- 去中心化——每个Agent自己决定该不该转
- 对话历史自动传递——下一个客服能看到之前聊了什么
- 简单——不需要复杂的图编排
场景2:自动化研究报告生成
需求:搜索多个来源 → 整理归纳 → 撰写报告 → 审核修改 → 定稿。审核不通过要循环修改。
最佳选择:LangGraph 子图 + 条件路由
理由:
- 搜索可以并行(多来源同时搜)
- 审核→修改是循环——需要条件边
- 需要 Checkpoint——报告生成可能很长时间
- 复杂状态——搜索结果、大纲、草稿、审核意见多个维度
场景3:内容营销流水线
需求:选题 → 研究 → 写稿 → SEO优化 → 配图 → 发布。每步有专人,严格顺序。
最佳选择:CrewAI Sequential
理由:
- 严格顺序——每步依赖上一步的输出
- 无并行需求——配图必须等稿子写完
- 认知负担最低——团队快速上手
- 够用——简单流水线不需要 Graph 的复杂能力
场景4:多文件代码审查
需求:N 个文件需要并行审查,每个文件独立打分,最后汇总。
最佳选择:LangGraph Send API 或 Agno Broadcast
理由:
- 动态并行——文件数量运行时决定
- 独立执行——审查互不干扰
- 自动聚合——Reducer 合并所有结果
9.10 本章总结
核心认知
-
拓扑决定上限:线性链无法并行,星型有中心瓶颈,网状难以控制——选择哪种拓扑就决定了系统的上限。
-
Handoff 是协作的灵魂:Agent 之间如何传递控制权和上下文,决定了协作的流畅度和信息保真度。
-
Human-in-the-Loop 不是可选项:在生产环境中,没有人类监督的 Agent 系统是危险的。框架对 HITL 的支持程度直接影响生产可用性。
-
容错 = 生产就绪的前提:多 Agent 系统中一个节点的失败会级联影响整个系统。完善的重试/恢复/熔断机制是必需品,不是锦上添花。
框架定位
- tRPC-Agent:提供”最常用的几种协作模式”作为开箱即用的原语。简单场景秒上手,复杂场景组合使用。适合”快速构建、够用就好”。
- LangGraph:不提供预制模式,但给你”图”这个万能工具来表达任意拓扑。适合”复杂场景、需要精确控制”。
- CrewAI:提供”最直觉的团队隐喻”。Sequential/Hierarchical 覆盖 80% 场景。适合”快速原型、业务导向”。
- Agno:提供”最丰富的预制模式”(4种 Team)。适合”想快速试多种协作方式”。
思考题
-
设计题:你要构建一个”AI 辩论系统”——正方 Agent 和反方 Agent 轮流发言,每方最多发言 3 轮,最后由评委 Agent 裁定。你会使用哪种框架和什么协作拓扑?如何确保双方严格轮流(不会一方连续发言)?
-
对比题:tRPC-Agent Swarm 的
transfer_to_agent和 LangGraph 的条件边路由,都可以实现”根据情况选择下一个 Agent”。它们的本质区别是什么?在什么场景下你会坚持用 Swarm 而不是 LangGraph 的条件路由? -
分析题:CrewAI 的 Hierarchical 模式中,Manager Agent 是由 LLM 驱动的——它自己可能犯错(比如反复把任务派给不合适的 Agent)。你认为这是设计缺陷还是有意为之?如何缓解这个问题?
-
综合题:一个大型企业知识库问答系统有 50+ 个专业领域 Agent。用户问一个问题,需要路由到对的 Agent。如果用 Swarm 模式(Agent 自主交接),链可能很长且不稳定。如果用 Coordinator 模式,单个 LLM 需要”认识” 50+ 个 Agent。你会怎么设计?考虑性能和准确性的平衡。
-
实践题:LangGraph 的
interrupt_before中断机制在技术上是如何实现的?(提示:与 Checkpoint 有关。)如果中断后用户 3 天没回来,再恢复时需要注意什么问题?如果在这 3 天内底层工具的 API 发生了变化呢?
返回目录:Agent 框架深度导读系列