多智能体集群实战:用MCP、A2A与Skills构建高效协作体系
一个下午我盯着屏幕上排列整齐的六个独立Agent窗口心里只有一个念头这玩意儿再不管马上就要失控了。每个Agent都在忙着查资料、写代码、调接口但它们彼此之间完全隔绝各干各的。前端Agent改完接口参数后端Agent还不知道数据库Agent发现表结构要动文档Agent拿到的还是旧版说明。人肉同步信息花了快一个小时团队协作的意义彻底消失。那一刻我意识到光把多个Agent堆在一起不叫多智能体系统充其量就是一套“分布式单干”。真正值钱的东西是怎么让它们像一支有组织的小队那样协作起来。于是就有了这套方案——用DeepAgents作为编排内核MCP统一工具接入A2A打通Agent之间的对话通道再用Skills沉淀每个可复用的能力单元最终组成一个能跑通全流程的多智能体集群。这篇博文就把这套东西从头到尾拆开讲包括架构设计、协议细节、完整配备代码的实战过程以及我调试一整天踩出来的一堆坑。想搭一套正经多智能体集群的或者已经在用多个Agent但感觉协作越来越吃力、准备升级架构的可以仔细看看。读完你应该能自己动手复现一套或者至少清楚该从哪个方向改造现有的方案。1. 从“多个Agent”到“多智能体集群”到底差在哪1.1 单Agent的边界与集群出现的必然性单个Agent再强其能力边界也被三件事锁死第一上下文窗口是有限的任务一多对话历史里塞满的中间过程就会挤掉真正重要的判断依据第二工具链是静态绑定的你给它接了一个代码解释器它就很难顺手再去调用数据库接口切换工具的成本极高第三职责是混乱的一个Agent既写代码又做代码评审还要管部署它自己都不知道当前这轮对话该用哪套决策逻辑。DeepAgents这类深度智能体框架解决了一部分问题它能做任务规划能把复杂目标分解成子任务然后逐步执行。但它在设计上还是面向“单兵作战”的就像弗兰克·赫伯特笔下那种经过高度训练的战士单挑能力强可一旦面对需要情报、掩护、火力协同的团队战就吃不消了。真正的多智能体集群是让不同的Agent各自有明确角色比如规划者、执行者、审查者、知识库管理助手并且建立两条关键通道一条是人到Agent的工具通道把外部数据源、系统接口统一接入这就是MCP要做的事另一条是Agent到Agent的通信通道让它们能互相发消息、传结果这就是A2A。再加上Skills把重复劳动固化成可复用的技能包。三层叠起来才算有了团队的样子。1.2 三层架构思维MCP、A2A、Skills各管哪一段拿一个真实系统来类比你让一支外包团队帮你交付一个功能。这时候需要三样东西第一员工用得顺手的工具包括需求管理后台、代码仓库、测试环境这就是MCP管的“工具接入层”第二员工之间的沟通机制比如站会、邮件、即时消息让前端知道后端数据结构变了没有这是A2A管的“通信层”第三老员工带新员工时候沉淀下来的经验文档比如“签名算法这样写就能过审”、“这个客户有个特殊字段必须带上”这是Skills管的“经验层”。这三层不冲突、不重叠。MCP不解决Agent之间的沟通问题它只解决“Agent怎么可靠地使用外部工具”A2A不解决工具接入问题它只解决“Agent之间怎么把自己的意图和结果告诉对方”Skills更不负责实时通信它把可复用的能力固化成包减少每次从头教的成本。我见过不少团队一上来就追问“MCP和A2A到底谁取代谁”这是一个误解。一个偏“人机”一个偏“机机”配合使用的场景远大于竞争。1.3 为什么选DeepAgents做编排内核市面上多智能体编排框架并不少有LangGraph这种图编排有AutoGen这种会话式也有CrewAI这种角色扮演式。但我最终把DeepAgents放在集群中心是基于三个实际考虑一是它的规划能力确实能打。复杂任务进来之后它能自动拆出DAG式的任务依赖图而不是简单的线性队列执行。比如“做一个数据看板需要先采集、再清洗、再建模、最后可视化”深模型能识别出“清洗”依赖“采集”“可视化”依赖“建模”从而调度并行执行那些互相无关的节点。二是它原生对MCP和A2A友好。DeepAgents很早就把MCP客户端作为标准组件支持了进去同时Agent的对外交互协议也预留了A2A Agent Card的暴露方式改造成本远低于其他框架。三是它的可观测性设计更成熟。集群一旦跑起来每步推理、每个工具调用、每条Agent间的消息都能被记录到追踪日志里。真出了问题时你不需要靠猜翻日志就行。当然这不是说其他框架不能用。实际上我的架构里有些边缘Agent比如专门跑批量任务的一个子Agent就是用LangGraph搭的。重点不在于某个框架“最好”而在于你的编排内核要能同时承载工具接入、通信路由和技能调度DeepAgents在这个组合里恰好站得住。2. MCP层实战给每个Agent一双可靠的手2.1 MCP协议到底改了什么MCP的全称是Model Context Protocol中文一般叫模型上下文协议。它解决的问题说白了就是“给AI接外部工具时别再用一堆乱七八糟的私有接口”。在MCP之前你给Agent接一个API通常得写一大堆胶水代码把API文档塞进提示词再教Agent怎么构造请求、怎么解析响应。每接一个工具就得做一遍这套动作而且换了模型框架还得重写。MCP把这个过程标准化成了一个三件套客户端发起请求、服务端声明能力和处理请求、双方通过统一的协议格式交换消息。你可以把MCP想象成电脑上的USB接口——以前每个设备都要专门设计一种插头现在大家都按统一标准来做任何Agent只要是MCP客户端就能“即插即用”地连接任何实现了MCP服务端的工具。2.2 手写一个MCP Server把内部系统接进来这步实操最关键。官方提供的脚手架能生成基本结构但真实项目里你一定会遇到自定义场景——你自己的业务系统、公司的数据仓库、某个老旧的内部管理后台没有现成的MCP服务端给你用。这时就得自己写。我用Python实现了一个简单的MCP Server核心代码长这样from mcp.server import Server, NotificationOptions from mcp.server.models import InitializationOptions from mcp.server.fastmcp import FastMCP from typing import Any import json # 第一创建MCP服务实例 mcp FastMCP(internal-api-bridge) # 第二声明一个工具供Agent调用 mcp.tool() def query_order_status(order_id: str) - dict[str, Any]: 查询订单状态。参数order_id 订单号。 注意这个接口只能查最近90天内的订单更早的数据请在返回参数中标注 data_lag: true。 # 这里是模拟业务调用真实场景替换成你的内部API或者SQL查询 payload { order_id: order_id, status: SHIPPED, tracking_no: SF1234567890, data_lag: False } return json.dumps(payload) # 第三启动服务监听stdio if __name__ __main__: mcp.run(transportstdio)这段代码看起来简单但里头有讲究。最关键的是那个函数文档字符串它不只是给人看的注释而是Agent理解工具用途的唯一线索。MCP不会像传统API那样给你一个写死的参数Schema让Agent照着填Agent是靠读这段描述来理解“这个工具是干嘛的、参数要什么格式、响应大概长什么样”。所以写工具描述时我踩过一个大坑一开始我写得特别简略就一句“查询订单状态”结果Agent不知道参数格式传了一堆怪东西进来后来我把描述写成小作文把边界条件、返回字段含义全部都写进去准确率一下就上去了。一句话描述写得越具体Agent用得越准。这个Server跑起来之后任何支持MCP的客户端就能连上它。DeepAgents里配置客户端地址就行{ mcpServers: { internal-api: { command: python, args: [/path/to/server.py], env: {} } } }2.3 资源Resource与工具Tool的正确分工很多人刚开始用MCP时会把Resource和Tool搞混。Resource是“让人能读的数据”Tool是“让Agent能执行的动作”。举个具体例子你的数据库连接配置、业务说明文档、代码规范手册这些应该暴露为ResourceAgent可以通过特定URI读取但不产生副作用而“执行一个SQL查询”“提交一条工单”“变更配置文件”这类动作必须暴露为Tool而且要加权限控制。我在实际集群里常用Resource来维护“运行时知识库”。比如让每个Agent启动时自动读取一遍当前项目的架构说明、接口文档、代码风格规范这些一次性写入上下文比每轮对话都让Agent去猜强太多了。Resource还支持订阅和变更通知文档一更新所有连着的Agent都能收到更新提示。这里有个实用性建议Resource的数量别贪多。我最初把整个技术文档库全拖进去结果Agent的上下文窗口被文档占掉一大半真正干活的空间反而小了。后来只挑了每类文档的核心摘要和关键索引其余交给RAG按需检索。2.4 MCP连接的资源开销与并发优化MCP连接不是免费的。每个工具调用本质上是客户端和服务端之间的一次进程级或网络级交互消息里带的是结构化JSON解析上下文也要消耗token。集群里几十个Agent同时挂着一堆MCP服务时资源开销很快就涨上来了。我的优化经验有三条第一工具加载要按需别全量挂载。DeepAgents支持MCP工具懒加载只有Agent判断“这一步需要查订单”才真正连接对应Server而不是启动时把所有工具塞进上下文。这一步就能把上下文占用量砍掉六七成。第二服务端要复用连接。不要让Agent每调一次工具就重新握手一遍维护一个长连接池复用已建立的会话。单机场景下用stdio管道直连跨节点场景下走SSE或Streamable HTTP注意用HTTP时一定要开Keep-Alive。第三敏感工具做代理隔离。比如生产数据库的写操作绝对不应该让Agent直接连上去执行。我习惯的做法是在前面加一层网关MCP ServerAgent只暴露给它有限的白名单工具其它一切请求拦截。注意MCP是把双刃剑。它让Agent能力变强的同时也把底层系统的风险面暴露给了AI。生产环境里所有工具调用必须保留完整审计日志这既是安全要求也是你自己排查问题的依据。3. A2A层实战让Agent之间说上话3.1 A2A协议解决的核心矛盾MCP解决的是“Agent用工具”的问题那A2AAgent-to-Agent解决的就是“Agent找Agent”的问题。多智能体集群里最尴尬的一幕就是Agent A需要数据但数据只在Agent B手里。没有A2A的时候得靠编排层硬编码“A的数据要来自B”或者在A的上下文里塞一段“你可以调用B”的说明。这种方式一是耦合严重二是扩展性极差——每加一个Agent就得去改别的Agent的提示词。A2A带来的是一种“服务发现会话协商”模式。每个Agent发布一张Agent Card公开自己的能力描述、通信地址、支持的消息类型。其他Agent就能动态发现它、向它发起任务、交换数据。这就像微服务架构里注册中心的作用服务之间不再通过硬编码地址互相调用而是通过注册发现完成解耦。3.2 Agent Card一张名片打通服务发现A2A协议的核心载体是Agent Card一个JSON文档相当于Agent对外公开的“营业执照”。{ name: data-analysis-agent, description: 负责数据分析与报表生成可处理CSV、JSON、SQL查询结果, url: http://agent-cluster.internal:8080/a2a, skills: [ { id: time-series-analyzer, name: 时间序列趋势分析, description: 输入时间序列数据输出趋势、季节性与异常点 } ], authentication: { schemes: [bearer], credentials: env://A2A_AUTH_TOKEN }, capabilities: { streaming: true, pushNotifications: true, stateTransitionHistory: true } }这里重点看几个字段url是接收A2A消息的端点skills数组声明自己能干啥别的Agent会用它来匹配任务capabilities声明通信能力能不能流式返回、能不能推送通知都影响协作体验。Agent Card还有个很实用的用法——不只能描述“我能干什么”还能描述“我不能干什么”。比如数据清洗Agent可以明确说“本Agent不做模型训练相关任务”这样调度器就不会把训练任务误分给它。3.3 一次完整的A2A对话从发现到响应打开抓包日志一次完整的A2A协作过程看下来非常清晰。第一步任务路由。规划Agent收到“帮我分析这周销售数据”这个任务它尝试做能力匹配。规划Agent自己不直接处理数据它去查集群里的Agent注册表筛选出“能处理数据分析而且具备表格处理能力”的Agent锁定>{ jsonrpc: 2.0, id: task-20250516-001, method: message/send, params: { agent_id: data-analysis-agent, message: { role: user, parts: [ { kind: text, text: 分析本周销售数据按品类汇总并标记异常波动。数据源在共享路径 /data/sales_week52.csv } ] } } }第三步异步响应与进度反馈。数据分析Agent收到任务后返回一个任务ID同时通过message/update逐步汇报进度——先是“读取CSV”、再是“正在做品类聚合”、最后“发现两个异常波动品类”。规划Agent不需要轮询它订阅了推送通知有状态更新自动收到。第四步从另一个Agent拿上下文。分析进行到一半数据分析Agent发现需要了解上个月的品类基线它自己不存这种数据于是给knowledge-agent发一条A2A请求拿到上月基线数据回来再继续分析。第五步返回最终结果并归档。分析结果完后封装成结构化Message返回规划Agent同时把结果存到集群共享存储更新自己的Skill文档下次再有相似任务可以直接套用处理流程。这一整套走下来没有一行硬编码调用关系Agent之间的协作完全是动态发现、任务驱动的。3.4 A2A与MCP的边界什么时候该走哪条路我经常碰到一个问题Agent之间要互相取数据能用MCP连接吗答案是可以但架构上不推荐。MCP更适合“Agent到外部系统”这种单向调用模式它的模型是客户端—服务端一个Agent作为客户端去调服务端拿数据。但Agent之间的协作是双向的A要问B要数据B也要问A要状态A有问题要找BB有进展要回报A。如果用MCP连接Agent就得两个方向上各自实现一套客户端/服务端管理起来极其痛苦。A2A的阿贾克斯式双向通信机制天然就是为这种场景生的。所以我的铁律是Agent访问数据库、文件系统、第三方API走MCPAgent之间互通信息、协调任务、传递结果走A2A。一个管外,一个管内泾渭分明。4. Skills层实战把经验沉淀成可复用的能力包4.1 Skills到底是什么它和普通提示词有何区别Skills是目前智能体生态里大家讨论非常多的一块。简单来说它就是把一组完成特定任务的提示词、工具调用模式、工作流步骤、甚至验证规则打包在一起让Agent在不重新“学习”的前提下直接调用这套处理能力。很多人问这不就是改了个花名的提示词模板吗确实有关联但区别在于Skills被设计成可独立分发、可版本管理、可在多个Agent间共享的标准包。它比提示词多出三层东西第一结构化的元数据。每个Skill有名字、描述、适用条件、依赖工具清单这些元数据让调度器能判断“这活儿该不该用这个Skill”第二内置校验步骤。用完一个Skill后结果会经过自动检查验证产出是否结构完整第三可组合性。Skill可以嵌套调用其他Skill比如“生成周报”这个Skill内部可以调用“从数据库抽取数据”和“生成图表”两个子Skill。Skills是这套集群能“越用越聪明”的关键。第一周你教它怎么处理销售数据写成一个Skill存下来第二周它再处理相关任务时直接复用不再需要你从头提示一遍。4.2 Skill的工程化结构一个生产级Skill长什么样拿我集群里一个被反复使用的“数据清洗Skill”举例data-cleaning-skill/ ├── SKILL.md ├── scripts/ │ ├── detect_outliers.py │ └── impute_missing.py ├── prompts/ │ ├── step1_validate.md │ └── step2_clean.md ├── schemas/ │ ├── input.schema.json │ └── output.schema.json └── tests/ └── test_cases.jsonSKILL.md是入口文件开头是描述和适用条件--- name:>skills-registry/ ├──>