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


参考资料:

相关推荐
太子釢4 小时前
手写一个 RAG:从零搭建可溯源的检索增强问答系统
人工智能
不吃辣4905 小时前
vibe coding | 如何做一个 AI 音乐生成工具?
java·人工智能·后端·ai·ai编程
文人sec5 小时前
从接口压测到全链路质量保障:AI智能客服系统的软件测试实践
人工智能
声讯电子5 小时前
实测A-59F音频模组:降噪稳、不啸叫、通话清晰
人工智能·语音识别·ai降噪·usb接口·回音消除
神奇霸王龙5 小时前
2026旗舰六阶段MCP流水线实战指南
人工智能·ai·ai作画·prompt·aigc·mcp
山河不见老5 小时前
【Cursor 、Qoder安装问题】Cursor 、Qoder安装卡在“正在准备安装”问题排查及解决
人工智能·windows·编辑器
新知图书5 小时前
2.3 开发工作流
人工智能·agent·ai agent·智能体
tuanxiang5 小时前
用免费 AI 改写工具处理批量文案的踩坑记录,附完整降噪脚本
人工智能
小邓的技术笔记5 小时前
DeepSeek 大模型落地应用与场景实战指南
大数据·人工智能
石榴5 小时前
从 Codex + Obsidian 到本地 RAG:我的私人知识库实践
人工智能