多智能体系统这两年从论文里的概念一路卷到了工程落地,但真正动手搭过的人都知道,把几个Agent凑在一起跑通Demo只是起点,难的是让它们可编排、可互通、可扩展——也就是标题里说的那三件事。DeepAgents、MCP、A2A、Skills这四个词放在一起,其实对应了四个不同层面的问题:Agent怎么组织、工具怎么接、Agent之间怎么通信、能力怎么复用。我最近花了不少时间把这套组合拳从零搭了一遍,踩的坑比想象中多,尤其是协议边界和能力封装这两块,文档里基本不会告诉你哪里会翻车。这篇就把整个搭建思路、关键决策和实测经验完整摊开讲,适合已经写过单Agent、想往多Agent集群方向走的开发者,也适合正在评估MCP和A2A到底该不该上的技术负责人。
1. 先把四个概念的关系理清楚,别一上来就写代码
很多人看到DeepAgents、MCP、A2A、Skills这四个词,第一反应是"这是不是四个竞品",然后开始纠结选哪个。我一开始也这么想过,后来发现完全搞错了方向——它们根本不在一个层面上,是互补关系。
1.1 四个概念各自解决什么问题
用一个类比来说:把整个多智能体集群想象成一家公司。
DeepAgents是公司的组织架构和岗位设计。它决定了有哪些Agent、每个Agent负责什么、谁向谁汇报、任务怎么拆解和汇总。它管的是"编排"这件事——一个复杂任务进来,怎么拆成子任务,分给哪些Agent,最后怎么把结果拼回去。
MCP(Model Context Protocol)是员工手里的工具箱和操作手册。它定义了Agent怎么调用外部工具、怎么读取外部资源。以前每个Agent接一个工具就要写一套适配代码,MCP把这个过程标准化了——工具方按MCP规范暴露能力,Agent方按MCP规范调用,双方解耦。
A2A(Agent to Agent)是部门之间的沟通机制。当Agent A需要Agent B帮忙时,它们怎么发现对方、怎么发起请求、怎么传递上下文、怎么返回结果。MCP解决的是"Agent调工具",A2A解决的是"Agent调Agent",这是两个完全不同的通信模式。
Skills是员工的技能包。一个Agent会什么、怎么把一项能力封装成可复用的模块、怎么在不同Agent之间共享同一套技能。它管的是"能力复用"和"能力扩展"。
理清这层关系之后,架构就清晰了:DeepAgents负责顶层编排,MCP负责工具接入,A2A负责Agent间通信,Skills负责能力封装。四者叠加,才构成一个完整的、可扩展的Agent集群。
1.2 为什么这四个必须一起上
单独用任何一个都能跑,但都会遇到天花板。
只用DeepAgents不用MCP,每个Agent的工具调用都要硬编码,接一个新工具就要改Agent代码,扩展性极差。只用MCP不用A2A,Agent之间没法直接协作,所有协调都得靠中心化的编排器,一旦编排逻辑复杂起来就变成一团乱麻。只用A2A不用Skills,每个Agent的能力都是散装的,同样的能力在多个Agent里重复实现,维护成本爆炸。
我实测下来的感受是:MCP和A2A是基础设施,Skills是复用层,DeepAgents是调度层。基础设施不稳,上面盖多高都会塌。所以搭建顺序应该是先MCP、再A2A、然后Skills、最后DeepAgents编排。很多人反过来做,先搭编排框架,结果发现底层通信和工具接入全是坑,返工成本极高。
1.3 一个容易混淆的点:MCP和A2A的边界
这是我在实际搭建中踩得最深的一个坑。MCP和A2A看起来都是"通信协议",很容易混用。我一开始甚至想过用MCP来让Agent互相调用,结果发现完全走不通。
核心区别在于:MCP是Client-Server模式,A2A是对等模式。MCP里,Agent是Client,工具是Server,Server是被动的、无状态的,它不会主动发起请求。而A2A里,两个Agent是对等的,任何一方都可以主动发起任务、可以有自己的状态和记忆、可以拒绝或协商。
举个具体例子:你要让一个"研究Agent"去查资料然后交给"写作Agent"成文。查资料这个动作,研究Agent通过MCP调用搜索工具,这是MCP的范畴。但"研究Agent把结果交给写作Agent"这个动作,是A2A的范畴,因为写作Agent是一个有自己判断能力的实体,它可能觉得资料不够要求补充,这种双向协商MCP做不了。
记住一条判断准则:如果对方是一个"无状态的能力提供方",用MCP;如果对方是一个"有状态、会思考、能协商的Agent",用A2A。混用这两个协议是多Agent系统里最常见的架构错误。
2. MCP接入:工具层的标准化是整个集群的地基
MCP这一层看起来最简单,实际上最容易埋雷。我见过太多项目在这一层偷懒,后面全部返工。
2.1 MCP Server的三种接入方式怎么选
MCP Server的接入方式主要有三种,各有适用场景,选错了后期很痛苦。
| 接入方式 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 本地进程(stdio) | 单机开发、工具轻量 | 启动快、调试方便 | 无法跨机器、并发能力弱 |
| HTTP/SSE | 生产环境、多Agent共享 | 可跨机器、支持并发 | 需要处理连接管理、超时 |
| 容器化部署 | 工具依赖复杂、需要隔离 | 环境干净、易扩展 | 启动慢、资源占用高 |
我的建议是:开发阶段用stdio快速验证,生产环境一律上HTTP/SSE。stdio方式在单Agent场景下很爽,但一旦多个Agent要共享同一个工具,stdio的进程模型就会出问题——每个Agent起一个进程,工具状态不共享,缓存全废。
容器化部署适合那些依赖特别重的工具,比如需要特定Python版本、特定系统库的工具。但要注意,容器启动有冷启动延迟,如果你的Agent对响应时间敏感,得做预热或者常驻。
2.2 工具描述写得好不好,直接决定Agent会不会用
这是最容易被忽视的一点。MCP工具的description字段,不是写给人看的文档,是写给模型看的prompt。我踩过的坑是:工具描述写得太简略,模型根本不知道什么时候该调用它,要么不调用,要么乱调用。
一个好的工具描述应该包含四个要素:
- 这个工具做什么:一句话说清楚核心功能
- 什么时候用:明确触发条件,最好给出典型场景
- 输入参数的含义和格式:每个参数都要说明,尤其是格式敏感的
- 返回什么:让模型知道调用后能拿到什么
举个例子,一个搜索工具的描述:
搜索互联网获取实时信息。当需要查询最新新闻、实时数据、 或训练数据之外的信息时使用。输入query为搜索关键词, 返回包含标题、摘要、链接的结果列表。对比一个糟糕的描述:
搜索工具。后者模型基本不会正确使用。我实测下来,工具描述从"能用"优化到"好用",Agent的任务完成率能提升30%以上,这个投入产出比极高。
2.3 MCP Resource和Tool的区别,别搞混
MCP里有两个核心概念:Tool和Resource。很多人一开始分不清。
Tool是动作,会改变状态或产生副作用,比如"发送邮件""写入文件""调用API"。Resource是数据,是只读的,比如"读取配置文件""获取数据库schema""读取文档内容"。
为什么要区分?因为模型对这两类的处理逻辑不同。Tool调用需要谨慎,因为可能有副作用,通常需要确认或限制频率。Resource读取相对安全,可以更自由地调用。
我见过有人把所有东西都做成Tool,结果模型频繁调用"读取"类操作,浪费token还拖慢速度。正确的做法是:只读的、幂等的能力做成Resource,有副作用的做成Tool。
2.4 流式输出到文件的实战细节
热词里提到"使用mcp工具流式输出内容到文件",这是个很实用的场景,但坑不少。
核心问题是:MCP工具调用默认是请求-响应模式,一次性返回结果。如果要流式输出,需要在工具实现里做特殊处理。我的做法是让工具返回一个流式句柄或者分块返回,Agent侧做拼接。
具体实现上,要注意几点:
- 分块大小要合理:太小则调用频繁,太大则失去流式意义。我一般用1-4KB一块
- 要处理中断:流式过程中如果Agent决定停止,工具侧要能感知并清理资源
- 要处理错误:流到一半出错,已经写入的部分怎么处理,要有明确策略
流式输出到文件时,建议先写到临时文件,全部完成后再原子性地重命名到目标文件。否则中途失败会留下半截文件,后续读取会出问题。
3. A2A通信:Agent之间怎么"说话"才不乱
A2A是多Agent系统里最复杂的部分,因为它涉及状态、协商、错误处理。我在这块踩的坑最多。
3.1 Agent Card:Agent的"名片"怎么设计
A2A里有个核心概念叫Agent Card,可以理解为Agent的名片。它描述了:这个Agent是谁、能做什么、怎么调用它、需要什么输入、返回什么输出。
Agent Card设计得好不好,直接决定了Agent之间能不能顺利协作。我一开始把Agent Card写得很简单,结果发现Agent A根本不知道该怎么跟Agent B沟通,经常传错参数或者发起B处理不了的任务。
一个好的Agent Card应该包含:
- 身份信息:Agent的名称、版本、描述
- 能力列表:这个Agent能处理哪些类型的任务
- 输入规范:接受什么格式的输入,必填和选填字段
- 输出规范:返回什么格式的结果
- 调用端点:怎么找到它、怎么发起请求
- 限制条件:速率限制、并发限制、超时设置
这里的关键是输入输出规范要足够明确,最好用schema定义。模型看到明确的schema,才知道怎么构造请求。
3.2 任务生命周期:从发起到完成的完整链路
A2A的任务不是简单的请求-响应,它有完整的生命周期。我把它拆成几个阶段:
- 发现阶段:Agent A怎么知道Agent B的存在和能力。这靠Agent Card的注册和查询机制。
- 协商阶段:Agent A发起任务,Agent B评估自己能不能做。如果不能,可以拒绝或建议转给其他Agent。
- 执行阶段:Agent B执行任务,期间可能通过流式事件汇报进度。
- 完成阶段:Agent B返回结果,Agent A接收并处理。
- 异常阶段:任何阶段出错,都要有明确的错误传递机制。
我踩的坑主要在协商阶段。一开始我假设Agent B总是能处理Agent A发来的任务,结果经常出现B处理不了但又不知道怎么拒绝的情况,任务就卡住了。后来加了明确的"能力评估"环节,B收到任务先判断自己能不能做,不能做就明确返回拒绝并附上原因,A再决定是重试、转派还是放弃。
3.3 上下文传递:传什么、传多少
Agent之间传递上下文是个微妙的问题。传太少,接收方信息不足做不了决策;传太多,token爆炸还引入噪声。
我的经验是分层传递:
- 必传:任务目标、关键约束、期望输出格式
- 选传:相关背景、历史对话摘要、参考案例
- 不传:完整的原始对话、无关的中间过程、大段原始数据
具体怎么把握?我一般遵循"接收方需要什么就传什么"的原则。在Agent Card里明确定义输入schema,发送方按schema构造,接收方按schema解析。这样既不会漏传,也不会多传。
还有一个技巧:用引用代替内联。如果上下文里有大段数据,不要直接塞进消息里,而是传一个引用(比如文件路径、资源ID),接收方需要时再去取。这样消息体积小,传输快,也避免了重复传输。
3.4 错误处理和重试:别让一个Agent挂掉拖垮整个集群
多Agent系统里,错误是常态。网络抖动、工具超时、模型输出格式错误,任何一个环节都可能出问题。如果错误处理做不好,一个Agent挂掉可能引发连锁反应。
我的做法是三层防护:
- 单次调用层:每个A2A调用都有超时设置,超时后返回明确的错误,而不是无限等待
- 重试层:对可重试的错误(网络超时、临时不可用)做指数退避重试,对不可重试的错误(参数错误、能力不匹配)直接失败
- 降级层:如果某个Agent持续不可用,编排层要能感知并切换到备用方案,比如换一个Agent、或者用简化流程
这里有个细节:重试要幂等。如果Agent B的任务有副作用(比如写文件、发请求),重试可能导致重复执行。所以要么任务设计成幂等的,要么在A2A协议里加去重机制(比如用任务ID去重)。
我实测下来,A2A调用里最容易出问题的是超时设置。默认超时往往太长,一个Agent卡住会拖慢整个流程。建议根据任务类型设置差异化超时:简单查询5-10秒,复杂任务30-60秒,超长任务用异步+轮询模式。
4. Skills封装:让能力真正可复用、可扩展
Skills这一层是决定系统能不能规模化的关键。如果每个Agent的能力都是散装的,Agent越多维护成本越高。
4.1 Skill的粒度怎么把握
Skill粒度是个需要反复权衡的问题。太粗,复用性差;太细,组合成本高。
我的经验法则是:一个Skill对应一个"完整的、有明确输入输出的能力单元"。判断标准是:这个能力能不能独立测试?能不能被不同Agent以相同方式调用?如果能,就是一个合适的Skill粒度。
举个例子,"发送邮件"是一个Skill,"格式化邮件内容"是另一个Skill,"选择收件人"又是一个Skill。这三个可以组合成"发送通知邮件"这个更复杂的能力,但底层是三个独立Skill。这样设计的好处是,写作Agent可能只需要"格式化邮件内容",通知Agent需要完整的"发送通知邮件",各取所需。
反过来,如果把"发送通知邮件"做成一个大Skill,写作Agent想复用它的一部分就做不到,只能重新实现,复用性就没了。
4.2 Skill的接口设计:稳定比灵活更重要
Skill一旦被多个Agent依赖,接口就不能随便改。所以设计Skill接口时,要面向稳定设计,而不是面向当前需求设计。
具体做法:
- 输入输出用结构化schema:不要用自由文本,用明确的字段定义
- 预留扩展字段:用可选字段而不是位置参数,方便后续加参数而不破坏兼容性
- 版本化:Skill要有版本号,重大变更时升版本,老版本保留一段时间
- 错误码标准化:不同Skill的错误码要统一规范,方便调用方统一处理
我踩过的坑是:早期Skill接口设计得太"贴合当前需求",用了很多位置参数和隐式约定。后来需求一变,接口就得改,所有调用方都得跟着改,痛苦不堪。后来改成结构化schema+可选字段,扩展就顺畅多了。
4.3 Skill的发现和加载机制
当Skill多起来之后,怎么让Agent知道有哪些Skill可用、怎么加载,就成了问题。
我的方案是注册中心+按需加载:
- 所有Skill在注册中心登记,包含名称、描述、schema、版本
- Agent启动时只加载元数据(名称、描述),不加载实现
- 真正调用某个Skill时,才动态加载其实现
这样做的好处是启动快、内存占用低。缺点是首次调用有加载延迟,可以通过预热常用Skill来缓解。
Skill的发现可以基于语义匹配:Agent描述自己需要什么能力,注册中心返回匹配的Skill列表。这比硬编码Skill名称灵活得多,新增Skill时不需要改Agent代码。
4.4 Skill的测试和版本管理
Skill要被多个Agent复用,质量必须过关。我的做法是每个Skill都要有:
- 单元测试:验证Skill在各种输入下的行为
- 契约测试:验证Skill的输入输出符合schema
- 集成测试:验证Skill在真实Agent调用下的表现
版本管理上,我用语义化版本:主版本号变更表示不兼容的接口变更,次版本号表示向后兼容的功能新增,修订号表示bug修复。Agent依赖Skill时指定版本范围,避免意外升级导致的问题。
一个实用技巧:给每个Skill加一个"健康检查"接口,返回Skill的可用状态和依赖状态。编排层可以定期检查,发现异常Skill及时告警或降级。
5. DeepAgents编排:把前面三层串起来
前面三层搭好之后,DeepAgents这一层就是做调度和协调。这层看起来是"上层建筑",实际上最容易出问题,因为它要处理的是全局的复杂性。
5.1 编排模式的选择:中心化还是去中心化
多Agent编排有两种基本模式:中心化编排和去中心化编排。
中心化编排:有一个主Agent(或编排器)负责拆解任务、分配子任务、汇总结果。所有Agent都向主Agent汇报。优点是逻辑清晰、容易调试、全局状态可控。缺点是主Agent是瓶颈,复杂任务下主Agent的上下文会爆炸。
去中心化编排:Agent之间直接协作,没有中心节点。优点是扩展性好、没有单点瓶颈。缺点是全局状态难追踪、调试困难、容易出现死循环或任务丢失。
我的实测结论是:混合模式最实用。顶层用中心化编排做任务拆解和结果汇总,子任务内部用去中心化协作。这样既有全局可控性,又有局部灵活性。
具体来说,主Agent负责把大任务拆成几个子任务,每个子任务交给一个"子编排Agent",子编排Agent内部可以自由调用其他Agent和Skill,完成后把结果返回给主Agent。主Agent不需要知道子任务内部的细节,只需要知道子任务的输入输出。
5.2 任务拆解的粒度:拆到多细才合适
任务拆解粒度直接影响系统效率和可靠性。拆得太粗,单个Agent负担重、容易失败;拆得太细,Agent间通信开销大、协调复杂。
我的经验是按"可独立完成的最小单元"拆解。判断标准:这个子任务能不能由一个Agent独立完成,不需要中途和其他Agent协商?如果能,就是一个合适的拆解粒度。
举个例子,"写一篇技术文章"可以拆成"调研资料""列大纲""写正文""校对"。这四个子任务各自可以由一个Agent独立完成,中间不需要协商(除了大纲到正文的传递)。如果拆成"写第一段""写第二段",就太细了,因为段落之间需要连贯性,拆开反而增加协调成本。
还有一个技巧:拆解时预留缓冲。不要把任务拆得刚刚好,留一些余量给Agent自主发挥。比如调研任务,不要规定"查5个来源",而是说"查足够多的来源以支撑论点",让Agent自己判断。
5.3 结果汇总:怎么把多个Agent的输出拼成完整答案
结果汇总是编排层的关键环节。多个Agent的输出格式可能不同、质量参差不齐、甚至互相矛盾。怎么汇总成一致的最终答案?
我的做法是结构化汇总+冲突消解:
- 每个子任务返回结构化结果,包含内容、置信度、来源
- 汇总时先做格式归一化,统一成标准结构
- 如果有冲突(比如两个Agent给出矛盾信息),触发冲突消解流程:要么让一个Agent仲裁,要么补充调研,要么在最终答案里标注分歧
这里要注意:不要简单拼接。我见过很多实现是把各Agent的输出直接拼起来,结果读起来像大杂烩。好的汇总应该是"融合"——提取各输出的核心信息,重新组织成连贯的答案。
5.4 可观测性:多Agent系统没有日志就是灾难
多Agent系统最怕的就是"黑盒"——出了问题不知道哪个环节错了。所以可观测性必须从第一天就做。
我的做法是全链路追踪:
- 每个任务有唯一ID,贯穿所有Agent调用
- 每次Agent调用、Skill调用、MCP调用都记录:输入、输出、耗时、状态
- 关键决策点记录决策依据(比如为什么选择这个Agent、为什么重试)
- 提供可视化界面,能看到任务的完整执行链路
这样出问题时,能快速定位是哪个Agent、哪个Skill、哪个工具出了问题。没有这套机制,多Agent系统的调试基本靠猜。
一个实用建议:日志里记录token消耗。多Agent系统token消耗很容易失控,有了消耗数据才能做优化。我一般会统计每个Agent、每个Skill的平均token消耗,找出消耗大户重点优化。
6. 实测中踩过的坑和对应的解法
前面讲了架构和设计,这一节专门讲实测中遇到的具体问题。这些坑文档里基本不会写,但实际搭建时几乎都会遇到。
6.1 Agent之间死循环调用
这是最危险的问题。Agent A调用Agent B,B又调用A,无限循环,token烧光。
根因:Agent之间没有调用深度限制,也没有循环检测。
解法:在A2A协议里加调用链追踪。每次调用携带调用链(谁调用了谁),接收方检查调用链里有没有自己,如果有就拒绝。同时设置最大调用深度,超过就中断。
我实测下来,光加调用链检测还不够,因为有些循环是间接的(A→B→C→A)。所以还要加全局的调用计数,单个任务的总调用次数超过阈值就告警。
6.2 上下文爆炸导致模型输出质量下降
多Agent协作时,上下文会不断累积。Agent A的对话历史传给B,B又传给C,上下文越来越长,最后模型被淹没在无关信息里,输出质量急剧下降。
根因:上下文无节制传递,没有做压缩和筛选。
解法:分层传递+摘要压缩。必传的信息直接传,选传的信息做摘要,不传的信息丢弃。我一般会在Agent之间传递时,把历史对话压缩成一段摘要,只保留关键决策和结论。
还有一个技巧:用结构化上下文代替自然语言上下文。把关键信息提取成结构化字段(任务目标、约束、已知事实、待解决问题),比传一大段对话有效得多。
6.3 Skill版本不兼容导致Agent行为异常
Skill升级后,依赖它的Agent行为变了,甚至报错。
根因:Skill升级没有做兼容性检查,Agent依赖没有锁定版本。
解法:Skill用语义化版本,Agent依赖时指定版本范围。Skill升级时跑契约测试,确保向后兼容。重大变更时升主版本号,老版本保留,给Agent迁移时间。
我踩过的坑是:早期Skill没有版本管理,直接覆盖升级,结果一个Agent的行为突然变了,排查了半天才发现是Skill升级导致的。后来加了版本管理,这类问题就没了。
6.4 MCP工具超时导致整个任务卡住
某个MCP工具响应慢,Agent一直等,整个任务卡住。
根因:MCP调用没有超时设置,或者超时设置太长。
解法:每个MCP调用都要有超时,超时后返回明确错误,Agent根据错误决定重试还是降级。超时时间根据工具类型设置:查询类5-10秒,写入类30秒,长任务用异步模式。
还有一个细节:超时后要清理资源。如果工具调用已经发起了外部请求,超时后要确保请求被取消,避免资源泄漏。
6.5 Agent能力重叠导致任务分配混乱
多个Agent都能做某件事,编排层不知道该分配给谁,或者分配了但效果不好。
根因:Agent能力定义不清晰,没有优先级和专长区分。
解法:Agent Card里明确能力范围和专长领域。编排层分配任务时,优先选专长匹配的Agent,没有专长匹配的再选通用Agent。同时记录每个Agent的历史表现,作为分配依据。
我一般会给Agent加一个"能力评分",表示它在某个领域的擅长程度。编排时按评分排序,选最高的。这样既避免了混乱,又能持续优化分配策略。
7. 从单Agent到集群的渐进式演进路线
如果你现在还在写单Agent,想往多Agent集群演进,我的建议是渐进式,不要一步到位。
7.1 第一阶段:单Agent+MCP
先把单Agent跑通,接入MCP工具。这个阶段的重点是:把工具描述写好、把MCP调用封装好、把错误处理做好。这个阶段不需要A2A和Skills,但要把基础打牢。
我见过很多人跳过这个阶段直接上多Agent,结果基础不牢,后面全是坑。单Agent阶段把MCP玩熟了,后面接A2A和Skills会顺很多。
7.2 第二阶段:引入Skills
把单Agent的能力拆成Skill,做封装和复用。这个阶段的重点是:Skill粒度设计、接口稳定性、版本管理。这个阶段还是单Agent,但能力已经模块化了。
这个阶段的好处是:当你后面要加第二个Agent时,可以直接复用已有的Skill,不用重新实现。
7.3 第三阶段:引入A2A,两个Agent协作
从两个Agent开始,引入A2A。这个阶段的重点是:Agent Card设计、任务生命周期、错误处理。两个Agent的协作比单个Agent复杂得多,但比多个Agent简单,适合练手。
我建议先做最简单的场景:一个Agent负责调研,一个Agent负责写作,调研Agent通过A2A把结果交给写作Agent。跑通这个场景,A2A的核心机制就掌握了。
7.4 第四阶段:DeepAgents编排,扩展到多Agent
有了前面的基础,再引入DeepAgents做编排,扩展到多个Agent。这个阶段的重点是:编排模式选择、任务拆解、结果汇总、可观测性。
这个阶段最容易出问题的是全局复杂性。Agent一多,交互路径爆炸,调试困难。所以可观测性必须提前做好,否则后面寸步难行。
7.5 每个阶段的验证标准
每个阶段都要有明确的验证标准,达标了再进下一阶段:
| 阶段 | 验证标准 |
|---|---|
| 单Agent+MCP | 工具调用成功率>95%,错误处理覆盖主要异常 |
| 引入Skills | Skill可独立测试,接口稳定,版本管理规范 |
| 引入A2A | 两Agent协作任务完成率>90%,无死循环 |
| DeepAgents编排 | 多Agent任务完成率>85%,全链路可追踪 |
这些标准不是绝对的,但能帮你判断是否准备好进入下一阶段。我见过太多项目在基础不牢的情况下强行上多Agent,最后推倒重来。
8. 一些关于扩展性和长期维护的思考
多Agent集群搭起来只是开始,长期维护才是真正的挑战。这一节聊聊扩展性和维护方面的经验。
8.1 新增Agent的成本要尽可能低
一个好的多Agent架构,新增一个Agent应该是低成本的:写好Agent Card、实现核心逻辑、复用已有Skill、注册到编排层,就能用。
如果新增一个Agent要改很多地方(改编排逻辑、改其他Agent、改通信协议),说明架构有问题。我在设计时会刻意追求"新增Agent零侵入"——新Agent通过标准接口接入,不影响已有Agent。
8.2 Skill的复用率是衡量架构好坏的关键指标
如果你的Skill大部分只被一个Agent使用,说明Skill粒度或设计有问题。好的Skill应该被多个Agent复用。
我一般会统计Skill的复用率,复用率低的Skill要么合并、要么重新设计。这个指标能反映架构的健康度。
8.3 协议变更要向后兼容
MCP和A2A的协议可能会演进,Agent Card的schema可能会变。任何变更都要考虑向后兼容,否则已有Agent会全部失效。
我的做法是:协议变更时保留旧版本支持,新版本并行运行一段时间,给Agent迁移时间。重大变更时提供迁移工具和文档。
8.4 定期做"混沌测试"
多Agent系统的健壮性很难通过正常测试验证。我一般会定期做混沌测试:随机让某个Agent不可用、随机让某个MCP工具超时、随机注入错误,看系统能不能正确处理。
这种测试能发现很多正常测试发现不了的问题,比如错误处理不完整、降级逻辑有bug、重试机制失效等。
混沌测试的关键是"可控"——要能精确控制注入什么故障、在哪个环节注入。我一般会做一个故障注入开关,测试时打开,生产时关闭。
9. 写在最后的一些个人体会
这套东西我从零搭到能跑,前后花了大概两个月,中间返工了好几次。最大的体会是:多Agent系统的难点不在单个技术点,而在整体的协调和边界划分。MCP、A2A、Skills、DeepAgents每一个单独看都不复杂,但组合起来,边界在哪里、谁负责什么、怎么交互,这些才是真正花时间的地方。
另一个体会是:不要追求一步到位。我一开始想直接搭一个完整的多Agent集群,结果处处是坑,进度极慢。后来改成渐进式,先把单Agent+MCP跑稳,再加Skills,再加A2A,最后上编排,每一步都验证通过再往下走,反而快了很多。
最后一个实用建议:从第一天就做可观测性。多Agent系统没有日志和追踪,调试就是噩梦。我早期偷懒没做,后来补的时候发现很多历史问题已经无法复现了。现在我的做法是,任何Agent、Skill、MCP调用都必须记录,宁可多记也不要漏记。
这套架构目前在我自己的项目里跑得还算稳,任务完成率在85%以上,主要的失败case集中在复杂任务的拆解和汇总环节,这也是我接下来要重点优化的方向。如果你也在搭类似的东西,欢迎交流踩坑经验,尤其是A2A的错误处理和Skill的版本管理这两块,我觉得还有很大的优化空间。