A2A 协议详解——Agent 间通信的开放标准
本文为「AI Agent 技术系列」第 8 篇。MCP(第 3 篇)侧重“Agent 如何接入工具与数据”,A2A 侧重“独立 Agent 如何发现彼此、委托任务并交换结果”。两者可以配合使用,但不存在必须依次接入的依赖关系。
让两个 Agent 互相发消息并不难。难的是:对方能做什么、任务是否仍在执行、缺少信息时怎样追问、断线后怎样继续跟进。如果每次对接都重新约定这些细节,多 Agent 协作很快就会变成集成工程。A2A 要标准化的正是这套交互约定。
一、A2A 是什么:独立 Agent 之间的协作协议
官方定义与本文版本
A2A(Agent2Agent)是一个开放标准,目标是让不同开发者、框架和组织构建的 AI Agent 能够通信与协作 [1]。它最初由 Google 发起,后来成为 Linux 基金会旗下的项目。
本文以已正式发布的 A2A 1.0 为基准。概念指南用于解释场景,具体字段、枚举和方法名以英文协议规范为准 [2][3]。除特别说明,JSON 示例均采用 1.0 的 JSON-RPC 绑定;示例域名和 ID 仅用于说明结构,不代表可调用的服务。
什么时候工具封装不够用
把一个 Agent 包装成另一个 Agent 的工具,是合理的集成方式。例如只需要查询汇率时,一个输入输出明确的接口就足够了。但“帮我安排国际旅行”不一样:执行方可能要确认预算、等待用户授权,或者先交付部分结果。
两类交互的侧重点可以这样理解 [1][4]:
| 维度 | 典型工具调用 | 面向任务的 Agent 协作 |
|---|---|---|
| 调用方表达什么 | 调用某个明确的能力,提供参数 | 委托一个目标,允许执行方澄清范围 |
| 怎样跟进 | 接收调用结果或错误 | 跟踪任务状态、补充输入、接收阶段产物 |
| 执行细节由谁决定 | 工具实现决定具体操作 | 执行 Agent 自行规划和使用内部工具 |
这不是说工具不能有状态、不能反问。问题在于,简单的函数封装未必约定了任务生命周期和多轮协作语义,开发者仍要自己补齐。A2A 将这些约定放进协议,让执行方可以作为独立 Agent 参与协作。
它也不能消除所有业务适配:双方仍要理解目标、数据和权限。它减少的是每次对接都要重做的发现、消息、任务与结果传递机制。
一个直观的协作场景
用户对 AI 助手说:”帮我规划一次国际旅行。” 这个请求需要协调四个专业 Agent [1]:
graph LR
User[🧑💻 用户] --> Assistant[🤖 AI 助手<br/>协调者]
Assistant -->|A2A| FBA[✈️ 航班预订 Agent]
Assistant -->|A2A| HRA[🏨 酒店预订 Agent]
Assistant -->|A2A| CCA[💱 货币兑换 Agent]
Assistant -->|A2A| LTA[🚌 当地旅游 Agent]
AI 助手作为协调者,通过 A2A 与四个互不隶属的专业 Agent 通信,把结果汇总成一份完整的旅行计划。这四个 Agent 可以来自不同公司、用不同框架开发,彼此看不到对方的内部实现。
A2A 与 MCP:互补而非竞争
官方比较文档用“纵向”和“横向”解释二者:MCP 扩展一个 Agent 能使用的工具和资源,A2A 连接不同系统里的 Agent [4]。
| 维度 | MCP | A2A |
|---|---|---|
| 主要关注点 | 工具、资源等能力的标准化接入 | 独立 Agent 之间的通信与协作 |
| 典型场景 | 查询数据库、读取资源、调用外部 API | 委托任务、澄清需求、跟踪执行与交付 |
| 放在旅行场景中 | 航班 Agent 查询航班数据 | 助手与航班 Agent 协商行程 |
不要把这个分工理解成“MCP 必须无状态,A2A 必须有状态”。MCP 也有 elicitation(向用户请求补充信息),并在 2025-11-25 版本引入实验性的 Tasks 支持 [5];A2A 则允许直接返回一条 Message,不一定创建任务。
一种常见组合是:Agent 之间用 A2A 协作,各 Agent 内部用 MCP 接入工具。模型和框架负责构建 Agent,但 A2A 不要求使用某个框架,也不要求先实现 MCP。
五条设计原则
官方给 A2A 定了五条设计原则 [1][2]:
- 简洁性:复用 HTTP、JSON-RPC、SSE 等现有标准;
- 企业就绪:认证、授权、安全、可观测性对齐标准 Web 实践;
- 异步优先:支持长时运行任务和人工介入;
- 模态无关:文本、文件、结构化数据都能交换;
- 不透明执行:协作不要求公开内部推理、内存和工具实现。
五条里我最在意的是“不透明执行”:调用方只需要理解对方声明的能力和交互约定,不需要知道它用了哪个模型、怎样规划任务。但黑盒不等于可信,认证、授权和数据保护仍要由两边的系统落实(详见第六节)。
二、核心概念:一次协作由什么组成
三个参与者
| 参与者 | 角色 | 说明 |
|---|---|---|
| 用户 | 需求发起方 | 人或自动化服务,提出目标 |
| A2A Client(客户端 Agent) | 协调者 | 代表用户发起 A2A 通信的应用或 Agent |
| A2A Server(远程 Agent) | 执行者 | 暴露 A2A HTTP 端点的 Agent,对客户端是黑盒 [6] |
五个通信要素
| 要素 | 是什么 | 解决的问题 |
|---|---|---|
| Agent Card | JSON 元数据文档:身份、能力、端点、技能、认证要求 | 客户端如何发现 Agent、判断它能不能干活 |
| Task(任务) | 有唯一 ID 和生命周期的有状态工作单元 | 长时操作如何被跟踪、多轮交互如何组织 |
| Message(消息) | 一次通信回合,带发送方角色 | 传递指令、上下文、提问和回答 |
| Part(部分) | 消息和工件的内容容器 | 让协议与内容模态解耦 |
| Artifact(工件) | 任务产出的交付物(文档、图片、结构化数据) | 把工作结果和对话内容区分开 |
在 1.0 的 JSON 中,Message 的 role 使用 ROLE_USER 或 ROLE_AGENT,分别表示客户端和服务端发送的消息。这里的 USER 不一定是直接操作界面的人,也可以是代表用户发起请求的另一个 Agent。
Part 每次必须且只能包含以下四个内容字段之一 [2][6]:
text:纯文本;raw:文件的内联字节,在 JSON 中编码成 Base64;url:文件内容的外部地址;data:结构化 JSON 值,适合表单和机器可读参数。
Part 还可带 mediaType、filename 和 metadata。1.0 不再需要 TextPart、FilePart 这样的嵌套结构或 kind 判别字段。实际选择内联还是文件引用,取决于数据量、传输成本和访问控制,并没有统一的大小阈值。
另外要区分两个标识符:Task 对象用 id 标识任务,消息和事件中通过 taskId 引用它;contextId 用来把相关任务与消息归为一组 [6][7]。它通常由服务端生成,不代表多个 Agent 自动共享同一份内存,也不是访问凭证。
概念如何拼成一次完整交互
graph LR
CA[A2A Client] -->|读取| AC[Agent Card<br/>能力名片]
CA -->|SendMessage| R{响应类型}
R --> M[Message<br/>直接答复]
R --> T[Task<br/>跟踪工作]
T --> H[history<br/>Message 历史]
T --> AR[artifacts<br/>工件]
M --> P[Part]
H --> P
AR --> P
P --> P1[text]
P --> P2[raw 或 url]
P --> P3[data]
下面以“服务端支持流式、请求需要创建任务、认证采用 OAuth 2.0”为例,串起一次交互。认证不是固定要拿 JWT,实际凭证由服务端声明的方案决定 [1][2]。
sequenceDiagram
participant C as A2A Client
participant S as A2A Server
participant A as 授权服务器
Note over C,S: ① 发现:读取名片并选择接口
C->>S: GET /.well-known/agent-card.json
S-->>C: Agent Card
Note over C,A: ② 带外获取访问令牌
C->>A: OAuth 2.0 授权流程
A-->>C: access token
Note over C,S: ③ 发消息并同时建立流
C->>S: SendStreamingMessage(认证头,A2A-Version: 1.0)
S-->>C: task:初始任务快照
Note over C,S: ④ 在同一响应流中接收更新
S-->>C: statusUpdate:TASK_STATE_WORKING
S-->>C: artifactUpdate:工件分块
S-->>C: statusUpdate:TASK_STATE_COMPLETED
Note over C,S: 服务端关闭响应流
SendMessage 与 SendStreamingMessage 是两种发送方式,不是必须先后调用的两个步骤。若已经拿到了任务 ID,只想订阅进度,应使用 SubscribeToTask,而不是再次发送业务消息。
三、Agent 发现:Agent Card 是数字名片
协作的前提是找到对方、看懂对方能干什么。A2A 用 Agent Card 标准化了 Agent 的”自描述”,但怎么拿到这张名片取决于部署环境。
Agent Card 里有什么
关键字段如下 [2]:
| 字段 | 说明 |
|---|---|
name / description / provider / version |
Agent 身份信息;version 是 Agent 自身的版本 |
supportedInterfaces |
接口列表,每项声明 url、protocolBinding、protocolVersion,可带 tenant;按偏好排序 |
capabilities |
streaming、pushNotifications、extendedAgentCard 等可选能力,以及扩展声明 |
securitySchemes / securityRequirements |
可用认证方案,以及访问服务需要满足的安全要求 |
defaultInputModes / defaultOutputModes |
默认支持的输入、输出媒体类型 |
skills |
技能的 ID、描述、示例、输入输出模态,以及可选的专属安全要求 |
signatures |
可选的 Agent Card JWS 签名,供客户端验证完整性与来源 |
下面是一张声明 Bearer 认证的简化 Agent Card:
1 | { |
这里的 1.2.0 是 Agent 版本,1.0 才是接口使用的协议版本。securityRequirements 引用上面命名为 bearer 的方案;Bearer 认证没有要列出的 OAuth scope,因此 list 为空。安全模型借鉴 OpenAPI,但不能直接照搬 OpenAPI 的 JSON 写法。
skills 同时服务于人和程序。协调者可以让 LLM 读取 description 和 examples 选择 Agent,也可以使用规则或固定配置;协议不规定选择算法。技能描述只说明“能做什么”,不是新的可调用 RPC 方法。
三种发现策略
| 策略 | 机制 | 优点 | 局限 | 适用场景 |
|---|---|---|---|---|
| Well-Known URI | 在域名下托管 https://{domain}/.well-known/agent-card.json(遵循 RFC 8615) |
路径固定,便于自动读取 | 仍需先知道目标域名 | 公共 Agent、域内发现 |
| 精选注册表 | 中央目录保存 Agent Card 或引用,按技能、标签等查询 | 集中治理、按能力发现、访问控制 | 需维护注册表;规范未规定统一查询 API | 企业内部、公共市场 |
| 直接配置 | 配置 Agent Card URL、内容或私有发现接口 | 简单直接 | 固定内容或地址变更时需调整配置 | 紧耦合系统、开发调试 |
Well-Known URI 解决的是“知道域名后去哪取卡”,不是“怎样搜索全网 Agent”。A2A 标准化了 Agent Card 和常用发现路径,但还没有统一注册表的查询 API [8]。
名片也要控制访问和缓存
Agent Card 可能暴露内部 URL 和受限技能。可以保护发卡端点,也可以把基本信息公开,通过 capabilities.extendedAgentCard 声明扩展 Agent Card 能力,认证后用 GetExtendedAgentCard 获取更完整的信息。Agent Card 描述认证要求,不应嵌入静态密钥 [2][8]。
如果使用 JWS 签名,客户端还需要信任签名方的密钥;有签名并不意味着应信任任意来源的 Agent。
名片也不必每次调用都重新下载。官方建议使用 Cache-Control、ETag 和条件请求,缓存过期后通过 If-None-Match 等机制检查变化。扩展 Agent Card 可能因身份和权限不同而变化,应按认证会话隔离缓存,不能把某个用户拿到的完整名片共享给所有调用方 [2][8]。
四、任务生命周期:状态机与不可变性
状态机
Task 用来跟踪一项具体工作。下面展示常见状态转换,不是穷举所有合法路径;图中省略了枚举前缀 TASK_STATE_,实际 JSON 必须保留 [2][7]。
stateDiagram-v2
[*] --> SUBMITTED : 创建任务
SUBMITTED --> WORKING : 开始处理
SUBMITTED --> REJECTED : 拒绝执行
WORKING --> INPUT_REQUIRED : 等待补充信息
INPUT_REQUIRED --> WORKING : 客户端答复
WORKING --> AUTH_REQUIRED : 等待授权
AUTH_REQUIRED --> WORKING : 授权满足后恢复
WORKING --> COMPLETED : 成功
WORKING --> FAILED : 失败
WORKING --> CANCELED : 取消成功
WORKING --> REJECTED : 确认无法承接
COMPLETED --> [*]
FAILED --> [*]
CANCELED --> [*]
REJECTED --> [*]
COMPLETED、FAILED、CANCELED、REJECTED 是终止状态;INPUT_REQUIRED 和 AUTH_REQUIRED 是中断状态,分别表示等待输入和等待授权。枚举还包含表示未知或未确定状态的 TASK_STATE_UNSPECIFIED。任务可以很快完成,客户端不一定观察到每个中间状态;CancelTask 也只是取消请求,不保证一定成功。
我更关注 INPUT_REQUIRED 的工程价值:客户端不用从一段自然语言里猜测“对方是不是在等我”。它可以据此暂停等待、展示问题,再通过 SendMessage 或 SendStreamingMessage 向同一个 taskId 补充输入。若同时传 contextId,它必须与该任务所属上下文一致。
终态不能重启,后续工作另起任务
任务到达终止状态后不能继续接受消息,也不能重启。比如“把上次的结果改一下”,应在原 contextId 下发起新的交互,而不是把旧任务的 ID 填进 taskId。如果需要执行新的工作,服务端创建新任务;简单澄清也可以直接返回 Message [2][7]。
消息中的 referenceTaskIds 可以指向旧任务,帮助执行方理解依赖,但它是可选字段。两种引用的区别是:taskId 表示“继续这个任务”,referenceTaskIds 表示“参考这些任务”。
这样可以把每次工作的输入、状态和产物分别追踪,也不用在完成后反复修改同一个任务。同一上下文还能容纳并行任务:官方例子中,订完去赫尔辛基的航班后,订酒店和订雪地摩托可以并行推进 [7]。但 contextId 和任务引用不会自动调度依赖,编排仍由应用负责。
工件版本由客户端管
细化任务会生成新工件,而“哪个版本已被用户接受”由客户端决定。官方建议客户端维护版本关系,服务端细化同一产物时尽量保持工件 name 一致,以方便识别 [7]。
这不是一个内置版本控制协议。name 是可选、可读的名称,不是唯一标识;artifactId 只要求在任务内唯一。客户端关联版本时,应结合任务 ID、工件 ID 和本次细化请求,不能只凭文件名。
Message 还是 Task:三种 Agent 形态
不需要跟踪执行过程的交互可以直接返回 Message,需要跟踪的工作则使用 Task。官方据此区分三种实现形态 [7]:
| 形态 | 行为 | 适用 |
|---|---|---|
| 仅消息 Agent | 只返回 Message,可用 contextId 串联交互 |
直接问答、轻量交互 |
| 任务生成 Agent | 总是返回 Task,简单请求也可建模为已完成任务 | 希望统一跟踪工作单元 |
| 混合 Agent | 协商阶段返回 Message,承接工作后生成 Task | 需要先确认范围再执行的协作 |
一旦交互已经绑定到某个任务,后续追问和答复围绕该任务展开;任务产生的交付物应放进 Artifact。任务历史也不是完整聊天记录的保证,服务端不一定保留所有临时消息 [2]。
一个完整的细化示例
沿用官方“画帆船”的场景,看 contextId 和 referenceTaskIds 如何配合 [7]。下面将图片改为 URL 引用,避免用省略的 Base64 字符串掩盖数据结构。
第一回合,客户端向绘图 Agent 的 JSON-RPC 端点发送以下消息。HTTP 请求使用 Content-Type: application/json、A2A-Version: 1.0,并按 Agent Card 要求携带认证信息:
1 | { |
假设 Agent 完成绘制,响应中的 result.task 包含任务与工件:
1 | { |
第二回合,客户端要求“把船涂成红色”,沿用 contextId,引用旧任务,但不设置指向旧任务的 taskId:
1 | { |
Agent 为细化工作创建新任务。它可以继续使用工件名 sailboat_image.png,客户端则把这次请求及新任务、工件与上一版本关联起来。如果原任务有多张图片,调用方还应明确要改哪一张;有歧义时,Agent 可以通过 INPUT_REQUIRED 请求澄清。
五、流式与异步:长任务怎么跟进
长任务不适合依赖一个普通请求一直等到结束。A2A 提供轮询、流式和 webhook 三种跟进方式 [2][9]。
SendMessage 默认等待任务到达终态或中断状态后返回。若希望创建任务后尽快返回,可设置 configuration.returnImmediately: true,再选择后续跟进方式;这个配置不改变 SendStreamingMessage 的流式行为。
| 机制 | 条件 | 适用 |
|---|---|---|
轮询 GetTask |
能主动发起请求 | 简单集成、低频更新、获取最终快照 |
流式 SendStreamingMessage / SubscribeToTask |
服务端支持流式,客户端能维持连接 | 实时进度、增量接收工件 |
| 推送通知(webhook) | 服务端支持推送,调用方提供可达的接收端 | 长任务、服务间异步通知 |
SSE 传递的是任务更新,不一定是模型 token
在 JSON-RPC 绑定中,服务端声明 capabilities.streaming: true 后,客户端可用 SendStreamingMessage 发起流式交互,响应类型为 text/event-stream。每个 SSE data 中都是一个 JSON-RPC 响应,其 result 是 StreamResponse,包含以下字段之一 [2]:
task:Task 快照;message:直接答复的 Message;statusUpdate:TaskStatusUpdateEvent,可附带进度说明或补充信息请求;artifactUpdate:TaskArtifactUpdateEvent,携带工件或工件分块。
有两条合法路径:服务端只返回一条 Message 后关闭流;或者先返回 Task,再发送状态和工件更新。后者通过 artifactId、append 和 lastChunk 组织工件分块,lastChunk 表示该工件的最后一块,不表示整个任务结束。
1.0 已移除 TaskStatusUpdateEvent.final,流的结束由连接关闭等绑定机制表达。任务进入终态时流应结束;遇到中断状态,要区分等待输入和带外授权,不能把一次断流当成任务完成。例如当前规范建议,在等待带外授权时维持活动流,以便授权完成后继续推送 [2]。
断线后,可用 SubscribeToTask 订阅仍未终止的任务,它首先返回当前 Task 快照。重新订阅不等于重放所有遗漏事件,尤其不能把临时状态消息当作可靠消息队列;需要完整结果时用 GetTask 核对。任务已经终止,则直接查询结果,不再尝试订阅。
上述 SSE 行为针对 JSON-RPC 和 HTTP+JSON 绑定;gRPC 使用自己的服务端流机制。
webhook 可以携带结果,但仍要考虑补拉
服务端需要声明 capabilities.pushNotifications: true。客户端用 TaskPushNotificationConfig 配置 webhook 的 url,还可提供校验用 token 与 authentication。配置可放进发送请求的 configuration.taskPushNotificationConfig,或对已有任务调用 CreateTaskPushNotificationConfig [2][9]。
推送的 HTTP body 直接使用 StreamResponse,可以带任务快照、消息、状态更新或工件更新,不是只能发一条“任务变了”的提醒。客户端需要完整、最新的 Task 时,再用 GetTask 补拉。
webhook 地址必须能被服务端访问。移动应用通常需要自己的后端接收通知,再转发给设备;不能因为客户端不保持连接,就假设手机本身能接收公网 HTTP 请求。
推送通知的安全与重复处理
webhook 让服务端主动请求客户端提供的 URL,需要同时考虑两端的安全 [9]:
- 防 SSRF:发送方验证目标地址,结合可信域白名单、地址限制、域名所有权验证和出站网络控制,避免访问内部服务或成为攻击第三方的跳板。
- 防冒充:接收方验证通知来源及任务 ID。JWT + JWKS 是一种方案,不是唯一要求;使用时应从预先信任的发行方获取密钥,验签并核对
iss、aud、有效期等声明。如果配置了token,也要核验。 - 防重放与重复执行:验证时间戳或一次性 ID,幂等处理通知。合法重试也可能造成重复投递,不能每收到一次通知就重新执行业务动作。
1.0 规范要求发送方至少尝试投递一次,但重试策略由实现决定,不能据此推导出通知一定送达或恰好送达一次。接收成功后返回 HTTP 2xx;对关键任务,客户端仍需保留查询和对账能力 [2]。
六、企业级设计:不重新发明轮子
A2A 的企业化设计复用现有的身份、网关和监控设施 [10]。这意味着可以沿用成熟的运维方法,不意味着接上协议就自动满足安全和合规要求。
传输认证与任务内授权是两件事
生产环境应使用加密传输:HTTP 绑定使用 HTTPS,gRPC 使用 TLS,并校验服务端证书。TLS 配置应遵循现代安全实践,当前英文规范推荐 TLS 1.3+ [2]。
客户端根据 Agent Card 的 securitySchemes 和 securityRequirements,通过 OAuth 流程或安全的凭证分发机制取得访问凭证。每次请求都要按所选方案携带凭证,例如 Authorization: Bearer ...;gRPC 则通过相应的 metadata 传递。不能把消息中的 ROLE_USER、任务 ID 或自报身份当作认证依据。
HTTP 场景中,凭证缺失或无效通常返回 401 Unauthorized,并提供 WWW-Authenticate 挑战信息;身份有效但无权执行操作则返回 403 Forbidden。
任务执行期间还可能需要额外授权,例如访问用户的另一个系统。服务端可设置 TASK_STATE_AUTH_REQUIRED 并说明需求,客户端通过带外流程完成授权。默认应使用独立的安全渠道传递这类凭证,而不是放进普通对话;只有经过带外约定或扩展协商,才使用带内交换机制 [2]。
AUTH_REQUIRED 只表示“还需要授权”,不代表已经获得权限,也不规定授权范围或有效期。完成一次授权能否用于后续操作,仍由服务端、凭证发行方或扩展规则决定。
授权与隐私仍由服务端落实
- 按认证身份控制技能、任务和数据的访问范围。知道某个
taskId不等于有权查询或取消它。 - 调用后端工具、读取数据或执行敏感操作前继续检查权限,遵循最小特权原则。
- 减少 Message 和 Artifact 中不必要的敏感信息;持久化时也要保护数据,按实际领域满足适用的隐私和合规要求 [2][10]。
多租户:把请求送对地方,不等于允许访问
多个 Agent 可以部署在同一个域名或网关后面。官方介绍了三种可组合的路由方式 [11]:
| 路由方式 | 如何区分目标 | 调用方需要做什么 |
|---|---|---|
| URL 子路径 | 例如 /billing 和 /support 指向不同 Agent |
使用各 Agent Card 声明的接口 URL |
| 认证信息 | 网关根据 token 声明或 API key 映射后端 | 按 Agent Card 要求携带凭证 |
tenant 字段 |
接口声明一个服务端定义的路由标识 | 每次请求原样携带该值 |
例如,账单 Agent 的 Agent Card 可以包含以下接口条目。这只是 supportedInterfaces 中的一项,不是完整 Agent Card:
1 | { |
请求仍发往该 URL,但 JSON-RPC 的 params 中需要带上 tenant:
1 | { |
所选接口声明了 tenant,客户端就必须在发往该接口的每个请求中原样回传;接口没声明,就省略该字段。它是不透明的路由标识,可以代表 Agent、工作空间或组织,客户端不应自行解释或编造 [2][11]。
路由与授权必须分开:填上 "tenant": "billing" 不能证明调用者有账单访问权限,网关和后端仍需检查认证身份。共享域名下的各个 Agent 也应分别提供 Agent Card,不能假设一张域名级名片就描述了所有后端。
可观测性
可以通过 W3C Trace Context 头传播追踪上下文,并接入 OpenTelemetry,但跨 Agent 链路需要客户端、服务端和中间网关共同配合 [10]:
- 日志关联
taskId、contextId、trace ID,必要时记录路由标识; - 监控请求速率、错误率、任务延迟和资源使用;
- 对任务创建、关键状态变化及敏感操作留存审计记录,避免日志泄露凭证或业务数据。
跨组织暴露的服务还可以通过 API 管理网关统一认证、限流、路由和配额。网关负责通用策略,不能代替 Agent 自身的数据级授权。
七、协议绑定与版本:概念相同,线上格式必须一致
A2A 1.0 将规范分为数据模型、抽象操作、协议绑定三个层次,以 Protocol Buffers 定义作为数据模型的权威来源 [2][3]。多绑定并不是 1.0 首次引入的功能,0.3.0 已定义 JSON-RPC、gRPC 和 HTTP+JSON/REST [12]。
| 绑定 | 调用与传输方式 |
|---|---|
| JSON-RPC 2.0 | 向 Agent Card 声明的 URL 发送 JSON-RPC 请求,流式使用 SSE |
| gRPC | 使用生成的客户端和 Protobuf 消息,流式使用服务端流 |
| HTTP+JSON/REST | 使用资源路径、HTTP 方法和 JSON,流式使用 SSE |
客户端根据 supportedInterfaces 的偏好顺序,选择双方支持的协议版本和绑定;实现不必同时支持三种绑定。接口可以共享 URL,也可以各用不同地址,不能单凭 URL 猜测绑定类型。
常用操作映射
1.0 中 JSON-RPC 与 gRPC 的方法名均使用 PascalCase [2]:
| 操作 | JSON-RPC / gRPC 方法 | REST 路径(相对服务基址) |
|---|---|---|
| 发送消息 | SendMessage |
POST /message:send |
| 流式消息 | SendStreamingMessage |
POST /message:stream |
| 获取任务 | GetTask |
GET /tasks/{id} |
| 列出任务 | ListTasks |
GET /tasks |
| 取消任务 | CancelTask |
POST /tasks/{id}:cancel |
| 订阅任务 | SubscribeToTask |
POST /tasks/{id}:subscribe |
| 创建推送配置 | CreateTaskPushNotificationConfig |
POST /tasks/{id}/pushNotificationConfigs |
| 获取单个推送配置 | GetTaskPushNotificationConfig |
GET /tasks/{id}/pushNotificationConfigs/{configId} |
| 列出推送配置 | ListTaskPushNotificationConfigs |
GET /tasks/{id}/pushNotificationConfigs |
| 删除推送配置 | DeleteTaskPushNotificationConfig |
DELETE /tasks/{id}/pushNotificationConfigs/{configId} |
| 获取扩展 Agent Card | GetExtendedAgentCard |
GET /extendedAgentCard |
JSON-RPC 的 SendMessage 是请求体里的 method,不是要求服务器暴露 /SendMessage 路径。REST 才按右侧的路径调用;如果服务基址包含 /billing 等前缀,需要保留该前缀。
从 0.3.0 迁移时重点检查什么
1.0 不是把版本号改掉就能兼容的升级。至少需要同时核对这些变化 [3]:
- 方法名由
message/send、tasks/get等改为SendMessage、GetTask; - Agent Card 用
supportedInterfaces,协议版本放进各接口的protocolVersion; - Message、Task 和 Part 不再使用
kind,发送响应通过result.task或result.message区分; - 文件 Part 改用扁平的
raw/url,流式状态事件删除final; - JSON 字段使用 camelCase,枚举保留完整名称,例如
TASK_STATE_INPUT_REQUIRED、ROLE_USER。
1.0 的 JSON-RPC 请求还应携带 A2A-Version: 1.0。协议兼容性按主、次版本识别,不靠 Agent 自身的 version,也不靠 URL 里的 v1;服务端不支持所请求版本时应返回版本不支持错误 [2]。
错误处理也不能只看 HTTP 状态码。A2A 定义了跨绑定的错误映射,但多个协议错误可能落到同一个 HTTP 400;客户端应保留并识别错误体中的协议语义,不能把所有 400 都当成同一种失败。
结论
A2A 的价值不是证明“Agent 不能当工具”,而是在独立系统之间提供一套可共同遵守的协作约定:用 Agent Card 描述能力,用 Message 沟通,用 Task 跟踪工作,用 Artifact 交付结果。
我的落地建议是先判断边界,再选交互方式:
- 同一应用内的协作:如果共享状态和本地编排已经够用,不必为了多 Agent 而引入远程协议。
- 跨团队、跨组织的 Agent 互联:先约定协议版本、发现方式、认证和数据权限,再接入一种双方支持的绑定。
- 需要长时跟进的工作:明确任务何时中断或结束,决定用轮询、流式还是 webhook,并验证断线、重复通知和取消失败的处理。
我仍然更关心“发现”环节的标准化:有了统一名片,不等于有了统一的搜索、信任和准入体系。对实际项目来说,先维护一份可信 Agent 目录,往往比一开始就追求开放式自动发现更容易落地。
准备实现时,可以从下面的官方概念指南进入,再用英文协议规范逐项核对请求、响应和能力声明。尤其不要把旧版 SDK 的方法名与 1.0 的 JSON 结构混在一起。
参考资料
以下链接为英文官方资料;latest 会持续更新,版本差异可对照固定的 1.0.0 规范与版本变更说明。
[1] What is A2A?
[2] Agent2Agent Protocol Specification:英文协议规范
[3] What’s New in A2A Protocol v1.0:版本变更
[4] A2A and MCP:详细比较
[5] MCP 2025-11-25 Key Changes
[9] Streaming & Asynchronous Operations:流式与异步操作
[10] Enterprise Features:企业级实现