资讯动态

Agent通信协议怎么选?一文理清JSON-RPC、MCP、A2A、ACP的边界与配合

发布时间:2026/10/21 12:45:02 来源:尧图企业网站定制
做AI应用开发这几年我每次在技术评审会上看到Agent相关的架构图都会下意识数一数上面画了多少种协议。这是个很真实的困惑为什么Agent通信协议这么多JSON-RPC、MCP、A2A、ACP名字一个比一个绕光听概念就觉得头大。这篇文章不打算把协议文档翻译成中文而是想从“需求倒推”的角度把这几个协议放在同一张坐标图里理清楚它们各自解决什么问题、边界在哪里、项目里到底该怎么选。你可以把它理解成一篇“协议选型前的摸底笔记”适合正在做Agent应用、正在纠结要不要引入某个协议、或者只是被各种缩写搞得晕头转向的朋友。1. 先弄清楚一套Agent系统里到底在“通什么信”很多人在协议上纠结本质上是没搞清楚一个问题消息到底要从哪里出发要到哪里停。一套Agent系统里通信场景远不止“客户端发请求、服务端返回结果”这一种。1.1 三条通信边界应用、模型、工具、Agent各说各话我把日常见到的Agent项目抽象一下通信其实发生在三条边界上。第一条是应用与模型服务的边界。比如你写了一个网页应用前端要向后端AI服务发起一次对话请求后端把结果流式返回给前端。这是最传统的“应用↔服务”通信谁做都绕不开常见方案就是HTTP加流式响应。第二条是模型与外部工具的边界。模型在推理过程中需要查数据库、调搜索接口、执行一段脚本这就产生了“模型↔工具”之间的通信需求。早期大家都是靠业务代码自己封装每个工具写一套调用方式模型根本无法统一对接。第三条是Agent与Agent之间的边界。一个Agent负责拆解任务另一个Agent负责执行计算第三个Agent负责生成报告。它们之间怎么互相发现、怎么传递中间结果、怎么同步任务状态这是“Agent↔Agent”的协作通信。这三条边界天然指向不同的协议诉求。只是早期大家都把通信混在一起做才会出现“一个项目一堆自定义接口”的混乱状态。1.2 协议扎堆出现的根本原因场景边界在裂变通信协议不会凭空增多它增多只有一种原因通信的对象变了。最初大家只需要一份API文档就能完成前后端联调。当LLM开始参与系统设计后消息的一方变成了“模型”它的交互模式是推理式的、流式返回的、需要按批次执行工具的这跟传统的请求响应模型不一样于是需要定义新的协议。再往后Agent的形态分化出“工具型Agent”“协作型Agent”“平台型Agent”分别对应上面三条边界。每个领域都开始有人尝试制定标准——你做一个“模型怎么调工具”的标准我做一个“Agent怎么协作”的标准他做一个“客户端怎么连Agent服务”的标准。于是JSON-RPC、MCP、A2A、ACP就走到了台前。这个发展路径跟前端工程化的演进非常像。一开始大家只需要写HTML后来JS越来越多产生了模块加载问题再后来状态管理、路由、打包器各自解决一个细分问题才会出现React、Vue、Webpack这些工具并存。协议也是同理不是越多越乱而是分工越来越细。2. 四个协议逐个拆解它们到底干了什么下面我把最常提到的四个协议按照从底层到上层的顺序逐一拆开看。先说明一点它们不是同一条赛道上的竞争者更像是高速公路的不同路段。2.1 JSON-RPC几乎被所有上层协议继承的“共同语法”JSON-RPC 2.0是一个非常轻量的远程调用协议核心思想就一句话把一次函数调用描述成一条JSON消息。它规定了几种消息结构请求带一个id响应带相同的id通知没有id、也不需要响应错误有统一编码。{ jsonrpc: 2.0, id: 1, method: add, params: [1, 2] }{ jsonrpc: 2.0, id: 1, result: 3 }我一开始不太理解为什么上层协议都愿意用JSON-RPC直到自己写了一遍才发现原因。它的优点有三个一是和语言无关任何语言都能解析JSON二是实现极简单不用像REST那样去纠结资源路径、状态码语义三是它只描述“调用”不夹带任何业务概念所以可以作为所有上层协议的地基。MCP的底层消息格式就是JSON-RPC 2.0A2A的消息结构也保留了jsonrpc字段。你可以这样理解JSON-RPC是语法规则它规定了句子怎么说至于句子说什么业务、在什么场景下说它一律不管。要注意JSON-RPC不解决状态管理也没有会话、鉴权、工具发现这些概念。它是“透明”的上层需要什么就在它的基础上扩展什么这也是它能成为公共底座的真正原因。2.2 MCP给模型世界开一扇标准化窗口MCP全称是Model Context Protocol它解决的是上面说的第二条边界模型与外部工具、数据源如何标准化的接口对接。MCP的定位很明确——让模型的上下文世界能连接任意工具和数据。MCP的核心架构是Host、Client、Server三层。Host是LLM应用本身Client负责与Server建立连接Server负责暴露工具、资源、提示词。理解它最简单的模型是MCP Server就是一个“工具箱”模型按统一的协议去翻工具箱找到合适的工具然后用统一格式调用。MCP定义了三种原语Tools可执行的工具调用模型可以在推理中主动触发Resources可读取的数据对象Prompts预置的提示词模板。这三种原语正好覆盖了Agent接入外部能力的大部分诉求。import datetime from mcp.server import Server from mcp.server.transport import stdio_server app Server(calc-demo) app.tool() def add(a: int, b: int) - int: 两数相加返回整数结果。适合做加法运算。 return a b app.tool() def today() - str: 返回今天的日期字符串格式YYYY-MM-DD。 return datetime.date.today().isoformat() if __name__ __main__: import asyncio async def main(): async with stdio_server() as (read_stream, write_stream): await app.run( read_stream, write_stream, app.create_initialization_options(), ) asyncio.run(main())MCP在设计上刻意做了一件事底层使用JSON-RPC但把工具结构、初始化握手、传输封装都标准化。这样工具提供方只需要实现一次Server就能被所有支持MCP的客户端复用。我在实际体验里的感受是MCP真正解决的是“碎片化”你不用再为每个工具写一套专属SDK模型侧只需要理解一套协议。它也有自己的适用范围。MCP更适合“模型复用工具”的场景比如Agent内部去查数据库、调用业务服务、读取文档。它并不直接解决多个Agent之间的协作问题——两个Agent互相派活、同步进度那不是MCP的主场。2.3 A2A为Agent之间的长时间协作而设计A2A全称是Agent2Agent它解决的是第三条边界Agent与Agent之间如何发现彼此能力、如何协同完成任务。它和MCP的关注点有明显不同MCP是“模型→工具”A2A是“Agent→Agent”。A2A里有个核心设计叫Agent Card。每个Agent会公开一份JSON格式的能力描述里面写清楚这个Agent能处理什么任务、它的通信端点在哪里、需要什么鉴权方式。另一个Agent可以通过获取AgentCard来“发现”对方的能力。这种设计思路很接近服务发现只是这里被发现的不是微服务而是有推理能力的Agent。消息模型方面A2A引入了Task的概念。一次协作会被建模成一个TaskTask下面有Message和Artifact。Message是Agent之间往来交流的信息Artifact是任务产出物比如一份报告、一段代码、一个计算结果。{ jsonrpc: 2.0, id: req-001, method: message/send, params: { agentCardPath: https://agent.internal.example/agent.json, taskId: task-20240630-001, messages: [ { messageId: msg-001, role: user, content: [ { type: text, text: 请把本周销售数据整理成摘要 } ] } ] } }A2A采用了一种“任务中心”的通信模型也就是Task-Centric。它不是简单地把消息丢给另一端就结束而是围绕任务状态去管理整个交互任务创建、状态更新、结果产出、错误处理、取消操作。这一点我后来想通了——Agent之间的协作天然需要长期运行、需要阶段性确认、需要处理失败重试如果只做消息投递根本没法支撑复杂协作。相比MCPA2A会更像“面向Agent的工作流协议”它引入了任务状态机、回调、流式更新等概念也更适合跨系统、跨组织的Agent协作场景。2.4 ACP把会话、流式、工具调用统一进一个连接ACP全称Agent Client Protocol它和前面几个协议的切入点又不一样。ACP更关注“客户端怎么跟一个Agent服务建立并维持一条可持续的会话通道”。它解决的是对话型Agent接入的体验问题重点在连接层。我对ACP的理解是基于一个真实场景你做一个IM机器人或者做一个Agent原生客户端用户会持续跟Agent对话对话里可能会穿插多次工具调用工具执行结果又要流式地返回给客户端。传统做法是把聊天接口和工具调用接口分开设计还要自己搞websocket的心跳、重连、消息id匹配很繁琐。ACP的思路是把这些统一进一条连接里。它维护一个会话状态连接一旦建立服务端知道你是谁知道你当前在哪个会话里还能识别同一连接里的消息上下文。在这条连接上既可以发送普通文本消息也可以触发Agent内部的工具调用工具执行的事件又能实时回流给客户端。它把“会话、流式、工具调用”揉在了同一个模型里。可以这样对比MCP是模型和工具间的桥梁A2A是Agent和Agent间的协作协议ACP更像是面向“终端用户↔Agent服务”的接入协议。它的核心假设是客户端会长期在同一会话里跟Agent交互所以连接模型远比请求响应模型重要。3. 它们是什么关系以及怎么选型搞清楚单个协议之后最关键的问题来了它们是怎么配合的项目里怎么选3.1 分层视角语法层、接入层、协作层、会话层我用一张表格把四个协议按层次放好方便大家对照协议所属层次核心解决对象典型交互JSON-RPC语法层消息的通用格式与调用语义请求、响应、通知MCP接入层模型与外部工具、数据源的标准化连接工具发现与调用A2A协作层Agent与Agent之间的任务协同与能力发现任务创建、状态同步ACP会话层客户端与Agent服务的持续会话连接流式消息、多轮会话这四层并不互斥反而可以叠加使用。一个Agent服务对外可以通过ACP做接入内部用MCP接工具同时通过A2A去跟其他Agent协作所有消息底层都可以是JSON-RPC格式。协议叠起来用的时候边界反而更清楚了。我见过不少项目把MCP和A2A混在一起讨论其实想清楚就一句话你的Agent内部要不要调工具要就走MCP。你的Agent要不要跟别的Agent协作要再考虑A2A。两个问题的答案不冲突因为解决的是不同层的事情。3.2 选型决策表什么场景选什么协议我把常见的几种项目场景整理成一份决策表方便直接抄作业场景推荐方案理由单应用内让LLM调用自定义工具和内部数据MCP统一工具接入规范模型侧只学一套协议已有内部服务只想做轻量RPC调用JSON-RPC直连实现成本最低不做额外抽象多个Agent需要跨模块协同、任务流转A2A标准的任务模型、能力发现适合协作做面向外部用户的Agent产品需要持续会话ACP或自定义WS协议会话、流式、工具调用一体化体验好既有Agent工具复用的需要又有跨系统协作的需要MCP A2A内部工具归一化对外协同标准化选型有个朴素原则不要为了协议而协议。如果只想实现两个服务间相互调用直接JSON-RPC就够用了没必要引入完整框架。反而是当你发现“接口碎片化”成为瓶颈时再上MCP或A2A效果会明显得多。我自己的习惯是画一张通信边界图把每个通信方向标出来再逐条问自己这一条通信里消息方是模型还是Agent还是用户它对状态、流式、长时间运行有没有要求答案明确后选型基本不会跑偏。3.3 一个最小可跑的组合示例演示一个最简单却完整的组合在A2A的Agent服务内部用MCP定义工具让模型在响应协作请求时真正调用外部的计算服务。第一步按前面2.2的示例代码起一个MCP Server暴露add和today两个工具。第二步在A2A服务里维护一个Task当收到协作请求时转发给内部的模型再通过MCP Client调用工具。第三步把工具结果封装成Artifact写到Task的产出物里。流程上几个关键点需要敲定任务状态要维护working、completed、failed、调用工具的结果要缓存、客户端轮询或回调要能拿到最终状态。如果只是演示可以直接用一个内存字典保存Task状态配合定时轮询。生产环境则要好好设计任务存储避免服务重启后任务状态丢失。4. 实操中的典型坑与排查方法协议本身不复杂真正让开发者头疼的是接入细节。我把实操中踩过的坑整理成几个典型问题希望你能少走点弯路。4.1 初始化握手没做对后面全白搭MCP这类协议一般都有初始化流程。客户端必须先发initialize请求完成能力协商之后才能正常调用工具。我见过很多“工具调用没反应”的问题最后排查下来都是客户端没有先初始化或者服务端没等初始化完成就接收了业务请求。排查方法很简单看日志里有没有initialize这步。没有就先补握手有就检查服务端返回的capabilities是否和工具类型匹配。如果客户端声明不支持某项能力服务端完全可以拒绝后续请求。另外请求ID的配对也很容易出错。JSON-RPC规定请求和响应用同一个id关联有些实现里通知消息不带id如果客户端错误地给通知也发了id服务端可能误判为普通请求而返回result两边状态就对不上了。4.2 工具描述写太差模型根本不知道怎么调用这是MCP里面特别隐蔽的坑。模型决定是否调用某个工具完全靠工具的name和description来理解。description写不清楚或者参数schema含糊模型要么不调用要么乱填参数。我在实际测试中发现描述从“两数相加”改成“输入两个整数返回它们的和适合计算总数、金额合计等场景”调用准确率会明显提升。所以工具描述要满足两个原则第一说清楚触发场景第二参数类型和约束要写具体。# 不推荐 add(a, b): 加法 # 推荐 add(a: int, b: int) - int: 输入两个整数返回它们的和。 当用户需要计算购物总额、统计数量总和、合并数值时使用。工具不是写给人看的是写给模型看的。写工具描述的思路要改成“站在模型的角度描述这个工具在什么场景下值得被调用”。这个观念转过来之后工具被正确调用的概率会大幅提升。4.3 轮询、回调、流式任务状态管理的三种陷阱A2A这种任务型协议最常见的问题就是任务状态卡在working不动。排查方向有几个任务执行进程是否还活着回调地址是否被网络策略拦截轮询接口是否有幂等性设计。幂等这一点容易被忽略。任务系统要确保同一个taskId的重复消息不会产生两个相同任务否则一次网络重试就会导致重复执行。我建议在任务日志里记录taskId、触发源和幂等标识重复请求直接返回原状态而不是新建任务。流式返回也会引入风险。如果服务端对单个请求有超时限制就尽量用“先返回任务id再异步推送状态”的方式避免长任务因为网关超时被掐断。我遇到过好几次“任务其实执行完了但客户端因为连接超时没拿到结果”的情况后来统一改成回调加兜底轮询问题才彻底解决。4.4 鉴权与安全性容易被忽略的两个点第一点很多内部Agent服务觉得“都是内网调用不用校验”。实测下来内网工具一旦被某个测试页面误调用可能导致脏数据或额度被消耗。最简单的处理是给MCP Server加一个调用方校验验证请求头或JSON-RPC的参数里是否包含约定的token。第二点AgentCard或能力描述文件里不应暴露过多的内部信息。A2A的AgentCard是用于能力发现的它本身就是给外部看的所以端点地址、支持的鉴权方式可以公开但任何内部密钥、数据库连接串都不应该出现在里面。对外的Agent服务还要给所有工具调用做一层审批或白名单控制。鉴权这部分我的经验是宁可先做一个简单的token校验也不要裸奔上线。协议本身没有强制要求鉴权方式你可以在上层自由实现但它必须存在。5. 我目前的实践结论5.1 先MCP、再A2A最后才考虑ACP如果从零开始做一个Agent项目我不会让团队一上来就铺开所有协议。我的习惯是按照演进路径走第一阶段只有一个Agent核心诉求是复用内部工具直接引入MCP把工具接入标准化第二阶段当出现多个Agent需要配合时再引入A2A用AgentCard和Task模型把协作串起来第三阶段如果要做对外产品需要考虑客户端持续会话体验这时再看ACP有没有必要。这个顺序背后的逻辑是先解决单点效率再解决多点协作最后解决接入体验。每走一步都有明确的触发条件避免“为了技术而技术”。实际项目里很多团队连MCP都还没用好就急着引入一堆协议结果反而增加了沟通和维护成本。A2A的普及依赖Agent生态的成熟度。如果协作对方还没支持它强行引入只会增加适配成本。我的做法是在接口层做抽象内部可以先自己实现一套轻量的任务状态管理等生态成熟后再平滑迁移到标准协议。这个思路也适合MCP前期——先用起来再逐步规范化。5.2 协议增多不是坏消息是专业分工的信号回到文章开头的问题为什么Agent通信协议这么多答案其实已经很明显了——因为它们服务的场景不一样。JSON-RPC是通用语法MCP管模型与工具的衔接A2A管Agent之间的任务协作ACP管终端会话接入。它们不是内卷而是各管一段。我目前最深的体会是协议本身不是复杂度来源边界模糊才是。我见过最痛苦的架构不是选错了协议而是什么协议都想要却说不清每一条通信线上走的是谁的消息、解决的是什么问题。只要先把通信边界画清楚再看哪个协议贴合那个边界你会发现这些协议并不冲突甚至可以在同一套系统里各司其职。最后再分享一个小技巧把每个协议的核心关键词写在架构图的通信线上比如“工具调用MCP”“Agent协作A2A”“接入会话JSON-RPC流式”。哪条线上没有标签哪里就是还没想清楚的地方。等所有线都有标签了架构图也就不会再有讨论不清的协议问题了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑