1. 为什么 Agent 通信协议突然“百花齐放”过去一年里只要你在做 AI Agent 相关的开发几乎绕不开一个困惑同样是让两个智能体互相说话怎么一会儿是 JSON-RPC一会儿是 MCP转头又冒出 A2A、ACP更让人头大的是每个协议背后都有一拨人在推文档写得都挺像那么回事但真到选型落地的时候你会发现它们解决的问题压根不在一个层面上。我自己是从最原始的“手搓 HTTP 接口”开始做 Agent 协作的。最早那会儿两个 Agent 要交换信息无非就是约定一个 URLPOST 一段 JSON 过去对面解析完再返回一段 JSON。简单粗暴能跑就行。但项目一复杂问题就来了工具怎么暴露给模型上下文怎么在多个 Agent 之间传递一个 Agent 调用另一个 Agent 的能力时怎么描述“我能干什么”这些事靠裸 HTTP 加自定义 JSON 结构维护成本高得离谱。于是协议开始分层。JSON-RPC 是底座它只规定“怎么调用一个远程方法”不关心你调用的是工具、是模型还是另一个 Agent。MCPModel Context Protocol解决的是“模型怎么发现和调用外部工具与数据源”它把工具、资源、提示词模板标准化让模型侧不用为每个工具写一套适配。A2AAgent-to-Agent则更进一步解决的是“Agent 之间怎么互相发现、协商、委派任务”它假设双方都是有一定自主性的智能体而不是单纯的函数。ACPAgent Communication Protocol走的是另一条路强调消息传递的可靠性和会话状态管理更像是一个面向 Agent 的消息中间层。这篇文章不打算给你背协议规范而是想把这几个协议为什么会长成这样、各自卡在哪个位置、实际选型时怎么权衡讲清楚。如果你正在做多 Agent 系统、工具调用平台或者只是好奇“我到底该用哪个”那这篇应该能帮你省下不少翻文档的时间。2. 从 JSON-RPC 说起所有 Agent 通信的“最小公约数”2.1 JSON-RPC 到底规定了什么没规定什么JSON-RPC 本质上就是一个极简的远程调用约定。一个请求长这样{ jsonrpc: 2.0, method: tools/call, params: { name: search_web, arguments: { query: agent protocol } }, id: 1 }响应就是{ jsonrpc: 2.0, result: { content: ... }, id: 1 }它只干了三件事方法名、参数、请求 ID。至于 method 叫什么、params 里放什么、result 怎么解析全靠双方自己约定。这就是它最大的优点也是最大的坑。优点在于极度灵活。你可以用它调一个本地函数也可以调一个远程服务甚至可以用它传文件流base64 塞进去就行。缺点在于没有语义层。当你的 Agent 要调用一个工具时它怎么知道有哪些工具可用每个工具需要什么参数参数类型是什么这些 JSON-RPC 一概不管。你得自己再写一套描述文件或者硬编码在代码里。我早期做过一个多 Agent 协作的小系统当时就是用 JSON-RPC 做底层上面自己定义了一套tool_list、tool_describe、tool_call的方法名。跑是能跑但每接一个新工具就要改一次描述逻辑而且不同 Agent 之间的描述格式还不统一。那段时间我最大的感受就是JSON-RPC 是很好的传输层但它不是协议层。2.2 为什么 Agent 场景下 JSON-RPC 不够用Agent 和传统微服务最大的区别在于调用方本身是概率性的。传统微服务里A 服务调 B 服务接口是确定的参数是确定的返回也是确定的。但 Agent 调用工具时它是在“理解”工具描述之后自己决定要不要调、怎么调、传什么参数。这就要求工具描述本身必须是机器可读、语义清晰的。JSON-RPC 没有能力表达“这个工具是干什么的”“参数有什么约束”“返回结果是什么结构”。你当然可以在 params 里塞一个 JSON Schema但那只是把问题往后推了一层谁来生成这个 Schema谁来保证不同 Agent 对同一个 Schema 的理解一致另一个问题是能力发现。在多 Agent 系统里一个 Agent 需要知道其他 Agent 能做什么。JSON-RPC 没有服务发现机制你得自己维护一个注册中心或者硬编码地址列表。这在 Agent 数量少的时候还行一旦超过十个维护成本就指数级上升。所以 JSON-RPC 的定位很明确它是通信的底座不是 Agent 协作的完整方案。MCP、A2A、ACP 这些协议本质上都是在 JSON-RPC或类似传输层之上补上了语义层、发现层和会话层。3. MCP让模型“看见”工具的那一层3.1 MCP 解决的核心问题工具描述的标准化MCP 最核心的贡献是把“工具”这个东西抽象成了模型可以理解的标准结构。一个 MCP Server 暴露出来的工具长这样{ name: get_weather, description: 查询指定城市的当前天气, inputSchema: { type: object, properties: { city: { type: string, description: 城市名称 } }, required: [city] } }模型看到这段描述就知道有个叫get_weather的工具需要传一个city参数。这看起来简单但它是整个工具调用链路能跑通的前提。没有这个标准每个模型厂商都要为每个工具写一套适配工具开发者也要为每个模型写一套接口。MCP 还定义了资源Resources和提示词模板Prompts。资源让模型可以读取外部数据比如文件、数据库记录提示词模板让模型可以复用预定义的指令。这三者加起来构成了模型与外部世界交互的完整接口。3.2 MCP 的传输层选择与实操细节MCP 本身不强制传输层但最常见的实现是stdio和HTTPSSE。stdio 适合本地工具比如你写一个 Python 脚本通过标准输入输出和模型通信。HTTPSSE 适合远程工具模型通过 HTTP 发请求工具通过 SSE 推事件。我实测下来stdio 的延迟最低但调试麻烦因为你看不到网络包。HTTPSSE 调试方便但要注意 SSE 的连接保持和重连逻辑。如果你用 Python 写 MCP Server官方 SDK 已经帮你处理了大部分细节from mcp.server import Server from mcp.server.stdio import stdio_server app Server(my-tool-server) app.list_tools() async def list_tools(): return [ { name: search, description: 搜索网页, inputSchema: { type: object, properties: {query: {type: string}}, required: [query] } } ] app.call_tool() async def call_tool(name, arguments): if name search: return {content: [{type: text, text: 搜索结果...}]}这段代码的关键在于list_tools和call_tool两个钩子。模型侧会先调list_tools拿到工具列表然后根据用户意图决定调哪个再通过call_tool执行。整个过程对模型来说是透明的它不需要知道工具是本地脚本还是远程服务。注意MCP 的inputSchema必须用 JSON Schema 描述而且 description 字段要写得足够清晰。我踩过的坑是description 写得太模糊模型会反复调同一个工具但传错参数。后来我把每个参数的 description 都写成“什么时候该传这个值”调用准确率明显提升。3.3 MCP 的边界它不解决 Agent 之间的协作MCP 的定位很清晰它是模型与工具之间的协议。但它不解决 Agent 与 Agent 之间的通信。比如你有两个 Agent一个负责规划一个负责执行它们怎么互相传任务MCP 没有定义这个。你当然可以让规划 Agent 把执行 Agent 当成一个“工具”来调用但这会丢失很多语义执行 Agent 的自主性、任务状态的同步、多轮协商的过程。这就是 A2A 要解决的问题。4. A2A当 Agent 开始互相“对话”4.1 A2A 的核心抽象Agent Card 与任务生命周期A2A 最关键的创新是Agent Card。每个 Agent 对外暴露一张卡片描述自己是谁、能做什么、怎么联系{ name: research-agent, description: 擅长文献检索和摘要, capabilities: [search, summarize], endpoint: https://example.com/a2a, authentication: { type: bearer } }这张卡片让其他 Agent 可以“发现”它。发现之后A2A 定义了一套任务生命周期提交任务、接受任务、更新状态、返回结果。这比简单的“调用-返回”要复杂得多因为 Agent 之间的任务往往不是一次性的而是需要多轮交互。比如规划 Agent 给研究 Agent 发一个任务“帮我查一下最近三个月关于 Agent 协议的论文总结成表格。”研究 Agent 可能先返回“已接受”然后过一会儿返回“正在检索”再返回“找到 15 篇正在摘要”最后返回结果。这个过程中规划 Agent 可以随时查询状态甚至取消任务。A2A 的传输层通常也是 HTTPJSON-RPC但它在上层定义了更丰富的消息类型Task、TaskStatus、Artifact、Message。这些结构让 Agent 之间的交互有了明确的语义。4.2 A2A 与 MCP 的关系互补而非竞争很多人第一次看到 A2A 和 MCP 会懵这俩不是都在讲 Agent 通信吗其实它们解决的是不同层面的问题。维度MCPA2A通信对象模型 ↔ 工具Agent ↔ Agent核心抽象Tool、Resource、PromptAgent Card、Task、Artifact交互模式请求-响应任务生命周期、多轮协商自主性工具无自主性Agent 有自主性典型场景模型调用搜索、数据库多 Agent 协作完成复杂任务我自己的做法是底层用 MCP 接工具上层用 A2A 做 Agent 协作。规划 Agent 通过 A2A 把任务委派给执行 Agent执行 Agent 内部再用 MCP 调用具体工具。这样分层之后每层职责清晰调试也方便。实操心得A2A 的 Agent Card 一定要版本化。我遇到过 Agent 升级后能力变了但卡片没更新导致其他 Agent 还在按旧能力发任务。后来我在卡片里加了version字段并且要求每次能力变更必须递增版本号问题就解决了。4.3 A2A 的落地难点状态同步与超时处理A2A 最麻烦的地方在于状态同步。因为 Agent 任务是异步的规划 Agent 发出任务后不能一直阻塞等待。它需要轮询或者订阅状态更新。A2A 支持 SSE 推送但 SSE 连接不稳定断线重连后状态可能不一致。我的做法是状态查询接口必须幂等并且每次返回都带一个last_updated时间戳。规划 Agent 本地维护一个任务状态缓存收到推送时更新断线重连后先拉一次全量状态再继续订阅。这样即使推送丢了也能通过轮询兜底。超时处理也很关键。A2A 任务可能跑很久规划 Agent 不能无限等。我一般会设置三级超时接受超时30 秒内没返回“已接受”就重试、心跳超时5 分钟没状态更新就查询一次、总超时30 分钟没结果就取消。这三层超时加上重试策略基本能覆盖大部分异常情况。5. ACP被低估的“消息中间层”5.1 ACP 的定位可靠消息传递与会话管理ACP 的知名度不如 MCP 和 A2A但它在某些场景下更实用。ACP 的核心是消息队列 会话状态。它不关心消息内容是工具调用还是 Agent 任务它只保证消息可靠送达并且维护会话的上下文。你可以把 ACP 理解成 Agent 世界的消息总线。一个 Agent 发消息到某个主题其他 Agent 订阅这个主题。消息可以持久化可以重放可以保证顺序。这对于需要高可靠性的场景很重要比如金融、医疗领域的 Agent 协作。ACP 的消息格式通常包含会话 ID、发送者、接收者、消息类型、负载、时间戳。会话 ID 让多轮对话可以关联起来消息类型让接收者知道怎么解析负载。5.2 ACP 与 A2A 的选型对比A2A 和 ACP 都能做 Agent 间通信但侧重点不同。维度A2AACP核心关注任务委派与生命周期消息可靠传递与会话发现机制Agent Card主题订阅交互模式任务导向消息导向状态管理任务状态机会话上下文适用场景多 Agent 协作事件驱动、高可靠复杂度较高中等我的经验是如果你的 Agent 协作是任务驱动的选 A2A如果是事件驱动的选 ACP。比如一个监控 Agent 发现异常发消息给处理 Agent处理 Agent 再发消息给通知 Agent这种链式反应用 ACP 更自然。而如果是“帮我写一份报告”这种任务拆解成多个子任务委派出去用 A2A 更合适。5.3 ACP 的实操要点消息幂等与死信队列ACP 落地时消息幂等是必须处理的。因为消息可能重复投递接收 Agent 必须能识别重复消息。我一般会在消息里加一个message_id接收方维护一个已处理 ID 的集合重复的直接丢弃。死信队列也很重要。如果一条消息处理失败多次不能一直重试要丢到死信队列里人工介入。我见过一个系统因为死信队列没配一条坏消息把整个 Agent 集群堵死了。后来加了死信队列并且设置了最大重试次数问题才解决。注意ACP 的会话状态不能无限增长。我一般会设置会话过期时间比如 24 小时没活动的会话自动清理。否则内存会被慢慢吃光。6. 协议选型实战我如何为一个多 Agent 系统做决策6.1 需求拆解先搞清楚你要解决什么问题选协议之前先问自己三个问题通信双方是谁模型和工具Agent 和 Agent还是 Agent 和消息总线交互模式是什么请求-响应任务委派事件驱动可靠性要求多高允许丢消息吗需要持久化吗这三个问题的答案基本能锁定协议范围。比如模型调工具 → MCPAgent 委派任务 → A2AAgent 事件通知 → ACP底层传输 → JSON-RPC 或 HTTP6.2 一个真实项目的协议组合方案我之前做过一个多 Agent 研究助手系统架构是这样的工具层用 MCP 接入了搜索、PDF 解析、数据库查询三个工具。Agent 层规划 Agent、检索 Agent、摘要 Agent、审核 Agent。协作层规划 Agent 通过 A2A 把任务委派给其他 Agent。通知层审核 Agent 发现问题时通过 ACP 发消息给规划 Agent。这个组合跑下来最大的感受是分层清晰。MCP 让工具接入标准化A2A 让任务委派有状态可查ACP 让异常通知不丢。如果全用 JSON-RPC 硬搓代码量至少翻三倍而且维护起来很痛苦。6.3 选型检查清单检查项问题推荐协议通信对象模型调工具MCP通信对象Agent 调 AgentA2A通信对象事件广播ACP交互模式请求-响应JSON-RPC交互模式任务生命周期A2A交互模式发布-订阅ACP可靠性允许丢消息JSON-RPC可靠性不允许丢消息ACP状态管理无状态JSON-RPC状态管理有状态会话ACP状态管理任务状态机A2A这张表不是绝对的但能帮你快速缩小范围。实际选型时往往是组合使用而不是二选一。7. 常见问题与排查技巧实录7.1 协议兼容性问题问题MCP Server 返回的工具描述模型解析不了。排查先检查inputSchema是否符合 JSON Schema 规范。我遇到过required字段写成字符串而不是数组导致模型解析失败。另外description 里不要用特殊字符有些模型对 Unicode 支持不好。解决用 JSON Schema 校验工具先验证一遍确保格式正确。description 尽量用英文或简单中文避免生僻字。7.2 A2A 任务状态不一致问题规划 Agent 认为任务还在进行执行 Agent 已经完成了。排查检查状态更新是否丢失。A2A 的 SSE 推送在弱网环境下容易丢。解决加轮询兜底。规划 Agent 每隔一段时间主动查询一次任务状态以查询结果为准。同时执行 Agent 的状态更新要带版本号避免旧状态覆盖新状态。7.3 ACP 消息重复消费问题同一个消息被处理了两次导致重复操作。排查检查消息是否有唯一 ID接收方是否做了幂等处理。解决消息里加message_id接收方用 Redis 或本地缓存记录已处理的 ID设置合理的过期时间。处理前先查 ID 是否存在存在就跳过。7.4 协议选型速查表问题现象可能原因排查方向解决方案模型调错工具工具描述模糊检查 description写清楚使用场景任务状态不同步SSE 丢推送检查网络和重连加轮询兜底消息重复处理无幂等设计检查消息 ID加去重逻辑会话内存泄漏会话未过期检查过期策略设置 TTL工具调用超时工具执行慢检查工具性能加超时和重试Agent 发现失败Agent Card 未更新检查卡片版本版本化卡片最后分享一个小技巧不管用哪个协议日志一定要打全。我一般会在每个协议层都加一个 trace_id从模型发起调用到工具返回结果全链路可追踪。这样出问题时能快速定位是哪一层挂了。踩过几次坑之后我发现排查时间至少缩短了一半。这个领域还在快速演进协议本身也在迭代。我的建议是先跑通最小闭环再逐步引入协议分层。不要一上来就追求大而全那样很容易被协议细节淹没。先用 JSON-RPC 把链路跑通然后根据实际痛点引入 MCP、A2A 或 ACP这样每一步都有明确的收益。