本文将深入协议规范的技术细节,逐层拆解其三层架构设计、完整数据模型、操作语义规则以及三种协议绑定的实现差异。
一、规范的三层架构
A2A 协议规范采用了清晰的三层分离架构,这一设计决策使得核心语义与具体传输协议解耦,为未来扩展新的协议绑定提供了坚实基础。
┌─────────────────────────────────────────────────┐
│ Layer 1: 规范数据模型(Canonical Data Model) │
│ Protocol Buffers 定义的核心数据结构 │
│ Task / Message / Part / Artifact / AgentCard │
├─────────────────────────────────────────────────┤
│ Layer 2: 抽象操作(Abstract Operations) │
│ 与协议无关的操作定义 │
│ SendMessage / GetTask / CancelTask / ... │
├─────────────────────────────────────────────────┤
│ Layer 3: 协议绑定(Protocol Bindings) │
│ 具体协议的映射实现 │
│ JSON-RPC / gRPC / HTTP+JSON/REST │
└─────────────────────────────────────────────────┘
这种分层确保了:
- 核心语义在所有协议绑定中保持一致
- 新增协议绑定无需修改底层数据模型
- 开发者可以独立于绑定细节来理解 A2A 操作
- 通过共享的规范数据模型维护互操作性
规范性内容的权威来源 :specification/a2a.proto 文件是所有协议数据对象和请求/响应消息的唯一权威规范定义。SDK 语言绑定、JSON Schema 及其他派生形式必须从 proto 文件(直接或通过代码生成)重新生成,而非手动编辑。
二、Layer 1:规范数据模型详解
A2A 协议使用 Protocol Buffers(proto3)定义其规范数据模型。所有协议绑定必须提供这些数据结构的功能等价表示。以下逐一剖析每个核心对象的字段设计与语义。
2.1 Task:有状态的工作单元
protobuf
message Task {
string id = 1; // [REQUIRED] 服务端生成的唯一标识(如 UUID)
string context_id = 2; // 逻辑会话标识,关联多个 Task 和 Message
TaskStatus status = 3; // [REQUIRED] 当前状态
repeated Artifact artifacts = 4; // 产出物列表
repeated Message history = 5; // 交互历史
google.protobuf.Struct metadata = 6; // 自定义元数据
}
设计要点:
id由服务端生成,客户端不可指定。这消除了 ID 冲突的可能性,也简化了服务端的实现逻辑。context_id是跨 Task 的逻辑分组标识。同一context_id下的所有 Task 和 Message 构成一个对话会话。Agent 内部利用它管理 LLM 上下文。history字段存储 Task 执行过程中的交互消息。但并非所有消息都保证被持久化------瞬态信息性消息可能不会被存储。Agent 负责决定哪些消息被持久化到 Task 历史中。metadata使用google.protobuf.Struct(即任意 JSON 对象),为扩展机制提供了灵活的数据承载点。
2.2 TaskState:状态机枚举
protobuf
enum TaskState {
TASK_STATE_UNSPECIFIED = 0; // 未知/不确定状态
TASK_STATE_SUBMITTED = 1; // 已提交并确认
TASK_STATE_WORKING = 2; // 正在处理
TASK_STATE_COMPLETED = 3; // 成功完成(终态)
TASK_STATE_FAILED = 4; // 失败(终态)
TASK_STATE_CANCELED = 5; // 已取消(终态)
TASK_STATE_INPUT_REQUIRED = 6; // 需要用户输入(中断态)
TASK_STATE_REJECTED = 7; // Agent 拒绝执行(终态)
TASK_STATE_AUTH_REQUIRED = 8; // 需要认证(中断态)
}
状态分为三类:
| 类别 | 状态 | 语义 |
|---|---|---|
| 进行中 | SUBMITTED, WORKING | Task 正在被处理 |
| 中断态 | INPUT_REQUIRED, AUTH_REQUIRED | 需要客户端介入后才能继续 |
| 终态 | COMPLETED, FAILED, CANCELED, REJECTED | Task 生命周期结束,不可逆 |
REJECTED 状态值得特别关注:Agent 可以在任务创建初期或处理过程中的任何时刻决定拒绝执行该任务。这赋予了 Agent 自主判断的能力------如果 Agent 判定自己无法或不愿处理某个请求,可以主动拒绝。
AUTH_REQUIRED 是 v1.0 新增的状态,用于支持任务内二次认证场景。当 Agent 在执行任务过程中需要访问某个外部系统但缺少凭证时,可以将任务转入此状态,将获取凭证的责任委托给客户端。
2.3 TaskStatus:状态容器
protobuf
message TaskStatus {
TaskState state = 1; // [REQUIRED] 当前状态
Message message = 2; // 与状态关联的消息
google.protobuf.Timestamp timestamp = 3; // 状态记录时间(ISO 8601)
}
message 字段允许 Agent 在状态变更时附带说明信息。例如,当状态转为 INPUT_REQUIRED 时,Agent 可以通过此字段告知客户端需要提供什么信息。
2.4 Message:通信的基本单位
protobuf
message Message {
string message_id = 1; // [REQUIRED] 消息创建者生成的唯一标识
string context_id = 2; // 可选,关联的上下文 ID
string task_id = 3; // 可选,关联的任务 ID
Role role = 4; // [REQUIRED] 发送者角色(USER / AGENT)
repeated Part parts = 5; // [REQUIRED] 消息内容
google.protobuf.Struct metadata = 6; // 可选元数据
repeated string extensions = 7; // 关联的扩展 URI 列表
repeated string reference_task_ids = 8; // 引用的任务 ID 列表
}
Message 的 context_id 和 task_id 遵循严格的关联规则:
- 服务端消息:
context_id必须提供;task_id仅在创建了 Task 时提供 - 客户端消息:两者均可选,但若同时提供,必须匹配(
context_id必须是该 Task 所属的上下文) - 若客户端仅提供
task_id,服务端将从中推断context_id - 若客户端提供的
context_id与task_id不匹配,服务端必须拒绝该消息
reference_task_ids 字段用于任务细化场景------客户端可以引用之前的任务,提示 Agent 当前请求是对先前结果的修改或后续操作。
Message 与 Artifact 的职责分离:规范明确指出,Message 不应用于传递任务输出,结果应通过与 Task 关联的 Artifact 返回。这一分离确保了通信(Message)与数据产出(Artifact)的清晰边界。
2.5 Part:模态无关的内容容器
protobuf
message Part {
oneof content {
string text = 1; // 文本内容
bytes raw = 2; // 原始字节(JSON 中为 Base64)
string url = 3; // 外部文件 URL
google.protobuf.Value data = 4; // 结构化 JSON 数据
}
google.protobuf.Struct metadata = 5; // 可选元数据
string filename = 6; // 可选文件名
string media_type = 7; // MIME 类型
}
oneof content 的设计确保每个 Part 恰好包含一种内容类型。四种内容类型覆盖了 Agent 间通信的所有常见需求:
| 内容类型 | 典型用途 | 示例 |
|---|---|---|
text |
自然语言对话、代码片段 | "今天天气如何?" |
raw |
内联传输二进制文件 | 图片的 Base64 编码 |
url |
引用外部大文件 | https://storage.example.com/report.pdf |
data |
机器可读的结构化数据 | {"latitude": 37.77, "longitude": -122.42} |
media_type 字段对所有内容类型均可用,这使得接收方能够正确解析和渲染内容,而无需猜测其格式。
2.6 Artifact:任务的正式产出物
protobuf
message Artifact {
string artifact_id = 1; // [REQUIRED] 任务内唯一标识
string name = 2; // 人类可读名称
string description = 3; // 可选描述
repeated Part parts = 4; // [REQUIRED] 内容(至少一个 Part)
google.protobuf.Struct metadata = 5; // 可选元数据
repeated string extensions = 6; // 关联的扩展 URI 列表
}
Artifact 与 Message 的关键区别在于:Artifact 是 Task 的正式交付物,代表 Agent 工作的具体成果。在流式传输场景中,Artifact 可以通过 TaskArtifactUpdateEvent 增量传输:
protobuf
message TaskArtifactUpdateEvent {
string task_id = 1; // [REQUIRED]
string context_id = 2; // [REQUIRED]
Artifact artifact = 3; // [REQUIRED]
bool append = 4; // 是否追加到同 ID 的已有 Artifact
bool last_chunk = 5; // 是否为最后一个分块
google.protobuf.Struct metadata = 6;
}
append 和 last_chunk 字段的组合使得大型产出物(如长文档、大图片)可以分块流式传输,客户端根据 artifact_id 进行重组。
2.7 AgentCard:Agent 的自描述清单
AgentCard 是 A2A 协议中信息密度最高的数据结构,完整定义如下:
protobuf
message AgentCard {
string name = 1; // [REQUIRED] Agent 名称
string description = 2; // [REQUIRED] Agent 描述
repeated AgentInterface supported_interfaces = 3; // [REQUIRED] 支持的接口列表(有序,首选优先)
AgentProvider provider = 4; // 服务提供方
string version = 5; // [REQUIRED] Agent 版本
optional string documentation_url = 6; // 文档 URL
AgentCapabilities capabilities = 7; // [REQUIRED] 能力声明
map<string, SecurityScheme> security_schemes = 8; // 安全方案定义
repeated SecurityRequirement security_requirements = 9; // 安全需求
repeated string default_input_modes = 10; // [REQUIRED] 默认输入媒体类型
repeated string default_output_modes = 11; // [REQUIRED] 默认输出媒体类型
repeated AgentSkill skills = 12; // [REQUIRED] 技能列表
repeated AgentCardSignature signatures = 13; // JWS 签名
optional string icon_url = 14; // 图标 URL
}
几个值得深入理解的子结构:
AgentInterface :v1.0 的重要变化------从单一 url 字段升级为 supportedInterfaces 列表,允许同一 Agent 通过多种协议绑定暴露服务:
protobuf
message AgentInterface {
string url = 1; // [REQUIRED] 接口 URL
string protocol_binding = 2; // [REQUIRED] 协议绑定类型:JSONRPC / GRPC / HTTP+JSON
string tenant = 3; // 租户 ID
string protocol_version = 4; // [REQUIRED] A2A 协议版本(如 "1.0")
}
列表有序,第一个条目为首选接口。客户端应按顺序选择自己支持的第一个接口。
AgentSkill:描述 Agent 的具体能力:
protobuf
message AgentSkill {
string id = 1; // [REQUIRED] 技能唯一标识
string name = 2; // [REQUIRED] 技能名称
string description = 3; // [REQUIRED] 技能描述
repeated string tags = 4; // [REQUIRED] 关键词标签
repeated string examples = 5; // 示例提示词
repeated string input_modes = 6; // 覆盖 Agent 默认的输入媒体类型
repeated string output_modes = 7; // 覆盖 Agent 默认的输出媒体类型
repeated SecurityRequirement security_requirements = 8; // 技能级别的安全需求
}
技能级别的 input_modes/output_modes 可以覆盖 Agent 级别的默认值,实现细粒度的媒体类型协商。security_requirements 支持按技能粒度的访问控制------例如,特定的 OAuth scope 可以授权客户端访问某些技能但不能访问其他技能。
AgentCapabilities:声明 Agent 支持的可选能力:
protobuf
message AgentCapabilities {
optional bool streaming = 1; // 是否支持流式响应
optional bool push_notifications = 2; // 是否支持推送通知
repeated AgentExtension extensions = 3; // 支持的扩展列表
optional bool extended_agent_card = 4; // 是否支持认证后的扩展 Agent Card
}
这些能力声明直接影响客户端可以使用的操作集合。如果客户端尝试使用 Agent 未声明支持的能力,服务端必须返回相应的错误。
2.8 安全对象模型
A2A 的安全方案定义直接对齐 OpenAPI 3.2 Security Scheme Object,支持五种认证方式:
protobuf
message SecurityScheme {
oneof scheme {
APIKeySecurityScheme api_key_security_scheme = 1; // API Key
HTTPAuthSecurityScheme http_auth_security_scheme = 2; // HTTP 认证(Basic/Bearer)
OAuth2SecurityScheme oauth2_security_scheme = 3; // OAuth 2.0
OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4; // OIDC
MutualTlsSecurityScheme mtls_security_scheme = 5; // 双向 TLS
}
}
OAuth 2.0 支持的流程类型:
| 流程 | 状态 | 适用场景 |
|---|---|---|
| Authorization Code | 推荐 | Web 应用、需要用户授权的场景 |
| Client Credentials | 推荐 | 服务间通信(M2M) |
| Device Code | 推荐 | IoT 设备、CLI 工具 |
| Implicit | 已废弃 | 应使用 Authorization Code + PKCE 替代 |
| Password | 已废弃 | 应使用 Authorization Code + PKCE 或 Device Code 替代 |
Authorization Code 流程新增了 pkce_required 字段,推荐所有客户端使用 PKCE(RFC 7636)以增强安全性。
三、Layer 2:抽象操作详解
3.1 操作总览
A2A 协议定义了 11 个核心操作,可分为四组:
| 操作组 | 操作 | 功能 |
|---|---|---|
| 消息交互 | SendMessage | 发送消息,返回 Task 或 Message |
| SendStreamingMessage | 流式发送消息,实时接收更新 | |
| 任务管理 | GetTask | 获取任务最新状态 |
| ListTasks | 按条件列出任务(支持分页) | |
| CancelTask | 取消进行中的任务 | |
| SubscribeToTask | 订阅任务更新(SSE 重连) | |
| 推送通知 | CreateTaskPushNotificationConfig | 创建推送通知配置 |
| GetTaskPushNotificationConfig | 获取推送通知配置 | |
| ListTaskPushNotificationConfigs | 列出推送通知配置 | |
| DeleteTaskPushNotificationConfig | 删除推送通知配置 | |
| Agent 发现 | GetExtendedAgentCard | 获取认证后的扩展 Agent Card |
3.2 SendMessage:核心交互操作
SendMessage 是 A2A 协议中最重要的操作。其请求结构为:
protobuf
message SendMessageRequest {
string tenant = 1; // 可选,租户 ID
Message message = 2; // [REQUIRED] 要发送的消息
SendMessageConfiguration configuration = 3; // 请求配置
google.protobuf.Struct metadata = 4; // 附加上下文
}
SendMessageConfiguration 提供了精细的行为控制:
protobuf
message SendMessageConfiguration {
repeated string accepted_output_modes = 1; // 客户端可接受的输出媒体类型
TaskPushNotificationConfig task_push_notification_config = 2; // 推送通知配置
optional int32 history_length = 3; // 返回的历史消息数量上限
bool return_immediately = 4; // 是否立即返回(非阻塞模式)
}
执行模式是一个关键的设计决策:
- 阻塞模式 (
return_immediately: false,默认):操作等待 Task 到达终态或中断态后才返回。适用于简单的同步交互。 - 非阻塞模式 (
return_immediately: true):操作在创建 Task 后立即返回,即使处理仍在进行中。客户端需要通过轮询、订阅或推送通知获取后续更新。
accepted_output_modes 允许客户端声明自己能处理的媒体类型,Agent 应据此调整输出格式。这实现了客户端与 Agent 之间的内容协商。
history_length 的语义在所有操作中保持一致:
- 未设置:无限制,服务端返回默认数量的历史
- 0:不返回历史,
history字段应被省略 - > 0:返回最多指定数量的最近消息
3.3 SendStreamingMessage:流式交互
SendStreamingMessage 使用与 SendMessage 相同的请求结构,但返回的是一个事件流。流的模式有两种:
模式一:纯 Message 流
[Message] → 流关闭
Agent 返回一个 Message 对象后立即关闭流。无任务跟踪。
模式二:Task 生命周期流
[Task] → [TaskStatusUpdateEvent]* → [TaskArtifactUpdateEvent]* → 流关闭(Task 到达终态)
流以 Task 对象开始,随后是零个或多个状态更新和产出物更新事件,当 Task 到达终态时流关闭。
事件顺序保证:所有实现必须按事件生成的顺序进行传递,不得在传输过程中重新排序。
多流并发:Agent 可以为同一 Task 同时服务多个流。所有活跃流必须接收相同的事件且顺序一致。关闭一个流不影响其他活跃流。
3.4 ListTasks:任务列表查询
v1.0 新增的操作,支持按条件过滤和分页查询任务:
protobuf
message ListTasksRequest {
string tenant = 1;
string context_id = 2; // 按上下文 ID 过滤
TaskState status = 3; // 按状态过滤
optional int32 page_size = 4; // 页大小(1-100,默认 50)
string page_token = 5; // 分页游标
// history_length, include_artifacts 等
}
设计要点:
- 采用游标分页(cursor-based pagination)而非偏移分页,避免深分页性能问题
- 结果按状态时间戳降序排列(最近更新的任务优先)
nextPageToken为空字符串表示没有更多结果- 默认不包含 Artifact(
includeArtifacts默认为 false),减少响应体积
3.5 多轮交互语义
A2A 协议对多轮交互定义了严格的语义规则:
contextId 的生成与管理:
- Agent 可以在处理不含
contextId的消息时生成新的contextId - 如果 Agent 生成了新的
contextId,必须在响应中包含 - Agent 可以接受并保留客户端提供的
contextId - 如果 Agent 无法接受客户端提供的
contextId,必须拒绝请求并返回错误,不得为响应生成新的contextId - 客户端不应向服务端提供自行生成的
contextId,除非了解服务端将如何处理
taskId 的生成与管理:
- Task ID 由服务端在创建新任务时生成
- 客户端在消息中包含
taskId时,必须引用已存在的任务 - 不支持客户端指定
taskId来创建新任务
INPUT_REQUIRED 交互模式:
Client → SendMessage("预订航班")
Agent → Task{state: INPUT_REQUIRED, message: "请提供出发地和目的地"}
Client → SendMessage(taskId: "xxx", "从旧金山到纽约")
Agent → Task{state: COMPLETED, artifacts: [...]}
AUTH_REQUIRED 交互模式(v1.0 新增):
Client → SendMessage("查询我的银行余额")
Agent → Task{state: AUTH_REQUIRED, message: {metadata: {authUrl: "...", scopes: [...]}}}
Client → (带外完成 OAuth 流程,获取凭证)
Client → SendMessage(taskId: "xxx", metadata: {credentials: "..."})
Agent → Task{state: COMPLETED, artifacts: [...]}
3.6 操作语义规则
幂等性:
- Get 操作天然幂等
- SendMessage 可以是幂等的------Agent 可利用
messageId检测重复消息 - CancelTask 是幂等的------多次取消请求产生相同效果
能力验证:客户端尝试使用 Agent 未声明支持的能力时,服务端必须返回相应错误:
| 能力 | 未声明时的错误 |
|---|---|
| streaming | UnsupportedOperationError |
| pushNotifications | PushNotificationNotSupportedError |
| extendedAgentCard | UnsupportedOperationError 或 ExtendedAgentCardNotConfiguredError |
3.7 错误处理体系
A2A 定义了 9 种特定错误类型:
| 错误类型 | 语义 |
|---|---|
TaskNotFoundError |
任务 ID 不存在或不可访问 |
TaskNotCancelableError |
任务不在可取消状态 |
PushNotificationNotSupportedError |
Agent 不支持推送通知 |
UnsupportedOperationError |
操作不被支持 |
ContentTypeNotSupportedError |
媒体类型不被支持 |
InvalidAgentResponseError |
Agent 返回了不符合规范的响应 |
ExtendedAgentCardNotConfiguredError |
扩展 Agent Card 未配置 |
ExtensionSupportRequiredError |
必需的扩展未被客户端声明 |
VersionNotSupportedError |
协议版本不被支持 |
所有错误响应必须包含:错误码(机器可读)、错误消息(人类可读)、可选的错误详情(受影响的字段、上下文信息、解决建议)。
安全性考量:服务端不得区分"资源不存在"和"无权访问",以防止信息泄露。
3.8 服务参数(Service Parameters)
v1.0 引入了服务参数机制,用于传递横切关注点的上下文信息:
| 参数名 | 用途 | 示例值 |
|---|---|---|
A2A-Extensions |
客户端希望激活的扩展 URI 列表 | https://example.com/ext/v1,https://std.org/ext/v1 |
A2A-Version |
客户端使用的协议版本 | 1.0 |
所有 A2A 定义的服务参数以 a2a- 为前缀。传输机制由具体的协议绑定定义(HTTP Header、gRPC Metadata 等)。
3.9 版本协商
- 客户端必须在每个请求中发送
A2A-Version头(空值被解释为 0.3 版本) - 服务端必须按请求的版本语义处理请求
- 版本号使用
Major.Minor格式,Patch 版本不影响协议兼容性 - 如果版本不被支持,服务端返回
VersionNotSupportedError - Agent 可以在同一或不同 URL 下为同一传输协议暴露多个版本的接口
四、Layer 3:三种协议绑定
A2A 协议定义了三种官方协议绑定,每种绑定将抽象操作映射到具体的传输协议。
4.1 方法映射总览
| 操作 | JSON-RPC 方法 | gRPC RPC | 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 |
| 创建推送配置 | CreateTaskPushNotificationConfig |
CreateTaskPushNotificationConfig |
POST /tasks/{id}/pushNotificationConfigs |
| 获取推送配置 | GetTaskPushNotificationConfig |
GetTaskPushNotificationConfig |
GET /tasks/{id}/pushNotificationConfigs/{configId} |
| 列出推送配置 | ListTaskPushNotificationConfigs |
ListTaskPushNotificationConfigs |
GET /tasks/{id}/pushNotificationConfigs |
| 删除推送配置 | DeleteTaskPushNotificationConfig |
DeleteTaskPushNotificationConfig |
DELETE /tasks/{id}/pushNotificationConfigs/{configId} |
| 获取扩展 Card | GetExtendedAgentCard |
GetExtendedAgentCard |
GET /extendedAgentCard |
4.2 错误码映射
| A2A 错误类型 | JSON-RPC Code | gRPC Status | HTTP Status |
|---|---|---|---|
| TaskNotFoundError | -32001 | NOT_FOUND | 404 |
| TaskNotCancelableError | -32002 | FAILED_PRECONDITION | 409 |
| PushNotificationNotSupportedError | -32003 | UNIMPLEMENTED | 400 |
| UnsupportedOperationError | -32004 | UNIMPLEMENTED | 400 |
| ContentTypeNotSupportedError | -32005 | INVALID_ARGUMENT | 415 |
| InvalidAgentResponseError | -32006 | INTERNAL | 502 |
| ExtendedAgentCardNotConfiguredError | -32007 | FAILED_PRECONDITION | 400 |
| ExtensionSupportRequiredError | -32008 | FAILED_PRECONDITION | 400 |
| VersionNotSupportedError | -32009 | UNIMPLEMENTED | 400 |
4.3 JSON-RPC 绑定
JSON-RPC 绑定是最简单的 HTTP 接口实现,基于 JSON-RPC 2.0 标准。
协议要求:
- 协议:JSON-RPC 2.0 over HTTP(S)
- Content-Type:
application/json - 流式传输:Server-Sent Events(
text/event-stream)
请求结构:
json
{
"jsonrpc": "2.0",
"id": "req-001",
"method": "SendMessage",
"params": {
"message": {
"role": "ROLE_USER",
"parts": [{"text": "你好"}],
"messageId": "msg-uuid"
}
}
}
响应结构(成功):
json
{
"jsonrpc": "2.0",
"id": "req-001",
"result": {
"task": {
"id": "task-uuid",
"contextId": "ctx-uuid",
"status": {"state": "TASK_STATE_COMPLETED"},
"artifacts": [...]
}
}
}
响应结构(错误):
json
{
"jsonrpc": "2.0",
"id": "req-001",
"error": {
"code": -32001,
"message": "Task not found",
"data": {"taskId": "task-uuid"}
}
}
SSE 流式响应:
http
HTTP/1.1 200 OK
Content-Type: text/event-stream
data: {"jsonrpc":"2.0","id":"req-001","result":{"task":{"id":"task-uuid","status":{"state":"TASK_STATE_WORKING"}}}}
data: {"jsonrpc":"2.0","id":"req-001","result":{"artifactUpdate":{"taskId":"task-uuid","artifact":{"parts":[{"text":"部分内容..."}]},"append":true}}}
data: {"jsonrpc":"2.0","id":"req-001","result":{"statusUpdate":{"taskId":"task-uuid","status":{"state":"TASK_STATE_COMPLETED"}}}}
服务参数通过 HTTP Header 传输。
4.4 gRPC 绑定
gRPC 绑定提供高性能、强类型的接口,基于 Protocol Buffers over HTTP/2。
协议要求:
- 协议:gRPC over HTTP/2
- 序列化:Protocol Buffers
- 流式传输:gRPC 服务端流(server-side streaming)
核心优势:
- 原生支持 Protocol Buffers,无需 JSON 序列化/反序列化
- HTTP/2 多路复用,减少连接开销
- 强类型接口,编译时类型检查
- 原生双向流支持
服务定义 直接使用 a2a.proto 中的 A2AService:
protobuf
service A2AService {
rpc SendMessage(SendMessageRequest) returns (SendMessageResponse);
rpc SendStreamingMessage(SendMessageRequest) returns (stream StreamResponse);
rpc GetTask(GetTaskRequest) returns (Task);
rpc ListTasks(ListTasksRequest) returns (ListTasksResponse);
rpc CancelTask(CancelTaskRequest) returns (Task);
rpc SubscribeToTask(SubscribeToTaskRequest) returns (stream StreamResponse);
// ... 推送通知和扩展 Card 操作
}
gRPC 绑定利用 google.api.http 注解实现 gRPC 到 HTTP 的自动映射,遵循 Google API 设计指南(AIP)。
服务参数通过 gRPC Metadata 传输。
4.5 HTTP+JSON/REST 绑定
REST 绑定提供标准的 RESTful HTTP 接口,使用 JSON 作为载荷格式。
协议要求:
- 协议:HTTP(S)
- Content-Type:
application/a2a+json(A2A 专用媒体类型) - 流式传输:Server-Sent Events
与 JSON-RPC 绑定的关键区别:
| 维度 | JSON-RPC | HTTP+JSON/REST |
|---|---|---|
| 载荷格式 | JSON-RPC 2.0 信封 | 纯 JSON(无信封) |
| Content-Type | application/json |
application/a2a+json |
| 方法路由 | method 字段 |
HTTP 方法 + URL 路径 |
| 错误格式 | JSON-RPC error 对象 | RFC 9457 Problem Details |
| 请求标识 | JSON-RPC id 字段 |
HTTP 请求本身 |
REST 风格的请求示例:
http
POST /message:send HTTP/1.1
Host: agent.example.com
Content-Type: application/a2a+json
Authorization: Bearer token
A2A-Version: 1.0
{
"message": {
"role": "ROLE_USER",
"parts": [{"text": "今天天气如何?"}],
"messageId": "msg-uuid"
}
}
REST 风格的响应:
http
HTTP/1.1 200 OK
Content-Type: application/a2a+json
{
"task": {
"id": "task-uuid",
"contextId": "ctx-uuid",
"status": {"state": "TASK_STATE_COMPLETED"},
"artifacts": [{
"artifactId": "artifact-uuid",
"name": "Weather Report",
"parts": [{"text": "今天晴,最高温 24°C"}]
}]
}
}
REST 风格的错误响应(遵循 RFC 9457 Problem Details):
http
HTTP/1.1 404 Not Found
Content-Type: application/problem+json
{
"type": "https://a2a-protocol.org/errors/task-not-found",
"title": "Task Not Found",
"status": 404,
"detail": "The specified task ID does not correspond to an existing task"
}
4.6 三种绑定的选型建议
| 场景 | 推荐绑定 | 理由 |
|---|---|---|
| 快速原型开发 | JSON-RPC | 最简单,工具链成熟 |
| 高性能微服务 | gRPC | 二进制序列化,HTTP/2 多路复用 |
| 公开 API / Web 集成 | HTTP+JSON/REST | RESTful 风格,浏览器友好 |
| 多语言生态 | gRPC | proto 文件可生成多语言客户端 |
| 已有 JSON-RPC 基础设施 | JSON-RPC | 无缝集成 |
五、JSON 序列化约定
A2A 协议对 JSON 序列化定义了明确的约定,这些细节对于正确实现协议至关重要。
5.1 字段命名:camelCase
所有 JSON 序列化必须使用 camelCase 命名,而非 Protocol Buffers 中的 snake_case:
proto: protocol_version → JSON: protocolVersion
proto: context_id → JSON: contextId
proto: default_input_modes → JSON: defaultInputModes
5.2 枚举值:原始字符串
枚举值按 ProtoJSON 规范序列化为其在 proto 定义中的原始字符串名称(SCREAMING_SNAKE_CASE):
proto: TASK_STATE_INPUT_REQUIRED → JSON: "TASK_STATE_INPUT_REQUIRED"
proto: ROLE_USER → JSON: "ROLE_USER"
5.3 时间戳:ISO 8601 UTC
所有时间戳字段使用 google.protobuf.Timestamp,JSON 序列化为 ISO 8601 格式的 UTC 时间字符串:
json
{
"timestamp": "2025-10-28T10:30:00.000Z"
}
要求:
- 时区必须为 UTC(以 'Z' 后缀标识)
- 推荐使用毫秒精度
- 不得包含 'Z' 以外的时区偏移
5.4 字段存在性
optional 关键字用于区分字段被显式设置与被省略。这一区分在两个场景中至关重要:
- 显式默认值:某些字段定义了与 Protocol Buffers 隐式默认值不同的默认值
- Agent Card 规范化 :创建 Agent Card 的加密签名时,需要生成规范化的 JSON 表示。
optional关键字使实现能够区分显式设置的字段(应包含在规范化形式中)和被省略的字段(应从规范化中排除)
前向兼容性:实现应忽略消息中的未识别字段,以支持协议的渐进演化。
六、Agent Card 签名机制
A2A 协议支持使用 JSON Web Signature(JWS,RFC 7515)对 Agent Card 进行数字签名,以确保真实性和完整性。
6.1 规范化要求
签名前,Agent Card 内容必须使用 JSON Canonicalization Scheme(JCS,RFC 8785)进行规范化。这确保了不同 JSON 实现之间签名生成和验证的一致性。
规范化步骤:
- 从 Agent Card 中移除
signatures字段 - 按 JCS 规范对剩余 JSON 进行规范化
- 对规范化后的 JSON 进行签名
6.2 签名格式
protobuf
message AgentCardSignature {
string protected = 1; // [REQUIRED] Base64url 编码的 JWS 受保护头
string signature = 2; // [REQUIRED] Base64url 编码的签名
google.protobuf.Struct header = 3; // 未受保护的 JWS 头
}
6.3 验证流程
客户端验证 Agent Card 签名时必须:
- 从 Agent Card 中提取
signatures数组 - 移除
signatures字段后对 Agent Card 进行 JCS 规范化 - 对每个签名,解码
protected头以获取算法和密钥 ID - 获取对应的公钥
- 使用公钥验证签名
支持多个签名以实现密钥轮换。
七、安全考量
7.1 数据访问与授权范围
- 所有操作必须实施适当的授权范围,确保客户端只能访问被授权的任务
- ListTasks 操作必须仅返回对已认证客户端可见的任务
- 服务端不得向未授权的客户端泄露任务的存在
7.2 任务内授权(AUTH_REQUIRED)
当 Agent 在执行任务过程中需要额外授权时:
Agent 职责:
- 将 Task 状态设置为
TASK_STATE_AUTH_REQUIRED - 在状态消息的
metadata中提供授权所需的信息(如授权 URL、所需 scope) - 支持在
AUTH_REQUIRED状态下接收客户端消息(用于协商或拒绝授权请求)
客户端职责:
- 解析授权请求中的元数据
- 通过带外安全通道完成授权流程
- 将凭证通过安全通道提供给 Agent
- 或者通过轮询/订阅等待 Task 状态变更
安全要求:
- 凭证应通过带外安全通道(如 HTTPS)传输
- 包含敏感信息的凭证应仅对发起请求的 Agent 可读(如通过加密)
7.3 扩展 Agent Card 的访问控制
- 客户端必须使用公开 Agent Card 中声明的认证方案进行认证
- 服务端可以根据客户端的认证级别返回不同的详细信息
- 客户端获取扩展 Card 后,应在认证会话期间用其替换缓存的公开 Card
- 扩展 Card 不应包含可能被利用的敏感信息(如内部服务 URL、未脱敏的凭证)
7.4 推送通知安全
推送通知涉及双向安全要求:
服务端(发送方):
- 验证 Webhook URL 的合法性,防止 SSRF 攻击
- 按配置的认证方案向 Webhook 进行身份认证
客户端(接收方):
- 验证推送来源的真实性(JWT 签名验证等)
- 防范重放攻击(时间戳校验、Nonce 机制)
- 幂等处理通知(可能存在重复投递)
八、典型工作流示例
8.1 基本任务执行
Client Agent
│ │
│ POST /message:send │
│ {"message": {"text": "天气?"}} │
│ ─────────────────────────────────→ │
│ │ 处理请求
│ 200 OK │
│ {"task": {status: COMPLETED, │
│ artifacts: [{text: "晴 24°C"}]}}│
│ ←───────────────────────────────── │
8.2 流式任务执行
Client Agent
│ │
│ POST /message:stream │
│ {"message": {"text": "写报告"}} │
│ ─────────────────────────────────→ │
│ │
│ SSE: task{WORKING} │
│ ←───────────────────────────────── │
│ SSE: artifactUpdate{chunk1} │
│ ←───────────────────────────────── │
│ SSE: artifactUpdate{chunk2} │
│ ←───────────────────────────────── │
│ SSE: statusUpdate{COMPLETED} │
│ ←───────────────────────────────── │
│ [流关闭] │
8.3 多轮交互(INPUT_REQUIRED)
Client Agent
│ │
│ POST /message:send │
│ {"message": {"text": "订机票"}} │
│ ─────────────────────────────────→ │
│ │
│ 200 OK │
│ {"task": {id: "t1", │
│ status: INPUT_REQUIRED, │
│ message: "请提供出发地和目的地"}} │
│ ←───────────────────────────────── │
│ │
│ POST /message:send │
│ {"message": {taskId: "t1", │
│ "text": "北京到上海"}} │
│ ─────────────────────────────────→ │
│ │
│ 200 OK │
│ {"task": {id: "t1", │
│ status: COMPLETED, │
│ artifacts: [...]}} │
│ ←───────────────────────────────── │
8.4 推送通知工作流
Client Agent Webhook
│ │ │
│ POST /message:send │ │
│ {message + pushConfig} │ │
│ ───────────────────────→│ │
│ │ │
│ 200 OK {task: SUBMITTED} │
│ ←───────────────────────│ │
│ │ │
│ [客户端断开连接] │ [Agent 后台处理...] │
│ │ │
│ │ POST /webhook │
│ │ {statusUpdate: COMPLETED}│
│ │ ────────────────────────→│
│ │ │
│ GET /tasks/{id} │ │
│ ───────────────────────→│ │
│ │ │
│ 200 OK {task + artifacts} │
│ ←───────────────────────│ │
九、v0.3 到 v1.0 的关键变化
通过对比规范文档中的版本信息,v1.0 相较于 v0.3 引入了以下重要变化:
| 变化 | 说明 |
|---|---|
| AgentInterface 列表 | 从单一 url 升级为 supportedInterfaces 列表,支持多协议绑定 |
| HTTP+JSON/REST 绑定 | 新增第三种协议绑定,使用 application/a2a+json 媒体类型 |
| ListTasks 操作 | 新增任务列表查询,支持按 contextId/状态过滤和游标分页 |
| AUTH_REQUIRED 状态 | 新增任务内二次认证机制 |
| 版本协商 | 引入 A2A-Version 服务参数,支持客户端-服务端版本协商 |
| Agent Card 签名 | 新增 JWS 签名机制,确保 Agent Card 的真实性和完整性 |
| 扩展机制增强 | 引入 A2A-Extensions Header 进行扩展激活协商 |
| Device Code OAuth | 新增 OAuth 2.0 Device Code 流程支持 |
| 废弃 Implicit/Password | 明确废弃不安全的 OAuth 流程 |
| Proto 作为权威来源 | 明确 a2a.proto 为唯一规范性定义 |
十、总结
本文从三层架构的视角,系统剖析了 A2A 协议规范的技术细节:
Layer 1(数据模型) 定义了 Task、Message、Part、Artifact、AgentCard 等核心数据结构,通过 Protocol Buffers 实现协议无关的规范定义。其中 Part 的 oneof 设计实现了模态无关性,AgentCard 的 supportedInterfaces 列表支持多协议暴露,安全对象模型对齐 OpenAPI 3.2 标准。
Layer 2(抽象操作) 定义了 11 个核心操作及其语义规则,包括阻塞/非阻塞执行模式、多轮交互的 contextId/taskId 语义、能力验证机制、9 种特定错误类型以及版本协商流程。
Layer 3(协议绑定) 将抽象操作映射到 JSON-RPC、gRPC 和 HTTP+JSON/REST 三种具体协议,每种绑定在保持语义一致的前提下,利用各自协议的原生特性(JSON-RPC 的简洁性、gRPC 的高性能、REST 的通用性)。
参考资料: