Multi-Agent Teams:让多个专家 Agent 像团队一样协作

Multi-Agent Teams:让多个专家 Agent 像团队一样协作 点击上方 前端Q关注公众号回复加群加入前端Q技术交流群上一篇我们用 Supervisor 模式搭了一个项目经理 组员的多 Agent 系统——主管看任务、分活、收结果Worker 各干各的通过主管中转。这套模式在流程明确的场景下好用极了。但你有没有想过一个问题——如果去掉那个项目经理让 Agent 之间直接对话、自己决定把任务交给谁行不行现实中我们也经常这么干你跟设计师说这个交互有问题设计师改完直接把稿子发给前端同事前端做完自己找测试——不用每一步都过项目经理。这就是今天的主角——Swarm 模式群体智能也叫 Multi-Agent Teams。先回顾Supervisor 的局限Supervisor 模式好用但不是万能的。用多了你会发现几个问题瓶颈一所有任务必须过主管。每个 Worker 做完都要汇报给 SupervisorSupervisor 再决定下一步。如果团队有 5 个 Agent、每个 Agent 平均来回 2 轮光调度决策就要调 10 次 LLM——这些调用全是排队等主管批复的开销。瓶颈二主管是单点瓶颈。Supervisor 的 Prompt 要描述所有 Worker 的能力和调度规则。Worker 越多Prompt 越长调度准确率越低。上篇提过一个 Supervisor 管 3~5 个最好就是这个原因。瓶颈三不够灵活。有些场景下Agent A 最清楚接下来该找 Agent B——因为它刚做完的事情和 B 的能力最匹配。但 Supervisor 模式下A 只能把结果扔回去等主管来判断。多了一层转手信息还可能丢失。一句话Supervisor 像项目经理制——适合流程明确的团队。但如果你的 Agent 需要自由协作、自主交接就该用 Swarm。Swarm 模式自组织的 Agent 团队▎什么是 SwarmSwarm群体的灵感来自蜂群、蚁群——没有中央指挥每个个体根据自己的感知做决策群体却能完成复杂任务。在多 Agent 系统里Swarm 模式的核心是▸没有中央调度者——去掉 Supervisor▸Agent 之间直接交接——通过 Handoff 工具把控制权转给另一个 Agent▸系统记住当前活跃 Agent——多轮对话时用户的下一条消息会发给上次活跃的 Agent▎用前端团队来类比▸Supervisor 模式 所有需求都提给 Tech LeadTech Lead 分配给具体同事做做完汇报给 Tech Lead▸Swarm 模式 用户直接找前端同学提 Bug前端同学发现是后端接口问题直接在群里 后端同学交接后端改完 测试同学验证——谁最合适谁接手不用中间人转发▎Swarm 的核心循环用户发消息 → 当前活跃 Agent 接收→ Agent 自己能搞定 → 直接回复用户→ Agent 发现需要别的专家 → 调用 handoff 工具交接给目标 Agent→ 目标 Agent 接手处理→ 又需要别人继续 handoff→ 搞定了回复用户关键点每个 Agent 自己决定要不要交接、交给谁。不需要一个上帝视角的调度者来安排。动手用 LangGraph.js 搭一个 SwarmLangGraph.js 提供了langchain/langgraph-swarm包让你用几十行代码就能搭一个 Swarm 系统。▎场景旅行助手团队假设你要做一个旅行 AI 助手包含三个专家 Agent▸FlightAgent负责机票查询和预订▸HotelAgent负责酒店查询和预订▸GeneralAgent处理通用问题、行程规划用户可能说帮我订一张下周去巴黎的机票顺便推荐个酒店——一个 Agent 搞不定需要多个专家接力。▎安装bashnpm install langchain/langgraph-swarm langchain/langgraph langchain/core langchain/openai▎完整实现typescriptimport { ChatOpenAI } from langchain/openai;import { createReactAgent } from langchain/langgraph/prebuilt;import { createSwarm, createHandoffTool } from langchain/langgraph-swarm;import { tool } from langchain/core/tools;import { z } from zod;import { MemorySaver } from langchain/langgraph;const llm new ChatOpenAI({ modelName: gpt-4o, temperature: 0 });// 1. 定义各 Agent 的专属工具 const searchFlightsTool tool(async ({ from, to, date }) {return 找到 3 趟航班1. CA123 ${from}→${to} 08:00-12:00 ¥2,8002. MU456 ${from}→${to} 14:00-18:00 ¥2,2003. CZ789 ${from}→${to} 20:00-00:00 ¥1,800;},{name: search_flights,description: 搜索指定出发地、目的地和日期的航班,schema: z.object({from: z.string().describe(出发城市),to: z.string().describe(目的地城市),date: z.string().describe(出发日期如 2025-03-15),}),});const bookFlightTool tool(async ({ flightNo }) E6DB74航班 ${flightNo} 预订成功确认号TK-${Date.now()},{name: book_flight,description: 预订指定航班,schema: z.object({ flightNo: z.string().describe(航班号) }),});const searchHotelsTool tool(async ({ city, checkIn, checkOut }) {return 找到 3 家酒店1. 巴黎花园酒店 ⭐⭐⭐⭐ ¥800/晚 含早餐2. 塞纳河景公寓 ⭐⭐⭐ ¥500/晚 近地铁3. 香榭丽舍大酒店 ⭐⭐⭐⭐⭐ ¥2,000/晚 豪华套房;},{name: search_hotels,description: 搜索指定城市和日期的酒店,schema: z.object({city: z.string().describe(城市名),checkIn: z.string().describe(入住日期),checkOut: z.string().describe(退房日期),}),});// 2. 创建 Agent每个带 handoff 工具 // Flight Agent —— 能搜航班、订票还能把用户交给 Hotel Agentconst flightAgent createReactAgent({llm,tools: [searchFlightsTool,bookFlightTool,createHandoffTool({ agentName: hotel_agent, description: 用户需要酒店相关帮助时转接给酒店专家 }),createHandoffTool({ agentName: general_agent, description: 用户问题跟机票无关时转接给通用助手 }),],name: flight_agent,prompt: 你是机票预订专家。帮用户搜索和预订航班。如果用户提到酒店需求交接给 hotel_agent。,});// Hotel Agent —— 能搜酒店还能交接给 Flight Agentconst hotelAgent createReactAgent({llm,tools: [searchHotelsTool,createHandoffTool({ agentName: flight_agent, description: 用户需要机票相关帮助时转接给机票专家 }),createHandoffTool({ agentName: general_agent, description: 用户问题跟酒店无关时转接给通用助手 }),],name: hotel_agent,prompt: 你是酒店预订专家。帮用户搜索和推荐酒店。如果用户提到机票需求交接给 flight_agent。,});// General Agent —— 通用助手能交接给任何专家const generalAgent createReactAgent({llm,tools: [createHandoffTool({ agentName: flight_agent, description: 用户需要预订或查询机票时转接给机票专家 }),createHandoffTool({ agentName: hotel_agent, description: 用户需要预订或查询酒店时转接给酒店专家 }),],name: general_agent,prompt: 你是旅行规划通用助手。回答一般性旅行问题遇到机票或酒店需求时交接给对应专家。,});// 3. 组装 Swarm const workflow createSwarm({agents: [flightAgent, hotelAgent, generalAgent],defaultActiveAgent: general_agent,});const checkpointer new MemorySaver();const app workflow.compile({ checkpointer });▎运行 观察typescriptconst config { configurable: { thread_id: travel-session-001 } };// 第一轮用户问通用问题general_agent 接手const r1 await app.invoke({ messages: [{ role: user, content: 我下周想去巴黎玩 5 天帮我规划一下 }] },config);// general_agent 做行程规划提到机票和酒店时…// 第二轮用户想订机票const r2 await app.invoke({ messages: [{ role: user, content: 先帮我看看北京飞巴黎的机票 }] },config);// general_agent 发现是机票需求 → 调用 handoff 交接给 flight_agent// flight_agent 接手调用 search_flights 搜索航班// 第三轮用户又想订酒店const r3 await app.invoke({ messages: [{ role: user, content: 机票先这样帮我看看巴黎的酒店 }] },config);// 此时 flight_agent 是活跃 Agent// flight_agent 发现是酒店需求 → 调用 handoff 交接给 hotel_agent// hotel_agent 搜索酒店回复用户整个过程中没有 Supervisor 参与——Agent 之间自己决定交接系统自动追踪活跃 Agent——第二轮之后flight_agent成为活跃 Agent第三轮的消息直接发给它上下文跨 Agent 传递——hotel_agent能看到之前的对话历史知道用户要去巴黎createSwarm 背后做了什么跟上篇的createSupervisor一样我们先搞懂底层原理才知道什么时候该自己写。▎核心机制1. Handoff 工具交接工具。createHandoffTool给每个 Agent 注册一个转接电话工具。当 Agent 调用transfer_to_hotel_agent时系统把控制权转给hotel_agent。typescript// createHandoffTool 生成的工具本质是这样的const transferToHotel tool(async () {// 内部实现通过 Command 把 goto 指向目标 Agentreturn new Command({ goto: hotel_agent });},{name: transfer_to_hotel_agent,description: 用户需要酒店相关帮助时转接给酒店专家,schema: z.object({}),});2. 活跃 Agent 追踪。Swarm 在 State 里维护一个activeAgent字段。每次 Agent 交接后这个字段更新为新的 Agent 名字。下一轮对话时系统自动把消息路由给activeAgent。typescript// Swarm State 里的关键字段{messages: [...],activeAgent: flight_agent // 记住当前活跃 Agent}3. 入口路由。createSwarm会自动添加一个路由节点根据activeAgent把消息发给对应 Agent。首次对话时用defaultActiveAgent。▎和 Supervisor 模式的关键区别维度SupervisorSwarm入口永远先到 Supervisor消息直接发给当前活跃 Agent路由方式Supervisor LLM 做决策Agent 自己调用 handoff 工具Agent 之间不直接通信都经过 Supervisor直接交接不需要中间人用户交互只跟 Supervisor 对话可以跟任何 Agent 对话调度开销每步都要调 Supervisor LLM只在交接时多一次工具调用可预测性较高中心控制较低Agent 自主决策手搓 Swarm用 StateGraph 理解底层跟上篇一样先手动搭一个 Swarm再用createSwarm简化。理解原理比会用 API 重要。typescriptimport { ChatOpenAI } from langchain/openai;import { createReactAgent } from langchain/langgraph/prebuilt;import { StateGraph, Annotation, START, END, MessagesAnnotation } from langchain/langgraph;import { Command } from langchain/langgraph;import { tool } from langchain/core/tools;import { z } from zod;import { BaseMessage, HumanMessage } from langchain/core/messages;// 1. 定义 State多了一个 activeAgent const SwarmState Annotation.Root({messages: AnnotationBaseMessage[]({reducer: (prev, next) [...prev, ...next],default: () [],}),activeAgent: Annotationstring({default: () general,reducer: (_, next) next,}),});// 2. 定义 Agent Node const llm new ChatOpenAI({ modelName: gpt-4o, temperature: 0 });async function flightNode(state: typeof SwarmState.State) {// flight_agent 处理逻辑const lastMsg state.messages.at(-1)?.content as string;if (lastMsg.includes(酒店)) {// 自主决策需要酒店专家 → 交接return new Command({goto: hotel,update: {activeAgent: hotel,messages: [new HumanMessage({ content: lastMsg, name: flight_agent })],},});}// 自己处理const response await llm.invoke([{ role: system, content: 你是机票预订专家 },...state.messages,]);return {messages: [response],activeAgent: flight,};}async function hotelNode(state: typeof SwarmState.State) {const lastMsg state.messages.at(-1)?.content as string;if (lastMsg.includes(机票) || lastMsg.includes(航班)) {return new Command({goto: flight,update: {activeAgent: flight,messages: [new HumanMessage({ content: lastMsg, name: hotel_agent })],},});}const response await llm.invoke([{ role: system, content: 你是酒店预订专家 },...state.messages,]);return {messages: [response],activeAgent: hotel,};}async function generalNode(state: typeof SwarmState.State) {const lastMsg state.messages.at(-1)?.content as string;if (lastMsg.includes(机票) || lastMsg.includes(航班)) {return new Command({goto: flight,update: { activeAgent: flight },});}if (lastMsg.includes(酒店)) {return new Command({goto: hotel,update: { activeAgent: hotel },});}const response await llm.invoke([{ role: system, content: 你是旅行规划通用助手 },...state.messages,]);return {messages: [response],activeAgent: general,};}// 3. 入口路由根据 activeAgent 分发 function routeToActiveAgent(state: typeof SwarmState.State) {return state.activeAgent; // 直接返回活跃 Agent 的名字}// 4. 组装 Graph const workflow new StateGraph(SwarmState).addNode(flight, flightNode).addNode(hotel, hotelNode).addNode(general, generalNode).addConditionalEdges(START, routeToActiveAgent) // 入口路由.addEdge(flight, END).addEdge(hotel, END).addEdge(general, END);const swarm workflow.compile();跟createSwarm对比手动版的关键差异State 里加了activeAgent字段——追踪谁在当前活跃入口用addConditionalEdges做路由——根据activeAgent分发到对应 NodeAgent 用Command做交接——设置goto 更新activeAgentAgent 自主决策交接时机——不需要外部调度者当然真实项目用createSwarm就够了——它帮你封装了这些细节还自带消息传递、错误处理和多轮对话支持。CrewAI另一种组建 Agent 团队的思路聊完 LangGraph.js 的 Swarm再看看另一个热门的多 Agent 框架——CrewAI。虽然它是 Python 生态的但它的设计理念对前端同学理解Agent 团队很有启发。▎CrewAI 的核心思路CrewAI 用人类团队的方式来组织 Agent四个核心概念Agent成员每个 Agent 有三个关键属性——Role角色、Goal目标、Backstory背景故事。比如一个技术研究员Agentpythonresearcher A6E22EAgent(roleSenior Tech Researcher,goal发现关于指定主题的最新、最准确的技术信息,backstory你在技术研究领域有 AE81FF10 年经验擅长从海量信息中提炼关键洞察...,tools[search_tool],llmA6E22EChatOpenAI(modelgpt-4o),)Task任务具体要做的事包含描述、期望输出、指定哪个 Agent 执行。pythonresearch_task A6E22ETask(description调研 {topic} 的最新技术动态输出结构化摘要,expected_output包含 AE81FF5 个核心要点的技术摘要,agentresearcher,)Tool工具跟 LangChain.js 的 Tool 一样Agent 的手脚。Crew团队把 Agent 和 Task 组装在一起定义执行策略——顺序执行、层级管理、或异步并行。pythoncrew A6E22ECrew(agents[researcher, writer, reviewer],tasks[research_task, write_task, review_task],processProcess.sequential, # 顺序执行verboseTrue,)result crew.A6E22Ekickoff(inputs{topic: React AE81FF19 新特性})▎CrewAI vs LangGraph.js怎么选维度LangGraph.js (Swarm)CrewAI语言TypeScript/JavaScriptPython核心理念图Graph 状态机角色扮演 任务驱动Agent 交互通过 Handoff 工具交接通过 Crew 编排支持委托灵活度极高能自定义一切中等框架约定多学习曲线较陡需理解 State/Node/Edge较平概念直觉适合场景前端生态、复杂自定义流程Python 生态、快速搭原型前端友好度⭐⭐⭐⭐⭐⭐⭐需要 Python 后端我的建议▸前端团队首选 LangGraph.js——原生 TS、跟前端工具链无缝衔接▸全栈团队LangGraph.js 做前端 Agent 逻辑 CrewAI 做后端复杂编排各取所长▸快速验证想法CrewAI 上手更快几行代码就能跑起来▸生产级系统LangGraph.js 的 Graph 架构在可控性和可观测性上更强实战客服团队 Swarm来个更贴近真实业务的例子——一个电商客服团队包含▸OrderAgent处理订单查询、退换货▸TechAgent处理技术问题、使用指导▸ComplaintAgent处理投诉、升级工单typescriptimport { ChatOpenAI } from langchain/openai;import { createReactAgent } from langchain/langgraph/prebuilt;import { createSwarm, createHandoffTool } from langchain/langgraph-swarm;import { tool } from langchain/core/tools;import { z } from zod;import { MemorySaver } from langchain/langgraph;const llm new ChatOpenAI({ modelName: gpt-4o, temperature: 0 });// 订单相关工具const queryOrderTool tool(async ({ orderId }) {return 订单 ${orderId}商品「无线耳机 Pro」已发货预计明天送达。物流单号SF123456;},{name: query_order,description: 查询订单状态,schema: z.object({ orderId: z.string().describe(订单号) }),});const applyRefundTool tool(async ({ orderId, reason }) {return E6DB74退款申请已提交。订单 ${orderId}原因${reason}。预计 3-5 个工作日到账。;},{name: apply_refund,description: 申请退款,schema: z.object({orderId: z.string().describe(订单号),reason: z.string().describe(退款原因),}),});// 投诉工具const createTicketTool tool(async ({ description, priority }) {return E6DB74工单已创建。编号TK-${Date.now()}优先级${priority}我们会在 24 小时内联系您。;},{name: create_ticket,description: 创建投诉工单,schema: z.object({description: z.string().describe(问题描述),priority: z.enum([low, medium, high]).describe(优先级),}),});// 组装 Agentconst orderAgent createReactAgent({llm,tools: [queryOrderTool,applyRefundTool,createHandoffTool({ agentName: tech_agent, description: 用户有技术使用问题时转接 }),createHandoffTool({ agentName: complaint_agent, description: 用户要投诉或情绪激动时转接 }),],name: order_agent,prompt: E6DB74你是订单客服专家。处理订单查询、退换货等问题。注意如果用户表达不满或要投诉立即转接给 complaint_agent。如果用户问产品使用方法转接给 tech_agent。,});const techAgent createReactAgent({llm,tools: [createHandoffTool({ agentName: order_agent, description: 用户有订单问题时转接 }),createHandoffTool({ agentName: complaint_agent, description: 用户要投诉时转接 }),],name: tech_agent,prompt: 你是技术支持专家。帮用户解决产品使用问题提供操作指导。,});const complaintAgent createReactAgent({llm,tools: [createTicketTool,createHandoffTool({ agentName: order_agent, description: 投诉处理完用户还需要订单帮助时转接 }),],name: complaint_agent,prompt: E6DB74你是投诉处理专家。对用户表示同理心记录问题并创建工单。态度要温和、专业。不要让用户觉得被推诿。,});// 组装 Swarmconst customerServiceSwarm createSwarm({agents: [orderAgent, techAgent, complaintAgent],defaultActiveAgent: order_agent,});const app customerServiceSwarm.compile({ checkpointer: new MemorySaver() });用户对话可能是这样的用户我的订单 ORD-12345 到哪了→ order_agent 查询订单回复物流信息用户耳机收到了但不知道怎么连蓝牙→ order_agent 发现是技术问题 → handoff 给 tech_agent→ tech_agent 指导蓝牙配对步骤用户按你说的做了还是连不上这什么破产品→ tech_agent 检测到用户情绪激动 → handoff 给 complaint_agent→ complaint_agent 安抚用户、创建工单每次交接都是 Agent 自主决策的不需要人工配置路由规则。进阶混合模式——Supervisor Swarm实际项目中你不需要非此即彼。最佳实践往往是混合使用▸上层用 Supervisor做大粒度调度这是内容任务还是代码任务▸下层用 Swarm让专家 Agent 自由协作typescript// 内容团队内部用 Swarm研究员、写手、审校之间自由交接const contentSwarm createSwarm({agents: [researcher, writer, reviewer],defaultActiveAgent: researcher,}).compile();// 技术团队也用 Swarmconst techSwarm createSwarm({agents: [coder, codeReviewer, tester],defaultActiveAgent: coder,}).compile();// 上层用 Supervisor 调度两个团队const topLevel createSupervisor({agents: [contentSwarm, techSwarm],llm,prompt: 你是项目总负责人。内容相关任务交给内容团队代码相关任务交给技术团队。,}).compile();形成的架构Project ManagerSupervisor├── Content TeamSwarm│ ├── Researcher ←→ Writer ←→ Reviewer│ └── 内部自由交接└── Tech TeamSwarm├── Coder ←→ Code Reviewer ←→ Tester└── 内部自由交接上层 Supervisor 只做大方向决策团队内部 Agent 自由协作——像真实公司一样经理管方向组内自行协作。怎么选一张图说清楚场景推荐模式原因流程固定A→B→C条件边 / 硬编码可预测、零调度成本需要统一协调流程灵活Supervisor中心控制可观测性好Agent 需要自由交接Swarm去中心化延迟低Agent 数量多10层级 Supervisor分层管理避免单点瓶颈大方向调度 内部协作Supervisor Swarm混合模式各取所长避坑指南▎坑 1Agent 之间踢皮球Agent A 交接给 BB 又交回给 A无限循环。typescript// ❌ 两个 Agent 的 prompt 都写了不确定就交给对方flightAgent.prompt: 不确定的问题交给 hotel_agenthotelAgent.prompt: 不确定的问题交给 flight_agent// ✅ 给每个 Agent 明确什么时候不交接flightAgent.prompt: 你是机票专家。- 机票相关问题自己处理- 酒店问题交给 hotel_agent- 其他问题交给 general_agent- 如果问题已经被另一个 Agent 交接过来尽量自己处理不要再交回去同时加recursionLimit兜底typescriptconst result await app.invoke({ messages: [...] },{ recursionLimit: 15, configurable: { thread_id: xxx } });▎坑 2交接后上下文丢失Agent A 处理了一半交给 B 时B 看不到 A 做了什么。createSwarm默认会传递完整消息历史所以用官方 API 时问题不大。但如果你手动实现 Swarm记得在Command的update里把关键信息带上typescriptreturn new Command({goto: hotel,update: {activeAgent: hotel,messages: [new HumanMessage({content: [从 flight_agent 交接] 用户要去巴黎航班已选定 CA123现在需要推荐酒店,name: flight_agent,})],},});▎坑 3默认 Agent 选错了defaultActiveAgent选错了用户第一句话就被发到不合适的 Agent。原则默认 Agent 应该是最通用的那个——它能理解大部分问题并正确交接给专家。不要默认到一个很窄的专家 Agent 上。▎坑 4Agent 不调用 handoff 工具Agent 知道应该交接但就是不调用 handoff 工具——而是自己硬答。解法在 Prompt 里明确指令并在 description 里强调触发条件typescriptcreateHandoffTool({agentName: complaint_agent,description: 当用户表达不满、使用负面词汇如投诉、差评、退货、或情绪激动时必须立即转接,})总结Multi-Agent Teams 的核心思想让 Agent 像真实团队一样协作——不是所有事都要过经理专家之间可以直接交接。用 LangGraph.js 实现 Swarm 的关键createHandoffTool给每个 Agent 注册交接工具——Agent 通过工具调用来交接控制权createSwarm一键组装——自动处理活跃 Agent 追踪、消息路由、多轮对话activeAgent状态——系统记住谁在值班下一条消息直接发给它defaultActiveAgent设为最通用的 Agent——确保用户第一句话被正确处理三种多 Agent 模式的关系Supervisor项目经理制✅ 流程明确需要统一协调时用❌ Agent 之间不能直接通信Swarm自组织团队✅ Agent 需要自由交接、去中心化时用❌ 可预测性较低调试更难混合模式最佳实践✅ 上层 Supervisor 管方向下层 Swarm 管协作✅ 兼顾可控性和灵活性下一篇我们深入Handoffs 与动态路由——Agent 之间交接时怎么传递上下文、怎么做条件路由、怎么实现人工介入。如果说 Swarm 解决了谁交给谁Handoffs 就是解决怎么交。推荐资源▸langchain/langgraph-swarmhttps://www.npmjs.com/package/langchain/langgraph-swarm▸LangGraph.js Multi-Agent 概念https://langchain-ai.github.io/langgraphjs/concepts/multi_agent/▸OpenAI Swarm灵感来源https://github.com/openai/swarm▸CrewAI 官方文档https://docs.crewai.com往期推荐Multi-Agent Teams让多个专家 Agent 像团队一样协作AI Agent 是怎么想一步做一步的拆解 ReAct 模式从零开始用 LangChain.js 构建你的第一个 Tool-Calling Agent最后点个在看支持我吧