RAG 进阶:Agentic RAG 与多智能体协作(系列三·第3篇):Swarm 模式与 Handoff 实战

没有主管,Agent 之间怎么自己"转接电话"?

上一篇 Supervisor 是"项目经理派活",这篇 Swarm 是"同事之间互相转接"------没有中央调度,谁发现自己搞不定,就把控制权交给更合适的人。


一、先搞清:Swarm 到底解决什么问题

Supervisor 模式里,主管是单点瓶颈。每次路由都要问主管"下一步找谁",多一轮 LLM 调用就多一笔开销。

Swarm 的思路更轻:每个 Agent 自带"转接工具",自己决定要不要移交、移交给谁。 底层靠 Command(goto=agent_name, graph=Command.PARENT) 实现子图间跳转。

适用场景:

  • 多轮对话中用户需求会"跑题"(订机票 → 顺便订酒店 → 查天气)
  • 流程难以预先画完整路由图
  • 需要降低 LLM 调用次数(省掉主管那一跳)

代价:调试更难,链路分散,容易互相推诿或死循环。


二、从零实现:旅行规划 Swarm

1. 安装依赖

复制代码
pip install -U langgraph langgraph-swarm langchain-openai

langgraph-swarm 封装了 create_handoff_tool 和 create_swarm,是官方推荐的快捷方式。

2. 定义工具

python 复制代码
from langchain_core.tools import tool

@tool
def search_flights(from_city: str, to_city: str, date: str) -> str:
    """搜索航班。当用户需要查询或预订航班时调用。"""
    return f"找到 {from_city}→{to_city} {date} 的航班,最低 ¥1200。"

@tool
def book_flight(flight_id: str) -> str:
    """预订航班。当用户确认预订时调用。"""
    return f"航班 {flight_id} 预订成功。"

@tool
def search_hotels(city: str, check_in: str, check_out: str) -> str:
    """搜索酒店。当用户需要查询或预订酒店时调用。"""
    return f"找到 {city} {check_in} 至 {check_out} 的酒店,最低 ¥450/晚。"

@tool
def book_hotel(hotel_id: str) -> str:
    """预订酒店。当用户确认预订时调用。"""
    return f"酒店 {hotel_id} 预订成功。"

关键点:每个工具的 docstring 是写给模型看的,不是写给人看的。描述不清,模型就选错工具。

3. 创建移交工具

ini 复制代码
from langgraph_swarm import create_handoff_tool

# 机票助手可以转给酒店助手
transfer_to_hotel = create_handoff_tool(
    agent_name="hotel_assistant",
    description="当用户需要查询或预订酒店时,转给酒店助手"
)

# 酒店助手可以转给机票助手
transfer_to_flight = create_handoff_tool(
    agent_name="flight_assistant",
    description="当用户需要查询或预订航班时,转给机票助手"
)

4. 创建两个专家 Agent

ini 复制代码
from langgraph.prebuilt import create_react_agent
from langchain_openai import ChatOpenAI

llm = ChatOpenAI(model="gpt-4o", temperature=0)

flight_assistant = create_react_agent(
    llm,
    tools=[search_flights, book_flight, transfer_to_hotel],
    name="flight_assistant",
    prompt="你是航班预订专家。只处理航班相关请求,遇到酒店需求就转接。"
)

hotel_assistant = create_react_agent(
    llm,
    tools=[search_hotels, book_hotel, transfer_to_flight],
    name="hotel_assistant",
    prompt="你是酒店预订专家。只处理酒店相关请求,遇到航班需求就转接。"
)

5. 组建 Swarm

ini 复制代码
from langgraph_swarm import create_swarm
from langgraph.checkpoint.memory import InMemorySaver

checkpointer = InMemorySaver()

swarm = create_swarm(
    agents=[flight_assistant, hotel_assistant],
    default_active_agent="flight_assistant",  # 默认启动谁
)

app = swarm.compile(checkpointer=checkpointer)

create_swarm 会自动扫描每个 Agent 身上的 handoff 工具,构建路由表,无需手动写调度逻辑。

6. 运行

ini 复制代码
config = {"configurable": {"thread_id": "trip-001"}, "recursion_limit": 15}

for chunk in app.stream(
    {"messages": [{"role": "user", "content": "帮我订一张北京到上海下周五的机票"}]},
    config=config,
    stream_mode="messages"
):
    print(chunk[0].content, end="", flush=True)

输出轨迹大致是:

makefile 复制代码
flight_assistant: 调用 search_flights → 找到航班
flight_assistant: 调用 book_flight → 预订成功
flight_assistant: 需要转接 → 调用 transfer_to_hotel
hotel_assistant: 收到转接,等待用户输入酒店需求

用户接着说"再帮我订一家外滩附近的酒店":

makefile 复制代码
hotel_assistant: 调用 search_hotels → 找到酒店
hotel_assistant: 调用 book_hotel → 预订成功

三、Handoff 底层机制拆解

很多人用 create_handoff_tool 但不知道它干了什么。简化版源码逻辑如下:

python 复制代码
from langgraph.types import Command
from langchain_core.tools import tool
from typing import Annotated
from langgraph.prebuilt import InjectedState

def create_handoff_tool(*, agent_name: str, description: str):
    @tool(f"transfer_to_{agent_name}", description=description)
    def handoff_tool(
        task_description: Annotated[str, "描述下一个 Agent 应该做什么"],
        state: Annotated[dict, InjectedState],
    ) -> Command:
        # 只传 task_description,不传完整历史
        task_msg = {"role": "user", "content": task_description}
        return Command(
            goto=agent_name,          # 跳到目标 Agent
            update={"messages": [task_msg]},  # 只传摘要
            graph=Command.PARENT,     # 在父图层面跳转
        )
    return handoff_tool

三个关键点:

  1. graph=Command.PARENT:告诉 LangGraph 在父图(Swarm 图)层面跳转,而不是在当前 Agent 的子图里跳。
  2. task_description 参数:强制 LLM 把移交意图总结成一句话,而不是把整个对话历史原样扔过去。这是性能优化的核心。
  3. InjectedState:让工具能读取当前 State,但不直接修改,修改权交给 Command.update。

四、一个常见坑:默认 Handoff 传全量历史

create_handoff_tool 的默认行为是把整个 conversation history 打包传给下一个 Agent。对话长了之后会:

  • 序列化慢
  • 下一个 Agent 被无关信息干扰
  • Token 费用爆炸

优化方案 :用上面自定义的 handoff 工具,强制 LLM 输出 task_description,只传摘要。实测长对话场景下移交速度提升 40% 以上。


五、防失控:Swarm 的三道保险

保险 做法 说明
递归限制 config={"recursion_limit": 15} 不设的话,两个 Agent 互相转接会跑到 API 账单爆炸
白名单校验 自定义 handoff 时校验 agent_name 防止 LLM 幻觉跳到不存在的节点
活跃 Agent 追踪 SwarmState.active_agent 新消息自动路由到上次干活的 Agent,避免"金鱼记忆"

六、Swarm vs Supervisor 选型指南

维度 Supervisor Swarm
控制方式 中心化,主管统筹 去中心化,互相转交
可预测性 高,路径清晰 较低,动态灵活
调试难度 低,日志集中 高,链路分散
LLM 调用 多(每轮都要问主管) 少(直接交接,省一跳)
适用场景 客服工单分派、报告生成 多轮对话转接、专家接力
推荐 Agent 数 3-10 5-15

一句话 :要可控选 Supervisor,要灵活选 Swarm。实际生产中最常见的是混合架构------团队内部用 Swarm,团队之间用 Supervisor 串起来。


七、生产级 Checklist

  • 安装 langgraph-swarm 并用 create_swarm 组建
  • 每个 Agent 的 handoff 工具 description 写清楚触发条件
  • 设 recursion_limit,防止互相转接死循环
  • 开启 Checkpointer,保证多轮对话连续性
  • 自定义 handoff 工具时只传 task_description,不传全量历史
  • 用 LangSmith 分布式追踪排查链路问题

相关推荐
ZzT1 小时前
Anthropic 建议少开子 agent:实测拆成 3 个并行,慢了 54%、花费翻倍
agent·ai编程·claude
武子康1 小时前
灰度完成率从100%变成50%:五条失败记录怎样消失
人工智能·后端·agent
米小虾2 小时前
Agent架构10——八条穿越模型迭代的设计原则
人工智能·agent
米小虾2 小时前
Agent架构09——从提示工程到 Graph 工程:五个范式的包含关系
人工智能·agent
DGT2 小时前
不用 LangChain,200 行 TypeScript 手搓一个能联网、能写文件的 Agent
前端·langchain·agent
宇擎智脑科技2 小时前
Kiora Companion:文档扫描与相册检索中的端侧智能体实践
人工智能·agent
灵动的艺术2 小时前
如一 Agent 架构解读(十三):缓存命中率工程——Agent 的成本地基
人工智能·agent·如一
前端双越老师2 小时前
程序员 1v1 访谈系列第六期:国企也降薪裁员
agent·ai编程·全栈
用户3126874877203 小时前
OpenAI 入驻谷歌云:Gemini、Claude、GPT 首次同台,Codex 智能体成了“新商品”
agent