
上个月我把一个什么都干的单体 Agent 拆成了三个第二天就后悔了——不是拆错了是拆完才发现让多个 Agent 协作这件事比给单个 Agent 多接几个工具要麻烦十倍。麻烦集中在四件事上谁负责哪一块Skills、用什么方式碰外部世界MCP、Agent 之间怎么通信A2A、以及最上面谁在盯着整条任务链流转编排层。这四个词最近在各种多智能体课程里高频出现但真正把它们拆开讲透、再串成一个可运行集群的内容并不多。这篇文章不打算复述概念定义而是把DeepAgents MCP A2A Skills 构建可编排、可互通、可扩展的 Agent 集群这件事按我实际落地时的思考顺序讲清楚先想明白为什么要集群化再逐个解决工具接入、技能沉淀、Agent 互通、任务编排四个问题最后给一个能直接照着搭的三 Agent 示例。适合已经写过几个 Agent、但觉得单智能体方案到瓶颈的人纯新手也可以看我会把必要的基础知识夹在过程中讲。1. 单体 Agent 的尽头先说清楚为什么要集群化1.1 单体 Agent 的三堵墙很多团队是从一个万能 Agent 开始的给它塞一堆 System Prompt挂上十几个工具让它处理所有请求。早期确实爽但随着场景变复杂你会撞上三堵墙。第一堵是上下文墙。每个模型窗口就那么大工具定义要占、历史对话要占、中间结果要占。工具超过十个之后光是保证每个工具的参数格式正确、不被其他工具说明干扰就已经很难了。用户问一句帮我查天气再写个出行建议Agent 要把天气工具、地点工具、文档生成工具的定义全部载入上下文真正的推理空间被压缩得很厉害。第二堵是工具墙。工具是拿来即用的接口但业务能力不等于接口。比如生成周报这件事涉及取数、汇总格式、按模板排版、发送到群——你当然可以把它拆成四个工具依次调用但每个 Agent 都要重新编排一遍流程编排逻辑散落在 Prompt 里改一次需求就要改十几个 Prompt。这种能力没有沉淀换个模型、换个项目就全丢了。第三堵是协作墙。真实业务的请求很少是单线完成的。有时候需要检索 Agent 先查资料、写作 Agent 再动笔、数据分析 Agent 提供图表数据。单体 Agent 要么把所有活都自己扛导致上下文爆炸要么你每次在业务代码里写死调用顺序Agent 之间完全没有会话的概念。所以说集群化不是赶时髦而是这面墙撞到一定程度后的自然选择。1.2 集群化的关键给四件事分好工我接触到的超级多智能体方案基本上都在围绕四层做文章能力层Skills把高频业务流程沉淀成可复用的技能包解决Agent 会不会干活的问题。工具层MCP用统一协议对接外部系统解决Agent 有什么工具可用的问题。通信层A2A让不同 Agent 之间能发现彼此、派发任务、回传结果解决Agent 之间怎么协作的问题。编排层DeepAgents负责任务拆解、路由、状态管理、人工介入解决谁在控制整场戏的问题。这四层不是并列的可选项而是层层递进的依赖关系。没有 SkillsAgent 只是个对话机器人没有 MCPAgent 碰不到外部数据没有 A2AAgent 集群只是多个独立进程而不是一个系统没有编排层前三者都是散沙。这也是为什么很多课程把这四个词绑在一起讲因为它们合起来才是完整的集群方案。1.3 一个让我下决心的失败案例我之前做过一个客服 Agent初始需求是能查订单、能退换货、能推荐商品。我一个 Agent 挂了 9 个工具跑了两个月效果能接受。后来需求膨胀到需要对接工单系统、知识库、物流查询、优惠券计算工具涨到 17 个。Prompt 里的工具说明越来越长模型开始出现工具幻觉——明明该查物流却调了订单接口两个工具返回矛盾信息时Agent 也不清楚该信哪个。最崩溃的一次是促销期间用户问优惠券能不能叠加Agent 把八竿子打不着的三个工具结果缝合出一个错误答案。那之后我彻底把架构改成多 Agent一个意图识别 Agent 负责路由一个订单 Agent 只管订单域一个营销 Agent 只管优惠。跑通之后效果立竿见影但那段时间踩的坑让我意识到集群化的架构不是酷而是必须。2. MCPAgent 与世界的标准化插座2.1 先澄清一个基本问题MCP 是软件协议不是硬件协议热搜里经常会看到MCP 是软件协议还是硬件协议的疑问这其实是用 USB-C 类比多了之后的困惑。明确回答MCPModel Context Protocol是应用层的软件协议走的是 JSON-RPC 2.0和硬件没有任何关系。大家习惯拿 USB-C 来打比方是因为它在逻辑上太像了——USB-C 统一了充电口和数据口而 MCP 统一了 AI 应用接入外部工具和数据的接口。在没有 MCP 之前接一个工具是一套代码对接订单 API 写一套 HTTP 调用对接数据库写一套查询封装对接浏览器操作再写一套自动化脚本。每多接一个工具就是 N×M 的集成矩阵。而且这些工具接口长什么样、参数怎么组织全部取决于各家 API 自己的风格Agent 光理解这些接口就要消耗大量上下文。MCP 要解决的就是这件事把工具提供方和AI 入口解耦大家按同一套协议来。2.2 MCP 的核心模型Client、Server 与三种原语MCP 的角色划分很简单MCP Client 是 AI 应用宿主MCP Server 是能力提供方。一次连接的生命周期是全双工的协议层主要靠三个原语在工作ToolsAgent 显式调用的函数比如get_weather、query_order这是大家最常用的。Resources需要给 Agent 预加载的数据片段比如文档、配置文件、数据库 Schema按 URI 去读。Prompts服务端预置的交互模板Agent 可以按特定流程去调用。从底层角度这三个原语都是通过initialize、tools/list、tools/call、resources/read这些 JSON-RPC 方法下发与返回的。传输方式目前最常见的是 stdio本地子进程通信和 HTTP/SSE远程服务。我刚开始学的时候把 MCP 当成一个特别牛的 API 框架后来才体会到它核心价值不在传输而在标准化所有工具描述长一样、调用方式长一样、错误格式长一样Agent 只需要一种工具识别能力就能用天下所有的 MCP Server。这对集群编排特别重要——编排层不需要为每个工具写适配器。2.3 十分钟跑通一个 MCP Server说再多不如跑一个。FastMCP 是目前最快的上手方式Python 环境一行安装pip install fastmcp建一个order_server.pyfrom fastmcp import FastMCP # 创建一个名为 order-demo 的 MCP Server mcp FastMCP(order-demo) # 用装饰器定义一个 Tool mcp.tool() def query_order(order_id: str) - str: 根据订单号查询订单状态和物流信息 # 这里实际会去调业务 API先返回模拟数据 return f订单 {order_id}: 已发货, 物流单号 SF1234567890 # 再定义一个 ResourceAgent 可以直接读取 mcp.resource(orders://{order_id}) def get_order(order_id: str) - str: 读取订单详细信息 return f订单 {order_id} 的收货地址: 杭州市西湖区... if __name__ __main__: # stdio 传输方式适合被本地 Agent 拉起 mcp.run(transportstdio)然后启动python order_server.py这时候再用任何支持 MCP 的客户端Claude 桌面端、Cursor、自研 Agent 框架里的 MCP Client连接这个 stdio 进程就能看到query_order工具被自动发现。关键点是工具的描述字符串根据订单号查询订单状态和物流信息会被作为工具元数据传给模型决定模型什么时候调用它。所以写 MCP 工具时描述要比代码注释还认真描述写不清楚模型就不可能在正确时机调用。2.4 选型参考MCP 与普通 API 怎么选Browser Use 与 Playwright 又有什么区别不是所有接入都非得走 MCP。我的判断标准场景推荐方案理由Agent 要动态决定调用哪个工具MCP工具可被发现、描述可被模型理解固定流程的数据对接普通 API 封装少一层进程通信简单直接需要频繁调整工具集MCP改服务端即可客户端自动发现一次性脚本直接函数调用上 MCP 是过度设计另外热搜里常被问到Browser Use MCP 和 Playwright MCP 有什么区别。这两者经常被并排提起但定位差异很大。Playwright MCP 是把浏览器自动化能力包装成 MCP 工具它偏控制导航、点击、填表、截图适合做 UI 自动化测试和数据采集。Browser Use MCP 则偏向理解它内部会用多模态模型去解析页面结构把网页上的信息抽成结构化的上下文再交还给主 Agent 决策。简单说Playwright 是手Browser Use 是手加眼睛加脑子。做爬虫、表单操作选 Playwright 更稳做读网页然后回答问题这种任务Browser Use 更省心。3. Skills让 Agent 不只能调用工具而是真的会干活3.1 Skill 的本质一个文件夹加一个 SKILL.mdMCP 解决了Agent 有什么可用但很多业务能力不是简单工具调用能覆盖的。比如写一篇产品周报你需要先知道周报的结构、取数的口径、排版规范、语气要求。把这些零散指令硬塞进 System Prompt会污染上下文换个场景就不适用。这就是 Skills 的用武之地。一个 Claude-style Skill 本质上是一个目录weekly-report/ ├── SKILL.md ├── templates/ │ └── report_template.md └── scripts/ └── collect_data.pySKILL.md是这个技能包的说明书里面写清楚触发条件、工作流程、注意事项、输出规范。当 Agent 判断当前任务需要用这个技能时会读取 SKILL.md按里面的步骤执行必要时调用同目录下的脚本和模板。再回头看我的理解Skill 就是把业务最佳实践固化成文件让不同的 Agent、不同的对话会话都能复用同一套方法论。3.2 Skills 与 MCP 的分工一个是工具箱一个是操作手册很多同学刚接触时会把 Skills 和 MCP 搞混甚至会问有了 MCP 还要 Skill 干什么。我的理解是MCP 提供能做什么的原子能力Skill 描述怎么做才专业。举一个贴切的类比MCP 像你家里的工具箱里面有螺丝刀、扳手、电钻Skill 像师傅的操作手册告诉你修一个漏水水龙头要先用扳手拧下螺母、再用生料带缠绕几圈、最后用电钻固定支架。工具是通用的手艺是专属的。落到代码架构上协作方式通常是这样的Agent 先决定要不要启用某个 SkillSkill 的执行步骤里再调用 MCP 工具取数。Skill 管流程和判断MCP 管连接和动作两者不是替代关系而是上下游关系。3.3 自己写一个 Skill从高频重复任务抽象出来写 Skill 没有想象中复杂核心是把你在某个任务上的操作经验结构化成 Markdown。我建议按这个骨架来写--- name: weekly-report description: 根据项目周数据自动生成中文周报 --- ## 触发条件 - 用户要求生成周报/月报 - 项目数据已更新到本周 ## 工作流程 1. 调用数据源 MCP 工具获取本周完成需求数、缺陷数、工时 2. 读取 templates/report_template.md 的正文结构 3. 按本周进展 - 数据表现 - 风险与问题 - 下周计划组织内容 4. 不要捏造数据, 缺失指标用待补充标出 ## 输出规范 - 长度 600-1200 字 - 不使用夸张广告用语 - 数据需要标注来源真正决定 Skill 质量的是可操作性。很多新手写 Skill 通篇都是生成一份高质量的周报确保逻辑清晰这种模糊指令模型看完等于没看。好 Skill 里每条指令都是可以执行的动作说要调哪个工具、按什么顺序、产出什么格式、避开什么坑。我把这当成写文档的最高标准让一个完全没做过这任务的模型按 SKILL.md 走一遍能交出七八十分的结果就算成功。3.4 Skills 生态市场上已经有大量现成技能包如果你不想从零写现在能找到的现成 Skill 已经非常多。Anthropic 官方有 Skills 市场社区也有大量聚合仓库。GitHub 搜 Awesome Claude Skills、或者直接搜 superpowers能找到不少高质量技能包——从写论文、做前端页面、代码审查、数据分析到各种垂直领域都有。注意一点下载回来的 Skill 别直接塞给生产环境先读一遍 SKILL.md确认里面的工作流和你期望的一致最好再让 Agent 跑两个测试任务验证一下。社区包里偶尔会夹带一些不合适的指令这跟装第三方依赖要锁版本、走 review 是一个道理。前端开发 skills、codex skills、论文写作 skills 这几个方向在社区里非常活跃。多提一嘴 codex 用户关心的现在不少主流的 IDE 和 Agent 客户端都支持直接把某个 GitHub 仓库里的 Skill 文件夹导入或者通过官方市场一键安装安装路径一般会在配置目录下的skills/文件夹具体以你所用的客户端文档为准。4. DeepAgents 编排层队列、路由与集群状态管理4.1 编排层到底要管哪些事前面三层解决的是能力供给而 DeepAgents 这个层面的核心是任务如何流转。如果只有 Skills、MCP、A2A没有编排层Agent 集群充其量是多个能干的个体不是一个整体。编排层的职责我总结为四件事任务解析把用户原始请求拆成子任务决定哪些自己做、哪些分发给其他 Agent。路由根据子任务类型选择合适的 Agent 或 Skill。并发与优先级多个任务同时进来时怎么排队、怎么限流、谁能插队。状态管理任务执行到哪一步、中间结果存哪里、出错了怎么重试或转人工。很多框架LangGraph、CrewAI都在做这件事但如果你像我是自研集群最朴素可靠的实现是任务队列 Worker 路由表三者组合。4.2 一种可落地的编排设计表驱动的路由 Worker我不太喜欢把路由逻辑写死在代码里因为路由规则会随着新 Agent 接入频繁变动。更好的做法是表驱动——把路由规则抽成配置Agent 元信息注册到一张表里编排 Worker 按表里的字段做分发。伪代码大概长这样# 注册表: 每个 Agent 的能力声明 AGENT_REGISTRY [ { name: rag-reader, description: 擅长从知识库检索资料, 回答事实性问题, route_keywords: [查询, 资料, 文档, 知识库], }, { name: data-analyst, description: 擅长计算指标, 生成数据表格和图表, route_keywords: [统计, 指标, 环比, 图表], }, { name: writer, description: 擅长撰写文章, 报告, 总结, route_keywords: [写, 生成报告, 总结, 周报], }, ] def route_task(task_text: str) - str: 按关键词粗排, 必要时让意图 Agent 做精细判断 for agent in AGENT_REGISTRY: for keyword in agent[route_keywords]: if keyword in task_text: return agent[name] # 没匹配上, 找兜底 Agent 或转人工 return fallback这个示例故意用了最简单的关键词匹配来说明思路实际项目里建议用一个意图路由 Agent 来做语义判断。但不管用哪种方式原则是一样的调度逻辑和 Agent 能力要解耦新增一个 Agent 只需要注册它的能力描述不需要改编排代码。4.3 状态管理撑住长任务的骨架多 Agent 协作的一个大坑是任务周期长中间某个 Agent 调用了外部 MCP 工具等了 30 秒结果自身进程重启了整个任务链条断了。单体 Agent 时代任务都是短对话出问题重来就行集群时代任务可能跑几分钟甚至几小时中间还有人工审批环节这时候没有状态持久化重来一次的成本难以接受。我的做法是把任务状态写进一个task_status表字段大致包括任务 ID、当前步骤、涉及 Agent 列表、每步的输入输出快照、运行批次号、终态标记。每个 Agent 执行完一步就上报一次状态编排层通过状态机推进下一步。这样即使 Worker 崩溃也能从最后成功的那一步恢复不用整条任务重跑。还有一个经常被忽略的点编排层要记录每步是谁干的、用了什么工具、给了什么结果。别小看这个日志它一方面用于全链路排障另一方面当最终结果不对劲时你能反推是哪个 Agent 的错误判断导致了问题。没有这个记录多 Agent 集群就是个黑盒。4.4 人工介入HITL集群里必须留的人位做多 Agent 集群最忌讳的是把一切交给自动化尤其是涉及对外发送、支付、删除这类不可逆操作。DeepAgents 编排层里必须有一个人工审批节点任务执行到敏感步骤时状态变成awaiting_human_approval暂停在队列里等人工确认后再继续。我见过不少事故都是因为贪图全自动跳过了这个节点。集群越复杂人工兜底的位置越重要——这不是效率妥协而是事故保险。5. A2AAgent 之间怎么对上话5.1 A2A 解决的核心问题Agent 发现 AgentMCP 解决 Agent 与工具之间的纵向打通而 A2AAgent-to-Agent解决的是 Agent 与 Agent 之间的横向互通。它要回答三个问题我怎么知道其他 Agent 存在我怎么把任务托付给它们我怎么拿到结果并确认它完成A2A 协议给我的感觉更像企业微服务通信规范而不是工具调用协议。它定义了一套基于 JSON-RPC 2.0 的交互方式核心模块有AgentCard每个 Agent 对外发布的名片声明自己的身份、能力、可接受的输入类型供其他 Agent 发现。Task一次跨 Agent 请求的完整生命周期状态从submitted→working→input-required→completed/failed/canceled流转。MessageA2A 通信中传递的消息体可以包含文本、文件引用、结构化数据。Negotiation/Partners用来协商双方能否合作、以什么条件合作。5.2 AgentCard 到底是什么样的一个 Agent 接入了 A2A最简单的事情就是先给出自己的 AgentCard。类似这样{ name: data-agent, description: 数据分析服务, 可计算聚合指标并生成图表, url: a2a://agent.internal/data-agent, capabilities: { streaming: true, push_notifications: false }, skills: [data-aggregation, chart-generation] }其他 Agent 拿到这个卡片后就能根据skills字段判断这件事该不该派给它再通过url发起 A2A 请求。整个发现机制是 可拔插 的不像单体代码里通过 import 另一个模块来调用那样耦合。这也是可互通的落地点只要对方发布了 AgentCard不需要知道它的实现语言、部署位置就能协作。5.3 A2A 与 MCP 的关系我会用一个例子说清楚很多人问A2A 是不是 MCP 的替代品真不是。二者解决的是不同维度的问题我常用一个比方MCP 是 Agent 的手脚A2A 是 Agent 的同事。假设你是一个写报告 Agent接到主任务生成季度数据报告。你需要从数据分析 Agent 那里拿数据于是通过 A2A 给它发一个 Task统计 Q2 各产品线营收按周聚合。数据分析 Agent 收到任务后它的执行阶段会调用数据库的 MCP Server工具层把数据取回来加工成结构化表格再以 A2A Message 的结果回传给写报告 Agent。在这条链路里A2A 负责Agent 间的请求与响应MCP 负责单个 Agent 与外部系统间的动作。纵向用 MCP 接触世界横向用 A2A 连接同事两者是正交组合关系不冲突。集群架构里你既要有 MCP 工具层也要有 A2A 通信层。5.4 A2A 生态现状与 Spring 集成A2A 的 SDK 已经比较成熟官方维护了 Python、TypeScript/JavaScript、Java 等语言的实现。Java 世界里a2a-spring是一个值得关注的集成方向——它基于 Spring AI能在 Spring Boot 应用里快速把业务服务包装成符合 A2A 规范的 Agent 端点对已有 Java 技术栈的团队格外友好。如果你们的 Agent 后端是 AI Gateway 或 Java 微服务可以重点研究一下a2a-spring的 Starter 用法先跑通一个服务间 A2A 请求再扩展外部 Agent。6. 端到端示例从零搭一个三 Agent 协作集群6.1 场景定义一份日报是怎么被协同完成的理论部分讲多了容易飘我拿一个真实落地过的场景完整串一遍企业知识库问答 日报生成 数据分析。这三个能力分别交给三个 Agentknowledge-agent负责知识库检索回答事实性问题属于检索 Agent。writer-agent负责撰写日报、周报属于写作 Agent。>from fastmcp import FastMCP project_mcp FastMCP(project-system) project_mcp.tool() def get_project_metrics(project_id: str, start_date: str, end_date: str) - str: 获取指定项目的需求数、缺陷数、工时等汇总指标 # 对接项目管理系统 API ... project_mcp.tool() def get_milestones(project_id: str) - str: 获取项目的里程碑列表及进度 ...同时knowledge-agent挂的是知识库 MCP Server提供search_docs工具。工具层各归各Agent 通过 MCP Client 连接自己需要的 Server。MCP 的标准化好处在这里就体现出来了三个 Agent 各自只用一套 MCP Client 逻辑改工具只需动 Server。6.4 一起跑起来一次请求的完整调用链编排层收到生成今天的项目日报后识别出这是一种daily-report型任务路由到writer-agent。writer-agent的流程如下从用户请求里解析出项目 ID 和时间范围。通过 A2A 向>