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]:

  1. 简洁性:复用 HTTP、JSON-RPC、SSE 等现有标准;
  2. 企业就绪:认证、授权、安全、可观测性对齐标准 Web 实践;
  3. 异步优先:支持长时运行任务和人工介入;
  4. 模态无关:文本、文件、结构化数据都能交换;
  5. 不透明执行:协作不要求公开内部推理、内存和工具实现。

五条里我最在意的是“不透明执行”:调用方只需要理解对方声明的能力和交互约定,不需要知道它用了哪个模型、怎样规划任务。但黑盒不等于可信,认证、授权和数据保护仍要由两边的系统落实(详见第六节)。


二、核心概念:一次协作由什么组成

三个参与者

参与者 角色 说明
用户 需求发起方 人或自动化服务,提出目标
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_USERROLE_AGENT,分别表示客户端和服务端发送的消息。这里的 USER 不一定是直接操作界面的人,也可以是代表用户发起请求的另一个 Agent。

Part 每次必须且只能包含以下四个内容字段之一 [2][6]:

  • text:纯文本;
  • raw:文件的内联字节,在 JSON 中编码成 Base64;
  • url:文件内容的外部地址;
  • data:结构化 JSON 值,适合表单和机器可读参数。

Part 还可带 mediaTypefilenamemetadata。1.0 不再需要 TextPartFilePart 这样的嵌套结构或 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: 服务端关闭响应流

SendMessageSendStreamingMessage 是两种发送方式,不是必须先后调用的两个步骤。若已经拿到了任务 ID,只想订阅进度,应使用 SubscribeToTask,而不是再次发送业务消息。


三、Agent 发现:Agent Card 是数字名片

协作的前提是找到对方、看懂对方能干什么。A2A 用 Agent Card 标准化了 Agent 的”自描述”,但怎么拿到这张名片取决于部署环境。

Agent Card 里有什么

关键字段如下 [2]:

字段 说明
name / description / provider / version Agent 身份信息;version 是 Agent 自身的版本
supportedInterfaces 接口列表,每项声明 urlprotocolBindingprotocolVersion,可带 tenant;按偏好排序
capabilities streamingpushNotificationsextendedAgentCard 等可选能力,以及扩展声明
securitySchemes / securityRequirements 可用认证方案,以及访问服务需要满足的安全要求
defaultInputModes / defaultOutputModes 默认支持的输入、输出媒体类型
skills 技能的 ID、描述、示例、输入输出模态,以及可选的专属安全要求
signatures 可选的 Agent Card JWS 签名,供客户端验证完整性与来源

下面是一张声明 Bearer 认证的简化 Agent Card:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
{
"name": "货币兑换 Agent",
"description": "提供实时汇率查询与货币换算",
"supportedInterfaces": [
{
"url": "https://fx.example.com/a2a",
"protocolBinding": "JSONRPC",
"protocolVersion": "1.0"
}
],
"provider": { "organization": "Example FinTech", "url": "https://fx.example.com" },
"version": "1.2.0",
"capabilities": { "streaming": true, "pushNotifications": false },
"securitySchemes": {
"bearer": { "httpAuthSecurityScheme": { "scheme": "Bearer" } }
},
"securityRequirements": [
{ "schemes": { "bearer": { "list": [] } } }
],
"defaultInputModes": ["text/plain", "application/json"],
"defaultOutputModes": ["application/json"],
"skills": [
{
"id": "currency-conversion",
"name": "货币换算",
"description": "按实时汇率换算两种货币金额",
"tags": ["currency", "fx"],
"examples": ["100 美元等于多少人民币"]
}
]
}

这里的 1.2.0 是 Agent 版本,1.0 才是接口使用的协议版本。securityRequirements 引用上面命名为 bearer 的方案;Bearer 认证没有要列出的 OAuth scope,因此 list 为空。安全模型借鉴 OpenAPI,但不能直接照搬 OpenAPI 的 JSON 写法。

skills 同时服务于人和程序。协调者可以让 LLM 读取 descriptionexamples 选择 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-ControlETag 和条件请求,缓存过期后通过 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 --> [*]

COMPLETEDFAILEDCANCELEDREJECTED 是终止状态;INPUT_REQUIREDAUTH_REQUIRED 是中断状态,分别表示等待输入和等待授权。枚举还包含表示未知或未确定状态的 TASK_STATE_UNSPECIFIED。任务可以很快完成,客户端不一定观察到每个中间状态;CancelTask 也只是取消请求,不保证一定成功。

我更关注 INPUT_REQUIRED 的工程价值:客户端不用从一段自然语言里猜测“对方是不是在等我”。它可以据此暂停等待、展示问题,再通过 SendMessageSendStreamingMessage 向同一个 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]。

一个完整的细化示例

沿用官方“画帆船”的场景,看 contextIdreferenceTaskIds 如何配合 [7]。下面将图片改为 URL 引用,避免用省略的 Base64 字符串掩盖数据结构。

第一回合,客户端向绘图 Agent 的 JSON-RPC 端点发送以下消息。HTTP 请求使用 Content-Type: application/jsonA2A-Version: 1.0,并按 Agent Card 要求携带认证信息:

1
2
3
4
5
6
7
8
9
10
11
12
{
"jsonrpc": "2.0",
"id": "req-001",
"method": "SendMessage",
"params": {
"message": {
"messageId": "msg-user-001",
"role": "ROLE_USER",
"parts": [{ "text": "生成一张海上帆船的图片。" }]
}
}
}

假设 Agent 完成绘制,响应中的 result.task 包含任务与工件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
"jsonrpc": "2.0",
"id": "req-001",
"result": {
"task": {
"id": "task-boat-gen-123",
"contextId": "ctx-conversation-abc",
"status": { "state": "TASK_STATE_COMPLETED" },
"artifacts": [
{
"artifactId": "artifact-boat-v1-xyz",
"name": "sailboat_image.png",
"parts": [
{
"url": "https://images.example.com/sailboat-v1.png",
"filename": "sailboat_image.png",
"mediaType": "image/png"
}
]
}
]
}
}
}

第二回合,客户端要求“把船涂成红色”,沿用 contextId,引用旧任务,但不设置指向旧任务的 taskId

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"jsonrpc": "2.0",
"id": "req-002",
"method": "SendMessage",
"params": {
"message": {
"role": "ROLE_USER",
"messageId": "msg-user-002",
"contextId": "ctx-conversation-abc",
"referenceTaskIds": ["task-boat-gen-123"],
"parts": [{ "text": "把上次图片中的帆船涂成红色。" }]
}
}
}

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 响应,其 resultStreamResponse,包含以下字段之一 [2]:

  • task:Task 快照;
  • message:直接答复的 Message;
  • statusUpdate:TaskStatusUpdateEvent,可附带进度说明或补充信息请求;
  • artifactUpdate:TaskArtifactUpdateEvent,携带工件或工件分块。

有两条合法路径:服务端只返回一条 Message 后关闭流;或者先返回 Task,再发送状态和工件更新。后者通过 artifactIdappendlastChunk 组织工件分块,lastChunk 表示该工件的最后一块,不表示整个任务结束。

1.0 已移除 TaskStatusUpdateEvent.final,流的结束由连接关闭等绑定机制表达。任务进入终态时流应结束;遇到中断状态,要区分等待输入和带外授权,不能把一次断流当成任务完成。例如当前规范建议,在等待带外授权时维持活动流,以便授权完成后继续推送 [2]。

断线后,可用 SubscribeToTask 订阅仍未终止的任务,它首先返回当前 Task 快照。重新订阅不等于重放所有遗漏事件,尤其不能把临时状态消息当作可靠消息队列;需要完整结果时用 GetTask 核对。任务已经终止,则直接查询结果,不再尝试订阅。

上述 SSE 行为针对 JSON-RPC 和 HTTP+JSON 绑定;gRPC 使用自己的服务端流机制。

webhook 可以携带结果,但仍要考虑补拉

服务端需要声明 capabilities.pushNotifications: true。客户端用 TaskPushNotificationConfig 配置 webhook 的 url,还可提供校验用 tokenauthentication。配置可放进发送请求的 configuration.taskPushNotificationConfig,或对已有任务调用 CreateTaskPushNotificationConfig [2][9]。

推送的 HTTP body 直接使用 StreamResponse,可以带任务快照、消息、状态更新或工件更新,不是只能发一条“任务变了”的提醒。客户端需要完整、最新的 Task 时,再用 GetTask 补拉。

webhook 地址必须能被服务端访问。移动应用通常需要自己的后端接收通知,再转发给设备;不能因为客户端不保持连接,就假设手机本身能接收公网 HTTP 请求。

推送通知的安全与重复处理

webhook 让服务端主动请求客户端提供的 URL,需要同时考虑两端的安全 [9]:

  • 防 SSRF:发送方验证目标地址,结合可信域白名单、地址限制、域名所有权验证和出站网络控制,避免访问内部服务或成为攻击第三方的跳板。
  • 防冒充:接收方验证通知来源及任务 ID。JWT + JWKS 是一种方案,不是唯一要求;使用时应从预先信任的发行方获取密钥,验签并核对 issaud、有效期等声明。如果配置了 token,也要核验。
  • 防重放与重复执行:验证时间戳或一次性 ID,幂等处理通知。合法重试也可能造成重复投递,不能每收到一次通知就重新执行业务动作。

1.0 规范要求发送方至少尝试投递一次,但重试策略由实现决定,不能据此推导出通知一定送达或恰好送达一次。接收成功后返回 HTTP 2xx;对关键任务,客户端仍需保留查询和对账能力 [2]。


六、企业级设计:不重新发明轮子

A2A 的企业化设计复用现有的身份、网关和监控设施 [10]。这意味着可以沿用成熟的运维方法,不意味着接上协议就自动满足安全和合规要求。

传输认证与任务内授权是两件事

生产环境应使用加密传输:HTTP 绑定使用 HTTPS,gRPC 使用 TLS,并校验服务端证书。TLS 配置应遵循现代安全实践,当前英文规范推荐 TLS 1.3+ [2]。

客户端根据 Agent Card 的 securitySchemessecurityRequirements,通过 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
2
3
4
5
6
{
"url": "https://agents.example.com/a2a",
"protocolBinding": "JSONRPC",
"protocolVersion": "1.0",
"tenant": "billing"
}

请求仍发往该 URL,但 JSON-RPC 的 params 中需要带上 tenant

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"jsonrpc": "2.0",
"id": "req-billing-001",
"method": "SendMessage",
"params": {
"tenant": "billing",
"message": {
"messageId": "msg-billing-001",
"role": "ROLE_USER",
"parts": [{ "text": "查询我的账单。" }]
}
}
}

所选接口声明了 tenant,客户端就必须在发往该接口的每个请求中原样回传;接口没声明,就省略该字段。它是不透明的路由标识,可以代表 Agent、工作空间或组织,客户端不应自行解释或编造 [2][11]。

路由与授权必须分开:填上 "tenant": "billing" 不能证明调用者有账单访问权限,网关和后端仍需检查认证身份。共享域名下的各个 Agent 也应分别提供 Agent Card,不能假设一张域名级名片就描述了所有后端。

可观测性

可以通过 W3C Trace Context 头传播追踪上下文,并接入 OpenTelemetry,但跨 Agent 链路需要客户端、服务端和中间网关共同配合 [10]:

  • 日志关联 taskIdcontextId、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/sendtasks/get 等改为 SendMessageGetTask
  • Agent Card 用 supportedInterfaces,协议版本放进各接口的 protocolVersion
  • Message、Task 和 Part 不再使用 kind,发送响应通过 result.taskresult.message 区分;
  • 文件 Part 改用扁平的 raw / url,流式状态事件删除 final
  • JSON 字段使用 camelCase,枚举保留完整名称,例如 TASK_STATE_INPUT_REQUIREDROLE_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 交付结果。

我的落地建议是先判断边界,再选交互方式:

  1. 同一应用内的协作:如果共享状态和本地编排已经够用,不必为了多 Agent 而引入远程协议。
  2. 跨团队、跨组织的 Agent 互联:先约定协议版本、发现方式、认证和数据权限,再接入一种双方支持的绑定。
  3. 需要长时跟进的工作:明确任务何时中断或结束,决定用轮询、流式还是 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

[6] Core Concepts:核心概念

[7] Life of a Task:任务生命周期

[8] Agent Discovery:Agent 发现

[9] Streaming & Asynchronous Operations:流式与异步操作

[10] Enterprise Features:企业级实现

[11] Multi-Tenancy and Multi-Agent Routing:多租户与多 Agent 路由

[12] A2A 0.3.0 Specification:历史版本规范