
去年下半年开始我把公司内部那些各自为战的 Agent 项目往“一个真正的集群”上面靠。靠完以后回头看最核心的变化不是模型换得多强了而是三样东西补齐了MCP把工具接入统一了A2A让 Agent 之间能正经地打招呼、交接任务Skills则把反复调教出来的能力沉淀成了可复用的模块。这套组合就是“超级多智能体”的基本盘——可编排、可互通、可扩展。这三个词听着抽象拆开落地其实非常具体MCP 解决“Agent 怎么用工具”A2A 解决“Agent 怎么找 Agent 干活”Skills 解决“Agent 的能力怎么被复用”最后再用一个编排内核把它们串起来。这篇文章我就按这个顺序把每一层都讲透给出一套可以直接照着搭的方案。1. 先把“超级多智能体”这句话拆开三个协议各管哪一层很多人一听到“多智能体”就以为是把一堆 Agent 进程拉起来让它们自由对话。这个理解错得挺远。真正的多智能体集群核心不在“多”而在“有序”。要有序就必须先回答三个问题工具从哪来、别人怎么找我、我沉淀下来的能力怎么复用。MCP、A2A、Skills 恰好分别回答了这三个问题。1.1 单 Agent 的瓶颈不是模型不够强是结构不可扩展先说单 Agent 的问题。一个 Agent 把目标、上下文、工具列表全部塞进提示词里模型自己决定调哪个工具、怎么调。这种方式跑 Demo 没问题一旦上生产就会撞墙工具越来越多提示词越来越长模型选择工具的准确率开始下降某个工具的输入输出格式调整了你得去翻提示词里所有相关描述想让另一个 Agent 复用这套能力只能把整段提示词复制过去之后两边各自维护改一处漏一处。我管这种状态叫“提示词的循环”你改提示词、跑一次、看结果、再改提示词Agent 只是一个被提示词驱动的函数谈不上编排。真正需要多智能体架构的场景是同一个系统里存在多种角色、多个职责边界、多种外部依赖比如客服入口、订单查询、翻译、内容审核、回复生成每个角色有独立的上下文和工作流彼此还需要交换中间结果。这种情况下单 Agent 的“一把梭”结构是撑不住的。1.2 三个协议的分工工具层、沟通层、能力层我把这套集群分成三层每一层都有对应的协议载体工具层用 MCPModel Context Protocol负责把外部工具标准化接入。Agent 不需要关心工具是 Java 写的还是 Python 写的也不需要关心它在哪台机器上只要通过 MCP 暴露能力Agent 就能像插 U 盘一样把它用起来。沟通层用 A2AAgent2Agent负责 Agent 之间的对话规则。包括怎么声明自己的能力、怎么发起一个任务、怎么同步任务状态、怎么交付结果。能力层用 Skills负责把“提示词 处理流程 辅助脚本”打包成一个可安装、可版本化、可共享的技能包。Skills 本身不一定走网络协议更像是一个按需加载的本地能力库。举一个生活化的类比MCP 是统一插座和插头规格A2A 是两个同事之间约定好的工作交接流程Skills 是每个人身上的专业技能包。没有统一插头每接一个新设备都得改线路没有交接流程两个同事只能靠默契一换人就全乱没有技能包一个人的经验永远只存在他脑子里。1.3 “可编排、可互通、可扩展”的最终检验标准这三个词不能停留在概念层面每条都得有可以验收的标准。可编排意味着任意一个任务丢进系统你能说清楚它由谁处理、用了哪些工具、按什么顺序执行、当前卡在哪个环节。可互通意味着新加入一个 Agent不需要改已有 Agent 的代码它对外声明自己会什么别人就能找到它并给它派活。可扩展意味着想给系统增加一个新能力不用改主程序塞一个 MCP Server 或一个 Skills 包进去即可模型侧可以通过能力发现机制自动感知。后面所有章节都在围绕这三条标准展开。先讲 MCP。2. MCP工具接入的“USB 接口”从写死代码到即插即用MCP 刚出来那阵子不少人问我同一个问题MCP 到底是软件协议还是硬件协议类似于 USB、PCIe 那种吗答案是它是纯软件协议但它的设计思想确实是照着硬件接口协议抄的作业。USB 解决了不同外设接入电脑的乱象MCP 解决的是不同工具接入 Agent 的乱象。2.1 MCP 的架构模型Agent 是主机工具是外围设备没有 MCP 之前每接一个工具你都要为 Agent 写一份适配代码。接 GitHub 写一套接数据库写一套接内部订单系统再写一套。最痛苦的是这些对接逻辑散落在各个 Agent 的代码里A 项目里写了一版B 项目里又复制了一版两边对接方式稍微不一样后面维护起来就是灾难。MCP 把架构改成了标准的客户端-服务器模型。Agent 那一侧是 MCP Client工具那一侧是 MCP Server。Agent 通过 MCP 协议发现 Server 上注册了哪些工具、每个工具的参数结构是什么样的然后像调用本地函数一样调用远程工具。所有对接细节被收进 MCP Server 内部Agent 侧不再感知工具的具体实现。MCP 协议里三块核心能力Tools工具可执行的函数Agent 按需调用。比如query_order_status(order_id)。Resources资源可读取的数据对象类似 REST API 里的资源。比如order://{order_id}。Prompts提示模板可复用的提示词模板供 Agent 按场景加载。传输层最常见的是 stdio 和 HTTP。stdio 适合本地进程调试HTTP 适合远程部署后面讲部署时会详细说这条路怎么选。2.2 写一个真实可跑的 MCP Server直接上代码。下面这个用 Python FastMCP 写的 MCP Server暴露了一个订单查询工具和一个订单详情资源from fastmcp import FastMCP mcp FastMCP(order-tools) mcp.tool() def query_order_status(order_id: str) - str: 查询订单当前状态返回状态描述文本。 # 这里替换成真实的订单服务调用 return f订单 {order_id} 当前状态已发货物流单号 SF1234567890 mcp.resource(order://{order_id}) def get_order_detail(order_id: str) - str: 订单详情资源返回订单基础信息。 return f订单 {order_id} 详情商品A x1收货地址..., 下单时间... if __name__ __main__: mcp.run(transportstreamable-http)把这段代码跑起来一个 MCP Server 就诞生了。然后用 MCP Client 连接它Agent 就能看到query_order_status和order_detail两个能力入口。不同版本的 FastMCP API 略有差异以官方文档为准但结构就是这样一个注册函数、声明入参和描述、跑服务。这里有个特别容易被忽略的点工具描述决定了模型会不会用它。模型靠description判断这个工具是干什么的、什么时候调用描述写得含糊比如“查询信息”模型很可能在错误场景下调用它或者在正确场景下漏掉它。写描述要像写 API 文档一样明确触发条件和输入输出。我见过不少团队在 MCP Server 上花了很多功夫最后效果不行一查发现工具描述全是“查询订单”“获取数据”这类词不达意的句子。2.3 从“能用”到“扛得住”并发、超时与可观测性很多人关心“AI Agent 怎么扛并发”这个问题的答案可能和直觉相反并发压力根本不在模型推理那边而在工具侧和调度侧。模型推理服务有独立的限流机制Agent 应用层能做的优化空间不大真正容易被冲垮的是 MCP Server——每个工具调用都是一次真实的业务操作可能是查数据库、调外部 API、执行一个本地脚本。所以并发问题首先要解决的是 MCP Server 的稳定性。我的实践原则有这么几条MCP Server 无状态化。不要把会话状态或者临时数据存在 Server 进程里否则水平扩展时状态对不上调用直接乱套。需要状态就放 Redis 这类外部存储。工具调用必须是幂等的至少查询类工具要支持重复调用。因为 Agent 在一次任务里完全可能重复调用同一个工具如果每次调用都有副作用结果就是脏数据满天飞。超时和熔断必须做。模型侧等待工具返回是有耐心上限的一个工具 30 秒不返回整个任务就卡死了。给每个工具调用设置明确超时外部依赖异常时快速失败不要让请求挂在连接池里空等。优先用 HTTP 传输容器编排下别用 stdio。stdio 是一个进程对应一个连接容器里想水平扩展非常别扭用 HTTP 可以把 MCP Server 当成普通服务挂在网关后拉几个副本就拉几个副本。可观测性这块我会在第 5 章集中展开这里先埋一个点每个工具调用都要记录入参摘要、出参摘要、耗时和错误码这是后面做任务追踪的基础。3. A2AAgent 之间真正意义上的“对话协议”而不是互相扔 JSONMCP 解决的是 Agent 和工具之间的连接。那 Agent 和 Agent 之间呢最朴素的方案是把对方也当成一个 MCP 工具来调用。这样做 Demo 确实可行但一旦场景复杂起来就会暴露问题。3.1 为什么不能直接把下游 Agent 当作 MCP 工具来调如果只把一个 Agent 包成一个 MCP 工具调用方和接收方之间的关系是请求-响应式的调用方传参数接收方计算完返回结果。这套模型有一个致命缺陷——没有任务生命周期。接收方干到一半需要用户补充信息怎么办干了两分钟没干完调用方怎么知道进度中途发现任务没法完成怎么取消这些在 MCP 的工具模型里都没有原生的表达方式。Agent 之间的协作本质上是一个长期运行的过程。比如内容生成 Agent 把一篇文档丢给翻译 Agent翻译 Agent 可能要跑几分钟期间进度在变还可能中途卡住要确认术语表。这种场景需要的不只是“传一个函数进去拿到返回值”而是一个任务对象有唯一 ID有状态流转有进度通知有结果交付。A2A 协议补的正是这一块。3.2 AgentCard让每个 Agent 自己讲清楚“我会什么、怎么找我”A2A 协议里有个核心概念叫 AgentCard本质是一份 JSON 格式的能力声明每个 Agent 把它放在一个固定 URL 上对外暴露。别的 Agent 想看你会什么就 GET 一下这个地址。{ name: translator-agent, description: 中英互译 Agent支持 Markdown 文档翻译和术语表定制, url: https://agent.example.com/a2a, version: 1.0.0, capabilities: { streaming: true, pushNotifications: true, stateTransitionLogging: true }, skills: [ translate::zh2en, translate::en2zh ], defaultInputModes: [text/markdown, text/plain], defaultOutputModes: [text/markdown, text/plain] }这份卡片的信息量很大name和description用于能力发现skills字段声明它掌握哪些技能包capabilities声明它支不支持流式输出、支不支持回调通知。上游 Agent 拿到这份卡片就能判断“这个任务能不能交给它”不需要提前硬编码对方的存在。我见过很多团队做 Agent 发现时用“配置文件里写死下游地址”的方案。那只能叫静态路由不是互通。互通的意思是新 Agent 上线只要把自己的 AgentCard 注册到目录里其他 Agent 就能通过能力搜索找到它然后把匹配的任务委托过去。如果你用了 Spring AI 那套 Java 技术栈A2A 也有对应的适配模块把 Agent 类写好框架能自动生成并发布 AgentCard省去手动维护 JSON 的麻烦。3.3 Task 生命周期一次翻译委托的实际消息流A2A 里任务叫做 Task核心状态包括submitted、working、input-required、completed、failed、canceled。一次翻译委托的完整流程是这样的内容生成 Agent 给翻译 Agent 发一个任务消息体里带 Task ID、源文本、目标语言。翻译 Agent 立即返回working状态并附带当前进度比如“正在处理第三章”。翻译过程中如果想确认术语它可以返回input-required把问题抛给上游。翻译完成后返回completed并把翻译结果作为 artifact 附在消息里。{ id: task-1234, sender: content-writer-agent, recipients: [translator-agent], status: { state: working, progress: 50, message: 正在翻译第三章 }, parts: [ { kind: artifact, artifact: { name: translated_doc.md, contents: # 第三章... } } ] }远程调用时A2A 通常走 HTTP 流式响应或者 webhook 回调。不管走哪种方式核心都是这个任务状态机。我强烈建议所有 Agent 间协作都显式建模这个状态机而不是让上游 Agent 阻塞地等待一个长连接。阻塞等一个可能跑几分钟的任务一旦网络抖动整个链路就断了。3.4 和 MCP 的边界什么该走 MCP什么该走 A2A判断标准很直接如果调用方需要的是一个明确的数据或者一个短操作比如查个订单、拉个天气、给某个系统写一条记录走 MCP。如果调用方需要把一个完整任务委托出去还要追踪状态、拿中间进度、处理中途交互走 A2A。我自己习惯用一句话来卡边界MCP 像你在命令行里敲一条指令A2A 像你给同事发了一条工单。指令式的调用适合工具工单式的协作适合 Agent。把这条边界划清楚后面做编排的时候就不会纠结路由逻辑了。4. Skills把“调教出来的能力”变成可复用的技能包协议把工具和 Agent 串起来了但还有一个问题没解决那些没法用网络接口表达的能力怎么办比如代码审查的检查清单、前端组件的生成规范、文档翻译的术语表策略。这些东西本质上是一套“提示词 处理流程 辅助脚本”过去只能写进某个 Agent 的提示词里换一个 Agent 就要复制一遍。Skills 解决的正是这个“能力复用”问题。4.1 Skills 跟 Prompt、Tool 的区别很多初学者分不清三者的关系我打个比方Prompt 是一段话告诉 Agent “你要注意什么”但 Agent 记不记得住、执行不执行全凭运气。Tool 是一个函数输入输出严格定义适合确定性操作但没法承载复杂流程。Skills 是一个能力包里面既有指令性的 SKILL.md相当于说明书又有处理流程、检查清单、辅助脚本Agent 在需要时加载它按里面定义的步骤去做。Skills 相比 Prompt 最大的优势是结构化。一份 SKILL.md 有 YAML 格式的元信息声明这个技能的名字、描述、期望输入输出正文部分把处理流程拆成一二三四步。模型加载后不是“参考一下”而是“照着流程执行”。4.2 写一个“前端开发 Skills”的能力包前端开发类 Skills 现在特别多正好拿它举例。假设你要做一个技能包让 Agent 负责生成新组件代码同时保证代码风格和可访问性达标。目录结构大致是这样frontend-dev/ ├── SKILL.md └── scripts/ ├── check_a11y.py └── generate_component.pySKILL.md 的核心内容--- name: frontend-dev description: 负责 Vue/React 组件生成与代码审查强制检查可访问性和样式规范。 inputs: - request: 用户要求的组件描述包含功能点和技术栈 - styleGuide: 可选的团队样式规范文件路径 outputs: - component_code: 生成的组件代码文件 - review_report.md: 代码审查报告 version: 1.1.0 --- ## 处理流程 1. 解析用户请求确认技术栈React/Vue和组件类型。 2. 从团队规范目录加载 styleGuide如果提供。 3. 生成组件代码代码内必须包含 aria 属性。 4. 调用 scripts/check_a11y.py 检查可访问性不通过则修改。 5. 输出组件代码并生成 review_report.md。这份 SKILL.md 看起来简单但里面有三个设计细节很关键输入输出写清楚。模型拿到技能包后知道自己该给什么、该产出什么不会答非所问。强制检查脚本。Skills 不能只靠提示词约束最好自带可执行的校验脚本把“检查可访问性”这种话变成实际跑的代码。版本号。团队里多人维护技能包时版本号能避免模型加载到过期版本。类似地“代码审查 Skills”“文档翻译 Skills”“论文写作 Skills”都能按这个模式拆声明适用场景定义输入输出把处理流程拆成步骤配上可执行脚本。Skills 的最佳实践不是“写一条高质量的提示词”而是“设计一个有交付标准的流水线”。4.3 Skills 目录怎么管理版本、发现、权限在单机工具里Skills 一般放在某个约定目录下像 Codex、Claude Code 这类编码 Agent 已经把这个机制做成一等公民了开发者把自己的 skills 目录放进去Agent 会自动读取。但到了多 Agent 集群场景Skills 不能只靠“每个 Agent 本地放一份”要有统一管理统一注册中心。所有技能包集中存放Agent 启动或任务需要时按名称拉取。相关技能包支持从本地目录、对象存储或 Git 仓库加载。按需加载。不要把所有 SKILL.md 塞进你的主提示词模型上下文不够用也会产生“工具选择困难”。任务匹配到某个技能后再把对应技能包内容注入上下文。权限分级。不是所有技能包都应该被任何 Agent 执行包含写数据库、发通知这类敏感操作的技能包要限制可调用的 Agent否则一个入口 Agent 被提示词注入攻击整个集群的能力都可能被滥用。5. DeepAgents 编排层从任务分解到状态机的一整套调度内核到这里工具、通信、能力三块积木都已经有了。那谁来决定怎么组合这就是编排层DeepAgents 编排内核的工作。我理解的 DeepAgents核心不是某个具体框架而是一套调度设计路线把任务分解、Agent 路由、工具调用、A2A 委托、状态追踪、结果汇聚统一成一个可解释的调度循环。下面拆开讲。5.1 编排器到底在编排什么任务计划与路由决策编排器的输入是用户目标输出是一个完整的任务链路。比如用户说“帮我处理一下这个英文订单投诉”编排器要回答几个问题这个任务需要哪些 Agent 参与客服入口 Agent、翻译 Agent、订单查询 Worker每个 Agent 需要用哪些工具MCP 订单工具、A2A 翻译任务执行顺序是什么先翻译再查订单最后生成回复某个 Agent 失败了怎么办重试、降级、换 Agent实际操作中我不建议把所有编排逻辑都交给模型做“自由发挥”。模型自由度太高链路就不可控出问题根本没法复盘。我习惯用“规则 模型”的混合模式先用规则做粗路由根据任务类型和 Agent 的能力声明把任务分给对应的 Agent再用模型做细编排让模型在粗路由的框架内决定调用哪些工具、生成什么内容。5.2 调度循环的最小实现状态机、工具调用与 A2A 委托核心调度循环听起来很高大上拆到底就是一个 while 循环加一个状态机。大概是这样from enum import Enum, auto class TaskState(Enum): PENDING auto() DISPATCHED auto() RUNNING auto() WAITING_A2A auto() COMPLETED auto() FAILED auto() CANCELLED auto() class Orchestrator: def __init__(self, registry, mcp_client, a2a_client, skill_registry): self.registry registry # Agent 注册表 self.mcp_client mcp_client # MCP 客户端 self.a2a_client a2a_client # A2A 客户端 self.skill_registry skill_registry # 技能注册表 def run_task(self, task): agent self.registry.discover(task) # 路由找到负责的 Agent tools self.mcp_client.list_tools(agent.mcp_server) skills self.skill_registry.get(agent.required_skills) while task.state ! TaskState.COMPLETED: response model.call( systemagent.prompt, toolstools, skillsskills, historytask.trace, ) for action in response.actions: if action.type tool_call: result self.mcp_client.invoke(action.name, action.args) task.trace.append((tool, action.name, result)) elif action.type a2a_delegate: subtask self.a2a_client.delegate(action.target_agent, action.params) task.state TaskState.WAITING_A2A self.a2a_client.wait_subtask(subtask.id) task.trace.append((a2a, action.target_agent, subtask.result)) elif action.type complete: task.output action.output task.state TaskState.COMPLETED这个伪代码已经把编排内核的骨架画出来了。现实项目里会比这个复杂比如要支持多个子任务并行、要接消息队列做异步调度、要处理重试和超时但骨架就是这三件事的循环模型决策、执行动作、记录轨迹。有个经验值得单独说一下子任务执行不要用阻塞等待。上面的代码里wait_subtask是阻塞的新手容易这么写但一旦下游 Agent 处理时间超过链路容忍度整个编排循环就卡住了。更稳的做法是把等待做成事件驱动子任务完成时通过回调或者轮询把结果写入任务队列编排器空闲时继续消费。这样编排器永远在处理“当前能推进的事”而不是干等一个慢任务。5.3 可观测性设计把三块协议串成一条追踪链没有可观测性的多 Agent 集群等于在工地摸黑施工出问题连哪个环节断的都不知道。我吃过这个亏早期系统没有统一追踪一个跨三个 Agent 的任务失败了只能看到一堆日志碎片定位花了一个下午。后来我强制所有环节都带trace_id每个动作都落一条结构化日志问题定位时间从小时级压到了分钟级。具体在每个环节要记录什么我整理了一张表环节必须记录的信息建议保留时间MCP 工具调用工具名、入参摘要、出参摘要、耗时、错误码30 天A2A 委托任务 ID、发送方、接收方、状态流转、每次消息的 part 类型90 天Skills 加载技能名、版本、加载时间、关键输出文件30 天模型调用模型名、输入 token 数、输出 token 数、耗时、是否重试30 天还要强调一点日志摘要要做脱敏。入参摘要不等于入参全量打印订单号、姓名、地址这些字段要么截断要么打码不然可观测性做成了数据泄露那就得不偿失了。6. 组装一台超级集群一个跨 Agent 订单售后任务的完整链路前面四章分别讲了零件现在我们来把整台机器装起来。用一个我实际搭过的“订单售后”场景来走一遍端到端链路你会看到 MCP、A2A、Skills、编排器是怎么协作的。6.1 集群拓扑从入口到工具的四层角色划分这个集群由几个独立服务组成客户入口 AgentGateway Agent接收用户提交的投诉工单判断工单需要哪些处理。它自己是编排器直接管理的 Agent。翻译 AgentTranslator Agent负责中英文转换通过 A2A 被入口 Agent 委托任务。订单查询 WorkerOrder Worker通过 MCP Server 连接订单系统和物流系统处理订单状态查询。回复生成 AgentReply Agent聚合所有中间结果生成给用户的最终回复。编排器Orchestrator串联所有角色维护每个任务的统一状态机。MCP Server 集群订单/物流/工单每个业务系统一个 MCP Server独立部署。6.2 走一遍链路从用户投诉到自动回复的全过程假设用户提交了一条中英混合的投诉“我的订单 #2024001 收到两周了还没发货Please help.”第一步入口 Agent 接收工单拆解任务。编排器给这个任务分配一个trace_idTRACE-001入口 Agent 发现文本里包含英文和订单号判断需要两件事查订单状态、翻译用户描述。第二步委托翻译 AgentA2A 链路。入口 Agent 通过 A2A 给翻译 Agent 发一个任务内容是“翻译这段投诉文本保留订单号不动”翻译 Agent 返回working业务方无需干等。这段时间里入口 Agent 可以同步去查订单状态。第三步查询订单状态MCP 链路。订单 Worker 通过 MCP Server 调用query_order_status(order_id2024001)拿到订单状态“已支付待发货”。这一步的入参、耗时、出参都会以trace_idTRACE-001写入日志。第四步聚合结果生成回复。翻译 Agent 返回翻译文本订单 Worker 返回订单状态回复生成 Agent 按“道歉 说明订单状态 给出预计处理时间”的回复模板生成最终答复交付给用户。整个链路里编排器始终握着每个子任务的状态入口 Agent 正在做什么、翻译任务到哪一步了、MCP 工具调用成功没有。任何一个环节出问题都能通过trace_id把整条链路的日志捞出来。6.3 部署形态与技术栈参考部署的时候我建议把职责拆到进程级别不要搞一个超级进程把所有逻辑塞进去编排器独立服务负责调度和状态管理可以水平扩展但要注意状态存储统一放 Redis。MCP Server每个业务域一个独立服务挂在网关后用 HTTP 传输对外暴露。Agent 服务按角色拆成独立部署单元可以共享同一份大模型 API但上下文隔离。Skills 目录放对象存储或 Git 仓库Agent 启动时按需拉取本地做缓存。技术栈上Python 生态用 FastAPI FastMCP A2A SDK不同语言的 A2A 库逐步成熟了Java 生态可以用 Spring AI 的 A2A 模块快速生成 AgentCardMCP Server SDK 也有官方 Java 版。选择的原则是“团队熟哪个用哪个”协议是语言无关的不要被某一栈绑死。6.4 一个真实故障的排查全程A2A 回调地址指向 127.0.0.1这套架构上线后我遇到过一个非常典型的故障值得单独拿出来复盘。现象是多 Agent 协作任务大部分超时报错信息只有一行“callback connection refused”。查日志发现翻译 Agent 完成翻译后要回传completed消息给上游 Agent但回调地址写成了http://127.0.0.1:8000/a2a/callback。根因分析翻译 Agent 部署在另一个容器里这个容器里的 “127.0.0.1” 是它自己不是上游 Agent入口 Agent 所在容器的地址。由于我没有建立服务发现机制A2A 回调地址被写死跨容器环境直接失联。修复方案把回调地址改成一个可解析的服务名例如http://gateway-agent:8000/a2a/callback或者统一走网关转发。同时在 A2A 消息结构里强制校验“回调地址是不是服务注册中心里的合法地址”从配置上杜绝写死 IP。这个故障的价值在于A2A 协议本身解决的是 Agent 之间的消息格式和任务状态但网络层的服务发现与地址解析是你自己去解决的协议不负责帮你找到对方。集群里所有 Agent 的对外地址都应该是注册中心里的逻辑名而不是某个容器或机器的 IP。7. 落地两年后的复盘哪些设计值得坚持哪些坑早晚会踩跟多智能体集群打了两年交道有一些原则我现在看是值得坚持的也有一些坑是团队大概率会遇到的总结在这里算是给同行的一些参考。7.1 三条值得坚持的工程原则第一条协议先行而不是 Agent 先行。很多人立项第一件事是“我要开发一个 Agent”结果 Agent 写完了才发现没法跟已有的系统对接。正确的顺序是先定好 MCP 工具边界、A2A 通信规则、Skills 仓库规范再去开发具体的 Agent。协议是骨架Agent 是挂在上面的肉。第二条先跑通一条端到端链路再加花活。我见过太多团队一上来就搭五六个 Agent结果链路迟迟没跑通成了一个庞大的玩具。我的建议是先一个编排器加两个 Agent 加一个 MCP 工具把“用户提问 - 工具调用 - 结果生成”这条主链路跑顺了再加并行、再加深度编排。复杂度应该由业务需求驱动不是由架构冲动驱动。第三条可观测性从第一天就做不然后补全是地狱。多 Agent 的链路过一次会跨好几个服务想之后再加追踪会非常痛苦因为涉及大量历史代码改造。从第一个任务开始就带上trace_id把每个环节的日志结构化落地后面排障节省的时间远超写日志的成本。7.2 早晚会踩的四个坑MCP Server 进程成为性能瓶颈。工具调用越来越频繁MCP Server 单实例撑不住但你没有及时做水平扩展。判断标志工具调用 P99 延迟升高、日志里反复出现超时。解决方法是把工具服务拆成可独立扩容的部署单元。A2A 循环委托导致的对话风暴。下游 Agent 遇到处理不了的问题又委托给别的 Agent被委托方再委托回来形成无限循环白白消耗 token。一定要在编排器里配任务深度上限和循环检测超过预设层数直接置失败。Skills 版本过期。模型读了旧版技能包按旧流程干活输出风格和内容跟团队当下规范不一致。统一技能注册中心并加版本锁是治这个问题最有效的办法。并发压力全堆在模型推理 API 上。为了“扛并发”拼命给一个大模型 API 加并发结果限流更严重成本也上去了。正确的思路是让编排器做并发控制不同任务尽量复用工具结果比如同一订单的查询减少重复的模型往返调用。7.3 给刚起步团队的具体建议如果你所在团队准备上手这套架构我的建议是别急着把体系铺满。先选一个业务边界清晰、重复性高的场景比如“工单分类 自动回复生成”或者“代码审查 规范检查”把 MCP、A2A、Skills 都小规模用起来跑通一两个真实流程后再逐步覆盖更多场景。技术选型上也不要过度设计。如果你现在只是单机、单体应用、一个模型一把梭能解决问题那就没必要上多 Agent只有当任务流程确实需要多个职责分明的角色协作、并且这个协作关系会长期存在时这套架构的投入才划算。7.4 我最后想单独提的一件事如果你去追各种“超级多智能体”的概念会发现本质上没有互不相容的新东西它们是在不同抽象层级解决不同问题MCP 管工具A2A 管协作Skills 管能力复用编排器管调度。这个分层理念值得任何一个准备做多 Agent 系统的团队认真参考。我现在可以在这套架构上加新的 Agent基本上只做三件事写一个 AgentCard、配一组 MCP 工具、塞一个技能包注册进来就能被调度这才是“可扩展”三个字真正落地时的样子。