03 · 传输与绑定
本章讲 A2A v1.0 最核心的架构决定:一份 proto 当唯一真相,投影到三种传输,而且必须语义等价。 这是“同一个 agent 能同时用 JSON-RPC、gRPC、REST 而行为一致”的根基。
1. 一个核心思想:proto 是规范,不是 gRPC 的实现细节
A2A v1.0 把 specification/a2a.proto 从“gRPC 专用文件”提升为所有绑定的通用、规范源(docs/whats-new-v1.md 主题 1)。也就是说:
┌───────────────────────────────┐
│ specification/a2a.proto │ ← 唯一真相(数据模型 + 11 个 RPC)
│ (lf.a2a.v1) │
└───────────────┬───────────────┘
│ 投影到三种传输,必须功能等价
┌────────────────────┼────────────────────┐
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌──────────────┐
│JSON-RPC │ │ gRPC │ │ HTTP+JSON │
│ 2.0/HTTP│ │ /HTTP2 │ │ REST │
└─────────┘ └─────────┘ └──────────────┘
好处是:数据结构只定义一次,JSON 表示交给 ProtoJSON 规则自动推导,三种传输天然一致。坏处是:proto 的字段存在性语义(optional/REQUIRED)会渗透到 JSON 层(见 §3、以及第 01 章的名片签名)。
“功能等价”是硬要求(docs/specification.md:1145-1152)。 一个 agent 若支持多种传输,所有传输必须:提供相同操作集合、对相同请求返回语义等价结果、一致地映射错误、支持相同的鉴权方案。
2. 11 个操作,一张方法映射表
协议的全部能力就是 service A2AService(specification/a2a.proto:19)里的 11 个 RPC。它们在三种传输里的名字/路径如下(docs/specification.md:1160-1174):
| 功能 | JSON-RPC 方法 | gRPC 方法 | REST 端点 |
|---|---|---|---|
| 发消息 | SendMessage | SendMessage | POST /message:send |
| 流式发消息 | SendStreamingMessage | SendStreamingMessage | POST /message:stream |
| 查任务 | GetTask | GetTask | GET /tasks/{id} |
| 列任务 | ListTasks | ListTasks | GET /tasks |
| 取消任务 | CancelTask | CancelTask | POST /tasks/{id}:cancel |
| 订阅任务 | SubscribeToTask | SubscribeToTask | POST /tasks/{id}:subscribe |
| 建 push 配置 | CreateTaskPushNotificationConfig | 同名 | POST /tasks/{id}/pushNotificationConfigs |
| 取 push 配置 | GetTaskPushNotificationConfig | 同名 | GET …/pushNotificationConfigs/{configId} |
| 列 push 配置 | ListTaskPushNotificationConfigs | 同名 | GET …/pushNotificationConfigs |
| 删 push 配置 | DeleteTaskPushNotificationConfig | 同名 | DELETE …/pushNotificationConfigs/{configId} |
| 取扩展名片 | GetExtendedAgentCard | 同名 | GET /extendedAgentCard |
proto 里这些 HTTP 路径其实是用 google.api.http 注解直接标在每个 rpc 上的(例如 SendMessage 的 post: "/message:send",specification/a2a.proto:21-30),REST 绑定即由此而来。注意每条都额外带一个 /{tenant}/… 变体——这是 v1.0 新增的多租户路由(见 §5)。
3. ProtoJSON:JSON 长什么样的规则
JSON 类绑定(JSON-RPC、REST,以及任何自定义 JSON 传输)的序列化必须遵循 ProtoJSON 规范,这条由 ADR-001 拍板(adrs/adr-001-protojson-serialization.md)。两条最常踩的规则:
字段名:proto 的 snake_case → JSON 的 camelCase(docs/specification.md:1202-1211):
| proto 字段 | JSON 字段 |
|---|---|
protocol_version | protocolVersion |
context_id | contextId |
default_input_modes | defaultInputModes |
push_notification_config | pushNotificationConfig |
枚举值:用 proto 里定义的字符串名,即 SCREAMING_SNAKE_CASE(docs/specification.md:1213-1220):
TASK_STATE_INPUT_REQUIRED→ JSON 值"TASK_STATE_INPUT_REQUIRED"ROLE_USER→ JSON 值"ROLE_USER"
⚠️ 这是 v1.0 的一个 breaking change:v0.3 的枚举是 kebab-case(如
input-required),v1.0 改成 SCREAMING_SNAKE_CASE 以贴合 ProtoJSON(docs/whats-new-v1.md主题 2)。
时间戳:统一 ISO 8601 UTC(docs/specification.md:1228-1255)。 proto 用 google.protobuf.Timestamp,JSON 里必须是 YYYY-MM-DDTHH:mm:ss.sssZ、只用 Z(不许其他时区偏移)、建议毫秒精度。
字段存在性(docs/specification.md:1257-1275)。 标 REQUIRED 的必须出现(required 数组至少一个元素);proto 的 optional 关键字用来区分“显式设过”和“没设”——这对默认值处理和名片签名规范化(第 01 章 §5)至关重要。实现应当忽略不认识的字段以便前向兼容。