LangGraph设计与实现-第17章-多 Agent 模式实战

《LangGraph 设计与实现》完整目录

第17章 多 Agent 模式实战

17.1 引言

单个 Agent 能力有限------它只有一组工具、一种提示、一个执行循环。当任务复杂度上升时(如"帮我分析这家公司的财报,然后写一份投资建议书"),单个 Agent 要么因为工具太多导致 LLM 选择困难,要么因为系统提示过长影响推理质量。多 Agent 系统通过分工协作解决这个问题:不同的 Agent 负责不同的能力域,通过明确的通信机制协同完成复杂任务。

LangGraph 的底层基础设施------StateGraph、Send、Command、子图------天然支持多 Agent 模式的构建。图的节点可以是子图(即嵌套的 Agent),条件边可以实现动态路由,Send 支持一对多的任务分发,Command 支持跨图的状态更新和导航。

本章将系统介绍基于 LangGraph 实现的四种典型多 Agent 架构:Supervisor(监督者)、Swarm(蜂群)、分层 Agent 和协作 Agent。每种模式都有其适用场景和权衡,我们会结合源码和实战代码深入分析它们的设计原理。

:::tip 本章要点

  1. Supervisor 模式------中央调度者分配任务给专业 Agent
  2. Swarm 模式------Agent 之间点对点的控制权交接
  3. 分层 Agent------多层级的 Supervisor 树形结构
  4. 协作 Agent------共享状态的对等协作
  5. 基于 LangGraph 原语实现各种模式的核心技术 :::

17.2 多 Agent 的核心抽象

17.2.1 Agent 作为子图

在 LangGraph 中,一个 Agent 本质上就是一个 CompiledStateGraph。多 Agent 系统就是一个图中嵌套了多个子图:

python 复制代码
# 每个 Agent 是一个独立的图
research_agent = create_react_agent(model, research_tools, name="researcher")
writer_agent = create_react_agent(model, writing_tools, name="writer")

# 多 Agent 系统是一个包含子图的父图
builder = StateGraph(TeamState)
builder.add_node("researcher", research_agent)
builder.add_node("writer", writer_agent)

17.2.2 通信机制的三种模式

graph TB subgraph "模式 1:共享状态" A1[Agent A] -->|写入状态| S1[共享 State] S1 -->|读取状态| B1[Agent B] end subgraph "模式 2:Command 导航" A2[Agent A] -->|"Command(goto='B')"| B2[Agent B] end subgraph "模式 3:Send 分发" Router[路由器] -->|"Send('A', task1)"| A3[Agent A] Router -->|"Send('B', task2)"| B3[Agent B] end

17.3 Supervisor 模式

17.3.1 架构概述

Supervisor 模式是最直观的多 Agent 架构:一个"主管"Agent 接收用户请求,分析任务需求,然后将子任务分配给专业 Agent。每个专业 Agent 完成后将结果返回给 Supervisor,Supervisor 决定是否需要更多工作。

graph TB User[用户] --> Sup[Supervisor Agent] Sup -->|"分配研究任务"| RA[Research Agent] Sup -->|"分配写作任务"| WA[Writer Agent] Sup -->|"分配代码任务"| CA[Coder Agent] RA -->|"研究结果"| Sup WA -->|"文章草稿"| Sup CA -->|"代码片段"| Sup Sup -->|"最终回复"| User style Sup fill:#ffe6e6,stroke:#333,stroke-width:2px

17.3.2 实现方式

python 复制代码
from typing import Annotated, Literal, TypedDict
from langchain_core.messages import BaseMessage, HumanMessage
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langgraph.prebuilt import create_react_agent
from langgraph.types import Command

class SupervisorState(TypedDict):
    messages: Annotated[list[BaseMessage], add_messages]
    next_agent: str

# 创建专业 Agent
research_agent = create_react_agent(model, research_tools, name="researcher")
writer_agent = create_react_agent(model, writing_tools, name="writer")

def supervisor_node(state: SupervisorState) -> Command:
    """Supervisor 决定下一步交给哪个 Agent"""
    response = supervisor_llm.invoke([
        SystemMessage(content="""你是一个任务分配主管。
        分析用户请求,决定交给哪个 Agent:
        - researcher: 负责信息检索和数据分析
        - writer: 负责内容创作和文档编写
        - FINISH: 任务完成,返回最终结果"""),
        *state["messages"]
    ])

    # 解析 LLM 的决策
    next_agent = parse_decision(response.content)

    if next_agent == "FINISH":
        return Command(goto=END, update={"messages": [response]})
    else:
        return Command(
            goto=next_agent,
            update={"messages": [response]}
        )

# 构建 Supervisor 图
builder = StateGraph(SupervisorState)
builder.add_node("supervisor", supervisor_node)
builder.add_node("researcher", research_agent)
builder.add_node("writer", writer_agent)

builder.add_edge(START, "supervisor")
builder.add_edge("researcher", "supervisor")  # 完成后返回 Supervisor
builder.add_edge("writer", "supervisor")

graph = builder.compile()

17.3.3 Supervisor 的关键设计

  1. 中心化决策:Supervisor 是唯一的决策者,专业 Agent 只负责执行
  2. 循环结构:Agent 完成后总是返回 Supervisor,由 Supervisor 决定下一步
  3. Command 导航 :使用 Command(goto=next_agent) 实现动态路由

17.3.4 适用场景与局限

适用场景

  • 子任务之间有明确的先后依赖
  • 需要中央协调来决定任务顺序
  • Agent 数量较少(3-5 个)

局限

  • Supervisor 是单点瓶颈
  • 每次决策都需要额外的 LLM 调用
  • 子任务之间无法直接通信

17.4 Swarm 模式

17.4.1 架构概述

Swarm 模式(蜂群模式)没有中央调度者。每个 Agent 在完成自己的工作后,可以直接将控制权交给另一个 Agent。这类似于客服系统中的"转接"------当前 Agent 判断用户的需求超出自己的能力范围时,主动转接给更合适的 Agent。

graph LR User[用户] --> A1[接待 Agent] A1 -->|"转接"| A2[技术支持 Agent] A2 -->|"转接"| A3[退款处理 Agent] A3 -->|"完成"| User A1 -.->|"也可以直接完成"| User A2 -.->|"也可以转回"| A1 style A1 fill:#e6f3ff,stroke:#333 style A2 fill:#f3ffe6,stroke:#333 style A3 fill:#ffe6f3,stroke:#333

17.4.2 实现方式

Swarm 的核心是"handoff"工具------每个 Agent 有一个特殊工具用于将控制权转交给其他 Agent:

python 复制代码
from langchain_core.tools import tool
from langgraph.types import Command

# 创建 handoff 工具
def create_handoff_tool(target_agent: str, description: str):
    @tool(name=f"transfer_to_{target_agent}")
    def handoff() -> Command:
        f"""将对话转交给 {target_agent}。{description}"""
        return Command(goto=target_agent)
    return handoff

# 定义 Agent 和它们的 handoff 能力
greeting_tools = [
    create_handoff_tool("tech_support", "当用户有技术问题时转交"),
    create_handoff_tool("billing", "当用户有账单问题时转交"),
]

tech_tools = [
    search_docs_tool,
    create_handoff_tool("greeting", "当问题解决后转回"),
]

billing_tools = [
    check_balance_tool,
    process_refund_tool,
    create_handoff_tool("greeting", "当问题解决后转回"),
]

# 创建各 Agent
greeting_agent = create_react_agent(model, greeting_tools, name="greeting")
tech_agent = create_react_agent(model, tech_tools, name="tech_support")
billing_agent = create_react_agent(model, billing_tools, name="billing")

# 构建 Swarm 图
builder = StateGraph(SwarmState)
builder.add_node("greeting", greeting_agent)
builder.add_node("tech_support", tech_agent)
builder.add_node("billing", billing_agent)
builder.add_edge(START, "greeting")  # 入口始终是接待 Agent

# 每个 Agent 可以通过 Command 跳转到任意其他 Agent
# 或者到达 END 结束对话
graph = builder.compile()

17.4.3 Swarm 的关键设计

  1. 去中心化:没有 Supervisor,Agent 之间是对等关系
  2. Handoff 工具 :通过返回 Command(goto=...) 实现控制权转移
  3. 工具即路由:LLM 通过选择"转交"工具来决定路由

17.4.4 适用场景与局限

适用场景

  • 客服系统、对话路由
  • Agent 之间是专业领域的分工
  • 同一时间只有一个 Agent 活跃

局限

  • 难以协调并行任务
  • 可能出现循环转交
  • 需要每个 Agent 都了解其他 Agent 的能力

17.5 分层 Agent

17.5.1 架构概述

分层 Agent 是 Supervisor 模式的扩展:多个 Supervisor 形成树形结构,每个 Supervisor 管理一组专业 Agent 或下级 Supervisor。这种模式适用于大规模复杂任务的分解。

graph TB CEO[CEO Agent
总调度] --> PM1[项目经理 Agent
研究方向] CEO --> PM2[项目经理 Agent
创作方向] PM1 --> R1[数据分析 Agent] PM1 --> R2[文献检索 Agent] PM2 --> W1[文案撰写 Agent] PM2 --> W2[校对编辑 Agent] style CEO fill:#ffe6e6,stroke:#333,stroke-width:2px style PM1 fill:#fff3e6,stroke:#333 style PM2 fill:#fff3e6,stroke:#333

17.5.2 实现方式

python 复制代码
# 第一层:专业 Agent
data_agent = create_react_agent(model, data_tools, name="data_analyst")
search_agent = create_react_agent(model, search_tools, name="searcher")
writing_agent = create_react_agent(model, writing_tools, name="writer")
editing_agent = create_react_agent(model, editing_tools, name="editor")

# 第二层:子 Supervisor
def research_supervisor(state):
    """研究方向的中层主管"""
    response = llm.invoke([
        SystemMessage(content="你管理数据分析和文献检索。决定分配给谁。"),
        *state["messages"]
    ])
    next_agent = parse_decision(response.content)
    return Command(goto=next_agent, update={"messages": [response]})

research_team = StateGraph(TeamState)
research_team.add_node("supervisor", research_supervisor)
research_team.add_node("data_analyst", data_agent)
research_team.add_node("searcher", search_agent)
research_team.add_edge(START, "supervisor")
research_team.add_edge("data_analyst", "supervisor")
research_team.add_edge("searcher", "supervisor")
research_graph = research_team.compile(name="research_team")

# 类似地创建 writing_team...

# 第三层:顶层 Supervisor
top_builder = StateGraph(TopState)
top_builder.add_node("ceo", ceo_supervisor)
top_builder.add_node("research_team", research_graph)  # 子图作为节点
top_builder.add_node("writing_team", writing_graph)
top_builder.add_edge(START, "ceo")
top_builder.add_edge("research_team", "ceo")
top_builder.add_edge("writing_team", "ceo")

top_graph = top_builder.compile()

17.5.3 子图作为节点的运行时行为

当 Pregel 执行到子图节点时:

  1. 子图的输入从父图状态中提取
  2. 子图内部按自己的超步循环运行
  3. 子图的 checkpoint_ns 自动嵌套(如 research_team:supervisor:task_id
  4. 子图完成后,输出合并回父图状态
sequenceDiagram participant Parent as 父图 (CEO) participant Sub as 子图 (Research Team) participant Agent as 子 Agent (Data Analyst) Parent->>Sub: 输入子任务 Note over Sub: checkpoint_ns = "research_team" Sub->>Agent: Supervisor 分配任务 Note over Agent: checkpoint_ns = "research_team:data_analyst:task_id" Agent-->>Sub: 返回结果 Sub-->>Parent: 合并输出到父图状态

17.6 协作 Agent

17.6.1 架构概述

协作 Agent 模式中,多个 Agent 共享同一个状态空间,按照预定义的顺序或条件轮流执行。每个 Agent 都能读取和修改共享状态,形成一种"接力赛"式的协作。

graph LR Start[START] --> P[规划 Agent] P --> R[研究 Agent] R --> W[写作 Agent] W --> Rev[审核 Agent] Rev -->|需要修改| R Rev -->|通过| End[END] style P fill:#e6f3ff style R fill:#f3ffe6 style W fill:#ffe6f3 style Rev fill:#fff3e6

17.6.2 实现方式

python 复制代码
class CollaborativeState(TypedDict):
    messages: Annotated[list[BaseMessage], add_messages]
    plan: str
    research_results: list[str]
    draft: str
    review_feedback: str
    iteration: int

def planner(state: CollaborativeState) -> dict:
    """规划 Agent:分析需求,制定计划"""
    response = planner_llm.invoke([
        SystemMessage(content="你是一个项目规划者。分析用户需求,制定详细的执行计划。"),
        *state["messages"]
    ])
    return {"plan": response.content, "messages": [response]}

def researcher(state: CollaborativeState) -> dict:
    """研究 Agent:根据计划收集信息"""
    response = research_llm.invoke([
        SystemMessage(content=f"执行以下研究计划:{state['plan']}"),
        *state["messages"]
    ])
    return {"research_results": [response.content], "messages": [response]}

def writer(state: CollaborativeState) -> dict:
    """写作 Agent:基于研究结果撰写"""
    context = "\n".join(state["research_results"])
    response = writer_llm.invoke([
        SystemMessage(content=f"基于以下研究结果撰写:\n{context}"),
        *state["messages"]
    ])
    return {"draft": response.content, "messages": [response]}

def reviewer(state: CollaborativeState) -> dict:
    """审核 Agent:评审草稿质量"""
    response = reviewer_llm.invoke([
        SystemMessage(content=f"审核以下草稿的质量:\n{state['draft']}"),
        *state["messages"]
    ])
    return {
        "review_feedback": response.content,
        "messages": [response],
        "iteration": state.get("iteration", 0) + 1
    }

def review_decision(state: CollaborativeState) -> str:
    """决定是否需要修改"""
    if "approved" in state["review_feedback"].lower():
        return END
    if state.get("iteration", 0) >= 3:
        return END  # 最多迭代 3 次
    return "researcher"

builder = StateGraph(CollaborativeState)
builder.add_node("planner", planner)
builder.add_node("researcher", researcher)
builder.add_node("writer", writer)
builder.add_node("reviewer", reviewer)

builder.add_edge(START, "planner")
builder.add_edge("planner", "researcher")
builder.add_edge("researcher", "writer")
builder.add_edge("writer", "reviewer")
builder.add_conditional_edges("reviewer", review_decision)

graph = builder.compile()

17.6.3 协作的关键设计

  1. 共享状态 :所有 Agent 读写同一个 CollaborativeState
  2. 专用字段:每个 Agent 有自己的输出字段(plan、research_results、draft、review_feedback)
  3. 迭代循环:reviewer 可以触发新一轮的 research -> write -> review

17.7 模式对比与选择

17.7.1 四种模式对比

graph TB subgraph Supervisor S_Hub[中央调度者] S_A[Agent A] S_B[Agent B] S_Hub --> S_A S_Hub --> S_B S_A --> S_Hub S_B --> S_Hub end subgraph Swarm SW_A[Agent A] SW_B[Agent B] SW_C[Agent C] SW_A <-->|handoff| SW_B SW_B <-->|handoff| SW_C end subgraph 分层 H_Top[顶层 Sup] H_Mid1[中层 Sup 1] H_Mid2[中层 Sup 2] H_A[Agent A] H_B[Agent B] H_Top --> H_Mid1 H_Top --> H_Mid2 H_Mid1 --> H_A H_Mid2 --> H_B end subgraph 协作 C_A[Agent A] --> C_B[Agent B] C_B --> C_C[Agent C] C_C -.->|可选循环| C_A end
维度 Supervisor Swarm 分层 协作
决策方式 中央决策 自主决策 层级决策 预定义顺序
通信模式 星形(hub-spoke) 点对点 树形 链式/环形
并行能力 可通过 Send 单一活跃 层内可并行 可通过 Send
适用规模 小型(3-5 Agent) 中型 大型 任意
复杂度
灵活性

17.7.2 选择建议

flowchart TB Q1{任务可以分解为
独立子任务?} Q1 -->|是| Q2{需要动态决策
任务分配?} Q1 -->|否| Q3{Agent 之间需要
直接交接?} Q2 -->|是| Sup[Supervisor 模式] Q2 -->|否| Collab[协作 Agent 模式] Q3 -->|是| Swarm[Swarm 模式] Q3 -->|否| Q4{Agent 数量 > 5?} Q4 -->|是| Hier[分层 Agent 模式] Q4 -->|否| Sup

17.8 高级技巧

17.8.1 并行 Agent 执行

使用 Send API 让 Supervisor 同时分配多个任务:

python 复制代码
def supervisor_with_parallel(state):
    """Supervisor 同时分配多个任务"""
    tasks = analyze_subtasks(state["messages"])
    return [
        Send(task["agent"], {"messages": state["messages"], "task": task["description"]})
        for task in tasks
    ]

17.8.2 Agent 间消息传递的状态设计

python 复制代码
class MultiAgentState(TypedDict):
    # 公共消息历史
    messages: Annotated[list[BaseMessage], add_messages]
    # 各 Agent 的中间结果(使用 reducer 合并)
    agent_outputs: Annotated[dict[str, Any], merge_dicts]
    # 当前活跃的 Agent
    active_agent: str
    # 全局上下文
    shared_context: dict[str, Any]

17.8.3 跨 Agent 的 Store 共享

python 复制代码
def research_agent_node(state, runtime: Runtime):
    """研究 Agent:结果保存到 Store"""
    results = do_research(state["messages"])
    runtime.store.put(
        ("shared", "research"),
        state["task_id"],
        {"results": results}
    )
    return {"messages": [AIMessage(content="研究完成")]}

def writer_agent_node(state, runtime: Runtime):
    """写作 Agent:从 Store 读取研究结果"""
    research = runtime.store.search(
        ("shared", "research"),
        limit=10
    )
    context = "\n".join(r.value["results"] for r in research)
    draft = write_with_context(context, state["messages"])
    return {"messages": [AIMessage(content=draft)]}

17.9 常见陷阱与最佳实践

17.9.1 避免无限循环

多 Agent 系统最常见的问题是循环转交------Agent A 认为应该交给 Agent B,Agent B 又认为应该交回 Agent A。解决方案:

python 复制代码
class SafeState(TypedDict):
    messages: Annotated[list[BaseMessage], add_messages]
    handoff_count: int
    max_handoffs: int

def safe_handoff(state: SafeState, target: str) -> Command:
    """带有次数限制的安全转交"""
    if state.get("handoff_count", 0) >= state.get("max_handoffs", 10):
        return Command(
            goto=END,
            update={"messages": [AIMessage(content="已达到最大转交次数,结束对话。")]}
        )
    return Command(
        goto=target,
        update={"handoff_count": state.get("handoff_count", 0) + 1}
    )

17.9.2 状态 Schema 设计原则

多 Agent 系统的状态设计需要平衡共享与隔离:

python 复制代码
class WellDesignedState(TypedDict):
    # 公共字段:所有 Agent 共享
    messages: Annotated[list[BaseMessage], add_messages]
    task_description: str

    # Agent 特有字段:使用 NotRequired 避免其他 Agent 被强制提供
    research_notes: NotRequired[str]
    draft_content: NotRequired[str]
    review_score: NotRequired[float]

    # 元数据字段:追踪执行过程
    current_agent: str
    iteration_count: int

17.9.3 消息历史管理

多 Agent 系统中,消息历史会快速膨胀。建议使用 pre_model_hook 或专门的消息管理节点:

python 复制代码
from langgraph.graph.message import REMOVE_ALL_MESSAGES, RemoveMessage

def trim_messages_hook(state):
    """保留最近 20 条消息,加上系统消息"""
    messages = state["messages"]
    if len(messages) > 20:
        # 保留系统消息 + 最近 20 条
        system_msgs = [m for m in messages if isinstance(m, SystemMessage)]
        recent = messages[-20:]
        return {
            "messages": [RemoveMessage(id=REMOVE_ALL_MESSAGES)] + system_msgs + recent
        }
    return {"messages": messages}

17.10 设计决策

17.9.1 为什么用子图而不是函数调用?

将 Agent 实现为子图(而非简单的函数调用)的优势:

  1. 独立 Checkpoint:每个子图有自己的 checkpoint 命名空间,支持独立的中断恢复
  2. 可视化:子图在图的可视化中自然展示层级关系
  3. 复用:同一个 Agent 子图可以在不同的多 Agent 系统中复用
  4. 流式输出:子图的流式事件自动携带命名空间前缀,便于追踪

17.9.2 为什么 Handoff 用 Command 而不是状态更新?

Swarm 模式中使用 Command(goto=target) 而不是 return {"next_agent": target} 的原因:

  1. 原子性:Command 的 goto 和 update 在同一步中执行
  2. 类型安全:goto 的目标必须是已注册的节点名
  3. 跨图能力 :Command 可以通过 graph=Command.PARENT 导航到父图

17.9.3 状态隔离 vs 状态共享

不同的多 Agent 模式对状态共享有不同的需求:

  • Supervisor:子 Agent 的状态可以通过子图隔离
  • Swarm:所有 Agent 共享同一个状态(消息历史)
  • 协作:共享状态是核心机制,每个 Agent 写入不同的字段
  • 分层:每层有自己的状态,通过子图接口传递数据

17.10 小结

本章系统介绍了基于 LangGraph 实现的四种多 Agent 模式。每种模式都是底层原语(StateGraph、Send、Command、子图)的不同组合方式:

  • Supervisor 通过 Command(goto=agent) 实现中央调度
  • Swarm 通过 handoff 工具返回 Command 实现点对点交接
  • 分层 Agent 通过子图嵌套实现多级管理
  • 协作 Agent 通过共享 State 的不同字段实现接力式协作

选择哪种模式取决于具体需求:任务是否可分解、是否需要动态路由、Agent 数量和并行需求。在实践中,这些模式也可以混合使用------例如,顶层使用 Supervisor 模式,底层使用协作模式。

LangGraph 的优势在于它不绑定任何特定的 Agent 框架模式。无论选择哪种多 Agent 架构,底层都是相同的 StateGraph + Pregel 调度器,享有相同的 Checkpoint 持久化、中断恢复、流式输出和类型安全能力。这使得开发者可以根据业务需求自由组合,而不是被框架限制在某个特定的模式中。

下一章是本书的最后一章,我们将从更高的视角审视 LangGraph 的设计模式与架构决策------Pregel 计算模型的选择、Channel 版本追踪的巧妙、Checkpoint 时间旅行的实现、中断/恢复机制的全貌,以及如何基于这些思想构建你自己的工作流引擎。

相关推荐
杨艺韬4 小时前
LangGraph设计与实现-第13章-流式输出与调试
langchain·agent
杨艺韬4 小时前
LangGraph设计与实现-第14章-Runtime 与 Context
langchain·agent
杨艺韬4 小时前
LangGraph设计与实现-第18章-设计模式与架构决策
langchain·agent
杨艺韬4 小时前
LangGraph设计与实现-第11章-子图与嵌套
langchain·agent
杨艺韬4 小时前
LangGraph设计与实现-第16章-预构建 Agent 组件
langchain·agent
杨艺韬4 小时前
LangGraph设计与实现-第5章-图编译:从 StateGraph 到 CompiledStateGraph
langchain·agent
杨艺韬4 小时前
LangGraph设计与实现-第12章-Send 与动态并行
langchain·agent
杨艺韬4 小时前
LangGraph设计与实现-第8章-Checkpoint 持久化
langchain·agent
杨艺韬4 小时前
LangGraph设计与实现-第4章-Channel 状态管理与 Reducer
langchain·agent