适用版本:A2A 协议 1.0。规范网站标记的发布版本是 1.0.0,规范仓库当前补丁 release 为 v1.0.1;协议协商只使用
1.0,不使用补丁号。示例固定a2a-sdk==1.1.2,资料与代码核对日期为 2026-09-10。
当“调用另一个 Agent”不再是一个函数
设想一个采购 Agent 收到任务:“调查星河科技是否适合作为供应商。”它自己不掌握企业登记、制裁名单和诉讼数据,于是把尽调工作交给另一个团队维护的供应商尽调 Agent。
如果两个 Agent 在同一个进程、同一个代码仓库里,调用也许只是一行函数:
const report = await dueDiligenceAgent.run({ supplier: "星河科技" });但只要尽调 Agent 属于另一套系统,采购 Agent 就要先回答一串函数签名没有覆盖的问题:去哪里找到它?它支持什么输入?任务要跑十分钟时怎样看进度?缺少“注册地区”时如何暂停并补充?最终报告是一条回复,还是一个可下载文件?双方由不同厂商实现时,错误和状态还能否对得上?
宽泛地说,Agent-to-Agent 是一种把复杂目标交给另一个自主系统、由双方围绕任务继续协作的设计思想。 但大写的 Agent2Agent Protocol(A2A)已经是一套正式的应用层协议:它规定能力怎样公开、消息和产物长什么样、任务怎样流转,以及这些语义如何映射到 JSON-RPC、gRPC 与 HTTP+JSON。1
所以,“A2A 没有统一约定”这个判断需要拆成两半:
- 不准确的部分:线上的消息、任务、状态、错误、版本和标准 binding 已有版本化规范,v1.0 还以 Protocol Buffers 作为规范数据对象的权威定义。
- 准确的部分:A2A 不统一 Agent 内部怎样推理、怎样调用工具、怎样选择最合适的 Agent,也没有规定全世界唯一的 Agent 注册中心或业务语义本体。
这条边界正是理解 A2A 的入口:它统一的是两个独立 Agent 之间的“合作表面”,不是它们脑内的实现。
从 Google 提案到开放标准
A2A 这个缩写也可能泛指任何 agent-to-agent 方案,因此谈实现前必须先确认语境。本文研究的是最初由 Google 在 2025 年 4 月公开的 Agent2Agent Protocol。Google 当时发布的还是开放协议草案;同年 6 月,项目进入 Linux Foundation 治理。2026 年 3 月发布稳定的 v1.0,2026 年 8 月又成为 Agentic AI Foundation(AAIF)的 Growth Stage 项目。2 3
治理归属不能证明所有厂商实现都已互操作,却能否定“它只是一个没有规范的理念”。截至核对日期,项目提供完整规范、规范化 a2a.proto、Python / Go / JavaScript / Java / .NET / Rust SDK,以及官方示例和兼容性测试方向。SDK 不是协议本身;不用官方 SDK,只要线上行为符合规范,也可以实现 A2A。
A2A 究竟统一了哪一层
一次 A2A 协作至少有两个协议角色:A2A Client 发起交互,A2A Server 暴露远端 Agent。Client 可以是另一个 Agent,也可以只是普通应用或服务;“调用方必须是一个会自主推理的 Agent”并不是协议要求。4
查看 Mermaid 源码
flowchart LR
user[采购人员] --> buyer[采购 Agent<br/>A2A Client]
buyer -->|读取 Agent Card| card[能力、接口、版本、鉴权]
buyer -->|A2A Message / Task| remote[尽调 Agent<br/>A2A Server]
remote -->|内部实现,不由 A2A 规定| runtime[工作流或 Agent 框架]
runtime -->|可选:MCP| tools[企业登记、制裁名单等工具]
remote -->|Artifact / 状态更新| buyer采购 Agent 只依赖尽调 Agent 暴露的协议表面;尽调 Agent 内部可以换模型、框架或工具,而不必向采购 Agent 公开记忆和推理过程。
A2A v1.0 把协议分成三层:
| 层次 | 统一的内容 | 允许变化的内容 |
|---|---|---|
| Canonical Data Model | AgentCard、Message、Part、Task、Artifact、错误等对象 | Agent 内部状态、提示词、模型与工具 |
| Abstract Operations | Send Message、Get / List / Cancel / Subscribe Task、Push 配置等语义 | 调度器、队列、数据库与业务实现 |
| Protocol Bindings | JSON-RPC、gRPC、HTTP+JSON 的方法、端点、错误和流映射 | 选择哪一种标准 binding;也可定义满足等价要求的自定义 binding |
三个标准 binding 不是三套各说各话的协议。规范要求同一 Server 暴露多个 binding 时,功能、结果、错误和鉴权必须语义等价;AgentCard.supportedInterfaces 按偏好顺序列出 URL、protocolBinding 和 protocolVersion,Client 再选择自己支持的接口。5
这也解释了 A2A 与 HTTP 的关系:HTTP 解决消息怎样送达,A2A 解决“能力、任务、状态与产物”在业务上是什么意思。A2A 的 HTTP+JSON binding 本身就是 REST 风格接口,并不是 REST 的竞争者。
四个对象把一次协作串起来
上一节说明了协议分层,却还没有解释两个 Agent 实际交换什么。回到供应商尽调任务,四个对象依次补上四个缺失事实。
Agent Card:先知道对方会什么、在哪里
尽调 Agent 在 /.well-known/agent-card.json 发布 AgentCard。Card 声明名称、版本、能力、输入输出媒体类型、鉴权方案、技能,以及一个有序的 supportedInterfaces 列表。Client 读它来选择远端 Agent 和连接方式。6
AgentSkill 更像可发现的能力说明,而不是远程函数的强类型签名。规范也把 Skill 描述为“largely descriptive”。两端即使都能解析 A2A,也可能对“高风险供应商”的口径理解不同。需要精确业务互操作时,仍应为 DataPart、扩展 URI、字段 schema 和业务错误建立可测试的语义契约。
发现也没有被完全包办。well-known URI 统一了“已知域名后去哪里拿 Card”,但 Client 怎样先得到这个域名,可以来自配置、私有目录、企业市场或未来的发现基础设施。A2A 没有指定一个全球唯一注册中心。
Message 与 Part:交换的是上下文,不是内部思维
Client 用 Message 发起或继续交互。role 表示用户侧或 Agent 侧,parts 可以承载文本、结构化数据、原始字节或 URL 文件引用,messageId 标识消息;多轮时还可带 taskId 与 contextId。
Part 负责内容形状,Message 负责一次交互意图。A2A 不要求远端公开 chain-of-thought、工具清单或内存;Client 只传完成任务所需的上下文,Server 只暴露约定的状态、消息和产物。
Task:把长任务从一次 HTTP 请求中解放出来
远端 Agent 收到 Message 后,可以直接返回一个无状态 Message,也可以创建有状态 Task。供应商尽调需要补资料、可能长时间运行,所以更适合 Task。
查看 Mermaid 源码
stateDiagram-v2
[*] --> SUBMITTED
SUBMITTED --> WORKING
WORKING --> INPUT_REQUIRED: 缺少注册地区
INPUT_REQUIRED --> WORKING: 同一 taskId 补充信息
WORKING --> COMPLETED: 生成报告
WORKING --> FAILED: 执行失败
SUBMITTED --> REJECTED: 不接受任务
WORKING --> CANCELED: 取消成功INPUT_REQUIRED 与 AUTH_REQUIRED 是可继续的 interrupted state;COMPLETED、FAILED、CANCELED、REJECTED 是 terminal state。规范禁止继续向 terminal Task 发送消息。contextId 可以把多个相关 Task 和独立 Message 归入同一上下文;taskId 则指向其中一个具体工作单元。7
Artifact:完成任务后交付可使用的结果
进度说明属于 Message 或 Task status,尽调报告属于 Artifact。Artifact 同样由多个 Part 组成,可以是文本、JSON、文件字节或 URL。
这个区分看似细小,却决定了调用方怎样消费结果。Client 不应从“正在查询制裁名单”之类的状态消息中猜最终结论,而应在 Task 完成后读取 Artifact。规范明确指出 Task history 未必保存全部 Message,流断线后也未必补发所有状态消息;关键业务事实不能只寄托在瞬时消息上。8
一条任务是怎样跑完的
把四个对象放回调用顺序,A2A 的心智模型可以压缩成一句话:Client 先用 Agent Card 选择一个不透明的远端能力,再用 Message 推进一个可观察的 Task,最后消费 Artifact。
查看 Mermaid 源码
sequenceDiagram
participant C as 采购 Agent / Client
participant S as 尽调 Agent / Server
C->>S: GET /.well-known/agent-card.json
S-->>C: Agent Card(JSONRPC, A2A 1.0)
C->>S: SendMessage(请尽调星河科技)
S-->>C: Task = INPUT_REQUIRED
C->>S: SendMessage(同一 taskId/contextId,地区:中国)
S-->>C: Task = WORKING
S-->>C: Artifact = 尽调报告
S-->>C: Task = COMPLETED非流式调用可以在 interrupted 或 terminal state 返回;交互式界面可以使用 streaming,长时间的服务间任务可以轮询 GetTask 或注册 push notification webhook。它们改变更新怎样送达,不改变 Task 与 Artifact 的语义。
用官方 SDK 跑通一次中断与恢复
下面的实验使用 Python 3.13.3 和 a2a-sdk==1.1.2。业务逻辑是确定性的,不调用模型;Starlette 的进程内 ASGI transport 让完整 Client、路由、序列化和 Task Store 在一个进程内运行,不占用本地端口。
从仓库根目录执行:
python3 -m venv /tmp/a2a-protocol-demo-venv
/tmp/a2a-protocol-demo-venv/bin/python -m pip install -r examples/a2a-protocol/requirements.txt
/tmp/a2a-protocol-demo-venv/bin/python examples/a2a-protocol/user_code/run_demo.py完整入口位于仓库的 examples/a2a-protocol/user_code/run_demo.py。正文按同一调用链精简代码,只保留理解协议所需的部分;可执行版本还包含参数校验、Task / Context 关联和资源关闭。
第一步,Server 用 AgentCard 描述能力和接口。protocol_version 是线上 A2A 兼容版本;AgentCard.version 则是这个 Agent 服务自身的版本,两者不能混用。
card = AgentCard(
name="供应商尽调 Agent",
description="演示 A2A 发现、任务中断和多轮恢复",
version="0.1.0",
supported_interfaces=[
AgentInterface(
url="http://due-diligence.test",
protocol_binding="JSONRPC",
protocol_version="1.0",
)
],
capabilities=AgentCapabilities(streaming=False),
default_input_modes=["text/plain"],
default_output_modes=["text/plain"],
skills=[skill],
)第二步,项目实现 AgentExecutor 作为协议适配器。它从 RequestContext 读取 Message 和已有 Task,通过 TaskUpdater 写状态与 Artifact。真正的 LangGraph、内部服务或模型调用应放在 execute 中,而不是泄漏给 A2A Client。
async def execute(self, context: RequestContext, event_queue: EventQueue) -> None:
task = context.current_task
if task is None:
task = new_task_from_user_message(context.message)
await event_queue.enqueue_event(task)
updater = TaskUpdater(event_queue, context.task_id, context.context_id)
collected_input = "\n".join(
[*(get_message_text(message) for message in task.history),
context.get_user_input()]
)
if "地区:中国" not in collected_input:
await updater.requires_input(
new_text_message("请补充供应商注册地区,例如:地区:中国")
)
return
await updater.start_work(new_text_message("正在核对供应商信息"))
await updater.add_artifact(
[new_text_part("供应商:星河科技\n注册地区:中国\n教学结论:未发现高风险项")],
name="due-diligence-report",
)
await updater.complete(new_text_message("供应商尽调已完成"))第三步,Server 组装 Card route、Request Handler、Task Store 和 JSON-RPC route;Client 先解析 Card,再由 create_client 根据 Card 创建相容 transport。第二条 Message 明确复用第一轮返回的 task.id 与 task.context_id。
resolver = A2ACardResolver(httpx_client=http_client, base_url=BASE_URL)
card = await resolver.get_agent_card()
client = await create_client(
agent=card,
client_config=ClientConfig(streaming=False, httpx_client=http_client),
)
first = await send(
client,
new_text_message("请尽调星河科技", role=Role.ROLE_USER),
)
task = first.task
assert task.status.state == TaskState.TASK_STATE_INPUT_REQUIRED
second = await send(
client,
new_text_message(
"地区:中国",
role=Role.ROLE_USER,
task_id=task.id,
context_id=task.context_id,
),
)
assert second.task.id == task.id
assert second.task.status.state == TaskState.TASK_STATE_COMPLETED实测输出是:
发现: 供应商尽调 Agent / supplier-due-diligence
协商: JSONRPC A2A 1.0
第一轮: TASK_STATE_INPUT_REQUIRED
第二轮: TASK_STATE_COMPLETED
供应商:星河科技
注册地区:中国
教学结论:未发现高风险项这条轨迹证明了三件事:Card 可以被标准路径发现,Client 与 Server 能按 v1.0 JSON-RPC 交换协议对象,中断后的第二条消息能继续同一个 Task。它没有证明内存存储可以跨重启恢复,也没有证明两个不同厂商的业务语义天然一致。
从教学 Demo 到生产系统,还缺哪些约束
协议把网络边界稳定下来,生产可靠性仍然属于实现。最容易低估的不是序列化,而是 Task 生命周期跨越请求、进程和组织边界以后产生的责任。
Task Store 不能停在内存里
示例的 InMemoryTaskStore 在进程退出后丢失全部 Task,多 worker 也不会天然共享状态。生产 Server 至少要持久化 Task 当前状态、Artifact、必要历史、调用者作用域和版本信息;后台执行器还要能在重启后判断任务应恢复、失败还是等待人工输入。
A2A 规定 Client 看见的 Task 状态,不规定你的 LangGraph checkpoint、队列 offset 或数据库事务。若远端 Agent 内部也有工作流状态,应显式保存 a2a_task_id ↔ workflow_run_id 映射,避免误以为两个 ID 会自动相等。
messageId 不是可靠副作用的充分条件
规范说 Send Message 可以利用 messageId 实现幂等,而不是强制所有 Server 去重。第一次尽调已经触发付费查询但响应丢失时,Client 重发同一 Message,Server 仍可能再次执行。
建议在业务入口建立独立幂等记录,例如 (caller_id, operation_id) 唯一约束,并保存请求指纹、Task ID 与结果。messageId 用来识别协议消息,operation_id 用来识别“仍是同一次付费尽调”;两者生命周期相同的简单系统可以映射,不能未经声明就假设等价。涉及付款、发信或工单创建时,下游系统也必须接受相同的业务幂等键。
鉴权声明不等于授权实现
Agent Card 可以声明 API Key、HTTP Auth、OAuth 2.0、OpenID Connect 或 mTLS;生产 HTTP binding 必须使用 HTTPS,Client 应验证服务端证书。凭证通过协议外的安全流程取得,再随请求发送。9
Server 仍要在每个操作上校验当前主体是否能读取、列出、取消或订阅目标 Task。tenant 是路由用的不透明字段,不是可信权限证明;知道一个 taskId 也不等于拥有它。扩展 Card 可能暴露额外技能,但必须在返回前做访问控制。
更新通道要按生命周期选择
| 机制 | 适合 | 主要代价 |
|---|---|---|
GetTask 轮询 | 更新少、网络限制多、实现先求稳 | 延迟较高,会产生空轮询 |
| SSE streaming / subscribe | 交互界面、频繁进度 | 长连接、代理缓冲、断线处理 |
| Push notification | 小时级任务、服务到服务回调 | webhook 鉴权、重试、重复投递与 SSRF |
规范要求流事件按生成顺序交付,但不保证断线后补齐所有历史状态 Message。Client 应把 GetTask 当作恢复最终状态的基线,而不是把收到过的 SSE 文本拼成真相。Webhook 接收方则要按重复投递设计幂等处理;Server 必须验证回调 URL,阻止访问 localhost、私网和 link-local 地址。10
可观测性必须跨过协议边界
建议至少关联 messageId、taskId、contextId、业务 operation_id、调用方身份和 trace context,并分别记录协议延迟、排队时间、Agent 执行时间、模型/工具成本与最终状态。日志不能记录 token、凭证或未经处理的敏感 Artifact。
A2A v1.0 没有替你规定成本预算、SLO、trace backend 或模型评估。一个请求在协议上 COMPLETED,只表示 Server 宣布完成,不表示报告事实正确。调用方还要验证 Artifact schema、来源、质量门槛与允许产生的副作用。
三条值得主动触发的失败路径
失败一:照抄 v0.3 示例,v1.0 Server 拒绝请求
症状:请求中仍使用 {"kind":"text","text":"..."},方法名还是 message/send,或者 REST 路径带旧的 /v1/ 前缀;Server 返回 invalid params、method not found 或版本不支持。
原因:v1.0 移除了 Part.kind,改为字段存在即判别类型;标准方法与路径也经历了 breaking change。协议版本只协商 major.minor,Client 每次请求应发送 A2A-Version: 1.0。11
处理与验证:先读取 Card 中每个 supportedInterfaces[].protocolVersion,锁定对应 SDK;用 v1.0 形状 {"text":"..."} 发送。集成测试同时断言请求版本、binding 和解析后的 Part,而不是只看 HTTP 200。
失败二:超时后重发,远端副作用执行两次
症状:同一供应商产生两次付费查询、两张工单或两封邮件,但 Client 只看到一次成功响应。
原因:网络超时不能说明第一次没有执行;A2A 只允许 Server 借 messageId 去重,没有强制 Send Message 幂等。
处理与验证:首次业务意图生成稳定 operation_id,传输重试复用它;Server 以调用者和 operation 建唯一约束并返回原 Task。故障测试在“副作用已成功、响应发送前”断开连接,再重试并断言下游记录仍只有一条。
失败三:SSE 重连成功,界面却永远停在 WORKING
症状:网络恢复后连接再次建立,但 Client 错过了 COMPLETED 更新,或只看到后续 Artifact chunk。
原因:规范明确提醒 streaming Client 断线重连后可能收不到全部 status message;Message 不是关键通知的可靠投递通道。
处理与验证:断线后调用 GetTask(taskId) 重建权威状态与 Artifact,再恢复订阅;界面 reducer 按 Task / Artifact ID 去重。测试应在 WORKING 与 COMPLETED 之间主动断流,确认最终仍能从 GetTask 收敛到 COMPLETED。
A2A、MCP、Agent 框架和任务队列怎样选
它们解决的不是同一个问题:
| 方案 | 它稳定的边界 | 更适合什么现场 |
|---|---|---|
| 普通函数 / 模块接口 | 同一代码库中的调用契约 | 同团队、同进程、可一起发布 |
| HTTP API / 任务队列 | 已知服务的资源或异步作业契约 | 固定调用方与固定业务 schema,不需要 Agent 发现和通用 Task 语义 |
| Agent 框架 | 一个 Agent 系统内部的规划、状态与工具编排 | LangGraph、ADK、CrewAI 等内部实现 |
| MCP | Agent 如何发现并使用工具、资源和提示模板 | 数据库、GitHub、文件、业务 API 等“垂直能力” |
| A2A | 独立且不透明的 Agent 如何发现、委派并围绕任务协作 | 跨框架、跨厂商、跨组织的“水平协作” |
一个尽调 Agent 可以内部使用 LangGraph 管理状态,通过 MCP 查询企业数据库,同时向外暴露 A2A。三者可以位于同一条调用链,彼此不替代。
如果只是页面后端调用一个已知的风险评分 API,普通 HTTP 已经足够;硬套 Agent Card、Task Store 和多轮状态会增加部署与运维成本。如果远端能力会自主分解任务、主动请求补充信息、产生多种 Artifact,并且调用方不能依赖其内部实现,A2A 的抽象才开始产生价值。
一条稳妥的落地顺序
在真实项目中,我会按边界而不是按 SDK 功能数量推进:
- 先选一个跨系统任务:写清输入、Artifact schema、最长耗时、可取消性和人工介入点;不要先把所有内部子 Agent 都网络化。
- 先做 Card + 非流式 Task:固定 A2A
1.0和一种 binding,跑通发现、Send Message、Get Task、INPUT_REQUIRED 与 terminal states。 - 再补持久化与身份:让 Task 跨进程恢复,把调用者、租户、Task 与内部 workflow 映射写进数据库,并对每个读取和变更操作授权。
- 再补幂等与故障实验:覆盖响应丢失、并发重试、执行器重启、取消竞争和 Artifact 重复写入。
- 最后选择更新机制:确有实时进度需求再开 SSE;确有长时服务回调需求再开放 webhook,并加入 SSRF 防护、签名、退避与重复消费。
- 用第二种实现验证互操作:至少让不同语言或不同 SDK 的 Client / Server 互测 Card、版本、错误、Task 状态和 Artifact,而不只是同一 SDK 自说自话。
回到开头的问题:A2A 的“思想”是把 Agent 当作拥有自主执行能力、可以围绕任务协作的不透明伙伴;A2A 的“协议”则把这种伙伴关系压缩成可发现、可版本化、可跨实现验证的线上契约。今天再把它说成“并没有统一约定落实”已经不准确;更准确的工程判断是:线上的合作语义已有标准,Agent 的内部智能、全局发现、业务本体和生产可靠性仍由实现负责。
参考资料
- A2A Protocol Specification(官方规范,核对日期:2026-09-10)
- A2A Protocol Definitions(规范化 Protocol Buffers 定义,核对日期:2026-09-10)
- A2A Core Concepts(官方文档,核对日期:2026-09-10)
- Life of a Task(官方文档,核对日期:2026-09-10)
- Agent Discovery(官方文档,核对日期:2026-09-10)
- What’s New in A2A v1.0(官方迁移说明,核对日期:2026-09-10)
- A2A Python Quickstart(官方教程,核对日期:2026-09-10)
- a2aproject/A2A releases(规范仓库 release,核对日期:2026-09-10)
- a2aproject/a2a-python v1.1.2(示例固定 SDK 源码,核对日期:2026-09-10)
Footnotes
-
A2A v1.0 规范定义三层结构:Canonical Data Model、Abstract Operations 和 Protocol Bindings;
a2a.proto是协议数据对象与请求/响应消息的权威规范定义。 ↩ -
Google 初始公告发布于 2025-04-09;Linux Foundation 公告发布于 2025-06-23;v1.0 公告发布于 2026-03-12。 ↩
-
A2A 加入 AAIF 的官方公告说明它在 2026-08-27 被接受为 Growth Stage 项目。 ↩
-
Core Concepts把 A2A Client 定义为代表用户发起通信的应用、服务或另一个 Agent;A2A Server 是暴露协议端点的不透明 Agent 系统。 ↩
-
规范第 5 节要求多个 binding 功能等价,并给出 JSON-RPC、gRPC 与 HTTP+JSON 的操作映射。 ↩
-
Agent Discovery规定 Agent Card 的作用与
/.well-known/agent-card.json,同时列出 well-known、私有配置和目录等不同发现策略。 ↩ -
Task 数据模型与生命周期区分无状态 Message、可中断 Task 与 terminal states;
contextId负责关联上下文,taskId标识具体任务。 ↩ -
规范 Messages and Artifacts指出 Task history 不保证持久化全部 Message,streaming 重连也可能丢失 status message。 ↩
-
规范 Authentication and Authorization声明生产传输、凭证获取与每请求认证要求;具体授权边界由 Agent 实现。 ↩
-
规范 Push Notification Security要求回调鉴权,并建议超时、指数退避、重复消费和 SSRF 防护。 ↩
-
v1.0 变化说明与规范迁移附录记录了 Part discriminator、操作命名、版本协商和 REST 路径等 breaking changes。 ↩