https://github.com/langchain-ai/langgraph-swarm-py多智能体协作库

一、这玩意是干啥的?
想象你有一个客服团队:Alice 是数学专家,Bob 是个说话像海盗的逗比。用户来了,先跟 Bob 聊天,然后问"5+7 等于几?",Bob 不会算,就说"这事儿得找 Alice",然后把用户转给 Alice。Alice 算完答案,对话结束。
这个库就是帮你实现"智能体之间自动转接"的。
GitHub:langchain-ai/langgraph-swarm-py
二、三个核心概念
1. Swarm(蜂群)— 一群智能体的管理者
就像蜂群里的蜂王,它知道:
- 现在谁在干活(active_agent)
- 该把活儿派给谁(路由逻辑)
- 怎么让智能体们互相交接(handoff 机制)
为什么需要它? 如果没有这个管理者,你每次跟 AI 对话,它都不知道上次是谁在回答你,每次都从头开始,像个金鱼记忆。
2. Handoff(交接)— 智能体之间的"传话筒"
每个智能体手里有个"转接电话"的工具。比如 Bob 会说:"我有个 transfer_to_alice 按钮,按一下就把用户转给 Alice。"
交接的时候做了三件事:
- 发一条消息:"已成功转接给 Alice"(让用户知道发生了什么)
- 告诉系统:"下一个找 Alice 干活"(更新 active_agent)
- 把之前的聊天记录打包带过去(这样 Alice 知道前面聊了啥)
为什么需要它? 就像医院分诊,你不能让一个心脏科医生去拔牙。每个智能体有自己擅长的活,遇到不擅长的就转给对的人。
3. Active Agent(活跃智能体)— 记住"上次是谁"
系统里有个小本本,记着"现在该谁干活"。新消息来了,先看小本本,找到那个人,让他继续处理。
为什么需要它? 保证对话的连续性。你问"5+7 等于几",系统知道上次是 Alice 在干活,就直接找 Alice,不会又去问 Bob"你要不要算一下?"
三、代码怎么组织的?
langgraph_swarm/ ├── swarm.py ← 蜂群管理者(怎么组队、怎么调度) ├── handoff.py ← 交接工具(怎么转接、转给谁) └── __init__.py ← 对外暴露的接口
三个模块各司其职:
| 模块 | 核心功能 | 关键函数 |
| swarm.py | 多智能体编排引擎 |
|
| handoff.py | 智能体间交接机制 |
|
| __init__.py | 公共 API 导出 | 暴露 4 个符号 |
四、一个完整的例子
# 1. 创建两个智能体 alice = 创建智能体(模型, 工具=[加法器, 转接给Bob], 角色="我是数学专家") bob = 创建智能体(模型, 工具=[转接给Alice], 角色="我是海盗") # 2. 组建成蜂群 swarm = create_swarm([alice, bob], 默认启动="Alice") # 3. 用户第一句话:"我想跟 Bob 说话" # → 系统启动 Alice,Alice 看到用户想找 Bob,按转接按钮 # → 系统切到 Bob,Bob 开始用海盗口吻聊天 # 4. 用户第二句话:"5+7 等于几?" # → 系统记得上次是 Bob,直接找 Bob # → Bob 不会算,按转接按钮找 Alice # → Alice 算出 12,返回答案
五、为什么这个设计好?
| 特性 | 说明 |
| 🎯 不用手动写调度代码 | 你只需要告诉每个智能体"你能转给谁",库自动处理所有路由 |
| 🧠 有记忆 | 通过 checkpointer 保存对话,关了重开还能继续聊 |
| 🔌 可扩展 | 想加第三个智能体?一行代码: |
| ⚙️ 可定制 | 不满意默认的转接方式?自己写个 handoff 工具,想传什么数据都行 |
六、频繁交接问题:会不会转错?
Q1:很多智能体互相转换,要维护复杂的 handoff 工具吗?
不需要。库自动搞定。 create_swarm 在添加每个智能体时,会调用 get_handoff_destinations() 自动扫描这个智能体身上有哪些 handoff 工具,自动识别路由目标。
关键机制在 get_handoff_destinations:
- 钻进智能体内部的图结构
- 找到 tools 节点里的所有工具
- 检查每个工具的 metadata 里有没有
__handoff_destination标记 - 把标记里的目标名字全部收集起来
Q2:会不会交接给错误的智能体?
有可能,但责任在 LLM,不在库。 交接的决策者是 LLM,库只负责执行。
库做的防护:
- 工具描述:
create_handoff_tool的description参数告诉 LLM 这个工具是干啥的 - system_prompt 引导:可以给每个智能体写清楚角色定位
- 没有强制路由:库不会替 LLM 做决定
怎么降低风险? 给每个 handoff 工具写清晰的 description,描述越具体,LLM 越不容易选错。
七、为什么要路由到"上次干活的 Agent"?
核心代码就一句话:
def route_to_active_agent(state: dict) -> str:
return state.get("active_agent", default_active_agent)
每次新消息进来,先看 state 里记的 active_agent 是谁,就找谁。
这个设计的巧妙之处
| 特点 | 说明 |
| 🔗 保证对话连续性 | 没有记忆的话,每次都要重新判断"该找谁",对话会断 |
| ⚡ 避免反复路由的性能浪费 | 有记忆 = 直接跳过去,省一次 LLM 调用 |
| 👤 符合人类对话直觉 | 你跟一个人聊天,不会每句话都重新自我介绍 |
| 🔄 和 handoff 工具形成闭环 | handoff 更新 active_agent,路由读取 active_agent,写和读分离 |
| 🛡️ 支持默认值降级 | 第一次对话时 active_agent 是空的,走 default_active_agent |
一句话总结: 这个设计把"谁在处理当前对话"这个状态从"每次都重新计算"变成了"记住就行"。就像你在餐厅点了菜,服务员不需要每上一道菜都问"这桌是谁的?",因为订单上写着桌号。active_agent 就是那个桌号。
八、从交接看执行全过程(基于测试用例)
场景还原
Turn 1: 用户说"我想跟 Bob 说话"
| 步骤 | 动作 |
| ① | 系统启动,active_agent 为空 → 走默认值 "Alice" |
| ② | 路由到 Alice,Alice 调用 transfer_to_bob 工具 |
| ③ | handoff 执行:goto → Bob 节点;update → 把 active_agent 改成 "Bob" |
| ④ | Bob 收到消息 + 完整历史,开始用海盗口吻回复 |
| 结束 | active_agent = "Bob" |
Turn 2: 用户说"5+7 等于几?"
| 步骤 | 动作 |
| ① | 路由函数读 state.get("active_agent") → "Bob" |
| ② | 直接路由到 Bob,Bob 调用 transfer_to_alice |
| ③ | handoff 执行:active_agent 被改成 "Alice" |
| ④ | Alice 调用 add(5, 7),返回 12 |
| 结束 | active_agent = "Alice" |
三个关键问题的答案
| 问题 | 答案 |
| Alice 交接给 Bob 后,当前活跃的是谁? | Bob。 handoff 在第 91 行立刻把 active_agent 改成了 "Bob" |
| 下次对话进来,路由到谁? | Bob。 路由函数读的是 |
| Bob 知道前面聊了啥吗? | 全知道。 所有历史消息都打包传给了 Bob |
关键洞察
active_agent 不是"谁在处理",而是"处理完之后的最后一个人是谁"。 就像接力赛,最后一棒是谁,active_agent 就记谁。下次发令枪响,直接找最后一棒的人。
消息是全量传递的,不是只传"当前智能体的消息"。所有智能体共享一个 messages 列表,所以 Bob 能看到 Alice 说过什么,Alice 也能看到 Bob 说过什么。
九、每个智能体都充当"路由器"角色
关键机制:handoff 工具就是一个普通的 tool
create_handoff_tool 创建的本质上就是一个普通的 LangChain Tool,跟 add(a, b) 这种计算工具没区别。它被塞进智能体的 tools 列表里,LLM 看到它,自己决定要不要调用。
完整流程拆解
场景: 用户说"帮我算 5+7"
| 步骤 | 动作 |
| ① | 路由到 Bob(active_agent = "Bob") |
| ② | Bob 的 LLM 看到工具列表中有 transfer_to_alice |
| ③ | LLM 决定调用 transfer_to_alice |
| ④ | 系统跳到 Alice 节点 |
| ⑤ | Alice 的 LLM 看到工具列表中有 add(a, b) |
| ⑥ | LLM 调用 add(5, 7) → 返回 12 |
| ⑦ | LLM 生成回复:"结果是 12" |
三个关键点
- 路由判断 = LLM 的工具选择 — 不是库写了 if 规则,而是 LLM 自己推理
- 每个智能体只需要知道"我能转给谁" — 不需要知道对方的能力
- 没有中心调度器 — 系统是去中心化的,每个智能体自己决定要不要转、转给谁
每个智能体都需要充当判断路由的作用——这个"判断"不是代码逻辑,是 LLM 的工具调用决策。库只负责:发按钮 → 按按钮后跳转 → 记住最后谁在干活。
十、消息归属问题:不是模糊记录
每条 AI 消息都有 name 字段,明确标记是谁说的。messages 列表不是"所有消息混在一起",而是每条都有角色归属:
messages = [
{"role": "user", "content": "我想跟 Bob 说话"}, # 用户说的
AIMessage(name="Alice", tool_calls=[transfer_to_bob]), # Alice 说的
ToolMessage(content="已转接给 Bob"), # 系统说的(handoff 工具生成)
AIMessage(name="Bob", content="Ahoy, matey! ..."), # Bob 说的
{"role": "user", "content": "5+7 等于几?"}, # 用户说的
AIMessage(name="Bob", tool_calls=[transfer_to_alice]), # Bob 转接的
ToolMessage(content="add(5,7)=12"), # 系统说的(add 工具结果)
AIMessage(name="Alice", content="结果是12"), # Alice 回答的
]
附:代码模块详解
swarm.py — 多智能体编排引擎
| 组件 | 说明 |
| SwarmState | 继承自 MessagesState,核心字段是 active_agent(记录当前活跃的智能体) |
| create_swarm() | 工厂函数,接收多个智能体列表,自动构建 StateGraph。验证非空、自动转换类型、添加节点、推断路由 |
| add_active_agent_router() | 底层路由函数,在 START 处添加条件边,根据 active_agent 状态字段决定路由 |
handoff.py — 智能体间交接机制
| 组件 | 说明 |
| create_handoff_tool() | 创建标准交接工具,工具名自动生成,内部生成 ToolMessage + 返回 Command(goto=...) |
| get_handoff_destinations() | 从已编译的智能体图中提取所有 handoff 目标 |
公共 API(__init__.py)
暴露 4 个公共符号:SwarmState、add_active_agent_router、create_handoff_tool、create_swarm

1222

被折叠的 条评论
为什么被折叠?



