☰
MCP、A2A、ACP与JSON-RPC:一张图看懂Agent通信协议的分层与选型
2026/10/21 21:37:58 网站建设 项目流程

1. 先搞明白:这些协议到底在“对谁说话”

最近在搭一个多Agent协作的模拟项目X,核心任务是让不同职能的Agent相互配合完成业务流程。翻了几天资料,发现相关缩写一下冒出好几个:MCP、A2A、ACP,底层还都号称基于JSON-RPC。第一反应是“又出新协议了,生态要分裂了”。等把架构图和数据流真正梳理完,才发现完全不是这么回事——这三个家伙虽然都被称为Agent通信协议,但通信对象截然不同,解决的问题根本不在一个层次。

1.1 MCP、A2A、ACP,通信对象完全不同

日常口语里“Agent通信协议”这个词太容易制造误解了。稍微拆一下就会发现,这个大帽子底下至少盖着三类截然不同的通信关系。

先看MCP(Model Context Protocol),它解决的是一个Agent或一个LLM应用,如何标准化地接入外部工具、数据源和业务系统。它的名字已经说得很直白:给模型补齐上下文,让模型能触达那些原本封闭的API和数据。通信双方是“LLM应用”和“外部工具/数据源”,这是纵向的连接。

再看A2A(Agent2Agent),它解决的是Agent与Agent之间如何互相发现能力、互相派发任务、反馈进度和交换结果。通信双方是“Agent”和“另一个Agent”,这是横向的连接。典型场景是主Agent把订酒店、查排期、发通知这类子任务分发给不同的专业Agent,并跟踪它们各自的执行状态。

最后是ACP(Agent Communication Protocol),同样面向Agent与Agent的通信,但它的建模方式更偏“会话”:把一次Agent协作当作一段可以控制的对话,消息被组织成Session和Turn,强调过程可控、人机可共治。

所以,MCP是Agent把工具接在自己身上的“接入协议”,A2A和ACP是Agent之间互相打交道的“协作协议”。把它们统称为“Agent通信协议”,再放在一起抱怨“为什么标准不统一”,其实是把三层问题压缩成了一层。我一开始就犯了这种认知错误。

1.2 通信对象不同,协议自然分裂

为什么这个区别会直接导致协议形态不同?因为不同通信对象对协议的需求约束完全不一样。

接入工具的场景,要求协议足够轻,能被模型端在几百行代码里快速调通;工具的清单和能力描述往往是相对静态的,所以协议里要设计一套“能力发现”机制,让客户端知道服务端到底提供了什么。MCP的tools/list就是干这件事的。

Agent协作的场景,要求协议能表达更复杂的交互状态。一个Agent把任务委派给另一个Agent,任务可能持续几分钟甚至几小时,执行中会经历排队、进行中、请求补充信息、完成、失败、取消等状态。更麻烦的是,任务执行到一半,可能还需要人类或其他Agent介入。这种需求没办法被一个简单的“请求-响应”模型覆盖,必须引入任务状态机或者会话这种更厚重的抽象。

通信对象决定数据模型,数据模型决定协议形态。拿生活里的例子类比:USB-C解决的是“设备怎么插进电脑”的问题,网线和HTTP解决的是“电脑之间怎么互相访问”的问题。你不能因为两者都叫“连接标准”,就要求它们合并成一个协议。四个字总结:“通信对象”是所有分歧的根源。

2. 一切的地基:为什么大家都选JSON-RPC

顺着MCP、A2A、ACP往下追,会发现它们不约而同都用了JSON-RPC 2.0作为消息层基础。这个细节很容易被忽略,但恰恰是理解整个协议族的关键。

2.1 JSON-RPC 2.0把远程调用压缩到三个字段

JSON-RPC是一个极简的远程过程调用协议,一次完整的请求只需要三个核心字段:版本标识、方法名、参数。看一个实际例子,一次工具调用长这样。

{ "jsonrpc": "2.0", "method": "tools/call", "params": { "name": "query_weather", "arguments": {"city": "某城市A"} }, "id": 1 }

服务端成功时返回result,失败时返回error对象,携带标准的错误码。如果请求里没有id,则表示这是一个通知(notification),服务端不需要回响应。请求和响应的对应关系靠id匹配,思路和HTTP请求里的correlation id一模一样。

{ "jsonrpc": "2.0", "result": { "content": [ {"type": "text", "text": "某城市A:晴,18℃"} ] }, "id": 1 }
{ "jsonrpc": "2.0", "error": {"code": -32601, "message": "Method not found"}, "id": 1 }

规范里还定义了批处理,多个调用可以打包成一个JSON数组一次传输。整个规范文本很短,十几分钟就能读完,实现成本低到可以手写。这种朴素特性在协议竞争里反而是极强的护城河。

2.2 为什么不是REST、gRPC、GraphQL

不少人会问:HTTP时代,为什么不直接用REST?追求性能用gRPC?或者做数据聚合用GraphQL?这些疑问在接触Agent协议时几乎一定会冒出来。

REST的问题在于资源语义太重。REST把“对象”建模成资源,用HTTP动词表达对资源的操作。但Agent之间的调用,很多时候不是“对资源增删改查”,而是“向对方发起一段交互过程”。硬套REST,最后会变成到处POST,语义从“操作资源”滑向“触发动作”,REST带来的规范化收益就消失了。

gRPC的问题在于契约和二进制链路。它需要定义proto文件、生成代码、管理序列化和HTTP/2连接。对一个希望“模型能快速生成客户端代码”的场景来说,这套东西太重了。而且二进制协议不好抓包调试,后端服务和外围系统对接时排障成本明显更高。

GraphQL站在另一个极端。它最擅长查询图结构数据,有强schema、复杂查询语义和权限控制,但“调用远端某个方法并拿结果”这件事它并不直接。Agent协作本质上是过程调用,不是数据查询,用GraphQL做底层会有一种绕远路的别扭感。

JSON-RPC恰好落在中间:方法调用语义直接,消息是纯文本JSON,可读、可调试、跨语言不需要代码生成,同时支持通知这种轻量机制,很适合长连接上的事件推送。而且还有一个LLM时代的隐藏优势——模型对JSON的理解能力非常强,让它“读着JSON-RPC规范生成客户端”或者“生成一个符合格式的调用包”,都是很容易的事;换成gRPC的二进制协议,这条路就走不通了。各方案对比可以总结成一张表。

方案擅长场景在Agent通信中的核心问题
REST资源型API动词有限,调用语义容易被拉伸
gRPC高性能内部服务合约与二进制链路重,AI代写客户端成本高
GraphQL数据查询与BFF查询模型强,过程调用弱
JSON-RPC跨语言远程方法调用极简、可读、模型友好,几乎无缺点

2.3 大家只把JSON-RPC当“消息信封”,真正的协议在信封之上

这里要澄清一个高频误解:MCP、A2A、ACP都基于JSON-RPC,并不代表它们就是同一种东西,更不代表“换了个名字的JSON-RPC”。

JSON-RPC只定义了“方法调用”这个信封结构。信封里装哪些方法、方法之间按什么顺序编排、消息有哪些状态生命周期、两端如何握手认证,全部由上层协议自己定义。

类比来说,JSON-RPC相当于信封和邮票。MCP规定了“我寄给你的是工具调用单”,A2A规定了“我寄给你的是任务委派单”,ACP规定了“我寄给你的是会话中的一条消息”。用的是同一个信封,但信件内容、投递规则、跟踪机制完全不同。

理解到这一层,再看整个协议族就不会乱了。真正有差异的部分不在消息编解码,而在消息的语义组织和生命周期管理。这也是后面MCP和A2A/ACP在形态上分道扬镳的根本原因。

3. MCP:Agent接入工具世界的标准插座

三个协议里,MCP是当前生态发展最快、生产环境落地最多的一个。对大多数做Agent项目的人来说,MCP也是第一个应该接触的协议。

3.1 没有MCP之前:一半代码都写在胶水层

用LLM接外部工具这件事,在MCP出现之前做得极其痛苦。

每个工具一套API文档,代码里要维护对应请求函数;参数要手工映射到模型的function calling定义;返回结果要转成模型能读懂的文本;换一个工具,几乎就要重写一遍类似的胶水代码。工具越多,胶水层越厚。更麻烦的是,模型端每接一个新工具都要重新调整function schema,schema一变,决定“模型什么时候该调这个工具”的逻辑又得重新测试。

这个痛点和USB-C统一充电口之前的情况很像:抽屉里堆满各种充电线,每根线只能给特定设备用。MCP做的,就是规定出插座和插头的标准形状:工具方统一暴露能力描述,LLM应用统一方式调用,双方不再需要针对每家的接口单独适配。

3.2 MCP的三个核心原语与传输方式

MCP把一次交互抽象成三个原语,理解这三个词基本就掌握了MCP的使用面。

Tools是最常用的。它是一个可以被模型调用的外部功能,包含名称、描述、入参schema。模型通过tools/call调用它,执行后返回结构化内容。这个语义和function calling很像,但MCP的价值在于把schema定义和服务发现放在了协议层,客户端可以自动感知有哪些工具、怎么调。

Resources是数据源,解决“把外部数据按需喂给模型”的问题。它先描述资源,再按需读取,适合让Agent从知识库、数据库、文件系统里拉取上下文。

Prompts是一些预置的提示模板和交互流程,把用户意图映射成Agent可以执行的指令。典型场景是固定业务流程,比如“投诉处理”“退货退款”这类有固定步骤的对话。

三个原语对应的是不同场景的接入需求,很好记:要执行就用Tools,要读数据就用Resources,要流程引导就用Prompts。

原语解决的问题典型场景
Tools让模型执行外部功能调用API、执行命令、查询订单
Resources把外部数据喂给模型读知识库、数据库查询
Prompts预置提示与交互流程固定业务步骤的引导

架构上,MCP是标准的Client-Server模型。一个LLM应用作为Host,可以挂载多个MCP Client,每个Client连接一个MCP Server,Server就是工具和数据能力的提供方。传输层支持三类:Stdio适合本地子进程;HTTP+SSE适合远程服务;Streamable HTTP是更现代的方案,用一个HTTP端点同时支持流式和非流式通信。新项目尽量选Streamable HTTP,不要再用旧式SSE-only模式。

一个真实项目里常见的拓扑是:主Agent运行在应用层,通过MCP Client连接多个Server,分别对接CRM、搜索服务、内部知识库。工具调用走tools/call,数据读取走resources/read,权限和审批由Host层统一拦截。这种模式让“接入一个新工具”变成了“接入一个配置好的Server”,重复劳动大幅减少。

3.3 一个最小的MCP Server有多简单

实际写一个MCP Server,比大部分人想象中简单。下面的例子基于一个使用广泛的开源MCP SDK,注册一个查询天气的工具,核心代码不到二十行。

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; const server = new McpServer({ name: "weather-server", version: "1.0.0" }); server.registerTool( "query_weather", { city: { type: "string", description: "城市名称" } }, async ({ city }) => ({ content: [{ type: "text", text: `${city}: 晴, 18℃` }] }) ); const transport = new StdioServerTransport(); await server.connect(transport);

这段代码跑起来之后,任意MCP Client都能自动发现这个Server有query_weather工具,看到它的schema,然后发起调用。SDK把JSON-RPC消息编解码、握手、方法分发全部处理掉了。

这个体验很重要。协议的价值不是让每个开发者手写协议实现,而是让接入变成“调SDK里的一个注册函数”。MCP能快速铺开,除了协议本身设计得当,更关键的是生态里快速出现了一批成熟的SDK和开箱即用的现成Server。新项目接一个Server往往只要几个小时,这种体验一传开,生态就滚起来了。

3.4 MCP不管Agent之间怎么对话

MCP再火,也回答不了“两个Agent如何协作”的问题。这是它最容易背上误解的地方。

MCP的通信角色天然不对称:一端是Host/Client,一端是Server,关系本质上是“消费者”和“提供者”。哪怕AB两个Agent互相都把对方当作MCP Server接入,它们也只是互相把对方当成工具来调,而不是对等的协作者。中间缺了“如何描述Agent身份”“如何形成任务闭环”“谁对最终结果负责”“任务中途如何协商”这些机制。

这些职责要交给A2A或ACP这类Agent协作协议来处理。一句话:MCP解决“连接工具”的问题,A2A/ACP解决“连接智能体”的问题,两者并不互相替代。

4. A2A与ACP:Agent之间的两种外交语言

A2A和ACP都面向Agent间协作,但两者对“一次协作”的建模方式不同。很多人把它们当成纯竞争关系,我更愿意把它们理解成具有不同侧重的协作范式。

4.1 A2A:从Agent Card到Task生命周期

A2A是目前由某搜索巨头联合一批厂商推动的Agent互联协议,设计目标很明确:让不同组织、不同技术栈的Agent能动态发现对方,并完成任务级协作。它的两个核心概念是Agent Card和Task。

Agent Card是一份公开的元数据,描述Agent的身份、能力、端点和通信方式。一个Agent要加入协作网络,先放出一张Agent Card,其他Agent看到Card就能判断“这个Agent能干什么事,我应该把什么任务交给它”。下面是一份极简示例。

{ "identifier": "agent-weather-001", "displayName": "天气助手Agent", "capabilities": { "tasks": { "streaming": true } }, "endpoints": [ { "url": "https://agent-service.example/a2a", "protocol": "a2a" } ] }

A2A的另一个核心机制是Task。它不是简单“一个请求拿一个响应”,而是“委派一个任务并跟踪它走到完成”。Task有明确的状态机:提交、工作中、需要输入、完成、失败、取消。执行期间的中间结果和最终结果会以Message和Artifact形式返回,支持通过SSE流式推送状态更新。

这个模型对真实业务非常友好。比如主Agent预订差旅,向“酒店预订Agent”发起一个Task,对方逐步上报进度;中途需要补充乘客信息,状态切到“需要输入”,主Agent再请求用户补充。这种端到端任务闭环,比“调一个函数拿一个返回值”的表达能力强得多。

4.2 ACP:把Agent对话建模成状态机

ACP走的是另一条路线。它由某操作系统与云计算厂商主导,设计上更偏向“会话式”协作。

ACP将交互组织成Session和Turn。Session是一段持续对话的上下文容器,Turn是会话中的一轮交流。每个Turn都有明确的角色和消息类型,结构上天然支持多轮态势感知。一个大幅简化的ACP消息大概长这样。

{ "type": "message", "session_id": "session-001", "turn_id": 1, "role": "user", "payload": { "kind": "content", "content": {"type": "text", "text": "把会议室订在明天上午10点"} } }

ACP同样具备Agent发现机制,但它的重点不是“任务完成状态机”,而是“对话过程控制”。它把Agent之间的通信看成一段可以控制、可以审计、可以中途插入干预的对话。在需要严格人机共治、过程审计的场景里,这种建模更顺手。

举个例子,两个Agent在协商过程中,人类需要随时插入一句“这里停一下”。在会话模型里,这就是一个普通的新Turn,很容易表达;换成任务模型,就得专门设计“请求人类输入”的中间状态,绕了一小段路。

4.3 为什么会有两套“外交语言”并存

A2A和ACP定位相近,却同时存在。这个现象值得认真复盘。

其一,发起方不同,设计哲学必然不同。A2A偏“任务契约”,强调异步、长时任务、跨组织协作;ACP偏“会话协议”,强调过程、状态、多轮对话的精确控制。一个更贴近工作流,一个更贴近对话协同。

其二,标准化初期,各方都想拿到定义权。通信协议是典型的“赢者通吃”领域,先铺开生态的一方,后来者就要付出巨大的替换成本。所以厂商不会轻易放弃自己的赛道,两个协议在相当长一段时间里会各自迭代。

其三,两者正在互相吸收。A2A的新版本已经加入更多会话控制能力,ACP也在扩展任务编排语义。短期看是双雄并存,长期大概率是其中一个占据主流,另一个通过兼容层继续存在。对应用侧来说,现在没必要死磕二选一,正确的做法是把Agent间协作逻辑封装在一个适配层里,让上层业务不直接绑定某一套协议。

5. 三套协议放在一张桌子上:对比与组合

把三个协议摊开之后,最需要做的是系统性对比,然后找到它们在真实架构里的位置。

5.1 一张表说清区别

下表是我在梳理模拟项目X时反复核对的结论,尽量把关注点压缩到核心维度。

协议解决的层级核心通信对象消息组织方式典型传输生态成熟度
JSON-RPC 2.0消息承载层任意客户端与服务端请求-响应、通知、批处理HTTP/WebSocket/stdio/任意流高,长期稳定
MCP工具与数据接入层LLM应用与外部工具/数据源能力发现、工具调用、资源读取stdio、SSE、Streamable HTTP高,工具生态快速扩展
A2AAgent协作层Agent与Agent任务生命周期、消息、产物HTTP、SSE中,头部厂商推动中
ACPAgent协作层Agent与Agent会话、回合消息HTTP、WebSocket中低,仍在演进

这张表最关键的信息不是“哪个更好”,而是MCP和A2A/ACP并不在同一层。MCP属于接入层,A2A/ACP属于协作层。这个区别决定了它们在不同项目里扮演不同角色,也决定了同时使用它们完全合理。

5.2 组合架构:MCP向上接入,A2A/ACP向外协同

在一个复杂多Agent系统里,三套协议完全可以各司其职。我在模拟项目X中的典型拓扑是这样的。

一个调度Agent作为入口,自己负责理解用户意图,通过MCP Client接入一组工具Server,比如内部订单系统、知识库、日历服务。与此同时,这个调度Agent通过A2A/ACP接口对外暴露一张Agent Card。其他团队部署的垂直Agent,比如客服Agent、数据分析Agent,可以直接向它派发任务或请求协作。

在这个架构里,MCP负责“向下”的工具接入,保证调度Agent有能力执行;A2A/ACP负责“平级”的Agent互联,保证各Agent能互相发现和分工。两条通信链路互不干扰,甚至可以在同一个进程里同时存在。

这里我要特别提醒一个容易混淆的点:A2A/ACP和“多Agent编排框架”不是一回事。编排框架承载的是“谁先跑、谁后跑、结果怎么融合”的控制流逻辑;A2A/ACP承载的是“双方如何描述自己、如何交换任务消息”的通信协议。实践中,可以用编排框架管理内部Agent的控制流,只在真正需要跨进程、跨组织互通的地方引入A2A/ACP。两者搭配而不是二选一,架构会干净很多。

5.3 协议泡沫:也要警惕过度标准化

话虽如此,我还是要泼一盆冷水。当前Agent协议领域已经出现了明显的“过度标准化”迹象。

当一个新概念成为热点,资本和注意力涌入,就会出现大量协议草案和标准组织。有的协议解决的是真实互操作痛点,有的纯粹是为了占坑。对应用团队来说,追逐每一个新协议是性价比极低的事情。协议本身不产生业务价值,只有它能帮我们消除跨系统互操作的重复劳动时,才值得被引入。

我判断一个协议值不值得跟进,主要看三件事:是否解决实际手工劳动;是否有多个相互独立的企业在真实使用;是否具备从点到面的生态扩展能力。三条里占两条以上,才认真投入。目前MCP三条全占,A2A占前两条,ACP还在争取第二条的路上。这套判断也直接影响了模拟项目X的方案取舍。

6. 实际工程里的选型与落地:从模拟项目X积累的经验

最后这部分,聊聊我在实际项目里验证过的选型逻辑和踩过的坑,属于“文档里不会写、跑一遍才发现”的经验。

6.1 什么项目适合什么协议

先给出一份我常用的选型判断。

项目形态建议
单个Agent只需要接几个API/数据源直接用MCP,别自己发明格式
LLM应用想消费现成工具生态直接对接MCP Client,现成Server很多
多个Agent在同一个进程内编排优先用编排框架控制流,协议层不是重点
跨进程、跨团队部署的Agent互相调任务用A2A,任务模型更贴近业务闭环
需要严格会话控制、人机共治、过程审计考虑ACP的建模思路,必要时封装成适配层
需求不明确,先做概念验证从MCP起步,再补一个Agent协作协议适配层

这套判断的核心逻辑其实就是四句话:接入用MCP,协作用A2A/ACP,控制流用编排框架,而JSON-RPC作为所有消息的统一底座。协议是“层”的选择,不是“一个替代另一个”的对立关系。

6.2 接入MCP最容易踩的四个坑

先说第一个坑:Transport选错。很多早期项目用的是SSE+HTTP GET的组合,它只适合单向事件流,双向交互会被迫开两个连接,部署和鉴权都比较别扭。现在的新项目应该优先选Streamable HTTP,一个端点搞定双向通信,后续扩展也省心。

第二个坑是工具Schema膨胀。工具一多,MCP返回的tools/list可能包含上百个方法,模型在执行时容易选错。这时候需要做工具分组、按需加载,甚至让模型先走一次“工具发现”再决定调用。把所有工具一股脑喂给模型,效果和prompt过长一样,决策质量反而下降。

第三个坑是认证和授权混在业务代码里。MCP本身把权限职责留给Host层,但很多团队把所有Server放在可信内网,不做鉴权。短期内没问题,一旦暴露到生产环境或者跨团队调用,认证问题会集中爆发。我在项目里做的处理是:每个Server独立签发token,Host统一管理凭证,调用层只关心业务,不接触敏感密钥。

第四个坑是版本漂移。MCP还在快速迭代,SDK版本和Server版本的兼容性并不总是一致。上线前要锁定SDK版本,还要对关键工具做回归测试,不能随手升级。我吃过一次亏:某个版本的SDK改了Transport握手逻辑,服务器端没同步更新,所有远程调用静默失败,排查了很久才发现是版本不匹配。

6.3 试跑A2A/ACP之后的几点体验

我在一个跨Agent演示系统里,同时跑了A2A和ACP的简化实现,有三点感受很直接。

第一,A2A的任务状态机落地时必须配合超时和心跳。真实Agent执行任务经常超过默认超时时间。如果协定里没有心跳机制,客户端很容易在长任务执行到一半时误判为失败。我在项目里给每条Task消息增加了进度字段和时间戳,接收方超过30秒没收到消息就会主动发起状态查询,这个改动直接解决了“任务明明在跑却报超时”的诡异问题。

第二,ACP的会话模型在中途插入人类干预时确实好用。实现“人类在Agent对话中喊停”这个需求,用ACP的Turn模型几乎不用额外设计;换成A2A的任务模型,则需要额外的input-required状态来兜底。如果你的产品特别重视人机共治,这个差异值得认真评估。

第三,Agent Card的发现机制更适合跨组织边界。在同一个团队、同一个进程内的多个Agent之间,用Agent Card交换反而显得重。团队内应用可以先用内部服务发现,把Agent Card留给对外边界场景。协议设计再先进,也要分场景使用,不然只是增加了无谓的复杂度。

6.4 别纠结协议,先把通信边界画清楚

最后说一句踩过坑之后才真正想明白的话:面对这么多协议,最开始要做的不是追新,而是先把自己系统里的通信边界画清楚。

先问自己四个问题:谁和谁需要直接通信?通信方向是纵向接工具,还是横向协同Agent?每次通信是短请求还是长任务?消息状态和失败由谁负责闭环?这四个问题回答完,选型基本就自己浮出来了——纵向接工具就找MCP,横向Agent协同再比较A2A和ACP,短请求直接用JSON-RPC也完全够。

我见过很多团队一上来就铺全套协议栈,结果大部分通信根本不需要协议化,做了不少重复建设。反过来,真正需要跨组织的Agent互操作时,自己发明一套格式又往往不够严谨。协议存在的意义不是让架构看起来很现代,而是省掉那层没必要的胶水代码。

我现在的做法很朴素:MCP管工具接入,A2A管外部Agent协作,内部控制流交给编排逻辑,JSON-RPC作为所有消息的统一底座。这套组合跑了几个月,改动成本明显低于早期“每个接口一套格式”的做法。先把边界画清楚,再谈协议选型,页面上那些花花绿绿的缩写就不会再让你焦虑了。

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

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

立即咨询