A2A 协议规范深度剖析:三层架构、数据模型、操作语义与协议绑定

本文将深入协议规范的技术细节,逐层拆解其三层架构设计、完整数据模型、操作语义规则以及三种协议绑定的实现差异。

一、规范的三层架构

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_idtask_id 遵循严格的关联规则:

  • 服务端消息:context_id 必须提供;task_id 仅在创建了 Task 时提供
  • 客户端消息:两者均可选,但若同时提供,必须匹配(context_id 必须是该 Task 所属的上下文)
  • 若客户端仅提供 task_id,服务端将从中推断 context_id
  • 若客户端提供的 context_idtask_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;
}

appendlast_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 UnsupportedOperationErrorExtendedAgentCardNotConfiguredError

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 关键字用于区分字段被显式设置与被省略。这一区分在两个场景中至关重要:

  1. 显式默认值:某些字段定义了与 Protocol Buffers 隐式默认值不同的默认值
  2. 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 实现之间签名生成和验证的一致性。

规范化步骤:

  1. 从 Agent Card 中移除 signatures 字段
  2. 按 JCS 规范对剩余 JSON 进行规范化
  3. 对规范化后的 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 签名时必须:

  1. 从 Agent Card 中提取 signatures 数组
  2. 移除 signatures 字段后对 Agent Card 进行 JCS 规范化
  3. 对每个签名,解码 protected 头以获取算法和密钥 ID
  4. 获取对应的公钥
  5. 使用公钥验证签名

支持多个签名以实现密钥轮换。

七、安全考量

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 的通用性)。


参考资料:

相关推荐
Mintopia1 小时前
如何降低 Prompt 对 AI 理解的干扰
人工智能
七夜zippoe1 小时前
OpenClaw 会话管理:单聊、群聊、多模型
大数据·人工智能·fastapi·token·openclaw
电商API_180079052471 小时前
电商平台公开数据采集实践:基于合规接口的数据分析方案
开发语言·数据库·人工智能·数据挖掘·数据分析·网络爬虫
Mintopia1 小时前
AI-coding 时代,人类如何减少对 AI 结果的纠错环节
人工智能
绝不裸奔0012 小时前
OpenClaw完整部署指南-从安装到开机自启
人工智能
Rolei_zl2 小时前
AIGC(生成式AI)试用 49 -- AI与软件开发过程4
人工智能·aigc
九天轩辕2 小时前
OpenClaw教程
人工智能
cyyt2 小时前
深度学习周报(3.16~3.22)
人工智能
Yeats_Liao2 小时前
华为开源自研AI框架昇思MindSpore应用案例:WaveNet实现音乐生成
人工智能·深度学习·算法·机器学习·边缘计算