一个 Agent 刚开始往往比较简单:接一个模型,提供几个 Tool,再加 System Prompt,就可以完成很多任务。

随着功能不断叠加,Agent 的结构也会逐渐变得复杂。当搜索、数据库、代码执行、订单查询、客服处理等能力都集中到同一个 Agent 中,模型每次都需要面对大量工具;不同业务的提示词混杂在一起,消息历史也会越拉越长。一旦任务变复杂,工具选择、上下文污染以及调试都会变得更加棘手。

这时,把不同职责分给多个 Agent 往往更合适。

但 Multi-Agent 的难点不在于“创建多少个 Agent”,而在于它们之间怎样交接工作:当前由谁处理,哪些信息需要传过去,任务完成后由谁继续。

LangChain 目前提供了 Subagents、Handoffs、Router 等几种常见的 Multi-Agent 组织方式。本文主要讨论其中关系最紧密的 Supervisor、Handoff 和 Swarm。

一、多智能体的适用边界

如果单 Agent 已足以稳定解决问题,就不必为了架构上的“完整”而将其拆成多个 Agent。

如果拆成多个 Agent 反而会增加模型调用、状态维护和排查成本。

更适合使用 Multi-Agent 的情况,通常是系统中已经存在了一些比较清楚的业务边界。

例如一个企业助手同时负责:

  • 邮件和日历;
  • CRM 和客户信息;
  • 内部知识库;
  • 技术支持。

这几类任务需要的 Tool 不同,提示词不同,数据权限也可能不同。

如果全部放到一个 Agent 中,处理邮件时,它也要看到技术支持和 CRM 工具;排查故障时,又可能带着大量与当前问题无关的邮件上下文。

而如果将它拆分为多个 Agent 后,每个 Agent 只保留自己需要的工具和上下文,职责会清楚很多。

接下来需要解决三个问题:

代码片段Text
现在由谁处理?
它需要拿到哪些信息?
处理完之后由谁继续?

Supervisor、Handoff 和 Swarm 的差别,主要就在这里。

二、Supervisor 模式

Supervisor 是比较常见的一种组织方式。

系统保留一个主 Agent。用户请求进入主 Agent 后,由它判断任务应该交给哪个子 Agent,等子 Agent 返回结果后,再继续处理或者整理最终答案。

结构大致是:

代码片段Text
             Research Agent

User → Supervisor → Writer Agent

               Data Agent

用户主要和 Supervisor 交互。

Research Agent、Writer Agent 这些子 Agent,更像是 Supervisor 可以调用的专业能力。

比如用户要求生成一份技术报告,Supervisor 可以先让 Research Agent 收集材料,再把整理好的内容交给 Writer Agent。用户只看到最终结果,不需要知道中间调用了几个 Agent。

现在比较常见的实现方式,是把子 Agent 包装成 Tool,再交给主 Agent 调用。

子 Agent 作为 Tool

子 Agent 作为 Tool 的实现方面是在 Tool 函数的内部再调用一个 Agent,其调用关系如下:

代码片段Text
Supervisor

   Tool

Subagent

例如:

代码片段Python
from langchain.agents import create_agent
from langchain.tools import tool
 
 
research_agent = create_agent(
    model="openai:gpt-5.5",
    tools=[],
    system_prompt=(
        "你是一名技术研究员。"
        "根据任务整理技术事实和关键结论,返回精炼结果。"
    ),
)
 
 
@tool
def research(topic: str) -> str:
    """研究一个技术主题并返回整理后的材料。"""
 
    result = research_agent.invoke(
        {
            "messages": [
                {
                    "role": "user",
                    "content": topic,
                }
            ]
        }
    )
 
    return result["messages"][-1].content
 
 
supervisor = create_agent(
    model="openai:gpt-5.5",
    tools=[research],
    system_prompt=(
        "你负责回答技术问题。"
        "需要补充技术背景时调用 research,"
        "再根据研究结果组织最终回答。"
    ),
)
 
 
result = supervisor.invoke(
    {
        "messages": [
            {
                "role": "user",
                "content": "介绍 RAG 及其功能。",
            }
        ]
    }
)
 
print(result["messages"][-1].content)

远行结果如下:

代码片段Text
RAG(Retrieval-Augmented Generation,检索增强生成)是一种让大模型结合外部知识库回答问题的技术。
它先根据用户问题检索相关资料,再把检索结果与问题一起交给大模型生成答案。
核心价值是让模型使用最新、私有或领域知识,提高回答准确性,并减少幻觉。

这里有一个细节很重要:

代码片段Python
return result["messages"][-1].content

Research Agent 内部可能调用了很多工具,也可能产生了不少中间消息,但 Supervisor 最后只拿到整理后的结果。

假设 Research Agent 连续做了十次搜索,又查了三次数据库,这些过程信息通常没必要全部进入主 Agent 的上下文。

只返回最后的研究结果,可以把两个 Agent 的工作区分开。

Supervisor 需要知道的是“研究结论是什么”,而不必知道 Research Agent 每一步具体做了什么。

这种上下文隔离,是 Supervisor 模式很实用的一点。

三、Handoff 的状态流转

Supervisor 的特点是主 Agent 一直留在中间。

有些业务更适合直接把会话交给另一个 Agent。

比如用户一开始咨询产品价格,由 Sales Agent 处理。聊了一会儿,用户开始询问登录失败的问题。

如果仍然让 Sales Agent 接收用户每一句话,再转给 Support Agent,再把答案转回来,流程会比较绕。

更直接的方式是让 Support Agent 接管后续会话。

这就是 Handoff。

系统一般会在 State 中保存当前负责处理的 Agent,例如:

代码片段Text
active_agent = "support_agent"

也可能保存的是当前业务阶段:

代码片段Text
current_step = "support"

当 Agent 调用转交工具后,State 被更新,后面的执行逻辑再根据这个状态决定进入哪个 Agent。

可使用 Command 来完成这件事。

Command 的用法

Command 从这里导入:

代码片段Python
from langgraph.types import Command

它可以在一次返回中同时更新 State,并指定接下来执行哪个节点。

例如:

代码片段Python
Command(
    goto="support_agent",
    update={
        "active_agent": "support_agent"
    }
)

其中:

代码片段Python
update

负责修改 State。

代码片段Python
goto

指定下一步进入哪个节点。

如果目标节点在当前子图之外,位于父 Graph 中,还可以加上:

代码片段Python
graph=Command.PARENT

我们看一个完整的 Handoff 示例:

代码片段Python
from langchain.messages import AIMessage, ToolMessage
from langchain.tools import ToolRuntime, tool
from langgraph.types import Command
 
 
@tool
def transfer_to_support(
    runtime: ToolRuntime,
) -> Command:
    """把当前会话转交给技术支持 Agent。"""
 
    last_ai_message = next(
        message
        for message in reversed(runtime.state["messages"])
        if isinstance(message, AIMessage)
    )
 
    transfer_message = ToolMessage(
        content="Transferred to support agent",
        tool_call_id=runtime.tool_call_id,
    )
 
    return Command(
        goto="support_agent",
        graph=Command.PARENT,
        update={
            "active_agent": "support_agent",
            "messages": [
                last_ai_message,
                transfer_message,
            ],
        },
    )

执行这个 Tool 后,状态会变成:

代码片段Text
active_agent = support_agent
next_node    = support_agent

这段代码里又出现了两个新对象。

ToolRuntime 是 Tool 执行时可以访问的运行时信息。

通过:

代码片段Python
runtime.state

可以读取当前 State。

通过:

代码片段Python
runtime.tool_call_id

可以拿到本次 Tool Call 的 ID。

ToolMessage 则表示工具调用的返回消息。

为什么转交 Agent 时还需要补一条 ToolMessage

因为从模型的消息记录来看,它刚刚发起了一次 Tool Call,这次调用需要有对应的返回消息。

即使这个 Tool 做的只是“把任务交给 Support Agent”,这次工具调用本身仍然要完整结束。

如果只改:

代码片段Text
active_agent

却没有补齐 Tool Message,下一个 Agent 拿到的消息历史可能会停在一次没有完成的 Tool Call 上。

所以一次 Handoff 同时涉及两部分信息:

代码片段Text
Graph 接下来执行哪里

模型当前看到哪些消息

路由状态和消息状态需要保持一致。

四、Swarm 的协作方式

如果多个 Agent 都能够把任务交给其他 Agent,系统就逐渐接近 Swarm 模式。

Supervisor 有一个固定的中心:

代码片段Text
           Agent A

User → Supervisor → Agent B

           Agent C

Swarm 没有居中的调度 Agent:

代码片段Text
Agent A ⇄ Agent B
   ↘       ↙
     Agent C

当前 Agent 根据任务情况决定是否把控制权交给其他 Agent。

例如:

代码片段Text
Sales → Support
Support → Billing
Billing → Sales

每个 Agent 只需要知道自己可以转交给谁。

langgraph-swarm 提供了两个比较直接的接口:

代码片段Python
create_handoff_tool()
create_swarm()

create_handoff_tool() 用来创建 Agent 之间的转交工具。

create_swarm() 接收多个 Agent,生成管理这些 Agent 的 StateGraph

返回的还是 Graph Builder,需要继续调用:

代码片段Python
compile()

才能得到可以执行的 Graph。

要使用 Swarm,需安装 langgraph-swarm 依赖:

代码片段Bash
pip install -U langgraph-swarm langchain langgraph langchain-openai

下面创建两个 Agent,一个负责产品和价格咨询,一个负责技术支持:

代码片段Python
from langchain.agents import create_agent
from langgraph.checkpoint.memory import InMemorySaver
from langgraph_swarm import create_handoff_tool, create_swarm
 
 
sales = create_agent(
    model="openai:gpt-5.5",
    tools=[
        create_handoff_tool(
            agent_name="support",
            description="用户需要技术排查时转交给 support",
        )
    ],
    system_prompt=(
        "你负责产品、价格和购买咨询。"
        "涉及登录故障或技术问题时转交给 support。"
    ),
    name="sales",
)
 
 
support = create_agent(
    model="openai:gpt-5.5",
    tools=[
        create_handoff_tool(
            agent_name="sales",
            description="用户需要价格或购买咨询时转交给 sales",
        )
    ],
    system_prompt=(
        "你负责技术支持。"
        "帮助用户处理登录、账号和系统错误。"
    ),
    name="support",
)
 
 
workflow = create_swarm(
    [sales, support],
    default_active_agent="sales",
)
 
 
app = workflow.compile(
    checkpointer=InMemorySaver()
)
 
 
config = {
    "configurable": {
        "thread_id": "user-001"
    }
}
 
 
first = app.invoke(
    {
        "messages": [
            {
                "role": "user",
                "content": "我的账号登录不了。",
            }
        ]
    },
    config,
)
 
print(first["active_agent"])
print(first["messages"][-1].content)
 
 
second = app.invoke(
    {
        "messages": [
            {
                "role": "user",
                "content": "提示 invalid session。",
            }
        ]
    },
    config,
)
 
print(second["active_agent"])
print(second["messages"][-1].content)

运行结果如下:

代码片段Text
support
请告诉我登录失败时显示的具体错误信息。

support
invalid session 通常表示当前会话已经失效。
可以重新登录,并检查相关 Session 或 Token 是否已经过期。

第一轮开始时,默认 Agent 是:

代码片段Text
sales

Sales Agent 判断这是技术问题,于是调用 Handoff Tool,把任务交给 support

第二轮用户继续输入错误信息时,系统没有重新回到 sales,而是继续由 support 处理。

这里依赖两个关键要素:

代码片段Python
checkpointer=InMemorySaver()

和:

代码片段Python
thread_id = "user-001"

InMemorySaver 是内存中的 Checkpointer,用来保存 Graph 的 Checkpoint。

thread_id 用来标识同一段会话。

第一轮结束后,类似这样的状态会被保存:

代码片段Text
active_agent = support

第二轮仍然使用相同的 thread_id,Graph 就能恢复上一轮状态,知道当前应该继续交给 support

所以在多轮 Swarm 中,当前活动 Agent 本身也是 State 的一部分。

五、上下文与状态

前面提到,在 Multi-Agent 中,真正麻烦的地方往往不是“怎么从 Agent A 跳到 Agent B”,而在于 Agent B 应该拿到多少信息。

假设 Support Agent 为了排查登录问题做了这些操作:

代码片段Text
读取用户信息
查询登录日志
检查 Session
检查 Token
查询最近登录记录

如果把每次 Tool 调用、完整日志和中间结果都传给下一个 Agent,上下文很快就会变得很大。

而下一个 Agent 真正需要的内容,可能只有:

代码片段JSON
{
  "customer_id": "C1024",
  "issue": "login_failed",
  "error_code": "invalid_session",
  "checked": [
    "重新登录",
    "清理 Cookie",
    "检查 Session"
  ]
}

这类已经确认的业务信息,比较适合放进共享 State。

至于搜索过程、临时结果、某个 Agent 的内部工具调用,则可以继续留在自己的上下文中。

换句话说,Agent 之间最好传递处理结果,而不是把整个执行过程原样带过去。

如果这一层边界没处理好,即使把一个大 Agent 分成五个小 Agent,上下文仍然可能越来越乱。

实际项目里,可以按这样的顺序来设计:

代码片段Text
确定职责边界

设计共享 State

确定需要传递的上下文

创建 Agent

定义路由和 Handoff

如果先确认清楚 State 和上下文边界,后面 Agent 之间的关系往往就会简单许多。

六、三种模式的选择

Supervisor、Handoff 和 Swarm 都属于 Multi-Agent,但适合的任务不同。

模式控制方式用户主要和谁交互上下文特点常见场景
Supervisor主 Agent 统一调度Supervisor子 Agent 上下文容易隔离研究、分析、任务编排
Handoff根据 State 转交当前 Agent强调会话连续性客服、审批、多阶段流程
SwarmAgent 之间互相转交当前活动 Agent需要保存活动 Agent多领域专家协作

如果系统需要一个统一入口,由它判断任务交给谁,再把结果整理回来,Supervisor 比较合适。

例如:

代码片段Text
资料研究
数据分析
内容生成
多步骤报告

这类任务通常需要一个 Agent 保持整体视角。

如果业务中存在明确的接管关系,Handoff 更合适。

例如:

代码片段Text
售前 → 技术支持
客服 → 退款专员
自动处理 → 人工审批

这种场景更在意“下一步由谁继续处理”。

如果多个专业 Agent 都可能把会话交给其他 Agent,而且下一轮还要由上一次的 Agent 继续处理,就可以考虑 Swarm。

另外还有一类任务:

代码片段Text
查询财务数据
查询市场数据
查询产品数据
最后统一分析

几个子任务之间相对独立,更适合 Supervisor 或 Router,也更容易并行执行。

Handoff 和 Swarm 更适合连续的职责切换。

实际系统里,这些模式也可以混合使用。

例如外层由 Supervisor 接收复杂任务,其中客服部分使用 Handoff;某个 Agent 内部还可以是一个包含多个 Node 的 LangGraph Subgraph。

没有必要为了架构整齐,把整个系统硬套成同一种结构。

总结

本文围绕 Supervisor、Handoff 和 Swarm 介绍了 LangGraph 中几种常见的 Multi-Agent 组织方式。它们的差别主要体现在任务由谁调度、会话由谁接管,以及上下文和状态怎样在 Agent 之间传递。

实际项目中,不必一开始就追求复杂的多 Agent 架构。先确定职责边界和共享 State,再选择集中调度、职责交接或动态协作,通常更容易得到清晰、稳定,也更容易维护的系统。