KEEL · 龙骨 · A CURRICULUM FOR THE AI ERA
01 · A2A 解决什么,不解决什么? — keel 龙骨
## 现场:两个 Agent,两套框架,一次谁都不肯让的集成
现场:两个 Agent,两套框架,一次谁都不肯让的集成
Acme 有两个团队各做了一个 Agent。
SRE 团队的事件响应助手跑在 LangGraph 上,能读指标、查工单。平台团队的变更分析 Agent 跑在另一套框架上,能读部署记录、判断某次发布是否可疑。两个 Agent 都是各自团队的资产,谁也不会把源码、提示词和数据库凭据交给对方。
应急响应时人肉串一次要四十分钟:值班工程师在 SRE 助手里问一句,把结论复制出来,再贴进变更分析 Agent,等它回答,再粘回 SRE 助手。
于是有了三个集成方案,三个都失败了:
方案一:给变更 Agent 直接开数据库只读账号。 安全评审直接否了——你给了它读数据的权限,就等于把数据模型的耦合也给了它;哪天表结构改了,两个团队一起挂。
方案二:让 SRE 助手直接调变更 Agent 的内部 HTTP 接口。 上线两周就出事:对方改了字段名没通知,SRE 助手把 deploy_id 当成了 release_id 填进去,返回空,助手回答「没有可疑变更」。值班的人信了。
方案三:把变更分析的提示词复制进 SRE 助手。 结果是上下文暴涨,两个团队的提示词互相污染,谁都说不清一次结论到底依据了什么。
先停一下,预测三个方案的共同点:它们都在试图打通两个系统的内部。而 A2A 的思路正好相反。
直觉模型:MCP 是竖向的,A2A 是横向的
一句话分清两条协议:
| 方向 | 边界 | 典型问题 | |
|---|---|---|---|
| MCP | 竖向:Agent 接工具和数据源 | Agent 与它自己的工具 | 这个工具有没有、能不能调、参数对不对 |
| A2A | 横向:Agent 接另一个 Agent | 一个自治系统与另一个自治系统 | 委派什么、进度如何、产物怎么取回 |
一个 Agent 节点通常同时用两者:对外用 A2A 接别的 Agent,对内用 MCP 接自己的工具。它们不是竞争关系,而是同一张图上的两条边。
精确定义:三个角色与一份不透明约定
一次 A2A 交互里有三个角色(来源:a2a-protocol.org v1.0.1 Core Concepts,检索于 2026-10-05):
| 角色 | 是什么 | 关键性质 |
|---|---|---|
| User | 发起请求的人或自动化服务 | 不一定是人 |
| A2A Client(Client Agent) | 代表用户发起通信的一方 | 今天它是客户端,明天它也可能是别人的服务端 |
| A2A Server(Remote Agent) | 暴露 A2A 端点、受理任务的一方 | 对调用方不透明(opaque) |
「不透明」是整个协议的地基:调用方看不到对方的提示词、模型、记忆和内部工具,也不需要看到。它只看得见四样东西——卡面(Agent Card)、任务(Task)、消息(Message)里的 Part、以及产物(Artifact)。
规范把 A2A 不是什么也写得很清楚(同一来源):
- 不是 Agent 开发框架。LangGraph、CrewAI、ADK 负责怎么造 Agent,A2A 只负责造好之后怎么说话。
- 不是子 Agent 或工具调用协议。你自己 Agent 内部的 sub-agent 怎么拆、工具怎么调,用框架的原生能力或 MCP。
- 不是 MCP 的替代品。
- 不是聊天软件。它是机器对机器的协议。
这四条里最容易踩的是第二条:A2A 不做你系统内部的编排。你团队内部的 Supervisor / Worker / Handoff 属于多 Agent 协作那一层,A2A 只在两个自治系统之间那一段起作用。
全链路图
先看形状:左边的进程、右边的进程、中间那条协议边界,以及失败时断在哪。
flowchart TD
subgraph LOCAL["本地系统 —— 你负责"]
U[用户目标] --> CA[Client Agent 编排方]
CA --> MCPL[本地工具 走 MCP]
end
subgraph BOUNDARY["协议边界 —— A2A 负责"]
DISC["① 发现 GET /.well-known/agent-card.json"]
SEND["② SendMessage 委派"]
BACK["③ 返回 Task 或 Message"]
FETCH["④ GetTask 或流式或推送"]
end
subgraph REMOTE["远端系统 —— 不透明"]
RA[Remote Agent] --> MCPR[它自己的工具 走 MCP]
RA --> INTERNAL[它自己的模型 记忆 提示词]
end
CA --> DISC
DISC -->|版本或绑定不匹配| ERR["错误 reason=VERSION_NOT_SUPPORTED"]
DISC -->|兼容| SEND
SEND --> RA
RA --> BACK
BACK --> CA
CA --> FETCH
FETCH --> RA
RA -.->|长任务未完 需补输入| BACK
ERR -.-> CA
图里最关键的是中间那一层:跨进程、可失败、且失败时只回一个 reason,不会告诉你远端内部发生了什么。后面十章就是把这个边界上的每一件事讲清楚。
一次完整运行
python courses/foundation/a2a-protocol-engineering/course/project/examples/01_discovery.py
实跑输出:
A. 发现路径: /.well-known/agent-card.json
B. 必填字段 8 个 -> 缺失: 无
C. 首选接口: JSONRPC 1.0 @ https://agents.acme.internal/a2a/incident-bridge
D. 只会 0.3 的客户端 -> no compatible A2A interface
E. 只想走 gRPC -> no compatible A2A interface
F. 卡上声明的能力: {'streaming': True, 'pushNotifications': True, 'extendedAgentCard': False}
G. 公开技能: ['service.status', 'diagnose.incident']
逐行读:
- 发现靠一个约定路径,不需要任何注册中心或配置同步——知道对方的域名就够了。
- 卡面有 8 个必填字段,少一个就不是合法卡(第 02 章逐个展开)。
- 卡上同时写了协议版本和绑定方式,双方能不能聊得起来,在读卡这一步就能判断完。
- D 和 E 是两种不兼容:版本不对、绑定不对,都是「没有可用接口」。注意这里返回的是失败原因,不是「连上了但聊不明白」——在边界上早失败,比在业务里晚失败便宜得多。
失败注入:把「连上了」当成「聊得来」
最典型的误判是:拿到了一张卡,就以为可以发消息了。试一试——把客户端能接受的版本改成 0.3:
choose_interface(card, versions=("0.3",))
实跑输出:
D. 只会 0.3 的客户端 -> no compatible A2A interface
它不会给你一个「勉强可用」的接口。这是刻意的设计:协议版本不匹配必须早失败。如果这里放行,后面每一个字段名、每一个枚举值都可能对不上,而错误会藏在业务语义里——就像现场那个 deploy_id / release_id 的事故。
生产边界
| 教学实现 | 生产替换 |
|---|---|
| 内存里的卡面字典 | HTTP 服务在 /.well-known/agent-card.json 上真发布,带缓存与 ETag |
| HMAC bearer 替身 | 卡面声明的真实方案(OAuth 2.0 授权码加 PKCE、Device Code、mTLS) |
| 进程内函数调用 | JSON-RPC over HTTPS、gRPC 或 HTTP+JSON 三种绑定之一 |
| 未签名卡 | JWS 签名卡 + 规范化(RFC 8785),否则无法防伪造(第 08 章) |
| 单一组织内的两个角色 | 跨组织、跨信任域,凭据与租户隔离 |
练习与验收
练习(有可观察结果):给 minimal_card_payload 加第二个接口(同一个 url,protocolBinding 用 GRPC,protocolVersion 用 1.0),然后分别用 binding="JSONRPC" 和 binding="GRPC" 调 choose_interface,断言两次拿到的是不同条目;再断言不传 binding 时拿到的永远是数组第一项。
验收标准:不传 binding 的结果必须等于 card["supportedInterfaces"][0]。如果你的实现按优先级数值排序或按字母排序,说明你把「有序数组」当成了「集合」——规范里第一项就是首选,排序权在服务端。
本章检查点
- 现场三个方案为什么都失败了?它们的共同假设是什么?
- 「不透明」具体不透明了什么?调用方还能看见哪四样东西?
- 什么时候该用 A2A,什么时候它帮不上忙?(举一个你系统内部的场景)
现在能解释什么
你现在能把 A2A 和其他几层分开:它是两个自治系统之间的协议,不是框架、不是内部编排、不是 MCP 的替代品。你也知道它最核心的约定是「不透明」——调用方拿不到对方内部,只能拿到卡面、任务、消息和产物,而这个限制正是它能跨组织工作的原因。下一章讲这套约定的入口:Agent Card 到底写了什么。