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/sendtasks/get 等路径式名称 改为 SendMessageGetTaskListTasksCancelTask 等统一 operation
Agent Card 顶层 protocolVersion,分散的 transport 字段 使用 supportedInterfaces[],每个接口声明 urlprotocolBindingprotocolVersion
流事件 依赖 kindfinal 判别 根据 taskStatusUpdatetaskArtifactUpdate 成员判别;连接结束语义交给 binding
Part 多个分离类型 使用统一 Part 结构与明确成员
错误 各 SDK/transport 容易自定义 映射到规范化错误与 google.rpc.Status / ErrorInfo
任务发现 没有标准 ListTasks 增加带过滤和分页的任务列表,并强制按调用者隔离
时间 字段和格式不够统一 使用 UTC ISO 8601,按规范处理 createdAtlastModified

不要把表格当成完整 schema diff。实际迁移要锁定你使用的 binding、SDK 版本和扩展,再以生成类型或规范 schema 对所有请求、响应和持久化事件做扫描。

第一步:先锁定现有行为,而不是立即升级依赖

在变更 SDK 前,为当前 v0.3 服务建立黑盒基线:

  • 当前 Agent Card 的公开地址、缓存策略和鉴权前后差异;
  • 已支持的消息、任务、取消、流式和 webhook 路径;
  • MessageTask 的返回条件;
  • 相同 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 还要统一:

  1. request/response 类型;
  2. MessageTask 的联合返回;
  3. ID、时间戳、分页 token;
  4. 取消的允许状态;
  5. transport 错误到领域错误的映射;
  6. 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_idcontext_id:协议级关联;
  • message_id:幂等和追踪线索;
  • policy_version:创建时采用的授权策略版本;
  • protocol_versionbinding:解释持久化事件所需的协议上下文。

认证凭据应放在 HTTP header 或 binding 规定的安全通道中,不要塞进 A2A message、artifact metadata 或 Agent Card。Agent 声称“我是财务机器人”不是授权证据;服务端必须以已验证身份与服务器侧 ACL 做决定。

第六步:建立幂等、超时与取消契约

A2A 规范说明部分操作天然幂等,SendMessage 可以借助 messageId 检测重复,但协议不会替你完成业务幂等。客户端超时可能发生在服务端创建任务之后,盲目重发会产生两个工作项。

一个可恢复的发送流程应是:

  1. 客户端生成稳定 messageId,并在同一业务意图的重试中复用;
  2. 服务端原子记录 principal + messageId → taskId
  3. 重放返回原 task,而不是再次扣费或执行;
  4. 客户端超时后先查询已知 task / context,再决定是否重发;
  5. 取消只改变允许取消的状态,并保持重复取消安全;
  6. 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”,而是同一业务意图在发现、发送、任务、流、错误和授权边界上都有稳定、可恢复、可审计的语义。