犀牛鸟 2026 · 开源研究笔记

第九章:跨框架对比——多 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 框架深度导读系列