☰
DeepAgents+MCP+A2A+Skills:多智能体集群架构设计与落地实操
2026/10/3 15:12:09 网站建设 项目流程

1. 从单体到集群:为什么需要超级多智能体

1.1 一个智能体扛不住的真实场景

做过 Agent 项目的人大概都有这种体验:一开始写个单体 Agent,接几个工具,跑个问答或者简单任务,感觉挺美好。可一旦业务复杂起来,比如要同时处理文档解析、数据查询、代码生成、外部系统调用、结果校验这几件事,单体 Agent 就开始露怯了。上下文窗口被塞爆、工具调用互相干扰、一个环节出错整个链路崩掉,调试起来像在拆炸弹。

我去年接过一个需求,让 Agent 自动完成"读取合同 PDF → 提取关键条款 → 比对内部风控规则 → 生成审查报告 → 推送到审批系统"这条链路。最开始用一个 Agent 硬扛,结果就是提示词越写越长,工具描述堆到几千 token,模型开始"幻觉式调用"——明明该查数据库它去调了文件写入。后来拆成三个 Agent 各管一段,问题立刻缓解了一大半。这就是多智能体(Multi-Agent)最朴素的价值:分而治之,各司其职。

但拆开之后新问题又来了:Agent 之间怎么通信?谁来决定任务分给谁?某个 Agent 挂了怎么办?外部工具怎么统一接入?这时候就需要一套完整的架构来支撑,也就是标题里说的DeepAgents + MCP + A2A + Skills这套组合拳。

1.2 四个关键词各管什么

先把这四个概念用大白话捋清楚,不然后面全是空中楼阁。

DeepAgents可以理解成"智能体的编排大脑"。它负责定义有哪些 Agent、每个 Agent 的职责边界、任务怎么流转、状态怎么维护。你可以把它类比成一支球队的教练组,不直接上场踢球,但决定谁上、打什么阵型、什么时候换人。

MCP(Model Context Protocol)是模型和外部世界之间的"标准插座"。以前每接一个工具就要写一套适配代码,数据库一套、文件系统一套、浏览器一套,重复劳动。MCP 把这些统一成一种协议,工具方按协议暴露能力,Agent 方按协议调用,插上就能用。热词里有人问"mcp 是软件协议还是硬件协议那个概念",答案是软件协议,而且是应用层的通信协议,跟硬件没关系。

A2A(Agent to Agent)解决的是 Agent 之间的对话问题。MCP 管的是"Agent 调工具",A2A 管的是"Agent 找 Agent"。一个负责采购的 Agent 需要财务 Agent 审批预算,它俩之间怎么传消息、怎么协商、怎么回传结果,这就是 A2A 的活儿。

Skills是 Agent 的"技能包"。一个 Agent 会什么,取决于它加载了哪些 Skill。写代码是一个 Skill,查数据库是一个 Skill,画流程图是一个 Skill。Skills 让 Agent 的能力可以像插件一样装卸,而不是写死在提示词里。

提示:这四个概念不是互斥的,而是分层协作。DeepAgents 在顶层编排,MCP 在底层接工具,A2A 在横向连 Agent,Skills 在纵向扩能力。理解这个分层,后面所有设计都不会乱。

1.3 这套架构适合谁

如果你只是做个玩具 Demo,单体 Agent 完全够用,别过度设计。但如果你遇到下面任意一种情况,这套架构就值得认真考虑:

  • 任务链路超过 5 个步骤,且步骤之间有依赖关系
  • 需要接入 3 个以上的外部系统或工具
  • 多个业务方各自维护自己的 Agent,需要互通
  • 对可靠性有要求,单个环节失败不能拖垮全局
  • 需要动态扩展能力,今天加个新工具明天加个新技能

说白了,当你开始觉得"一个 Agent 管不过来"的时候,就是该上多智能体编排的时候。这篇文章我会把整套架构从设计思路到落地细节拆开讲,包括参数怎么定、坑在哪里、怎么排查,尽量让你看完能直接抄作业。

2. 整体架构设计与选型考量

2.1 分层架构:把复杂度关进笼子

多智能体系统最容易犯的错,是把所有逻辑揉在一起,最后变成一坨谁也不敢改的意大利面。我的做法是严格分层,每层只干一件事:

层级职责对应技术类比
编排层任务分解、路由、状态管理DeepAgents教练组
通信层Agent 间消息传递、协商A2A对讲机
能力层技能定义、加载、执行Skills球员技能
接入层外部工具、数据源统一接入MCP标准插座
执行层具体模型调用、推理LLM Runtime球员上场

这样分层的好处是,任何一层出问题都能快速定位。比如 Agent 调不到工具,先看接入层 MCP 服务是否正常;Agent 之间不通信,先看通信层 A2A 的消息格式对不对。排查范围被压缩到单层,而不是全链路大海捞针。

2.2 为什么选 MCP 而不是自己写适配

有人会问,我直接写函数调用不行吗,为什么要引入 MCP 这层协议?我踩过的坑告诉你答案。

早期项目里,我让 Agent 直接调 Python 函数,工具描述写在提示词里。三个工具还好,到第八个工具的时候,提示词里光工具描述就占了两千多 token,而且每次加工具都要改提示词、改代码、重新测试。更麻烦的是,不同模型对工具描述的格式要求还不一样,换模型就得重写一遍。

MCP 的价值在于把"工具能力"和"模型调用"解耦。工具方只需要按 MCP 协议暴露一个服务,声明"我能做什么、需要什么参数、返回什么",Agent 方通过标准客户端去发现和调用。换模型不用改工具,加工具不用改 Agent 核心逻辑。热词里"ruoyi-vue-pro 合并 mcp 功能"说的就是这种思路——把 MCP 能力集成进现有系统,让老系统也能被 Agent 调用。

2.3 A2A 与 MCP 的边界

这两个最容易混。记住一句话:MCP 是纵向的(Agent 向下调工具),A2A 是横向的(Agent 平级找 Agent)。

举个例子,一个"报告生成 Agent"需要数据,它通过 MCP 调用数据库工具拿到原始数据,这是纵向。但它发现数据需要财务部门确认,于是通过 A2A 发消息给"财务 Agent"请求审核,这是横向。财务 Agent 审核完,又通过 MCP 调用审批系统接口提交结果,这又是纵向。

边界清晰了,设计时就不会纠结"这个功能该放 MCP 还是 A2A"。判断标准很简单:被调用方是"工具"还是"智能体"。工具没有自主决策,给参数就返回结果,走 MCP;智能体有自主决策,需要理解意图再行动,走 A2A。

2.4 Skills 的加载策略

Skills 的设计核心是"按需加载"。一个 Agent 可能拥有几十个 Skill,但一次任务只用得上三五个。如果全量加载,上下文直接爆炸。

我的策略是三级加载:

  1. 元数据常驻:每个 Skill 只保留名称、一句话描述、触发条件,占几十 token
  2. 按需注入:任务路由阶段判断需要哪些 Skill,把完整定义注入上下文
  3. 执行后卸载:Skill 执行完,从上下文移除,释放窗口

这样即使 Agent 挂载了 50 个 Skill,单次任务的上下文占用也能控制在合理范围。实测下来,三级加载比全量加载的 token 消耗降低约 60%,响应速度提升明显。

3. 核心组件实操:从零搭起一个可编排集群

3.1 DeepAgents 编排层怎么落地

编排层的核心是三个东西:Agent 注册表、任务路由器、状态机。

Agent 注册表记录每个 Agent 的 ID、能力描述、可接收的任务类型、当前负载。任务路由器根据任务特征决定分给谁。状态机跟踪整个任务链路的执行状态,哪个环节完成、哪个环节卡住、哪个环节失败。

先看注册表的数据结构,我用 JSON 定义:

{ "agent_id": "report_generator", "capabilities": ["document_parse", "data_query", "report_write"], "accepts": ["generate_report", "revise_report"], "max_concurrent": 3, "current_load": 1, "endpoint": "a2a://report-agent.local" }

任务路由器的逻辑我建议用"能力匹配 + 负载均衡"双因子。先按任务类型筛出能接的 Agent,再按当前负载排序,选最闲的那个。别用复杂的打分模型,初期没必要,规则清晰反而好调试。

状态机的实现要点是每个状态转移都要可追溯。我习惯给每个任务分配一个 trace_id,所有状态变更都带上这个 ID 写日志。出问题时按 trace_id 一捞,整条链路清清楚楚。

3.2 MCP 服务接入的完整流程

接入一个 MCP 服务分四步:发现、握手、调用、回收。

发现阶段,Agent 通过 MCP 客户端向服务端请求能力清单。服务端返回一个 tools 列表,每个 tool 包含名称、描述、参数 schema。这一步的关键是缓存能力清单,别每次调用都重新拉,浪费往返时间。

握手阶段,确认协议版本、认证方式、超时配置。这里有个坑:不同 MCP 服务端的超时默认值不一样,有的 30 秒有的 5 分钟。我建议在客户端统一设一个上限,比如 60 秒,超过就判定失败并重试。

调用阶段就是标准的请求-响应。参数校验一定要做,别指望服务端帮你兜底。我见过因为参数类型不对导致服务端直接崩的情况,客户端传了个字符串,服务端期望整数。

回收阶段容易被忽略。MCP 连接是长连接,用完不释放会占资源。我的做法是连接池管理,空闲超过 5 分钟自动回收,池子大小按并发量设,一般 10 到 20 够用。

# MCP 客户端调用示例(伪代码,展示结构) class MCPClient: def __init__(self, endpoint, pool_size=10, idle_timeout=300): self.endpoint = endpoint self.pool = ConnectionPool(size=pool_size, idle_timeout=idle_timeout) self.capabilities = None def discover(self): # 拉取能力清单并缓存 if self.capabilities is None: self.capabilities = self._request("list_tools") return self.capabilities def call(self, tool_name, params, timeout=60): # 参数校验 schema = self._get_schema(tool_name) self._validate(params, schema) conn = self.pool.acquire() try: return conn.request(tool_name, params, timeout=timeout) finally: self.pool.release(conn)

3.3 A2A 通信的消息设计

A2A 的消息格式我建议参考"信封 + 正文"的结构。信封放路由信息(发送方、接收方、消息类型、trace_id),正文放业务内容。

消息类型至少要有四种:请求、响应、通知、错误。请求是"我需要你做件事",响应是"做完了,结果在这",通知是"我这边状态变了,同步给你",错误是"出问题了,需要处理"。

这里有个设计决策值得说:同步还是异步。同步调用简单,但一个 Agent 卡住会阻塞整条链路。异步解耦好,但状态管理复杂。我的经验是,短任务(预期 10 秒内完成)用同步,长任务用异步。异步的话,接收方处理完通过回调或者消息队列通知发送方。

消息幂等性必须考虑。网络抖动导致重发是常事,接收方要能识别重复消息。做法是在信封里放一个 message_id,接收方维护一个已处理 ID 的集合,重复的直接丢弃。

3.4 Skills 的定义与注册

一个 Skill 的定义包含五部分:名称、描述、触发条件、执行逻辑、输出格式。

skill: name: contract_clause_extract description: 从合同文本中提取关键条款 triggers: - "提取条款" - "合同审查" input_schema: text: string clause_types: array output_schema: clauses: array confidence: float executor: contract_skill.py

触发条件的设计很关键。写得太宽,Skill 会被误触发;写得太窄,该用的时候用不上。我的做法是用语义相似度而不是关键词匹配。把触发条件转成向量,任务描述也转成向量,相似度超过阈值就触发。阈值一般设 0.75 左右,具体看业务调。

Skill 注册到 Agent 时,只注册元数据。真正执行时才加载 executor。这样 Agent 启动快,内存占用低。

4. 完整实操:搭一个合同审查 Agent 集群

4.1 场景定义与 Agent 拆分

光讲理论没意思,我用一个完整案例串起来。需求是:用户上传合同 PDF,系统自动完成条款提取、风险比对、报告生成、审批推送。

拆成四个 Agent:

  • 解析 Agent:负责 PDF 转文本、分段落、识别条款结构
  • 风控 Agent:负责拿条款去比对内部规则库,标记风险点
  • 报告 Agent:负责把风险点组织成人类可读的审查报告
  • 审批 Agent:负责把报告推送到审批系统并跟踪状态

四个 Agent 通过 A2A 串联,各自通过 MCP 调用所需工具。解析 Agent 调文件解析 MCP,风控 Agent 调规则库 MCP,报告 Agent 调模板 MCP,审批 Agent 调审批系统 MCP。

4.2 任务流转的完整链路

用户上传 PDF 后,编排层生成一个 trace_id,启动状态机。

第一步,任务路由器把"解析合同"任务分给解析 Agent。解析 Agent 通过 MCP 调用 PDF 解析工具,拿到结构化文本,通过 A2A 把结果发给风控 Agent,同时更新状态机。

第二步,风控 Agent 收到条款,通过 MCP 调用规则库查询接口,逐条比对。这里有个性能优化点:批量查询而不是逐条查询。我一开始逐条查,100 条条款查了 100 次,耗时 40 秒。改成批量后一次查完,耗时降到 3 秒。

第三步,风控 Agent 把风险标记结果发给报告 Agent。报告 Agent 通过 MCP 调用模板工具,把风险点填入模板,生成报告。

第四步,报告 Agent 把报告发给审批 Agent。审批 Agent 通过 MCP 调用审批系统接口推送,拿到审批单号,回传给编排层。

整个链路的状态机长这样:

阶段负责 Agent输入输出失败处理
解析解析 AgentPDF结构化条款重试 2 次,仍失败则终止
风控风控 Agent条款风险标记降级为人工审核
报告报告 Agent风险标记报告文本重试 1 次
审批审批 Agent报告审批单号入队重试

4.3 关键参数的计算与选择

并发数怎么定?我的公式是:并发数 = 平均任务耗时 / 目标吞吐量。假设单个合同处理平均 30 秒,目标是每分钟处理 10 个,那并发数 = 30 / 6 = 5。再留 50% 余量,设 8 个并发。

超时怎么定?超时 = 平均耗时 × 3。平均 30 秒,超时设 90 秒。超过就判定失败,触发重试或降级。别设太短,网络抖动会误杀;别设太长,卡住的连接会拖垮整个池子。

重试次数怎么定?最多 2 次。第一次失败可能是偶发,第二次失败大概率是系统性问题,再重试就是浪费资源。重试间隔用指数退避,1 秒、2 秒、4 秒。

上下文窗口怎么分配?假设模型窗口 128K,我的分配是:系统提示 5K、Skill 定义 10K、任务上下文 20K、工具返回 30K、预留 63K 给推理和输出。预留一定要留足,不然推理到一半被截断,结果就是半截话。

4.4 实操现场记录

实际跑起来的时候,我遇到几个有意思的现象。

第一个是解析 Agent 的输出格式不稳定。同样的 PDF,有时候返回的条款是数组,有时候是嵌套对象。原因是 PDF 解析工具对表格的处理不一致。解决办法是在解析 Agent 后面加一个"格式归一化"步骤,不管上游返回什么,统一转成标准结构再往下传。

第二个是风控 Agent 的规则库查询超时。规则库数据量大,单次查询慢。除了前面说的批量查询,我还加了本地缓存,热门规则缓存在内存里,命中率大概 70%,查询耗时进一步降低。

第三个是报告 Agent 生成的报告太长。模型倾向于把所有细节都写进去,导致报告几千字,审批人根本看不完。后来在 Skill 里加了"摘要优先"的约束,先给 200 字摘要,再附详细内容,审批体验好很多。

5. 常见问题与排查技巧实录

5.1 Agent 调用工具失败的排查路径

工具调用失败是最常见的问题,排查按这个顺序走:

  1. 看 MCP 服务是否存活:直接 curl 服务端健康检查接口
  2. 看能力清单是否拉到:客户端缓存的能力清单是否包含目标工具
  3. 看参数是否匹配 schema:类型、必填项、枚举值逐个核对
  4. 看超时配置:客户端超时是否小于服务端处理时间
  5. 看认证:token 是否过期,权限是否足够

我遇到最多的是第 3 步,参数类型不对。比如 schema 要求 integer,传了 string "123",服务端直接拒绝。加一层参数校验能省很多事。

5.2 Agent 之间消息丢失怎么办

A2A 消息丢失通常有三个原因:网络抖动、接收方未启动、消息队列满。

网络抖动靠重试解决,但重试要幂等。接收方未启动的话,消息要持久化,等接收方上线再投递。消息队列满的话,要么扩容要么限流。

我的做法是消息落库 + 确认机制。发送方发消息前先写库,接收方处理完回一个 ack,发送方收到 ack 才标记消息完成。超时没收到 ack 就重发。这样即使中间环节挂了,消息也不会丢。

5.3 上下文爆炸的应急处理

跑着跑着上下文满了,模型开始胡言乱语,这是多智能体系统的经典故障。

应急处理:立即截断历史,只保留最近 3 轮对话 + 系统提示 + 当前任务。然后重新触发。

根治方案:上下文压缩。把历史对话用一个小模型总结成摘要,摘要控制在 500 字以内,替换原始历史。我实测下来,压缩后上下文占用降低 70%,任务成功率基本不受影响。

还有一个技巧是分阶段清理。任务进入新阶段时,把上一阶段的中间结果清理掉,只保留最终结论。比如解析阶段完成后,原始 PDF 文本就不需要了,只留结构化条款。

5.4 常见问题速查表

现象可能原因排查动作解决方案
工具调用超时服务端慢或网络差查服务端日志、测网络延迟加超时重试、优化服务端
Agent 不响应消息未送达或 Agent 挂了查消息队列、查 Agent 心跳重发消息、重启 Agent
结果格式错乱上游输出不稳定对比多次输出加格式归一化层
上下文溢出历史累积过多查 token 计数压缩历史、分阶段清理
任务卡住不动状态机未推进查 trace_id 日志手动推进或重置状态
重复执行消息重发未幂等查 message_id 去重加幂等校验

5.5 几个踩过的坑

坑一:Agent 注册表没做版本管理。改了 Agent 能力描述,老任务还在用旧描述,导致路由错误。后来加了版本号,任务绑定创建时的版本,升级时灰度切换。

坑二:MCP 连接池泄漏。异常路径下连接没释放,跑一天池子就满了。后来用 try-finally 强制释放,加了泄漏检测告警。

坑三:Skill 触发条件冲突。两个 Skill 的触发条件相似,任务来了不知道该用哪个。解决办法是给 Skill 加优先级,冲突时高优先级胜出,同时记录冲突日志,定期人工review。

坑四:A2A 消息没有大小限制。有个 Agent 把整个 PDF 文本塞进消息里,几 MB 的消息把队列撑爆了。后来加了消息大小上限,超过 1MB 的走对象存储,消息里只传引用。

6. 扩展方向与个人体会

6.1 从单机到分布式的演进

上面这套跑在单机上没问题,但任务量上来后要分布式。演进路径我建议分三步:

第一步,Agent 无状态化。把 Agent 的状态全部外置到 Redis 或数据库,Agent 本身可以随意启停、扩容。

第二步,消息队列解耦。A2A 通信从直连改成走消息队列,发送方和接收方彻底解耦,一方挂了不影响另一方。

第三步,编排层分片。按 trace_id 哈希把任务分到不同编排节点,每个节点管一部分任务,水平扩展。

这三步不用一次做完,按业务量逐步推进。我见过一上来就搞全套分布式的,结果复杂度爆炸,调试成本远超收益。

6.2 Skills 生态的想象空间

Skills 最大的价值是让能力可以像 App 一样分发。今天你写了个"合同条款提取" Skill,明天别人写了个"发票识别" Skill,大家通过标准接口互相调用,不用重复造轮子。

热词里"codex 好用的 skills""skills 推荐"反映的就是这种需求。未来可能会出现 Skill 市场,开发者上传 Skill,Agent 按需订阅。这需要一套标准:Skill 怎么描述、怎么版本管理、怎么计费、怎么保证安全。目前还在早期,但方向是明确的。

6.3 我个人在实际操作中的体会

搭这套系统最大的感受是:复杂度是守恒的,你不在架构上花时间,就要在调试上花时间。

单体 Agent 看起来简单,但业务一复杂,提示词会变成一坨谁也不敢动的黑箱。多智能体架构前期投入大,要设计协议、要写编排、要处理通信,但一旦跑通,后续加功能就是加 Agent、加 Skill,边际成本很低。

另一个体会是别过度设计。我一开始想搞一套通用的 Agent 编排框架,支持各种花哨的路由策略、动态扩缩容、智能负载均衡。结果写了两个月,发现 80% 的功能用不上,真正跑起来就是"能力匹配 + 轮询"最简单。后来砍掉一半代码,反而更稳。

最后一个建议:日志和可观测性一定要从第一天就做。多智能体系统的调用链路长,出问题时没有完整日志就是盲人摸象。trace_id 贯穿全链路、每个状态变更都记录、关键参数都打点,这些前期花的时间,后期排查时能十倍百倍地还回来。

这套架构还在快速演进,MCP 协议在更新,A2A 的标准也在完善,Skills 的生态刚起步。但核心思路是稳的:分层解耦、标准接口、按需加载。抓住这三点,具体技术怎么变都能跟上。

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

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

立即咨询