犀牛鸟 2026 研究笔记 estelledc.github.io

第九章:跨框架对比——多 Agent 协作模式

本章目标:理解多 Agent 协作的五种基本拓扑结构,掌握各框架提供的协作机制、handoff 语义和人机协同设计,建立”协作模式选择决定系统上限”的认知。


9.1 为什么需要多 Agent?

日常类比

你一个人搬家:打包、搬运、开车、整理,全部自己来。虽然能做,但效率低、出错多。如果你找几个朋友帮忙,就有了分工:一个人打包、一个人搬运、一个人开车、一个人在新家整理。

多 Agent 协作就是”找朋友帮忙”的 AI 版本。单个 Agent 可以做很多事,但:

  1. 一个 Agent 的 system prompt 不可能覆盖所有专业领域——让一个 Agent 既懂法律又懂编程又懂市场分析,它每个方面都不够深入
  2. Context window 有限——把所有任务的上下文塞给一个 Agent,信噪比极低
  3. 不同任务需要不同的”人设”和工具——写代码的 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 综合结果回复用户

核心特点

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 自主决定交给谁

核心特点

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 语义

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 交接的本质区别


9.6 人机协同(Human-in-the-Loop)对比

为什么需要人类介入?

即使是最先进的 Agent,也有需要人类确认或干预的场景:

  1. 高风险操作:删除数据库、发送邮件、支付转账——需要人类确认
  2. 模糊决策:Agent 不确定怎么做,需要人类指导
  3. 质量把关:自动生成的内容需要人类审核后才能发布
  4. 合规要求:某些行业(金融/医疗)要求人类参与关键决策

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")

核心优势

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, "用户取消")
    }
}

特点

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}
            )

局限性

Human-in-the-Loop 对比总结

能力 LangGraph tRPC-Agent CrewAI
中断粒度 任意位置 (interrupt()) 工具调用级 方法级
人类可做什么 确认/取消/修改State/跳过/fork 确认/取消/修改工具参数 文本反馈
断点持久化 自动(Checkpoint) 需手动保存Session 不持久化
恢复灵活性 极高(可从任意checkpoint恢复) 中等(从中断点继续)
实现复杂度 中(需理解State/Checkpoint) 低(事件驱动)

9.7 动态 Agent 创建

问题定义

有时你不知道需要多少个 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 执行失败怎么办?

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 本章总结

核心认知

  1. 拓扑决定上限:线性链无法并行,星型有中心瓶颈,网状难以控制——选择哪种拓扑就决定了系统的上限。

  2. Handoff 是协作的灵魂:Agent 之间如何传递控制权和上下文,决定了协作的流畅度和信息保真度。

  3. Human-in-the-Loop 不是可选项:在生产环境中,没有人类监督的 Agent 系统是危险的。框架对 HITL 的支持程度直接影响生产可用性。

  4. 容错 = 生产就绪的前提:多 Agent 系统中一个节点的失败会级联影响整个系统。完善的重试/恢复/熔断机制是必需品,不是锦上添花。

框架定位


思考题

  1. 设计题:你要构建一个”AI 辩论系统”——正方 Agent 和反方 Agent 轮流发言,每方最多发言 3 轮,最后由评委 Agent 裁定。你会使用哪种框架和什么协作拓扑?如何确保双方严格轮流(不会一方连续发言)?

  2. 对比题:tRPC-Agent Swarm 的 transfer_to_agent 和 LangGraph 的条件边路由,都可以实现”根据情况选择下一个 Agent”。它们的本质区别是什么?在什么场景下你会坚持用 Swarm 而不是 LangGraph 的条件路由?

  3. 分析题:CrewAI 的 Hierarchical 模式中,Manager Agent 是由 LLM 驱动的——它自己可能犯错(比如反复把任务派给不合适的 Agent)。你认为这是设计缺陷还是有意为之?如何缓解这个问题?

  4. 综合题:一个大型企业知识库问答系统有 50+ 个专业领域 Agent。用户问一个问题,需要路由到对的 Agent。如果用 Swarm 模式(Agent 自主交接),链可能很长且不稳定。如果用 Coordinator 模式,单个 LLM 需要”认识” 50+ 个 Agent。你会怎么设计?考虑性能和准确性的平衡。

  5. 实践题:LangGraph 的 interrupt_before 中断机制在技术上是如何实现的?(提示:与 Checkpoint 有关。)如果中断后用户 3 天没回来,再恢复时需要注意什么问题?如果在这 3 天内底层工具的 API 发生了变化呢?


上一章:第八章 跨框架对比——状态管理哲学

下一章:第十章 跨框架对比——生产就绪度与协议生态

返回目录:Agent 框架深度导读系列