又在重新发明 MCP。
Agent2Agent Protocol(简称 A2A)是一种用于独立 AI agent 应用之间通信的开放协议。它支持一个 agent 发现另一个 agent 的能力、向其分派工作、继续多轮交互、跟踪长时间运行的任务,并在不了解远程 agent 实现方式的情况下接收结构化结果。
A2A 由 Google 发起,后捐赠给 Linux Foundation。它与框架、模型、语言和供应商无关。使用 Java 构建的 A2A 客户端可以与使用不同模型和 agent 框架、以 Python 构建的远程 agent 协作。
重要边界如下:
Agent A 知道 Agent B 提供什么,以及如何与其通信。
Agent A 不需要知道 Agent B 的提示词、记忆、工具、模型或源代码。本文介绍已发布的 A2A v1.0 数据模型。由于规范仍在演进,生产环境实现应协商并验证远程 Agent Card 声明的协议版本。
A2A 解决的问题 #
假设一个通用助手需要安排商务旅行。它可能需要与以下 agent 协作:
- 航班 agent;
- 酒店 agent;
- 雇主差旅政策 agent;
- 地面交通 agent;以及
- 费用管理 agent。
没有通用协议时,每一对系统都需要定制集成:
编排器 -> 航空公司供应商的私有 API
编排器 -> 酒店供应商的私有 API
编排器 -> 政策 agent 的定制 WebSocket
编排器 -> 费用 agent 的消息队列每个集成都要自行定义:
- 能力描述;
- 请求和响应 schema;
- 身份验证元数据;
- 进度事件;
- 取消行为;
- 文件处理;
- 会话标识符;
- 长时间运行作业模型;以及
- 错误表示。
随着独立开发的 agent 数量增加,这些点对点适配器的构建和治理成本会不断上升。
当远程操作具有确定性且完全明确时,传统 REST API 可以解决其中一部分问题。但 agent 交互通常不那么僵化:
客户端:请在公司政策范围内安排差旅。
Agent:我需要员工 ID 和首选出发时间窗口。
客户端:员工 E-1042,18:00 之后。
Agent:唯一符合政策的航班会在酒店入住时间之后抵达。我可以申请例外吗?
客户端:可以;准备例外申请,但不要购买任何东西。工作具有状态,可能需要澄清或授权,可能持续很长时间,也可能产生多个不断演进的制品。A2A 标准化这种交互封装,同时将领域推理留给各个 agent。
从集成矩阵到单一协议 #
如果每个 agent 都有专用接口,连接 \(N\) 个 agent 可能需要接近 \(N \times (N - 1)\) 个有向集成。使用 A2A 后,每个实现都面向共享协议:
Agent A --\
Agent B ----> A2A 数据模型和操作
Agent C --/这不会消除领域差异。航班预订技能和安全审查技能仍然会接受不同的信息。A2A 标准化的是能力的发布方式,以及工作、状态、内容和结果的交换方式。
A2A 不是什么 #
A2A 是:
- agent 应用之间的线协议和交互模型;
- 适用于跨进程、框架、团队和组织边界的场景;以及
- 为内部不透明、可能长时间运行的 agent 设计的协议。
A2A 不是:
- LLM;
- agent 框架;
- 编排算法;
- 提示词格式;
- 工作流引擎;
- 模型路由系统;
- 身份提供商;
- 注册表 API 标准;
- 对已发布 agent 可信性的保证;或
- 对定义明确的普通 API 的替代品。
如果远程能力只是 getExchangeRate(from, to),普通 API 或工具协议可能更清晰。远程参与方是能够推理、协商、维护任务状态、请求更多信息并返回复杂交付物的自治协作者时,A2A 才具有价值。
A2A 与 MCP #
A2A 和 Model Context Protocol 解决不同的问题:
| 协议 | 关系 | 典型远程能力 |
|---|---|---|
| MCP | Agent 到工具或资源 | 查询数据库、读取文件、调用 API |
| A2A | Agent 到 agent | 研究、协商、规划、委派和交付结果 |
一个 agent 可以同时使用二者:
差旅编排器
|
| A2A:“查找符合政策的航班选项”
v
航班 Agent
|
| MCP:调用库存和定价工具
v
航空公司系统MCP 向 agent 暴露相对受限的原语。A2A 将独立 agent 作为不透明的协作者暴露出来。把每个 agent 都封装成一个大型工具,会隐藏有用的任务生命周期、澄清、制品和异步行为;把每个简单工具都当作 agent,则会增加不必要的自治性和复杂性。
三个协议层 #
A2A v1.0 分离了三个层次:
- 规范数据模型: Agent Card、Message、Task、Part、Artifact、事件、安全声明和扩展。
- 抽象操作: 发送 Message、流式发送 Message、获取/列出或取消 Task、订阅 Task 更新、管理推送通知配置,以及获取扩展 Agent Card。
- 绑定: 映射到 JSON-RPC、gRPC、HTTP+JSON 或受治理的自定义绑定。
规范 Protocol Buffer 定义具有规范性。绑定以不同方式序列化相同概念。客户端应使用持续维护的 SDK,而不应假设某个 JSON-RPC 示例描述了所有绑定。
官方项目维护 Python、Go、Java、JavaScript、.NET 和 Rust SDK,并提供独立的示例仓库。
参与者 #
一次交互包含三个概念上的参与者:
- 用户: 目标驱动请求的人类或自动化系统。
- A2A 客户端: 代表用户发起协议操作的应用程序或客户端 agent。
- A2A 服务器: 发布 A2A 接口并执行工作的远程 agent。
同一个应用程序可以同时充当服务器和客户端。差旅编排器为用户处理请求,同时作为航班和酒店 agent 的客户端。
用户
-> A2A 服务器:差旅编排器
-> A2A 客户端:航班 Agent
-> A2A 客户端:酒店 Agent
-> A2A 客户端:政策 AgentA2A 不标准化编排器的规划逻辑。编排器可以使用固定工作流、规则引擎、LLM 规划器或人工决策。
Agent 发现 #
A2A 服务器通过 Agent Card 描述自身。Agent Card 是一个 JSON 文档,包含:
- agent 名称、描述、提供商和版本;
- 一个或多个受支持的接口和 A2A 协议版本;
- 受支持的协议绑定;
- 默认输入和输出媒体类型;
- 可选的流式、推送通知和扩展卡能力;
- 安全方案和要求;
- 技能、描述、标签、示例和媒体类型;以及
- 可选的签名和扩展。
简化的 v1.0 Agent Card 如下:
{
"name": "Corporate Travel Agent",
"description": "Plans policy-compliant business travel",
"version": "3.4.0",
"provider": {
"organization": "Example Travel",
"url": "https://travel.example"
},
"supportedInterfaces": [
{
"url": "https://agents.example/a2a/travel",
"protocolBinding": "HTTP+JSON",
"protocolVersion": "1.0"
}
],
"capabilities": {
"streaming": true,
"pushNotifications": true,
"extendedAgentCard": true
},
"defaultInputModes": [
"text/plain",
"application/json"
],
"defaultOutputModes": [
"text/plain",
"application/json"
],
"skills": [
{
"id": "plan-business-trip",
"name": "Plan a Business Trip",
"description": "Finds policy-compliant flight and hotel options",
"tags": ["travel", "flight", "hotel", "corporate-policy"],
"examples": [
"Plan a trip from Singapore to Helsinki for 12-16 October"
],
"inputModes": ["text/plain", "application/json"],
"outputModes": ["application/json"]
}
]
}确切的 JSON 字段名源自 v1.0 规范模型。应通过 SDK 或生成的 schema 验证卡片,而不要接受任意 JSON。
发现策略 #
对于基于域名的发现,标准 well-known URL 是:
https://agent.example/.well-known/agent-card.json其他实用方法包括:
- 精选注册表: 企业目录或市场根据技能、提供商、地域或信任策略返回已批准的 Agent Card。
- 直接配置: 部署清单为一组封闭的 agent 提供已知卡片 URL。
- 经过身份验证的扩展卡: 公共卡片发布基本能力;经过身份验证的操作返回对当前调用者可用的敏感技能或接口。
A2A 标准化卡片,而不是集中式注册表的 API。
应将发现结果视为策略输入,而不是自动信任。在调用 agent 前,客户端可能需要检查:
- 可信发行者或注册表;
- 卡片签名;
- 允许的域名和 TLS 身份;
- 协议版本;
- 所需绑定;
- 支持的媒体类型;
- 已批准的提供商;
- 数据驻留要求;
- 安全方案;
- 合同关系;以及
- 当前用户是否获授权使用该技能。
Agent Card 的变化频率低于 Task,应使用正常的 HTTP 缓存,包括 Cache-Control、ETag 和条件请求。
Agent Card 的交换时机 #
Agent Card 是发现期间使用的控制平面元数据,通常在第一个 A2A 操作之前获取:
客户端 -> 发现或接收 Agent Card
客户端 -> 验证信任、版本、绑定、安全性和媒体类型
客户端 -> 从 supportedInterfaces 选择接口 URL
客户端 -> 向该接口发送 A2A 操作通常不会将卡片附加到每条 Message。客户端可以缓存卡片,并根据正常 HTTP 缓存规则或本地策略刷新。
A2A 服务器 必须提供 Agent Card,但 v1.0 允许多种发现机制:标准 well-known URI、注册表或目录,以及直接配置的卡片 URL 或卡片内容。因此,/.well-known/agent-card.json 是传统的互操作位置,但不是交换卡片的唯一合规方式。
如果公共卡片发布了 extendedAgentCard,经过授权的客户端之后可以调用 Get Extended Agent Card。该认证卡片可以披露调用者特有的技能或接口,而这些信息不应在匿名发现期间公开。
Message 和 Part #
Message 是一次通信轮次,包含:
- 调用方生成的
messageId; ROLE_USER或ROLE_AGENT角色;- 一个或多个 Part;
- 可选的
contextId和taskId; - 对先前 Task 的可选引用;
- 可选元数据;以及
- 声明的扩展。
Part 恰好包含一种内容表示:
| 内容 | 用途 |
|---|---|
text | 纯文本内容 |
data | 结构化 JSON 值 |
url | 外部内容的引用 |
raw | 内联字节,在 JSON 中表示为 Base64 |
Part 还可以包含 MIME mediaType、文件名和元数据:
{
"messageId": "msg-01JZ8P...",
"role": "ROLE_USER",
"parts": [
{
"text": "Review the attached contract for renewal risk.",
"mediaType": "text/plain"
},
{
"url": "https://files.example/contracts/42",
"filename": "contract.pdf",
"mediaType": "application/pdf",
"metadata": {
"sha256": "..."
}
},
{
"data": {
"jurisdiction": "Singapore",
"riskTolerance": "low"
},
"mediaType": "application/json"
}
]
}对于大文件,应使用 URL,而不是在每个协议跳转中都放置 Base64 数据。URL 应为短期有效、限定于目标 agent、经过完整性检查,并受到混淆代理访问的保护。
媒体协商很重要。客户端声明其接受的输出媒体类型,Agent Card 描述 agent 支持的媒体类型。当某项技能提供 application/json 时,机器编排器应请求结构化数据,而不是解析散文。
直接 Message 和有状态 Task #
Send Message 操作可以返回:
- 用于即时、自包含响应的 Message;或
- 用于有状态或长时间运行工作的 Task。
例如,agent 可以直接回答能力问题:
客户端:你能审查受新加坡法律管辖的合同吗?
Agent:可以。审查一份 300 页的合同应创建一个 Task,以便跟踪、中断、取消,并与输出制品关联。
Task 模型 #
Task 包含:
- 服务器生成的 Task ID;
- 服务器生成的上下文 ID;
- 当前状态和时间戳;
- 可选的状态 Message;
- Message 历史;
- 零个或多个 Artifact;以及
- 元数据。
生命周期状态如下:
| 状态 | 含义 |
|---|---|
TASK_STATE_SUBMITTED | 已接受,但尚未主动处理 |
TASK_STATE_WORKING | 处理中 |
TASK_STATE_INPUT_REQUIRED | 需要更多用户或客户端信息 |
TASK_STATE_AUTH_REQUIRED | 需要额外的带外身份验证 |
TASK_STATE_COMPLETED | 成功的终止结果 |
TASK_STATE_FAILED | 终止性处理失败 |
TASK_STATE_CANCELED | 终止性取消 |
TASK_STATE_REJECTED | Agent 拒绝该工作 |
INPUT_REQUIRED 和 AUTH_REQUIRED 会中断处理,但不是终止状态。客户端可以发送与该 Task 关联的另一条 Message 来继续处理。
终止状态的 Task 不可变。修正或细化会在同一上下文中创建新 Task,并引用之前的 Task:
上下文 C
Task T1:生成行程 -> 已完成 -> Artifact A1
Task T2:更换上午航班 -> 已完成 -> Artifact A2
引用 T1这样可以实现可审计性:每个 Task 都是输入、状态和输出的稳定单元。
上下文 ID 与 Task ID #
contextId 用于对相关 Task 和 Message 分组。taskId 标识一个工作单元:
上下文:“规划赫尔辛基旅行”
Task:查找航班
Task:预订酒店
Task:申请政策例外不要将上下文 ID 与身份验证、授权或租户身份混淆。它只是服务器分配的不透明关联句柄。服务器仍必须验证经过身份验证的调用方是否有权访问每一个被引用的上下文、Task 和 Artifact。
Artifact #
Artifact 是具体的 Task 输出,而不是对话状态 Message。示例包括:
- 行程;
- 安全报告;
- 生成的源代码;
- PDF 合同分析;
- 图像;
- 结构化报价列表;或
- 部署清单。
Artifact 在其 Task 内具有唯一 ID、名称、描述、Parts、元数据和可选扩展:
{
"artifactId": "offers-v1",
"name": "flight-offers.json",
"description": "Policy-compliant flight options",
"parts": [
{
"mediaType": "application/json",
"data": {
"currency": "SGD",
"offers": [
{
"offerId": "F-812",
"price": 1420.30,
"refundable": true
}
]
}
}
],
"metadata": {
"expiresAt": "2026-07-20T10:15:00Z"
}
}Artifact 不会自动持久化,也不自动可信。客户端必须验证其 schema、媒体类型、大小、完整性、来源、过期时间和业务含义。
核心操作 #
与绑定无关的 v1.0 操作包括:
| 操作 | 目的 |
|---|---|
| Send Message | 开始或继续交互 |
| Send Streaming Message | 开始交互并接收增量事件 |
| Get Task | 获取当前 Task 状态、历史和 Artifact |
| List Tasks | 使用过滤器和分页列出已授权的 Task |
| Cancel Task | 请求 agent 取消正在进行的工作 |
| Subscribe to Task | 重新连接到活跃 Task 的更新 |
| Manage Task Push Notification Config | 创建、读取、列出或删除 webhook 配置 |
| Get Extended Agent Card | 获取调用者特定的 Agent Card 详细信息 |
绑定与端点映射 #
Agent Card 中的接口 URL 和 protocolBinding 决定这些抽象操作在网络上的具体形式。
对于 JSON-RPC,发布的接口 URL 是所有操作共用的一个 HTTP 端点。类似 /a2a 的 URL 只是实现选择;A2A 规范并不要求该路径。客户端发送包含 JSON-RPC 主体的普通 HTTP 请求,由 method 字段选择 A2A 操作:
POST /a2a HTTP/1.1
Host: agents.example
Content-Type: application/json
{
"jsonrpc": "2.0",
"id": "request-17",
"method": "SendMessage",
"params": {
"message": {
"messageId": "msg-01JZ8P...",
"role": "ROLE_USER",
"parts": [{"text": "Hello"}]
}
}
}这里的 JSON-RPC 是叠加在 HTTP 之上的协议约定,而不是 HTTP 的替代品。HTTP 提供传输、标头、身份验证和状态码;JSON-RPC 标准化主体字段 method、params、id、result 和 error,以及请求关联、通知和协议错误语义。其他协议也可以在 WebSocket 或标准输入输出等传输方式上使用 JSON-RPC,但 A2A 的 JSON-RPC 绑定专门使用 HTTP(S),并使用 Server-Sent Events 进行流式传输。
所有 JSON-RPC A2A 方法都发送到同一个已发布 URL:
| A2A 操作 | JSON-RPC 方法 |
|---|---|
| Send Message | SendMessage |
| Send Streaming Message | SendStreamingMessage |
| Get Task | GetTask |
| List Tasks | ListTasks |
| Cancel Task | CancelTask |
| Subscribe to Task | SubscribeToTask |
| 创建推送配置 | CreateTaskPushNotificationConfig |
| 获取推送配置 | GetTaskPushNotificationConfig |
| 列出推送配置 | ListTaskPushNotificationConfigs |
| 删除推送配置 | DeleteTaskPushNotificationConfig |
| Get Extended Agent Card | GetExtendedAgentCard |
HTTP+JSON 绑定则使用相对于已发布接口基准 URL 的独立、面向资源的路径:
| HTTP 端点 | A2A 操作 |
|---|---|
POST /message:send | Send Message |
POST /message:stream | 使用 SSE 的 Send Streaming Message |
GET /tasks/{id} | Get Task |
GET /tasks | List Tasks |
POST /tasks/{id}:cancel | Cancel Task |
POST /tasks/{id}:subscribe | 使用 SSE 的 Subscribe to Task |
POST /tasks/{id}/pushNotificationConfigs | 创建推送配置 |
GET /tasks/{id}/pushNotificationConfigs/{configId} | 获取推送配置 |
GET /tasks/{id}/pushNotificationConfigs | 列出推送配置 |
DELETE /tasks/{id}/pushNotificationConfigs/{configId} | 删除推送配置 |
GET /extendedAgentCard | Get Extended Agent Card |
这些路径属于 HTTP+JSON 绑定;JSON-RPC 实现不会为每个操作暴露一个 HTTP 路由。gRPC 绑定同样暴露 RPC 方法,而不是这些 REST 路径。
阻塞式和非阻塞式发送 #
Send Message 配置决定调用是否等待:
returnImmediately为 false 或缺省时,调用会等待 Task 达到终止或中断状态。returnImmediately为 true 时,Task 创建后立即返回;客户端通过轮询、订阅或等待推送通知获取后续状态。
简化的 HTTP+JSON 请求如下:
POST /message:send HTTP/1.1
Host: agents.example
Authorization: Bearer eyJ...
Content-Type: application/json
{
"message": {
"messageId": "msg-01JZ8P...",
"role": "ROLE_USER",
"parts": [
{
"text": "Review repository example/payments for critical vulnerabilities",
"mediaType": "text/plain"
}
]
},
"configuration": {
"acceptedOutputModes": [
"application/json",
"text/markdown"
],
"historyLength": 10,
"returnImmediately": true
}
}即时响应可能包含:
{
"task": {
"id": "task-sec-2048",
"contextId": "ctx-release-91",
"status": {
"state": "TASK_STATE_SUBMITTED",
"timestamp": "2026-07-20T08:30:00Z"
}
}
}该示例面向 v1.0 HTTP+JSON 绑定。有关确切的请求和响应封装,请使用 SDK。
流式传输与异步工作 #
Agent 工作持续时间往往超过普通请求超时。A2A 提供三种更新模式。
轮询 #
客户端发送非阻塞请求,并定期调用 Get Task:
Send Message -> Task(submitted)
Get Task -> Task(working)
Get Task -> Task(completed, artifacts=[...])轮询简单,并且能够应对进程重启。应使用指数退避、抖动和服务器提示,而不是每几毫秒轮询一次。
流式传输 #
如果 Agent Card 发布了流式能力,Send Streaming Message 会返回一个流。根据绑定不同,HTTP 可以使用 Server-Sent Events,gRPC 可以使用服务器流。
Task 生命周期流从 Task 开始,随后发出:
- 状态变更或中间状态 Message 的
TaskStatusUpdateEvent; - 新建或增量更新 Artifact 的
TaskArtifactUpdateEvent。
Task(submitted)
Status(working: "正在扫描依赖")
Artifact(report, append=true, chunk=1)
Status(working: "正在分析可达调用路径")
Artifact(report, append=true, chunk=2, lastChunk=true)
Status(completed)Artifact 更新会标识数据是否追加到现有 Artifact,以及最后一个分块是否已到达。客户端应按 Task ID 和 Artifact ID 组装分块,实施大小限制,并在重连时避免重复内容。
如果 Task 处于活跃状态时流断开,Subscribe to Task 可以恢复接收后续更新。应在处理流式数据前持久化 Task ID,以便能够恢复。
推送通知 #
对于持续数分钟、数小时或数天的工作,如果 agent 发布了推送通知支持,客户端可以注册 HTTPS webhook:
客户端 -> Agent:启动 Task 并注册回调
客户端断开连接
Agent 执行工作
Agent -> 客户端 webhook:Task 完成
客户端 -> Agent:Get Task通知告知客户端状态发生了变化;客户端通常随后获取权威的 Task。
Webhook 支持会引入严重风险:
- SSRF: 恶意调用方提供内部 URL 或云元数据 URL。
- 欺骗: 攻击者发送伪造的完成事件。
- 重放: 有效的旧事件被再次处理。
- 放大: 服务器被诱导向第三方发送大量请求。
- 重复投递: 网络重试导致同一事件被多次投递。
服务器应允许列表或验证回调所有权,并限制出站网络。接收方应验证发送者,校验发行者、受众、过期时间、Task ID 和唯一事件 ID,拒绝过期事件,并以幂等方式处理通知。
输入和身份验证中断 #
长时间运行的工作可能需要用户做出额外决定:
客户端 -> Agent:“预订最符合政策的选项”
Agent -> 客户端:Task(INPUT_REQUIRED)
“两个选项的政策评分相同。优先选择更低价格还是可退款票价?”
客户端 -> Agent:Message(taskId=T1,“优先选择可退款票价”)
Agent -> 客户端:Task(WORKING)
Agent -> 客户端:Task(COMPLETED, artifact=booking)AUTH_REQUIRED 表示工作期间需要额外授权。例如,差旅 agent 可能发现需要用户授权航空公司账户。
客户端应在带外执行 OAuth 或同意流程,然后继续该 Task。不要仅因为 agent 请求身份验证,就把密码、刷新令牌或可复用的密钥作为普通 Message 文本发送。
详细示例:企业差旅 #
考虑一个协调独立运营 agent 的企业助手。
参与者 #
差旅编排器
-> 政策 Agent
-> 航班 Agent
-> 酒店 Agent
-> 费用 Agent每个远程系统都发布一个 Agent Card。编排器的批准注册表包含受信任的卡片,以及各提供商的 OAuth audience。
流程 #
用户提出请求:
text规划 10 月 12 日至 16 日从新加坡前往赫尔辛基的差旅。 我需要在周一 09:00 的会议之前到达。 暂时不要进行购买。编排器将员工级别、成本中心、路线和日期作为结构化 Part 发送给政策 Agent。
政策 Agent 返回包含政策 Artifact 的已完成 Task:
json{ "cabin": "economy", "maxNightlyHotelRate": 240, "currency": "EUR", "approvalRequiredAbove": 1800 }编排器在自己的工作流中并行启动航班和酒店 Task。它只向每个 agent 发送所需的政策字段。
航班 Agent 流式返回状态:
textworking: 正在搜索直达路线 working: 直达路线违反到达约束 working: 正在搜索一次中转路线 completed: artifact flight-offers.json酒店 Agent 因为存在两个办公地点而进入
INPUT_REQUIRED。编排器向用户询问后,使用所选办公地点继续同一个 Task。编排器合并两个结构化 Artifact,计算完整的行程选项,并呈现给用户。
用户选择一个选项。这会在相同的编排上下文中创建新的预订 Task,而不会修改已完成的搜索 Task。
由于总金额超过政策阈值,费用 Agent 创建审批 Task 并配置推送通知。几小时后,经理批准该请求。
编排器接收经过身份验证的通知,获取 Task,确认批准金额和过期时间,然后才继续预订 Task。
A2A 的作用 #
航班和酒店提供商保留各自的模型、工具、定价系统和规划逻辑。编排器看到的是已声明的技能、Task 状态、Message 和 Artifact。提供商可以替换内部框架,而无需改变协作契约。
A2A 不解决事务原子性。如果航班预订成功而酒店预订失败,编排器仍需要补偿逻辑、带过期时间的预订,或特定领域的事务工作流。
详细示例:软件交付 #
一个发布编排器协调由不同工程团队拥有的 agent:
编码 Agent -> 测试 Agent -> 安全 Agent -> 部署 Agent流程 #
编码 Agent 完成 Task
T1并发出:- Git commit 引用;
- patch Artifact;
- change-summary Artifact;以及
- 预期的测试范围。
编排器向测试 Agent 和安全 Agent 分别发送 Message。每条 Message 都引用
T1,但只包含针对该仓库和操作获授权的短期仓库凭据。测试 Agent 流式返回:
textTask submitted 正在编译 payments 模块 正在运行 1,842 个单元测试 正在运行 37 个集成测试 Artifact: junit-results.xml Task completed安全 Agent 返回
INPUT_REQUIRED:text该补丁更改了授权行为。该端点是面向服务身份、最终用户,还是二者?编排器从代码所有者处获取答案,并继续同一个安全 Task。
安全 Agent 以结构化发现结果完成:
json{ "decision": "block", "findings": [ { "severity": "high", "file": "AuthorizationService.java", "line": 118, "summary": "Tenant constraint omitted" } ] }编排器不执行部署。它为编码 Agent 创建 refinement Task
T4,引用T1和安全 Task,并请求修复。新补丁会获得新的测试 Task 和审查 Task。已完成的 Task 仍然是不可变的审计记录。
是否允许部署由确定性的发布政策决定,而不是由 LLM 的散文决定。
A2A 的作用 #
每个团队都可以使用不同框架构建自己的 agent。结果是类型化 Artifact,而不是从聊天内容中抓取的文本。流式传输为昂贵的测试套件提供进度信息,中断的 Task 也可以支持提问,而无需丢弃之前的工作。
该协议不会使生成的代码自动安全。分支保护、授权、可复现构建、签名、隔离执行和人工审批仍然是必要条件。
详细示例:保险理赔 #
理赔受理 agent 从移动应用接收文本、图像和文档:
理赔受理 Agent
-> 损失评估 Agent
-> 保障范围 Agent
-> 欺诈审查 Agent
-> 维修网络 Agent多模态交换 #
受理 Message 包含:
- 描述事故的文本;
- 通过限定范围的 URL 引用的图像 Part;
- PDF 警察报告;
- 结构化车辆和保单数据;以及
- 用于完整性验证的哈希。
损失评估 Agent 返回:
- 面向人的摘要;
- 受损部件的结构化列表;
- 带注释图像 Artifact;以及
- 不确定性分数。
保障范围 Agent 可以看到保单条款,但看不到不必要的事故图像。欺诈 Agent 接收适合其职责的脱敏证据。编排器在每次 A2A 调用前执行数据最小化。
Human in the Loop #
如果评估置信度较低,Task 会进入 INPUT_REQUIRED:
请提供一张与地面齐平拍摄的左后轮照片。移动用户上传另一张图像后,受理 agent 继续同一个 Task。
如果预计损失超过阈值,工作流会为人工理赔员创建独立的人工 Task。A2A 跟踪协作,但具有法律后果的决定仍由确定性的理赔政策和具备资质的人类作出。
A2A 的作用 #
Part 支持在一次交互中传输文本、结构化数据和文件引用。Task 可以跨越不稳定的移动连接继续存在。Artifact 表示持久交付物,远程专业 agent 的内部实现仍然保持不透明。
实现仍必须执行同意、保留、区域处理、医疗数据规则,并对每个 Artifact URL 实施严格授权。
详细示例:采购与谈判 #
采购 agent 需要从多个供应商 agent 获得 500 台笔记本电脑的报价。这说明 agent 不只是无状态的价格工具。
流程 #
客户端将必要规格、交付地点、服务条款和响应截止时间发送给三个已批准的供应商 agent。
每个供应商创建一个 Task,并可能提出澄清问题。
供应商返回包含价格、库存、保修、有效期和商业条款的报价 Artifact。
采购 agent 在新的 Task 中请求细化:
text保持相同硬件和交付日期。提供三年保修,并报价价格差额。每次细化都引用之前的 Task 和报价 Artifact。
客户端负责跟踪报价版本,因为 Artifact 的谱系和接受情况由客户端负责。
确定性的策略引擎比较强制性条款。模型可以总结权衡,但不能放弃要求。
人类批准选定的报价。
独立的购买 Task 使用幂等性密钥和最小权限授权执行订单。
A2A 的作用 #
谈判是多轮的,每个供应商都拥有自己的推理和库存工具。Task 和 Artifact 提供了一致的机制,而无需强迫所有供应商使用同一个专有 agent SDK。
A2A 不标准化报价 schema。买方和供应商仍然需要共享领域 schema,或使用扩展来表示可供机器比较的商业条款。
安全模型 #
A2A 复用标准 Web 安全机制,而不是将凭据放入协议 Message。
Agent Card 声明支持的安全方案和要求。客户端在带外获取凭据,然后通过传输标头发送:
Authorization: Bearer eyJ...生产环境的 A2A 接口应使用 HTTPS 并验证服务器身份。跨组织部署通常使用 OAuth 2.0/OIDC、工作负载身份、mTLS 或其组合。
身份验证回答调用者是谁。每个操作仍然需要授权:
- 该调用者是否可以调用此技能?
- 它是否可以访问此 Task 和上下文?
- 它是否可以引用此 Artifact?
- 它是否可以请求此输出模态?
- 它是否可以取消此工作?
- 远程 agent 是否可以代表此最终用户执行底层操作?
不透明执行保护的是实现细节,而不是用户。恶意或已被入侵的 agent 仍然可能返回有害内容、滥用委派的凭据,或在工作未成功时声称成功。
委派 #
一个 agent 跨越另一个 agent 的边界代表用户执行操作时,应保留:
- 在策略要求时保留最终用户身份;
- 调用方工作负载身份;
- 租户;
- audience;
- 已授权的 scopes;
- 目的和同意;
- 令牌过期时间;以及
- trace context。
不要仅仅因为两个 agent 在技术上都能接受,就转发范围过宽的 bearer token。应将其交换为短期、绑定到 audience 且只包含远程技能所需 scopes 的令牌。
不可信内容 #
每条 Message、每个 Part、每个 Artifact、每个 Agent Card 以及每个被引用的文件都是不可信输入。应应用:
- JSON 和媒体类型验证;
- 字节数、历史记录和嵌套层级限制;
- 恶意软件扫描;
- 解压缩限制;
- URL scheme 和目标限制;
- 对渲染输出的内容安全策略;
- 提示词注入防御;
- 输出 schema 验证;以及
- 独立于 agent 文本的域名授权。
不要在具有特权的应用程序源中直接渲染 HTML Artifact。
可靠性与工作流语义 #
A2A 提供任务状态,但不是分布式事务协调器。客户端和服务器仍然需要:
- 持久化 Task 存储;
- 幂等的业务操作;
- 请求去重;
- 带抖动的有界重试;
- 截止时间和过期时间;
- 取消传播;
- 对部分成功的补偿;
- Artifact 完整性和保留;
- 背压;
- 并发限制;以及
- 进程重启后的恢复。
已取消的 Task 不证明外部副作用已经撤销。航班可能已经出票,邮件也可能已经发出。应将取消与业务补偿分别建模。
网络故障会造成不确定性:
客户端发送“购买”
服务器执行购买
响应丢失
客户端不知道购买是否发生对于有重大后果的操作,应使用领域幂等性密钥和可查询的业务引用。messageId 标识一个协议 Message,但除非实现明确规定并持久化该行为,否则它不应成为唯一的业务保证。
Task 和 Artifact 需要保留策略。除非服务合同明确保证,否则客户端不能假定完成的 Task 可以永久获取。
可观测性 #
在 A2A 调用之间传递 W3C Trace Context 标头。有用的 span 属性和日志包括:
- 远程 agent 的身份和版本;
- 协议和绑定版本;
- 操作;
- Task 和上下文 ID;
- 技能;
- 状态转换;
- 媒体类型;
- Artifact 大小;
- 重试次数;
- 延迟;以及
- 已认证的客户端应用程序。
不要将提示词文本、文档内容、访问令牌或个人数据放入指标标签。包含 Message 或 Artifact 的日志需要脱敏、访问控制和保留期限限制。
重要指标包括:
- 按操作统计的请求率和错误率;
- Task 队列和执行延迟;
- 各状态的 Task 数量;
- 等待输入或身份验证所花费的时间;
- 流断开和重新订阅率;
- 推送通知交付失败;
- 取消延迟;
- Artifact 字节数;以及
- 下游模型和工具成本。
将具有后果的操作与诊断日志分开审计。
扩展 #
扩展可以在不改变核心协议的情况下添加行为或数据。Agent Card 通过稳定 URI、描述、参数以及客户端是否必须理解该扩展来声明每个扩展。
如果不支持必需的扩展,客户端不得调用该 agent。可选扩展应能够平滑降级。
当多个独立实现需要可互操作的语义时,使用扩展,而不是仅仅为了携带某个应用程序的私有元数据。供应商扩展可能重新造成 A2A 试图减少的锁定,因此应尽可能保持核心交互可用,并准确记录兼容性。
实现 A2A 服务器 #
实际的服务器实现可以按以下步骤进行:
- 定义有限的技能集合及其输入/输出媒体类型。
- 发布最小化的公共 Agent Card。
- 首先选择一个官方绑定和 SDK。
- 在 HTTP 或传输边界进行身份验证。
- 对技能、Task、上下文和 Artifact 执行授权。
- 验证 Message,并安全地解引用文件。
- 决定何时返回直接 Message,何时创建 Task。
- 在确认非阻塞工作之前持久化 Task。
- 发出有效的状态转换和不可变的终止状态。
- 使用完整性和保留元数据存储或引用 Artifact。
- 只有在中间更新有价值时才添加流式传输。
- 只有在具备 SSRF 防护和已认证交付时才添加推送通知。
- 传递跟踪信息并记录审计事件。
- 发布兼容性和保留保证。
Agent 的内部实现可以使用 LLM、确定性工作流、人工队列、工具或其他 A2A agent。不要将思维链或内部凭据作为状态 Message 暴露。
实现 A2A 客户端 #
健壮的客户端应:
- 从可信来源发现 Agent Card。
- 验证并缓存它。
- 选择双方支持的协议版本、绑定和媒体类型。
- 执行本地信任和数据共享策略。
- 获取绑定到 audience 的凭据。
- 生成并持久化 Message 关联数据。
- 发送所需的最小上下文。
- 立即持久化返回的 Task 和上下文 ID。
- 处理直接 Message 和 Task 响应。
- 处理每一种 Task 状态,包括拒绝和身份验证。
- 以幂等方式重新连接、轮询或处理推送通知。
- 在使用 Artifact 前进行验证。
- 在执行具有后果的后续操作前应用确定性策略。
- 记录委派 Task 之间的跟踪和审计关系。
不要让 LLM 选择任意 Agent Card URL,然后立即向这些 URL 发送凭据或私有数据。发现和调用都需要允许列表或信任策略。
何时使用 A2A #
以下情况适合使用 A2A:
- 独立拥有的 agent 需要互操作;
- 远程系统确实是 agent,而不是单一函数;
- 能力发现具有价值;
- 交互可能需要澄清或协商;
- 工作是长时间运行或可恢复的;
- 需要丰富或增量的 Artifact;
- 必须保持内部 agent 实现不透明;或
- 多种语言和框架需要共享一个契约。
以下情况更适合普通 API、事件 schema、工作流活动或 MCP 工具:
- 输入和输出完全确定;
- 一个短请求总能完成操作;
- 不存在有意义的对话或任务生命周期;
- 双方属于一个紧密耦合的应用程序;
- 强静态领域 schema 比 agent 灵活性更重要;或
- 增加自治行为会带来风险,却没有用户价值。
常见错误 #
- 把发现当成信任。 Agent Card 描述声明;是否信任由策略决定。
- 将凭据放入 Message。 使用传输层身份验证和带外授权。
- 将上下文 ID 用作授权。 它只是一个不透明的分组标识符。
- 向每个 agent 发送全部用户上下文。 按委派技能最小化数据。
- 在提供结构化输出时解析散文。 协商适当的媒体类型。
- 重新启动终止状态的 Task。 在相同上下文中创建新 Task,并引用之前的 Task。
- 假定取消会撤销副作用。 实现领域补偿。
- 信任任意推送 URL。 防止 SSRF 并验证回调身份。
- 假定流式传输具有持久性。 持久化 ID,并支持重新订阅或轮询。
- 将 A2A 与编排混淆。 客户端仍然负责工作流和故障策略。
- 将 A2A 与 MCP 混淆。 Agent 通过 A2A 协作,Agent 通过 MCP 使用受限工具。
- 赋予编排模型最终权限。 确定性代码必须执行权限、预算和审批规则。
- 忽略协议协商。 Card 可以发布多个绑定和版本。
- 不加修改地使用 1.0 之前的旧示例。 v1.0 规范模型更改了字段名、枚举和绑定结构。
A2A 的价值不在于 agent 可以相互发送 JSON;普通 HTTP 已经能够做到这一点。它的价值在于提供了一个共享模型,用于发现不透明的 agent、委派有状态工作、协商模态、处理中断,以及获取增量或持久化结果。这个通用层使跨框架协作成为可能,同时将业务 schema、编排、信任和安全留给理解这些问题的应用程序。
评论
在 GitHub 上阅读或评论(发表评论需要登录)