A2A Protocol 1.0 的迁移重点不是把几个 JSON-RPC 方法改名,而是重新确认发现、版本协商、任务可见性、流事件解析和错误处理的契约。生产迁移应先建立 v0.3 基线,再让 Agent Card 同时声明兼容接口,逐项升级客户端与服务端,最后用鉴权隔离、重放、断流和降级测试证明行为一致。
A2A 1.0 解决了什么问题?
A2A Protocol 1.0 官方公告把它定位为 Agent-to-Agent 通信的首个稳定版本。它面向彼此独立、内部实现可能完全不透明的 Agent:客户端先发现远端能力,再发送消息;简单请求可以直接得到 Message,长任务可以得到 Task,后续通过轮询、流式连接或 webhook 获取进展。
A2A 与 MCP 不是替代关系。MCP 更常用于一个 Agent 连接工具与上下文,A2A 负责 Agent 之间的发现、委派和任务协作。一个远端 Agent 完全可以在内部使用 MCP,但调用方不需要知道它有哪些内部工具。工具层的授权、参数校验和审计仍可沿用这份 MCP Server 安全清单。
迁移的价值也不只是“跟上版本”。1.0 把多个模糊行为变成可测试契约:接口版本放到具体 binding 上,operation 使用统一名称,任务列表和租户边界更明确,流事件类型改用成员名判别,错误采用结构化模型。代价是 v0.3 客户端不能只靠改一个版本字符串继续工作。
v0.3 到 v1.0 有哪些必须处理的 breaking changes?
A2A v1.0 变更说明列出了影响最大的结构与行为变化。迁移清单至少应覆盖下表。
| 区域 | v0.3 常见形态 | v1.0 迁移动作 |
|---|---|---|
| Operation | message/send、tasks/get 等路径式名称 |
改为 SendMessage、GetTask、ListTasks、CancelTask 等统一 operation |
| Agent Card | 顶层 protocolVersion,分散的 transport 字段 |
使用 supportedInterfaces[],每个接口声明 url、protocolBinding、protocolVersion |
| 流事件 | 依赖 kind 与 final 判别 |
根据 taskStatusUpdate 或 taskArtifactUpdate 成员判别;连接结束语义交给 binding |
| Part | 多个分离类型 | 使用统一 Part 结构与明确成员 |
| 错误 | 各 SDK/transport 容易自定义 | 映射到规范化错误与 google.rpc.Status / ErrorInfo |
| 任务发现 | 没有标准 ListTasks |
增加带过滤和分页的任务列表,并强制按调用者隔离 |
| 时间 | 字段和格式不够统一 | 使用 UTC ISO 8601,按规范处理 createdAt、lastModified |
不要把表格当成完整 schema diff。实际迁移要锁定你使用的 binding、SDK 版本和扩展,再以生成类型或规范 schema 对所有请求、响应和持久化事件做扫描。
第一步:先锁定现有行为,而不是立即升级依赖
在变更 SDK 前,为当前 v0.3 服务建立黑盒基线:
- 当前 Agent Card 的公开地址、缓存策略和鉴权前后差异;
- 已支持的消息、任务、取消、流式和 webhook 路径;
Message与Task的返回条件;- 相同
messageId重放时是否重复创建任务; - 调用者能否读取其他租户的任务;
- 流中断后能否恢复,以及客户端如何识别最终状态;
- 下游错误如何映射为 HTTP、JSON-RPC 或 gRPC 状态。
记录协议输入、类型和状态转换即可,不要把 bearer token、prompt、用户文件或完整任务历史写进迁移日志。若没有基线,升级后出现“客户端收不到最后一个 artifact”时,很难判断是协议变化、SDK bug、代理缓冲还是旧实现本就没有保证。
第二步:把 Agent Card 改成显式版本协商
1.0 的关键变化是一个 Agent 可以同时声明多个接口与协议版本。下面是结构示例,字段应以你锁定的规范和 SDK 生成模型为准:
{
"name": "report-agent",
"description": "Creates a report from approved inputs",
"supportedInterfaces": [
{
"url": "https://agents.example.com/a2a",
"protocolBinding": "JSONRPC",
"protocolVersion": "1.0"
},
{
"url": "https://agents.example.com/a2a-v03",
"protocolBinding": "JSONRPC",
"protocolVersion": "0.3"
}
],
"capabilities": {
"streaming": true
}
}
客户端必须选择自己真正支持的接口,不要在服务端返回“不支持”后静默降级并丢失功能。降级应该是显式策略:记录选择的版本,检查需要的 capability 是否仍存在,并在缺少强制功能时失败关闭。
Agent Card 是声明,不是信任证明。卡片 URL、内容和签名都需要绑定到可信发现渠道。公开 Card 不应包含内网地址、私有 scope、调试 endpoint 或临时凭据。若启用签名,应按规范使用 JSON Canonicalization Scheme 与 JWS,并明确密钥轮换、过期和撤销策略。
第三步:迁移 operation 与生成类型
最安全的改造方式是在协议适配层集中完成,而不是让业务代码到处判断版本:
async def send_work(client, request):
if client.protocol_version == "1.0":
return await client.send_message(request)
return await client.send_message_v03(request)
这只是结构示例。真正的 adapter 还要统一:
- request/response 类型;
Message与Task的联合返回;- ID、时间戳、分页 token;
- 取消的允许状态;
- transport 错误到领域错误的映射;
- telemetry 中的 operation 名称。
不要用字符串替换批量改 operation。ListTasks 是新增能力,GetTask 的历史语义和调用者可见性也更明确;旧的自定义列表接口不能自然等价于 1.0。
第四步:重写流事件解析器
流式迁移最容易出现“HTTP 200 但状态机错了”。v1.0 不再依赖旧 kind 字段,客户端应根据事件对象中的成员判断是状态更新还是 artifact 更新。
def apply_stream_event(event, state):
if event.task_status_update is not None:
return state.apply_status(event.task_status_update)
if event.task_artifact_update is not None:
return state.apply_artifact(event.task_artifact_update)
raise UnsupportedEvent("unknown A2A stream event")
示例刻意采用生成类型属性,而不是手写原始 JSON 键。生产实现还要验证:
- 同一个 task 的事件是否保持规范要求的顺序;
- 断流后是订阅恢复、轮询
GetTask,还是重新发送消息; - 重复 artifact chunk 是否可幂等合并;
- 终态由任务状态还是 transport 关闭确定;
- 多个并发订阅者是否得到一致的有序事件;
- 客户端取消连接是否会误取消远端业务任务。
如果前端还需要展示长任务进度,可以参考 FastAPI SSE 生产实战中的游标、断线恢复与代理缓冲边界,但不要把浏览器 SSE 的 Last-Event-ID 直接等同于 A2A task 状态。
第五步:把身份、任务和租户边界绑定
A2A 1.0 规范明确要求服务器只返回调用者可见的任务。实现时不能只在 ListTasks 过滤;GetTask、取消、订阅、artifact 下载和 webhook 管理都要使用同一授权谓词。
推荐把下列字段作为服务端状态的一部分:
tenant_id:任务所属租户;principal_id:创建或被授权的身份;task_id与context_id:协议级关联;message_id:幂等和追踪线索;policy_version:创建时采用的授权策略版本;protocol_version与binding:解释持久化事件所需的协议上下文。
认证凭据应放在 HTTP header 或 binding 规定的安全通道中,不要塞进 A2A message、artifact metadata 或 Agent Card。Agent 声称“我是财务机器人”不是授权证据;服务端必须以已验证身份与服务器侧 ACL 做决定。
第六步:建立幂等、超时与取消契约
A2A 规范说明部分操作天然幂等,SendMessage 可以借助 messageId 检测重复,但协议不会替你完成业务幂等。客户端超时可能发生在服务端创建任务之后,盲目重发会产生两个工作项。
一个可恢复的发送流程应是:
- 客户端生成稳定
messageId,并在同一业务意图的重试中复用; - 服务端原子记录
principal + messageId → taskId; - 重放返回原 task,而不是再次扣费或执行;
- 客户端超时后先查询已知 task / context,再决定是否重发;
- 取消只改变允许取消的状态,并保持重复取消安全;
- webhook 与流消费端按 event/task revision 去重。
这与 ZoyTown AgentStudio 这类多步骤 Agent 产品的通用工程边界一致:模型可以规划,但身份、状态转换和副作用必须由确定性系统约束。
第七步:用双栈灰度代替原地切换
官方 A2A Python SDK实现 1.0,并为 0.3 提供兼容模式。兼容模式不代表所有自定义扩展都自动兼容,仍应采用三阶段迁移:
阶段 A:服务端先支持双版本
保持 v0.3 行为不变,新增 1.0 endpoint / binding,在 Agent Card 中同时声明。所有 1.0 状态写入独立的兼容层,避免污染旧消费者。
阶段 B:按客户端灰度
先迁移内部测试客户端,再迁移低风险调用方。按版本观察成功率、任务终态比例、流恢复次数、未知事件数和授权拒绝原因;不要记录敏感正文。
阶段 C:移除 v0.3
只有当存量调用量归零、回滚窗口结束、持久任务全部能被 1.0 客户端读取时才移除。旧任务可能比旧流量活得更久,不能只看最近请求版本。
迁移测试矩阵应该覆盖什么?
| 测试 | 通过条件 |
|---|---|
| 发现与协商 | 客户端选择支持的版本;缺少必要 capability 时明确失败 |
| 直接响应 | 简单请求返回 Message,类型与内容稳定 |
| 长任务 | 创建、查询、列表、取消与终态转换符合规范 |
| 流式 | 状态和 artifact 正确判别;断流恢复不重做副作用 |
| 幂等 | 相同 messageId 重放不创建第二个任务 |
| 租户隔离 | 任何读、取消、订阅和 artifact 路径都不能越权 |
| 错误映射 | JSON-RPC、REST、gRPC 对同一失败给出语义等价错误 |
| webhook | 目标验证、签名、重试、去重和禁用流程完整 |
| 降级 | 版本不支持时不静默丢功能 |
| 观测 | trace 可关联 message/task,但不泄露凭据和用户内容 |
可把这些跨服务测试接入类似 Cronova 的周期工作流,但调度成功只代表测试运行完成,不代表兼容门禁通过;最终判定必须来自明确断言。
常见迁移失败
只改 SDK,没有迁移持久化事件
旧事件仍含 kind、旧 enum 或旧字段名,新消费者读历史 task 时失败。解决方法是版本化事件 envelope,并让读取端按写入版本转换。
Agent Card 同时声明两个版本,但都指向同一不兼容端点
声明与实际行为不一致会让客户端错误协商。每个 supportedInterfaces 项都必须有独立的 contract test。
用连接关闭判断任务成功
网络断开、代理超时和服务端滚动重启都可能关闭连接。成功必须来自协议终态,不来自 EOF。
只在列表接口做租户过滤
攻击者仍可枚举 taskId 调用详情、取消或订阅。所有入口要复用同一授权函数,并把“不存在”与“无权访问”的外部行为统一到安全策略。
FAQ
A2A 1.0 会替代 MCP 吗?
不会。官方将两者定位为互补层:MCP 连接单个 Agent 的工具与上下文,A2A 连接不同 Agent。系统可以只使用其中一个,也可以组合使用。
v0.3 客户端能直接调用 1.0 吗?
不能假设可以。1.0 有 operation、结构、事件和错误模型的 breaking changes。应使用明确兼容层或官方 SDK 的兼容能力,并用真实契约测试验证。
是否必须同时支持 JSON-RPC、REST 和 gRPC?
不必。选择业务需要且团队能完整运维的 binding,并在 Agent Card 中准确声明。多 binding 的功能、授权和错误语义必须等价,否则会形成安全与行为分叉。
最小可行迁移是什么?
冻结 v0.3 基线,增加 1.0 双栈 endpoint,迁移 Agent Card 与类型,修复流事件和任务授权,用幂等与断流测试灰度客户端,最后在存量任务可兼容后移除旧版本。
上线检查清单
- [ ] 锁定 A2A specification 与 SDK 版本。
- [ ] 保存 v0.3 黑盒行为基线。
- [ ] 每个
supportedInterfaces项通过 contract test。 - [ ] operation、Part、enum、时间和错误映射已迁移。
- [ ] 流事件不再依赖旧
kind/final。 - [ ] 所有任务入口复用同一租户授权。
- [ ]
messageId重放不会重复执行副作用。 - [ ] 断流、超时、取消、webhook 重试均可恢复。
- [ ] 降级不会静默丢失强制 capability。
- [ ] 日志与 trace 不包含 token、prompt 或 artifact 正文。
- [ ] 双栈灰度有回滚与旧任务读取方案。
A2A 1.0 迁移完成的标准不是“新客户端请求返回 200”,而是同一业务意图在发现、发送、任务、流、错误和授权边界上都有稳定、可恢复、可审计的语义。