尧图精选

MCP与A2A协议:大模型工具调用与智能体协作实战

🕒 发布时间:2026/10/1 15:57:37 📁 来源:尧图网络
最近手上一堆大模型应用项目绕不开两个缩写MCP 和 A2A。如果你也在做 AI Agent 的工具接入八成已经被这两个词刷屏了。MCP 解决的是大模型怎么标准地调用外部工具和数据源A2A 解决的是多个智能体之间怎么互相通信、交接任务。这篇就是我在实际项目中把两者落地的记录适合正在做 Agent 框架选型、或者打算把现有服务开放给大模型调用的开发者也适合那些只是听过名字、想知道 MCP 和 A2A 到底是什么关系的人。我不会把协议规范从头抄一遍重点讲清楚它们解决什么问题、怎么干活、以及最容易踩坑的地方。1. 先把两个协议放在一张图里看1.1 大模型接外部能力的三个老毛病在没有 MCP 之前让大模型调用外部工具是一件很“拧巴”的事。你要给每个工具写一套定制接口理论上是用 OpenAI Function Calling 还是 Claude Tool Use格式化 Prompt 的方式都不一样。更麻烦的是工具返回的数据怎么进入上下文、怎么处理鉴权、怎么声明工具的输入输出格式每家各搞一套业务代码里塞满了胶水层。我见过很多团队模型层其实已经跑通了但工具接入层写得像蜘蛛网。今天接一个巡检脚本明天接一个数据库查询后天再接一个消息推送每个都要写一套调用逻辑。更别提当你换了模型厂商原来写好的工具适配层可能直接作废。这几个痛点概括起来就是接口碎片化、上下文格式不统一、鉴权与权限难管理。MCP 就是冲着这个问题去的。它把“模型要调工具”这件事标准化成一组原语工具声明、资源读取、提示词模板传输层走 JSON-RPC 2.0。你可以把它理解为电脑外设领域的 USB 接口过去打印机要装专用驱动鼠标要装专用驱动现在一个 USB 口全解决。MCP 之于大模型就是这个“统一的插口”。1.2 MCP 与 A2A 的分工边界A2A 的全称是 Agent-to-Agent注意这里的“A”不是 API 也不是 Application而是 Agent。它解决的是另一个维度的问题一个 Agent 发现自己搞不定需要把任务交给另一个 Agent 接着干或者几个 Agent 并行协作它们之间怎么互相发现、怎么传达任务、怎么收结果。这里最容易搞混的地方是MCP 和 A2A 不是竞争关系也不是同一层的东西。我用一句话区分MCP 是模型对工具的接口A2A 是模型对模型的接口。打个比方一个智能客服 Agent 要查订单状态它通过 MCP 调用订单系统但它在服务过程中发现用户的问题涉及退款政策它自己不确定于是通过 A2A 把会话上下文和问题转给另一个法务 Agent这是协作层面的通信。你可能在一个系统里同时用两种协议它们解决的问题不一样覆盖面会有重叠但不互斥。对比项MCPA2A通信双方大模型 / 工具或数据源Agent / Agent核心目的标准化外部能力接入标准化智能体间协作消息模型JSON-RPC 2.0工具调用与资源读取任务流转、事件订阅、消息传递生产者视角工具提供方暴露可以被模型调用的接口Agent 暴露自身能力与可承担的任务常见落地场景数据库查询、代码执行、浏览器操作多 Agent 任务拆解、专家协作、跨系统交接1.3 设备协议别混进来在一些技术社区里搜索 MCP总能看到“CAN 协议”“Modbus 协议”“SPI 协议”这些词混在一起。它们完全是两码事。CAN、Modbus、UART、SPI、I2C 是设备与设备之间的通信协议跑在物理链路或者现场总线上解决的是“字节怎么传、电平怎么判、报文怎么解析”的问题。MCP 和 A2A 跑在应用层解决的是“语义怎么表达、指令怎么组织、结果怎么理解”的问题。不过这两类协议在真实项目中经常碰面比如工业场景里一个设备上报的 CAN 报文要被大模型理解中间往往要套一层工具先用脚本解析报文帧再把结构化结果包装成 MCP 工具。也就是说MCP 和 A2A 设计得再好也替代不了设备协议解析它们是不同层次的分工。2. MCP 服务端落地从写工具到调通2.1 传输方式选型stdio 还是 Streamable HTTPMCP 的传输方式目前主流有两种stdio 和 Streamable HTTP。选择哪一种取决于你的服务跑在哪里、谁来发起连接。stdio 适合本地进程场景。你启动一个 Python 脚本大模型客户端在同一台机器上把它作为子进程拉起来两者通过标准输入输出通信。这种方式的优势是部署简单、进程生命周期好控制适合本地开发、自带 CLI 的工具、以及需要直接访问文件系统的场景。缺点也很明显跨机器、跨进程就抓瞎了你没法在一个浏览器页面里直接连到远端机器的 stdio 服务。Streamable HTTP 适合服务化场景。你把 MCP 服务部署在一台服务器上暴露一个 HTTP 或 WebSocket 端点客户端通过网络连接。我现在的做法是凡是多人共用的工具一律用 Streamable HTTP凡是只给本机脚本用的用 stdio。有一个细节值得注意新版 MCP 规范里已经不太推荐直接用 HTTPSSE 那套旧写法而是转向 Streamable HTTP支持 POST 请求做 JSON-RPC也支持升级到 WebSocket 做双向流。如果你在维护旧代码看到text/event-stream这种历史实现别急着抄先看看依赖的 SDK 版本是否已经切换到新的传输类型。选型时我给自己定了一个标准有本地文件操作需求就选 stdio有远程调用或者会被多个客户端共享就选 HTTP如果客户端跑在浏览器里优先确认服务端是否支持 WebSocket 端点。2.2 用 FastMCP 快速实现一个带鉴权的工具服务我常用 Python 生态的 FastMCP 来搭工具服务上手快写起来基本是装饰器风格。下面这个例子是一个简化版的服务端它把“解析 CAN 报文”的脚本封装成一个工具传入接口名和帧 ID返回解析后的字段。from fastmcp import FastMCP mcp FastMCP(vehicle-tools) mcp.tool() def parse_can_frame(interface: str, frame_id: int) - dict: 解析指定接口的 CAN 报文帧返回字段级信息。 frames { 0x123: {rpm: 1200, speed_kmh: 60.5}, 0x456: {battery_voltage: 24.8, temperature_c: 42} } if frame_id not in frames: return {error: frame not found} return {interface: interface, frame_id: hex(frame_id), data: frames[frame_id]} if __name__ __main__: mcp.run(transportstreamable-http, host0.0.0.0, port8000)这只是最基础的骨架实际项目里还要补上鉴权。我推荐在入口处做两步先校验请求里的认证标识再校验调用者是否有权限使用某个具体工具。MCP 的工具粒度可以很细不要只做到“能访问服务”就收工要能做到“能访问服务并且只能调用白名单内工具”。在做服务端时有三个容易忽视的点。第一工具描述要写得像写给同事的接口文档而不是一句话带过。工具描述会被模型用来决定要不要调用、传什么参数写得太模糊模型就会传错参数。第二超时时间要分类型工具执行时间不一定是均等的一个查缓存的工具可能 50 毫秒返回一个跑 SQL 的工具可能要 30 秒统一用 5 秒超时会把慢工具全部打死。第三把所有工具返回值设计成结构化 JSON不要返回一段已经格式化好的文本。模型处理结构化数据比处理散文文本靠谱得多。2.3 客户端侧调用与生命周期服务端写完客户端连接也要搞清楚。以官方 Python SDK 为例客户端要做的事其实就是四步建立连接、列出可用工具、调用工具、关闭连接。from mcp import ClientSession, StdioServerParameters from mcp.client.stdio import stdio_client params StdioServerParameters(command[python, server.py]) async with stdio_client(params) as (read, write): async with ClientSession(read, write) as session: tools await session.list_tools() result await session.call_tool( parse_can_frame, arguments{interface: can0, frame_id: 0x123}, ) print(result)注意这里的调用是异步的协议层不保证结果返回顺序客户端要自己维护请求 ID 和超时状态。还有一个心得MCP 的 session 不是无状态的别每来一个请求就新建连接频繁握手会带来不必要的开销。但也不要一条连接用到死我一般在空闲超过 30 秒后会主动断开让系统回收资源。工具调用的生命周期里最常见的困惑是“模型怎么知道该调用哪个工具”。答案是模型读取服务端暴露的工具列表结合当前对话上下文自主选择。所以工具列表里不要堆太多无关工具每多一个工具模型选错的概率就大一点。我维护过的一个服务工具数量从 40 个精简到 12 个后工具调用准确率反而明显上升。这算是给工具列表做“减法”的价值。2.4 一个真实组合Playwright MCP 与 Chrome DevTools MCP浏览器自动化应该是 MCP 目前最热门的落地场景之一。社区里关于 Playwright MCP 和 Chrome DevTools MCP 的讨论非常多我也都试过。Playwright MCP 的定位是“页面操作工具集”适合从零开始做端到端自动化。它把打开页面、点击、输入、截图、提取内容都封装成工具模型可以在一个受控浏览器里逐步完成任务。Chrome DevTools MCP 则是把 DevTools 协议的能力暴露给模型适合调试类任务比如看网络请求、分析控制台报错、检查 DOM 状态。我实际使用时是这样区分的需要稳定跑业务流程测试用 Playwright MCP需要排查页面为什么报错、某个请求返回了什么用 Chrome DevTools MCP。两者也可以组合先用 DevTools 类工具看网络日志定位问题再用 Playwright 类工具执行复现步骤。这个场景也给了一个安全提醒浏览器自动化工具等于把鼠标键盘交给了大模型权限边界一定要收紧。我的做法是启动一个独立的浏览器 profile不让它访问生产环境的本地存储和登录态避免模型误操作导致敏感数据被带出去。还要设置操作白名单比如禁止跳转到非业务域名、禁止下载执行文件。3. A2A 场景拆解Agent 之间怎么说话3.1 Agent Card能力发现机制A2A 协议里有一个关键设计叫 Agent Card本质上是一份 JSON 文档描述这个 Agent 是谁、能干什么、怎么联系它。它相当于智能体的“名片”加“服务目录”。我在设计 Agent Card 时会重点写清楚三个部分基本身份、能力清单、连接方式。基本身份就是 Agent 名称和描述能力清单要写它能接受什么任务、输出什么结果最好能附上输入输出的样例连接方式则是告诉其他 Agent该往哪个端点发请求支持哪些传输格式。一份简化示例大概是这样的{ name: patching-agent, description: 根据诊断结果生成软件补丁说明与代码建议, skills: [ { id: generate_patch_note, name: 生成补丁说明, input: {diagnosis_result: string}, output: {patch_note: string} } ], endpoint: https://agent.example.com/, capabilities: { streaming: true, push_notifications: true } }Agent Card 的价值在于“可发现”。两个本来互不相识的 Agent 可以通过交换 Agent Card 快速判断对方能不能接这个活省掉了大量人工对接。它让我想到 REST API 时代的 OpenAPI 文档区别是 Agent Card 不只描述接口格式还描述了这个接口背后的“能力边界”。3.2 任务模型与消息流转A2A 协议里的核心抽象是任务Agent 之间协作不是闲聊式的对话而是围绕一个任务的状态流转。一个任务从创建开始通常会经历已提交、处理中、等待补充信息、已完成、失败等状态。这个状态机跟人做事很像。你给同事派一个活他先接单然后开始干遇到不清楚的会回来问你干完了交付结果干砸了说明原因。A2A 里的消息流转就是把这一套变成机器可读的协议。任务提交方负责描述需求执行方负责维护任务状态状态变化会以事件的方式推送或让订阅方查询。我把任务流转落到项目里时最注意的一点是“等待补充信息”这个状态。很多 Agent 协作在第一步就想当然上游任务一股脑把数据丢给下游但下游真正需要的可能是另一个字段。协议里应该有显式的“回问”通道让下游 Agent 能暂停任务、追加上下文、再继续。没有这个机制你只能在任务失败时靠日志猜原因。结果传递上A2A 支持用 Artifact 结构返回产物可以是一个文本、一份 JSON、一个文件引用。我在设计产物格式时要求必须包含元数据字段比如生成时间、版本号、处理人。因为后续 Agent 拿到结果后很可能要判断这结果是不是够新鲜没有时间戳就会造成误判。3.3 A2A 与 MCP 的协同层级在同一个系统里A2A 和 MCP 是可以形成清晰层级关系的。我的经验是Agent 内部垂直调用工具用 MCPAgent 之间横向协作用 A2A。举个例子我有一个诊断 Agent 和一个补丁 Agent。诊断 Agent 内部通过 MCP 调用一个解析 CAN 报文的工具得到车辆故障特征然后它需要把诊断结论交给补丁 Agent。这时诊断 Agent 不会直接去操作补丁 Agent 内部的工具那是越权行为而是通过 A2A 提交一个“根据诊断结论生成补丁说明”的任务把结论作为任务输入传给对方。这个链路里MCP 保证“工具有标准接口”A2A 保证“Agent 有标准合作方式”。你不需要让一个 Agent 暴露自己的全部工具给另一个 Agent只需要暴露它愿意承接的任务。这个边界非常关键否则多 Agent 系统会退化成一座由无数 API 拼接成的复杂迷宫。我在架构上通常会画三层底层是设备协议和业务系统中层是 MCP 工具层把底层能力封装成大模型可调用的工具上层是 A2A 协作层让不同职责的 Agent 按任务模型配合。每一层各管各的演进不会因为底层换了设备协议就推翻上层协作逻辑。3.4 协议实现注意点A2A 落地的实际问题往往不在于协议本身而在于“谁负责推进任务”。我见过最简单的多 Agent 系统是线性管道上游做完推到下游再复杂一点就出现了一个编排者负责拆分任务、派发给多个 Agent 并汇总结果。无论哪种结构都要记得任务幂等性。下游 Agent 可能收到重复请求可能是因为网络超时后上游重试也可能是因为编排者误发了两次。下游必须有能力识别重复任务最简单的做法是让任务 ID 对内容做哈希同一个哈希的任务视为同一个任务直接返回历史结果。推送与轮询的选择也影响实现复杂度。如果下游 Agent 处理任务要很长时间同步等待结果会拖垮调用方。我一般建议短任务用同步返回长任务用事件订阅或者轮询。事件订阅省流量但要求双方都维持长连接轮询实现简单但要控制频率通常 5 到 10 秒轮一次就够了。4. 常见问题与排查技巧实录4.1 MCP 连接老断传输与心跳用 Streamable HTTP 部署 MCP 服务后最先遇到的问题是连接不稳定。我排查过几次多数原因不在协议本身而是网关的超时配置太激进。代理层如果认为连接空闲就断开而 MCP 客户端又没有自动重连就会出现“偶发断连”的假象。对策是区分“应用层空闲”和“协议层活跃”。JSON-RPC 长连接里即使没有业务请求也要有心跳机制。我通常在 MCP 客户端层做两层保险一层是应用心跳定时发 ping 类请求另一层是断线重连检测到连接断开后等一段时间自动重连重连后重新执行工具列表加载。另一个常被忽略的点是 WebSocket 端点的负载均衡。如果部署了多个副本而会话状态存在进程本地那一次断连重连很可能落到另一台机器上客户端会发现自己“失忆”了。解决办法是把会话状态外置到 Redis 一类共享存储或者在网关层配置会话粘滞。4.2 工具调用超时不要把长任务做成同步工具第一次把耗时的更新操作包成 MCP 工具时我踩过一个大坑。表面上看工具返回了但实际上后端任务还在跑前端拿到的是一个还没跑完的结果整个流程被撕裂了。我花了一些时间才确认MCP 工具不适合直接承载“提交一个需要十分钟的任务再同步等它跑完”的语义。正确的做法是把提交和查询拆成两个工具一个负责提交任务并返回任务 ID另一个负责按任务 ID 查询执行状态。这跟 A2A 的任务模型其实是一个思路只不过在 MCP 工具层你需要自己实现这两个工具的状态串联。超时时间也值得统一设计。不要把所有工具的默认超时都设成一个值。短工具设短超时可以更快暴露故障长工具要允许单独配置并且客户端侧要能区分“请求超时”和“任务执行失败”否则排查问题时会非常困惑。4.3 A2A 消息对不上版本与元数据多 Agent 协作最容易出现的问题是双方对“任务状态”理解不一致。上游 Agent 认为任务已经完成下游 Agent 其实还在等待。这个问题的根源很多时候是协议版本不一致或者元数据缺失。A2A 还在快速演进不同版本对任务状态、事件字段的命名可能有差异。我的建议是在 Agent Card 里明确声明协议版本并在每次消息里带上版本号。双方在协作之前先做一次能力协商如果版本不兼容直接拒绝合作而不是带病运行这样比跑到一半突然报错好处理得多。另外消息里尽可能带上上下文中的关键字段比如任务来源、关联会话 ID、时间戳。这些信息在正常流程里看起来冗余但是在回溯问题时价值极高。尤其在多个 Agent 交错工作的时候没有关联 ID你很难把一条报错对应到具体的协作链路。现象可能原因排查思路MCP 客户端偶发断连代理层空闲超时、心跳缺失检查网关超时设置加应用层心跳与重连工具调用返回慢但任务没完成把长任务做成了同步工具拆分为提交工具和查询工具按任务 ID 管理状态模型调用工具时参数总是错误工具描述太模糊、命名不清晰精炼工具描述补充参数样例减少工具数量A2A 下游 Agent 对状态理解不一致协议版本不一致、消息缺元数据声明协议版本携带关联 ID 和时间戳上游任务重复执行缺少幂等控制对任务内容生成哈希 ID重复请求直接返回历史结果4.4 安全边界清单协议跑通之后安全永远是最后一道底线。不管 MCP 还是 A2A本质上都是给大模型开放了外部影响力。我对每一个要上线的工具和 Agent 协作都做一遍清单检查这个工具允许模型做什么、不允许做什么鉴权是否细化到工具级操作是否有审计日志Agent 协作时是否会把敏感上下文泄露给不相关的接收方。浏览器自动化这类高风险工具我会再叠加一层“人审”。比如模型生成的操作计划先进入一个待确认队列人工同意后才实际执行。虽然牺牲了一些自动化程度但在涉及生产系统的场景里这样的冗余是值得的。日志是另一个容易被低估的环节。MCP 服务端和 A2A 消息流都要有结构化日志记录请求 ID、工具名、参数摘要、耗时、返回结果。不是所有字段都要打全量参数里的敏感值应该脱敏。但请求来源、时间、结果状态这些元数据必须完整保留否则出了安全问题等于盲人摸象。最后再分享一个我实践下来比较有用的习惯先把单个 Agent 和它内部的 MCP 工具调通再加 A2A 协作层。很多人一上来就搭多 Agent 系统结果排查问题时不知道是工具层出错还是协议层出错。我自己的节奏是先让一个 Agent 独立完成端到端任务再把它封装成可被协作调用的服务这样每一条链路都是单独验证过的。协议只是标准真正让系统稳定运行的还是你围绕它建立的那一套工程习惯。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →