多智能体协作实战:MCP、A2A与Skills如何各司其职
1. 内容整体设计与思路拆解MCP、A2A、Skills 到底各司其职多智能体系统最让人头疼的不是智能体不够聪明而是一堆智能体凑在一起之后谁也说不清谁。A 要调数据库B 要查文档C 要操作浏览器D 想把结果回传给用户——每个工具都有自己的接入方式每个智能体又都有自己的「方言」结果就是你写了一堆胶水代码最后项目烂在集成阶段。DeepAgents MCP A2A Skills 这四个词凑在一起其实正好是解决这个问题的完整组合拳。你可以把它们理解成四条互相配合的标准轨MCPModel Context Protocol管的是智能体 ↔ 外部工具/数据这半边。用一句话说它把数据库、API、文件系统、浏览器、内部系统全部包装成统一的工具接入层智能体不用再为每个工具单独写一套 SDK。Skills技能封装管的是智能体自身的可复用行为。它把一种操作模式——比如做代码审查整理周报分析日志——沉淀成一份结构化的技能包附带说明、提示词、校验规则甚至一份可运行的代码。A2AAgent-to-Agent管的是智能体 ↔ 智能体这半边。智能体之间要开会、派活、催进度、交结果A2A 就是让这些动作有了统一协议相当于给智能体们配了一本《沟通礼仪手册》。DeepAgents深度智能体集群架构管的是全局编排。谁先干、谁后干、任务怎么拆、出错了怎么重试所有这些都由编排层来统一处理。拿一个生活化的例子来类比。你开了一家数字人公司MCP 是公司内部统一的协同办公系统所有员工智能体都通过同一套流程去申请资源、调取数据、提交工单Skills 是公司的《标准作业手册》每个岗位都有一套最佳操作模板新人上手照着做就行A2A 是公司内部通用的部门间对接规范市场部给研发部下需求格式永远是同一张表DeepAgents 就是公司的项目总监负责把一个大目标拆成小任务然后盯着各个部门推进。这个分层思路的价值还在于四个层可以独立演进、独立替换。你今天觉得 A 家的 MCP server 不好用可以换 B 家的协议不变明天觉得某个技能写得太笨重可以只改这个 Skill 的模板不动其它部分。这在多智能体项目的迭代期里极其重要——因为早期你会发现需求每天都在变如果每个层次之间都紧耦合改一个功能等于动全身。我当时的机会点也在于全网关于这一块的资料还很散到处都是单点讲解很少有人把四者串成一个端到端的实战链路来讲。这篇文章我就打算用一套完整的多智能体集群项目作为主线把架构怎么设计、协议怎么对接、技能怎么封装、集群怎么跑起来一次性讲透。2. 核心细节解析与实操要点四个组件的落地姿势在真正动手写代码之前有几个非常容易踩的坑是看文档看不出来、非得自己跑一遍才知道的。我按组件逐个拆开讲。2.1 MCP先搞清楚它的三个原语再动手写 ServerMCP 的协议本身不复杂它的核心是三个原语Tools工具智能体可以主动调用的一次操作类似于查订单发消息执行 SQL。工具是有副作用的也就是说它会改变外部系统的状态。Resources资源只读的数据来源比如用户资料项目文档数据库表结构。资源是无副作用的这一点和 Tools 相比要格外注意。Prompts提示模板预置的对话脚本告诉智能体在特定场景下应该按什么思路来工作。相当于给你家 AI 员工装了一本话术手册。传输层上当前主流就三种玩法stdioMCP server 以子进程方式启动智能体直接和它通过标准输入输出通信。适合本地单体部署缺点是没法远程复用。SSEServer-Sent Events通过 HTTP 建立长连接服务端向客户端单向推流。适合跨机器访问。Streamable HTTP这是目前最实用的模式它把 Server-Sent Events 和 JSON-RPC 统一在一个 HTTP 端点里既支持流式响应又支持普通请求。想要部署到远程服务器我建议直接用这种。我在实际项目中使用的模式是 Streamable HTTP因为集群里会有多个 Worker 分布在不同的服务器上stdio 的进程模型在这种场景下非常不灵活。协议数据格式统一走 JSON-RPC 2.0消息里有jsonrpc、id、method、params四个字段其实和调用 HTTP API 的逻辑完全一致只是格式标准化了。这里有一条非常重要的实操心得MCP server 的一举一动都要写在它自己的能力声明里。tools/list、resources/list、prompts/list这几个接口的返回结果决定了智能体看不看得到你的能力。我发现很多新手写 server 的时候只把核心接口暴露出来连一句这个工具会在调用失败时抛出什么错误都不写。结果就是智能体调用失败后一脸懵不知道是参数不对还是网络不通。所以建议在每个工具的描述里写清楚参数含义、默认值、错误场景甚至给一个调用示例。描述写得好比你多写十个工具都管用。2.2 Skills把经验变成可以验证的包Skills 的本质就是某一类任务的打包方案。一个完整的 Skill 通常包含这么几部分SKILL.md技能的主说明文件。它描述技能用途、适用场景、边界条件、工作流程以及如何判断这个技能该被调用。技能代码 / 脚本可执行的部分。比如一个日志分析技能就附带一个 Python 脚本专门做日志解析。参数定义这个技能需要什么输入、可选参数有哪些、输出结构如何。验证用例 / 测试这是最容易被忽略的一环但恰恰是它才是Skills 工程化的核心。没有测试你无法知道技能是被正确执行还是幻觉式执行。讲到这我要点一个特别常见的误区很多人以为 Skill 就是一段写得很好的提示词。提示词只是 Skill 的最小单元完整的 Skill 还应该包含执行代码、输入输出规范、边界条件、验证样例。否则你不过是在给智能体多准备了一段话术它仍然可能一本正经地给你编结果。实操中我习惯把 Skill 分成两类Prompt Skill纯提示词模板类型适合做分析报告代码审查会议纪要这类判断型任务。Code Skill提示词 可执行代码适合做批量重命名日志聚合数据抽取这类需要确定结果的任务。另外要关注find skills这个方向。现在社区里已经有很多公开的 Skills 市场你可以先把别人写好的技能拿回来做二次改造不要一上来就自己从零造轮子。我踩过的坑是——一开始总觉得别人写的技能不一定适配我们的业务结果每个技能都自己重写时间成本直接翻倍。后来我调整策略先拿社区成熟技能跑一遍验证集哪里不对改哪里效率高得多。2.3 A2A智能体之间的协同协议A2A 协议解决的核心问题是智能体互相发现、互相下单、互相交付。它有几个核心设计Agent Card每个智能体对外发布一张名片上面写清楚它能提供什么服务、接受什么格式的输入、以什么方式交付结果。其他智能体通过这张卡片来决定要不要找它干活。Task 生命周期一次跨智能体交互就是一个 Task它有明确的状态机从request-created到work-created再到input-required、work-completed、failed、canceled等。关键在于每个状态都要被明确记录和传输这是双方对齐的基础。消息格式采用 JSON-RPC 风格的请求/响应模型保留了流式传输和多轮交互能力适配长任务场景。实际项目中我发现一个现象很多人在两个智能体之间传递任务的时候脑子里的模型还是调用 API。但 A2A 的模型更像是派工单——你发出去的是一份任务描述 上下文 期望输出格式而不是一个带参数的函数调用。对方怎么实现、用几步实现你不需要关心你只需要在约定的状态回调和结果交付点上接应它。这个差异对我来说非常关键。它意味着我可以随时随地换掉集群里的某一个 Worker 而不改动其他部分——只要新 Worker 的 Agent Card 上写的服务能力和之前一致它的内部实现完全不在协作范围之内。2.4 DeepAgents 编排层任务拆解与状态回收最后是全局大脑。DeepAgents 这一层要处理的四件事我总结得很简单任务拆解把一个大目标拆成 DAG有向无环图式的子任务比如写一篇市场报告可以拆成调研 → 提纲 → 初稿 → 润色 → 配图五个子任务。路由分发根据 Agent Card 的能力描述把子任务分发给对应的 Worker。上下文传递分发任务的时候要带上必要的历史信息、参考材料、业务约束。失败回收子任务失败时编排层要决定是重试、降级还是重新拆解。在设计编排层的时候有一个关键取舍不要试图让编排器自己成为全能智能体。我看到过很典型的反面案例把所有逻辑都堆在编排器里。最后编排器的上下文爆掉任务一复杂就答非所问。正确的做法是编排器只做拆、派、看结果、做决定具体干活一律委托给专职 Worker。这就像项目总监不做执行层的工作只负责排期和盯进度。3. 实操过程与核心环节实现一个真实的多智能体集群理论说多了容易飘直接上一套可以在本地跑起来的项目。我用的场景是自动化研发助理集群——你给它一个需求描述它自动完成需求拆解、代码生成、代码审查、编写单元测试、输出总结报告。这套集群由 4 个成员组成Orchestrator编曲器/总监基于 DeepAgents 思路写的编排层负责拆任务、派活、回收结果。分析 Agent负责分析需求和现有代码结构。开发 Agent负责生成代码。审查 Agent负责代码评审和测试用例生成。3.1 先起一个 MCP Server供应层我先用一个简单的文件服务 MCP Server 来做示范它给智能体提供读写项目文件的能力。语言选 Python框架用官方的mcpPython SDK传输方式用 Streamable HTTP。# mcp_file_server.py import json from pathlib import Path from mcp.server.fastmcp import FastMCP mcp FastMCP(project-files-server) BASE_DIR Path(./workspace) mcp.tool() def read_file(relative_path: str) - str: 读取工作区文件。relative_path 是相对 workspace 的文件路径。失败时抛出异常。 p BASE_DIR / relative_path if not p.exists(): raise FileNotFoundError(f文件不存在: {relative_path}) return p.read_text(encodingutf-8) mcp.tool() def write_file(relative_path: str, content: str) - str: 写入或覆盖工作区文件。relative_path 是相对 workspace 的文件路径。 p BASE_DIR / relative_path p.parent.mkdir(parentsTrue, exist_okTrue) p.write_text(content, encodingutf-8) return f写入成功: {relative_path} mcp.tool() def list_files(prefix: str ) - str: 列出工作区内所有文件可带目录前缀过滤。 p BASE_DIR / prefix if not p.exists(): return 目录不存在 files [str(x.relative_to(BASE_DIR)) for x in p.rglob(*) if x.is_file()] return json.dumps(files, ensure_asciiFalse) if __name__ __main__: mcp.run(transportstreamable-http)跑起来之后这个服务的地址是http://localhost:8000/mcp如果你用官方 SDK 默认配置端口和路径会略有差异以启动日志为准。注意每个工具函数的docstring 就是智能体看到的工具说明这不是写给人看的注释是写给模型看的使用手册。我在上面特意写了异常场景就是为了避免模型在文件不存在时懵圈。3.2 写一个真正的 Skill接下来给开发 Agent 配一个代码生成技能。这个技能要解决的问题是让开发 Agent 生成的代码符合项目规范而不只是能跑。我的实现是一个 Code Skill用一个 JSON Schema 来做输出校验再用一个 Python 脚本执行最终的后处理比如格式化、去空行、生成文件。目录结构如下skills/code-gen/ ├── SKILL.md ├── schema.json ├── generate.py └── test_cases/ └── sample_request.jsonSKILL.md的核心内容长这样# 代码生成技能code-gen ## 用途 根据需求描述和现有项目结构生成符合规范的业务代码文件。 ## 适用场景 - 新增接口 / 新增页面 / 新增工具函数 - 代码结构已经明确只需按规范填充实现 ## 边界条件 - 不处理架构设计类问题那是架构 Agent 的工作 - 不处理部署和环境搭建类问题 - 如果需求描述不明确必须要求调用方补充信息 ## 工作流程 1. 读取需求描述 2. 读取工作区相关文件通过 MCP 文件服务 3. 生成代码并确保通过 schema.json 的校验 4. 调用 generate.py 输出文件 5. 给调用方返回文件清单和变更摘要 ## 输入要求 { requirement: 需求描述文本, target_path: 目标文件相对路径, language: python | typescript | java } ## 输出格式 { files: [路径列表], summary: 变更摘要, success: true }schema.json就是校验输出的规则我截取关键片段{ type: object, required: [files, summary, success], properties: { files: { type: array, items: { type: string } }, summary: { type: string }, success: { type: boolean } } }写这个 Skill 的时候我的重心不是写提示词让模型输出漂亮代码而是定义好输入边界和输出规范。这就像一个外包团队你给它的需求文档越详细、验收标准越明确它的交付越稳定。把所有细节都压给模型自己去理解的技能跑三五个任务之后一定会出现各种意外的惊喜。3.3 用 A2A 暴露 Agent现在我们需要把每个 Worker 包装成一个标准的 A2A Agent。我用 Python FastAPI 实现一个最简的 A2A server让它暴露/agent-card和一个/task端点。# a2a_worker.py from fastapi import FastAPI from pydantic import BaseModel app FastAPI(title开发Agent) AGENT_CARD { name: dev-agent, description: 负责业务代码的生成与文件落地依赖 MCP 文件服务访问工作区。, capabilities: { tasks: { supported: [code_generation, code_editing], inputModes: [application/json], outputModes: [application/json] } }, skills: [code-gen] } app.get(/agent-card) def get_agent_card(): return AGENT_CARD class TaskRequest(BaseModel): task_id: str type: str payload: dict app.post(/task) def create_task(req: TaskRequest): if req.type ! code_generation: return { task_id: req.task_id, status: failed, message: f不支持的 task type: {req.type} } return { task_id: req.task_id, status: work-created, message: f已接收任务 {req.task_id}开始处理 }这只是一个极简骨架真实项目里你会在创建任务后启动一个后台线程去执行生成逻辑同时留下查询进度的接口。但核心思路已经体现得很清楚了Agent Card 是别人找你的入口Task 是双方协同的最小单元。A2A 的妙处就在于一旦大家都遵守 Agent Card 和 Task 状态流你就完全不需要在代码里硬编码调用某个 Agent的逻辑了——每次派活前先拉一遍 Agent Card动态决定把任务发给谁整个集群就有了弹性。3.4 编排层把整个闭环串起来我写编排层的时候核心代码就是一张 DAG 表 一个循环执行器。伪代码结构如下# orchestrator.py 任務 [ {id: analyze, depends: [], agent: analysis-agent}, {id: generate, depends: [analyze], agent: dev-agent}, {id: review, depends: [generate], agent: review-agent}, {id: summary, depends: [review], agent: summary-agent} ] # 执行逻辑 # 1. 找到所有依赖已满足且未执行的任务 # 2. 根据 agent 名称对应 A2A 端点创建 Task # 3. 轮询 Task 状态直到 work-completed 或 failed # 4. 把上一个阶段的输出作为下一个阶段的输入附加到 payload 里 # 5. 全部完成后聚合输出这个编排层有两个地方需要特意点名上下文传递要在 payload 里带上上一阶段的产出摘要但要控制大小。我习惯的做法是只传摘要关键文件路径不传完整代码。否则上下文一膨胀后面的模型就飘了。失败回收不能简单重试。如果review阶段失败我会先判断失败类型如果是代码编译错误就把错误信息回传给generate阶段让它改代码如果是模型输出不规范才重跑review本身。跑通整个闭环后我观察到的最典型效果是以前让一个大模型完成从需求到测试报告的活经常在中间步骤上翻车现在拆成四个专业 Agent 之后每个阶段只需要关注自己那一小块成功率明显上升。代价是多轮调用的延迟变大但上下文清晰度和稳定性大幅提升。4. 常见问题与排查技巧实录多智能体集群的踩坑记录这个章节我打算写成速查风格每一条都是我在实际项目中真正遇到过、并最终定位到根因的问题。4.1 MCP server 连不上但代码看起来没问题这个问题我排查过很多次。你会发现配置明明是对的但智能体就是连不上 MCP server或者连上了但拿不到工具列表。排查步骤先用 curl 直接访问 server 的健康检查端点确认服务本身活着。检查传输模式。如果智能体端配置的是stdio但 server 以streamable-http模式启动两边根本对不上。这是最常见的第一坑。检查路由前缀。很多框架默认 MCP 端点是/mcp但你用 Nginx 反代时如果把路径改成了/api/v1/mcp那就要同步改客户端的 URL。不要想当然认为HTTP 服务嘛路径肯定写在文档里路径变了客户端不会自动感知。检查鉴权配置。如果 server 要求 Bearer Token 或 API Key但客户端没配请求会直接 401。经验总结MCP server 的可发现性比功能丰富度更重要。先把协议连通性搞定再追求工具数量。4.2 Skills 执行结果看起来合理实际上在瞎编这是最危险的一类故障。技能调用了输出格式也符合 schema但内容完全是模型编的压根没有执行真实代码。根因Skill 的定义只写了提示词没有配套可执行的脚本也没有强制校验哪些结果必须来自代码执行。当模型发现自己直接编一个结果比调用工具更省事时它会选择编。解决方案把 Skill 拆成 Code Skill 和 Prompt Skill 两类。凡是有确定性答案的任务一律走 Code Skill让模型只负责生成参数、执行脚本、拿到真实结果后再做总结。在 Skill 描述里明确写上结果必须来自脚本执行输出禁止推断。这个提示虽然不完全可靠但能显著降低瞎编概率。给 Skill 增加验收测试。执行完结果后自动跑一遍校验比如生成的文件必须存在且能通过编译。4.3 A2A 任务挂在 input-required 状态上不动了A2A 的 Task 有一个状态叫input-required意思是对方把任务退回来了需要你补充信息。常见现象是Worker 返回了这个状态之后编排层没有监听该状态于是任务就永远停在那里。排查思路检查编排层的状态处理分支是否覆盖了input-required。如果只处理了work-completed和failed遇到这种退单场景就直接断了。检查 Worker 返回input-required时是否携带了需要补充什么信息的具体描述。如果只是空状态调用方不可能知道下一步该怎么做。在编排层里加一个兜底策略任务在同一个状态停留超过 N 分钟强制标记为废弃并通知负责人。经验总结A2A 协议给你规定的是状态全集但每个字段的语义你要自己定义清楚。尤其是input-required它本质上是协同失败请求方要介入的信号不要把它当成普通的中间状态。4.4 多 Agent 并行时出现互锁死等当集群里有多个 Worker 并行执行任务时可能出现这样的场景Agent A 在等 Agent B 的输出Agent B 又在等 Agent A 的输出两者互不通信谁都收不到结果。表面上看是大模型的问题其实是编排图设计出了问题。根因任务拆解的时候没有检查依赖关系是否形成了环。比如分析 Agent 产出报告后要汇总给开发 Agent开发 Agent 又以为自己要先产出一个初步方案再触发分析实际是一个环形依赖。解决方案任务拆解时用 DAG 图先做一次建模确认没有环再启动执行。给每个子任务设置执行超时。我通常设 5 分钟超过就报警。编排层定期打印当前等待链让我能直观看到每个 Agent 在等谁。不要相信模型能自己意识到它在死等——大模型没有时间感它不会主动说我等太久了。4.5 上下文爆炸后阶段 Agent 完全失忆多 Agent 协同一定会遇到上下文传递的问题。最开始我的做法是把前边所有阶段的完整输出拼到 next prompt 里结果到第三个阶段模型已经把最开始的需求忘记了甚至开始胡编新需求。解决方案传递内容做三层设计核心指令当前任务要什么、上下文摘要前序阶段的结论、附件路径详细产出通过 MCP 文件服务读取。不要让下一阶段自己去大海捞针式找信息。在编排层统一做摘要提炼摘要不是简单截断而是调用摘要 Agent 生成一段结构化摘要。这也是多智能体集群里常设摘要 Agent的原因。问题速查表症状可能原因排查顺序智能体调不到任何工具MCP 传输模式 / 路由不匹配1. 检查 server 日志 2. 核对客户端配置Skill 输出格式对但内容是编的Skill 没有强制走 Code 执行1. 检查 SKILL.md 是否有禁止推断约束 2. 增加验证测试A2A 任务长期不结束状态机处理缺分支 / 编排层没兜底1. 检查任务当前状态 2. 检查编排层状态处理器多 Agent 卡死DAG 图存在环 / 无超时机制1. 检查依赖图 2. 增加超时与报警后阶段 Agent 丢上下文传递内容超长 / 摘要缺失1. 检查 payload 大小 2. 引入摘要 Agent5. 一些实战心得记录这里就不再做总结了聊几个我在连续多周处理这类项目时最有体感的事情。第一MCP 生态的成熟速度比大多数人想象得快。不只是常规的数据库、文件服务现在连逆向工程、前端设计稿、Spring 框架、游戏引擎行业都有对应的 MCP 工具在涌现。你不需要自己从头写所有接入先花一个下午把社区里现成的 MCP server 清单翻一遍能省掉好几天的开发量。第二Skills 是最容易被低估的一层但也是 ROI 最高的一层。MCP 和 A2A 是协议协议一旦确定就很少改动而 Skills 是你持续迭代业务能力的地方。我见过很多团队花了大量时间调了无数版大模型的提示词效果始终不稳定后来把高频任务沉淀成 Skill 包之后效果才真正稳定下来。技能沉淀这件事做得越早越好。第三多智能体集群的调试困难程度远超单 Agent。单 Agent 出了问题看一个对话记录就能定位。多 Agent 集群出了问题你要同时看编排日志、多个 Worker 的调用记录、MCP server 的访问日志、A2A 的状态流转四份日志对照着才可能定位到根因。所以从一开始就要把日志链路设计好——每个任务 ID 贯穿到底不要等到集群跑大了再去补那会儿你已经补不回来了。最后分享一个我在集群设计上的核心思路转变一开始我总想着让所有 Agent更聪明后来发现真正该做的是让每个 Agent更标准化、更透明、更可验证。聪明是不可控的标准是可以复用的。把这四个组件理解成一套工程规范而不是什么炫技框架你的多智能体项目会走得稳得多。