课程0基础Agent开发课 / LangGraph / LangGraph-Multi-Agent多智能体协作架构
— 19 min read

LangGraph-Multi-Agent多智能体协作架构

单个 Agent 的能力有天花板。当任务足够复杂、足够专业,或者足够长时,单 Agent 开始暴露它的结构性缺陷。Multi-Agent 架构(多智能体架构:多个 AI Agent 各司其职,协同完成单个 Agent 无法独立胜任的复杂任务)不是技术炫技,而是在特定场景下解决单 Agent 无法解决的问题的工程方案。

LangGraph Multi-Agent:多智能体协作架构

单个 Agent 的能力有天花板。当任务足够复杂、足够专业,或者足够长时,单 Agent 开始暴露它的结构性缺陷。Multi-Agent 架构(多智能体架构:多个 AI Agent 各司其职,协同完成单个 Agent 无法独立胜任的复杂任务)不是技术炫技,而是在特定场景下解决单 Agent 无法解决的问题的工程方案。


1.1 为什么单 Agent 不够用

LangGraph 多 Agent 协作架构图
LangGraph Supervisor 模式 — Supervisor 节点动态调度多个 Worker Agent,通过共享 State 通信

1.1.1 上下文长度限制

LLM 的上下文窗口是有限的(各模型具体数字以官方文档为准,通常在 128K 到 200K token 之间)。看起来很大,但一个中等规模的代码库,或者一份几十页的报告,轻松就能打爆这个限制。

单 Agent 处理长任务时,会遭遇两个问题:一是物理上超出 context window(上下文窗口:LLM 一次能处理的文本长度上限,超过则无法运行)无法运行,二是即使没超出,在超长 context 里 LLM 的注意力会分散,"迷失在中间"(Lost in the Middle 现象:LLM 对长文本开头和结尾的关注度远高于中间部分),中间部分的信息容易被忽略。

1.1.2 专业化分工

通用 LLM 什么都会,但什么都不精。一个任务如果需要"先搜索资料,再写代码,再写报告",让同一个 Agent 用同一个 system prompt 做这三件事,效果通常不如让三个各自优化过 system prompt 的专业 Agent 分工协作。

就像公司里不会用一个人既做销售、又做研发、又做财务——术业有专攻,专业化带来效率。

1.1.3 并行执行

某些任务天然可以并行。研究一个课题时,同时搜索"历史背景"和"最新进展",比串行搜索节省一半时间。单 Agent 本质上是串行的,Multi-Agent 架构可以让多个 Agent 并发执行,大幅缩短总耗时。


1.2 LangGraph 的 Multi-Agent 模式

LangGraph 实现 Multi-Agent 主要有两种模式:

模式 结构 适用场景 控制复杂度
Supervisor(监督者)模式 一个 Supervisor Agent 动态调度多个 Worker Agent 任务类型多样、需要动态决策 中等
Hierarchical(层级)模式 Supervisor 管理 Sub-Supervisor,Sub-Supervisor 再管理 Worker 超大规模、多层级任务分解
Network(网状)模式 Agent 之间可以相互调用,无中心协调者 去中心化协作,各 Agent 自治 很高

本文重点介绍最常用的 Supervisor 模式,它覆盖了绝大多数实际场景。


1.3 Supervisor 模式架构

Supervisor 模式的核心思路:有一个 Supervisor Agent 负责理解任务、决定当前应该让哪个 Worker Agent 来处理,Worker 执行完把结果写回共享 State,Supervisor 再决定下一步。

分配搜索任务

分配写作任务

任务完成

返回搜索结果

返回写作内容

需要继续

需要继续

用户输入

Supervisor Agent
任务协调者

Researcher Agent
负责信息收集

Writer Agent
负责内容生成

输出最终结果

Supervisor 本身也是一个节点,它的职责不是执行任务,而是决策:下一步应该调用哪个 Worker,或者任务是否已经完成。


1.4 完整代码示例:研究员 + 写作员协作系统

下面是一个完整可运行的示例,模拟"给定主题,自动完成资料研究和文章撰写"的协作系统。

python
# multi_agent_demo.py
# 演示 Supervisor 模式的 Multi-Agent 协作
# 包含:Supervisor Agent、Researcher Agent、Writer Agent

import operator
from typing import TypedDict, List, Annotated, Literal
from langgraph.graph import StateGraph, START, END

# ============================================================
# 1. 定义共享 State
# 所有 Agent 读写同一个 State,通过 State 实现通信
# ============================================================

class MultiAgentState(TypedDict):
    topic: str                          # 研究主题
    search_results: List[str]           # 研究员收集的资料
    draft: str                          # 写作员的草稿
    final_output: str                   # 最终输出
    # operator.add 保证日志是追加而非覆盖,避免节点间相互覆盖历史记录
    messages: Annotated[List[str], operator.add]
    # 下一步执行哪个节点,由 Supervisor 设置
    next_worker: str
    # 记录迭代次数,防止死循环
    iteration_count: int


# ============================================================
# 2. Researcher Agent
# 职责:收集信息。实际场景这里会调用搜索工具或爬虫。
# ============================================================

def researcher_agent(state: MultiAgentState) -> dict:
    """
    研究员 Agent:负责信息收集。
    这里用模拟数据代替真实搜索,实际使用时接入 Tavily、Bing 等搜索 API。
    """
    topic = state["topic"]

    # 模拟搜索结果(实际场景替换为真实搜索调用)
    simulated_results = [
        f"【基础知识】{topic} 的定义:指通过...方式实现...的技术方法。",
        f"【应用案例】{topic} 在工业界的主要应用包括:自动化流程、智能决策支持等。",
        f"【最新进展】截至 2026 年,{topic} 领域的主要突破在于...研究方向取得了显著进展。",
        f"【常见问题】实践中 {topic} 面临的挑战主要有:数据质量、模型可解释性、部署成本。",
    ]

    return {
        "search_results": simulated_results,
        # messages 是 Annotated[List, operator.add],返回列表会被追加,不会覆盖
        "messages": [f"[Researcher] 完成资料收集,找到 {len(simulated_results)} 条相关信息"],
    }


# ============================================================
# 3. Writer Agent
# 职责:基于研究结果生成文章草稿。
# ============================================================

def writer_agent(state: MultiAgentState) -> dict:
    """
    写作员 Agent:基于 Researcher 收集的资料撰写文章。
    实际场景这里会调用 LLM,传入 search_results 作为上下文。
    """
    topic = state["topic"]
    search_results = state.get("search_results", [])

    if not search_results:
        # 防御性处理:如果没有搜索结果,不应该被调用到,但加上保护更稳健
        return {
            "draft": f"[错误] 没有可用的研究资料,无法生成关于「{topic}」的文章。",
            "messages": ["[Writer] 错误:没有研究资料,无法写作"],
        }

    # 模拟基于资料生成文章(实际替换为 LLM 调用)
    results_text = "\n".join(f"- {r}" for r in search_results)
    draft = f"""# {topic} 综述

## 概述

{topic} 是当前技术领域的重要研究方向。

## 核心内容

基于最新研究资料整理如下:

{results_text}

## 总结

综合以上资料,{topic} 在实际应用中具有重要价值,未来发展空间广阔。
"""

    return {
        "draft": draft,
        "messages": [f"[Writer] 完成文章草稿,共 {len(draft)} 字符"],
    }


# ============================================================
# 4. Finalizer Agent
# 职责:对草稿做最终润色和输出
# ============================================================

def finalizer_agent(state: MultiAgentState) -> dict:
    """
    最终整合节点:检查草稿质量,生成最终输出。
    实际场景这里可以做格式检查、事实核对、排版优化等。
    """
    draft = state.get("draft", "")

    if not draft or draft.startswith("[错误]"):
        final_output = "任务未能完成,请检查错误信息后重试。"
    else:
        # 模拟最终润色(添加元信息)
        final_output = draft + f"\n\n---\n*本文由 Multi-Agent 系统自动生成,共经历 {state['iteration_count']} 次协作迭代。*"

    return {
        "final_output": final_output,
        "messages": ["[Finalizer] 最终报告生成完成"],
    }


# ============================================================
# 5. Supervisor Agent
# 这是整个系统的核心:决定下一步调用哪个 Worker
# ============================================================

def supervisor_agent(state: MultiAgentState) -> dict:
    """
    Supervisor:任务调度中枢。
    根据当前 State 决定下一步该做什么。

    决策逻辑:
    1. 没有搜索结果 → 先去搜索
    2. 有搜索结果但没有草稿 → 去写作
    3. 有草稿 → 进入最终化
    4. 迭代次数超限 → 强制结束,防止死循环
    """
    iteration = state.get("iteration_count", 0) + 1

    # 安全阀:超过最大迭代次数强制结束
    # 这是 Multi-Agent 系统中防止死循环的关键机制
    MAX_ITERATIONS = 5
    if iteration > MAX_ITERATIONS:
        return {
            "next_worker": "finalizer",
            "iteration_count": iteration,
            "messages": [f"[Supervisor] 达到最大迭代次数 {MAX_ITERATIONS},强制结束"],
        }

    has_research = bool(state.get("search_results"))
    has_draft = bool(state.get("draft"))

    if not has_research:
        next_step = "researcher"
        reason = "尚未收集资料,分配给研究员"
    elif not has_draft:
        next_step = "writer"
        reason = "资料已收集,分配给写作员"
    else:
        next_step = "finalizer"
        reason = "草稿已完成,进入最终化"

    return {
        "next_worker": next_step,
        "iteration_count": iteration,
        "messages": [f"[Supervisor] 第 {iteration} 次决策:{reason}{next_step}"],
    }


# ============================================================
# 6. 条件路由函数
# 根据 Supervisor 设置的 next_worker 字段决定跳转到哪个节点
# ============================================================

def route_by_supervisor(state: MultiAgentState) -> Literal["researcher", "writer", "finalizer"]:
    """
    条件边的路由函数。
    读取 Supervisor 写入的 next_worker 字段,返回下一个节点名称。
    这是 Supervisor 模式的关键连接点。
    """
    return state["next_worker"]


# ============================================================
# 7. 构建图
# ============================================================

def build_multi_agent_graph():
    graph = StateGraph(MultiAgentState)

    # 注册所有节点
    graph.add_node("supervisor", supervisor_agent)
    graph.add_node("researcher", researcher_agent)
    graph.add_node("writer", writer_agent)
    graph.add_node("finalizer", finalizer_agent)

    # 入口:从 START 进入 Supervisor
    graph.add_edge(START, "supervisor")

    # Supervisor 出发的条件边:根据 next_worker 字段决定去哪里
    graph.add_conditional_edges(
        "supervisor",
        route_by_supervisor,
        {
            "researcher": "researcher",
            "writer": "writer",
            "finalizer": "finalizer",
        }
    )

    # Worker 完成后,回到 Supervisor 进行下一轮决策
    # 这形成了 Worker → Supervisor → Worker 的循环结构
    graph.add_edge("researcher", "supervisor")
    graph.add_edge("writer", "supervisor")

    # Finalizer 完成后直接结束
    graph.add_edge("finalizer", END)

    return graph.compile()


# ============================================================
# 8. 运行演示
# ============================================================

def run_demo():
    app = build_multi_agent_graph()

    initial_state = {
        "topic": "大型语言模型的 Agent 应用",
        "search_results": [],
        "draft": "",
        "final_output": "",
        "messages": [],
        "next_worker": "",
        "iteration_count": 0,
    }

    print("=" * 60)
    print(f"研究主题:{initial_state['topic']}")
    print("=" * 60)

    # 使用 stream 模式观察每个节点的执行过程
    for event in app.stream(initial_state, stream_mode="updates"):
        for node_name, updates in event.items():
            if node_name.startswith("__"):
                continue
            # 打印每个节点产生的日志消息
            new_messages = updates.get("messages", [])
            for msg in new_messages:
                print(msg)

    # 获取最终结果
    final_state = app.invoke(initial_state)
    print("\n" + "=" * 60)
    print("最终输出:")
    print("=" * 60)
    print(final_state["final_output"])

    print("\n执行日志:")
    for msg in final_state["messages"]:
        print(f"  {msg}")


if __name__ == "__main__":
    run_demo()

1.5 Agent 间通信的两种方式

1.5.1 方式一:通过共享 State 通信(推荐)

上面的示例就是这种方式。所有 Agent 读写同一个 State 对象,通过字段传递信息:Researcher 把结果写入 search_results,Writer 从 search_results 读取,再把结果写入 draft

优点:简单直接,State 天然具有可观测性(可以随时查看当前状态),也支持持久化和断点续跑。

1.5.2 方式二:通过子图直接调用

对于复杂的子系统,可以把一个完整的 Agent 封装成子图,在父图中作为单个节点调用。

python
from langgraph.graph import StateGraph, START, END

# 子图:独立的研究 Agent
def build_research_subgraph():
    sub = StateGraph(ResearchState)
    sub.add_node("fetch", fetch_node)
    sub.add_node("summarize", summarize_node)
    sub.add_edge(START, "fetch")
    sub.add_edge("fetch", "summarize")
    sub.add_edge("summarize", END)
    # 编译子图
    return sub.compile()

# 父图中把子图作为一个节点注册
research_subgraph = build_research_subgraph()

parent_graph = StateGraph(MainState)
# 子图可以直接作为节点传入,LangGraph 会自动处理 State 的映射
parent_graph.add_node("research", research_subgraph)
# ...

子图的好处是封装性强,子图内部可以有自己的 State 类型,父图只关心子图的输入和输出,不需要了解内部细节。适合把复杂子系统模块化。


1.6 防止死循环的三种机制

Multi-Agent 系统最容易出的生产事故之一:Agent 陷入死循环,无限互相调用,直到 token 费用爆炸或触发超时。

机制一:迭代计数器(最常用)

在 State 里维护一个 iteration_count 字段,Supervisor 每次决策时递增,超过阈值强制路由到结束节点。

python
MAX_ITERATIONS = 10

def supervisor(state):
    count = state.get("iteration_count", 0) + 1
    if count >= MAX_ITERATIONS:
        return {"next_worker": "end", "iteration_count": count}
    # 正常决策逻辑...

机制二:LangGraph 内置的 recursion_limit

编译图时或调用时设置最大递归深度:

python
# 在 invoke 时设置,超过后抛出 GraphRecursionError
app.invoke(initial_state, config={"recursion_limit": 25})

机制三:任务完成标志位

在 State 里设置明确的完成标志,Supervisor 每次检查是否已完成:

python
class State(TypedDict):
    is_complete: bool  # 明确的完成标志

def supervisor(state):
    if state.get("is_complete"):
        return {"next_worker": "end"}
    # ...

推荐同时使用机制一和机制二,形成双重保险。


1.7 Supervisor 模式的局限

Supervisor 模式不是万能的,有几个场景要注意:

Supervisor 成为瓶颈:所有决策都经过 Supervisor,如果 Supervisor 本身调用了 LLM 来决策(而非基于规则),每轮调度都有 LLM 延迟。任务链长的时候,调度开销显著。

调试困难:Agent 的输出影响 Supervisor 的决策,Supervisor 的决策影响下一个 Agent,出现错误时定位原因需要逐步追踪 State 的变化。

Worker 之间无法直接通信:在 Supervisor 模式下,Worker 之间不能直接对话,必须通过 State 或经由 Supervisor 中转。如果需要 Worker 之间频繁交互,可以考虑 Network 模式。


1.8 小结

Supervisor 模式的设计要点:

  • State 里设好通信字段(哪些字段是 Annotated[list, operator.add] 的追加型(Annotated 是 Python 类型工具,用于给字段附加合并规则;operator.add 表示新旧列表相加而非覆盖),哪些是直接覆盖型)
  • Supervisor 的路由逻辑尽量基于规则而非 LLM,降低延迟和不确定性
  • 总是加上最大迭代次数限制

下一篇讲错误处理与容错机制。Multi-Agent 系统里,Worker 随时可能失败,这不是异常情况,是常态。

本页目录