☰
多智能体集群实战:用MCP、A2A与Skills构建高效协作体系
2026/10/5 9:26:33 网站建设 项目流程

一个下午,我盯着屏幕上排列整齐的六个独立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(transport="stdio")

这段代码看起来简单,但里头有讲究。最关键的是那个函数文档字符串,它不只是给人看的注释,而是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能执行的动作”。举个具体例子:你的数据库连接配置、业务说明文档、代码规范手册,这些应该暴露为Resource,Agent可以通过特定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 Server,Agent只暴露给它有限的白名单工具,其它一切请求拦截。

注意:MCP是把双刃剑。它让Agent能力变强的同时,也把底层系统的风险面暴露给了AI。生产环境里,所有工具调用必须保留完整审计日志,这既是安全要求,也是你自己排查问题的依据。

3. A2A层实战:让Agent之间说上话

3.1 A2A协议解决的核心矛盾

MCP解决的是“Agent用工具”的问题,那A2A(Agent-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有问题要找B,B有进展要回报A。如果用MCP连接Agent,就得两个方向上各自实现一套客户端/服务端,管理起来极其痛苦。A2A的阿贾克斯式双向通信机制,天然就是为这种场景生的。

所以我的铁律是:Agent访问数据库、文件系统、第三方API,走MCP;Agent之间互通信息、协调任务、传递结果,走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.json

SKILL.md是入口文件,开头是描述和适用条件:

--- name:>skills-registry/ ├──>

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询