数据截至 (上游 commit e1ea56db72a6)
AP2 — 并存的两代数据模型(诚实章)
这一章解释一个会让初读者困惑的事实:同一个仓库里有两套 mandate 模型,名字都不一样。 不是 bug,是协议从 v0.1 走向 v0.2 的演进现场。读懂它你才能正确地把「文档说的」对到「代码做的」。
1. 两套模型,一眼对照
仓库里两条 import 路径,代表两代设计:
旧代:ap2/models/ | 新代:ap2/sdk/generated/ | |
|---|---|---|
| mandate 类型 | IntentMandate / CartMandate / PaymentMandate | OpenCheckoutMandate / CheckoutMandate / OpenPaymentMandate / PaymentMandate |
| 怎么定义 | 手写 Pydantic 模型 | 由 JSON Schema 用 datamodel-codegen 生成 |
| 安全绑定方式 | 字段里塞商家签的 JWT + 哈希(merchant_authorization / user_authorization) | 整条 SD-JWT-VC 委托链,跳间用 sd_hash 绑 |
| 版本标记 | 无 vct | 有 vct(如 mandate.payment.1)做 schema 版本 |
| 对应规范 | v0.1 时代的 Cart/Intent 概念 | 规范 v0.2(specification.md 的 Checkout/Payment Mandate) |
| 文件位置 | code/sdk/python/ap2/models/mandate.py | code/sdk/python/ap2/sdk/generated/*.py |
旧代的 mandate(回顾)
旧 PaymentMandate 是个两段式结构:payment_mandate_contents(明文数据)+ user_authorization(对 cart+payment 双哈希的可验证表述)(code/sdk/python/ap2/models/mandate.py:167-203)。CartMandate 则把商家担保塞进 merchant_authorization JWT,内含 cart_hash(mandate.py:107-136)。注意它复用了 W3C Payment Request API 的对象(PaymentRequest/PaymentResponse,code/sdk/python/ap2/models/payment_request.py:184,204)——这是它「贴着浏览器电商生态」的痕迹。
新代的 mandate(回顾)
新 PaymentMandate 是个干净的扁平模型,带 vct='mandate.payment.1'、transaction_id(= checkout_jwt 的哈希)、payee、payment_amount(整数最小货币单位)、payment_instrument 等(code/sdk/python/ap2/sdk/generated/payment_mandate.py:16-55)。它不自带签名字段——签名和绑定全交给外层的 SD-JWT 链(第 02 章)。
2. 哪套用在哪(真实 grep 结果)
这不是猜测,是仓库里 import 的事实:
| 样例角色 | 用哪套模型 | 传输 |
|---|---|---|
shopping_agent(原始) | ap2.models(Intent/Cart) + ap2.sdk | A2A |
merchant_agent(原始) | ap2.models + ap2.sdk | A2A |
shopping_agent_v2 | ap2.sdk(SD-JWT 链) | A2A |
*_mcp 角色(merchant/credentials/mpp/x402) | ap2.sdk | MCP |
原始 A2A 样例的子 agent(shopper / payment_method_collector / catalog_agent)仍 import ap2.models(code/samples/python/src/roles/shopping_agent/subagents/shopper/tools.py 等),而 shopping_agent_v2 和所有 MCP 角色走 ap2.sdk(code/samples/python/src/roles/shopping_agent_v2/shopping_agent/mandate_tools.py、code/samples/python/src/roles/*_mcp/server.py)。
给 agent 的实用判断: 看到
IntentMandate/CartMandate/PaymentRequest→ 旧 A2A 路径;看到OpenPaymentMandate/MandateClient/vct→ 新 SD-JWT 路径。规范 v0.2(specification.md)描述的是新路径;旧路径是过渡期的参考实现。
3. 为什么会并存(推断)
规范头部写明是 v0.2(specification.md:1),并明确用 SD-JWT 承载 Checkout/Payment Mandate(specification.md:106-107,396-401),还引入了 vct schema 版本号(specification.md:137-143)。SDK README 也整体围绕 SD-JWT/dSD-JWT 重写(code/sdk/python/ap2/sdk/README.md:1-7)。
但仓库本质是 「code samples and demos」(README.md:11),原始 A2A 演示是较早写就、贴着 W3C Payment Request + 商家签 JWT 的那套;新的 SD-JWT 运行时和 shopping_agent_v2、MCP 角色是后来对齐 v0.2 加上的。两者没有互删,于是同仓共存。(此段为基于版本 号、import 分布、README 重写迹象的合理推断 (inferred);仓库里没有一处明文写「我们正在迁移」。)
4. vct 版本控制:新代的演进保险
新代埋了一个面向未来的机制:每个 mandate 用 vct 声明 schema 版本,带数字后缀(mandate.payment.1、mandate.checkout.open.1)。规范要求实现精确匹配整个 vct 串(含版本后缀),未来不兼容的修订会换新后缀(.2),让新旧版本无歧义区分(specification.md:137-143)。代码里 vct 就是个带默认值的 Literal 字段(code/sdk/python/ap2/sdk/generated/payment_mandate.py:21-24)。
这正是「文档随上游演进而能区分新旧」的协议级保险——和本库「引用带符号名以抗行号漂移」是同一种思路。
5. 横向对比(ai-protocol-reference shelf 内)
AP2 在协议库里的独特取舍:
- 传输无关 vs 传输绑定。 AP2 刻意不绑传输——示例同时跑 A2A 和 MCP(
code/samples/python/src/common/a2a_extension_utils.py:17定义了 A2A 扩展 URI)。它是「叠加在 commerce 协议之上的安全层」,与 A2A / MCP 是正交关系:A2A/MCP 管「agent 怎么对话」,AP2 管「这笔钱凭什么算授权」。 - 凭证密码学 vs 会话密码学。 不少协议靠 TLS / OAuth token 保会话;AP2 把信任做进可离线核验的凭证本身(SD-JWT 委托链),因为它假设中间的 LLP agent 就是攻击面,会话级信任不够。
- 选择性披露作一等公民。 用 SD-JWT 让 agent 「只露验证必需字段」,把隐私最小化做进协议机制,而非靠各方自觉。
6. 边界与诚实
- 本章「为什么并存」是推断,见 §3 标注。代码里没有迁移说明文档。
- 旧
ap2/models是否会被删、新旧是否长期共存,代码里看不出计划。 - 两套模型的字段并非一一对应(旧
CartMandate含完整 W3CPaymentRequest,新CheckoutMandate把 checkout 装进一个被sd_hash绑的checkout_jwt),迁移不是机械改名。
7. 代码地图
| 主题 | 文件 | 符号名 |
|---|---|---|
| 旧代模型 | code/sdk/python/ap2/models/mandate.py | IntentMandate CartMandate PaymentMandate |
| 旧代 W3C 对象 | code/sdk/python/ap2/models/payment_request.py | PaymentRequest PaymentResponse PaymentItem |
| 新代闭支付模型 | code/sdk/python/ap2/sdk/generated/payment_mandate.py | PaymentMandate (vct='mandate.payment.1') |
| 新代开支付模型 | code/sdk/python/ap2/sdk/generated/open_payment_mandate.py | OpenPaymentMandate AmountRange Budget |
| 新代 checkout 模型 | code/sdk/python/ap2/sdk/generated/checkout_mandate.py | CheckoutMandate |
| 新代开 checkout 模型 | code/sdk/python/ap2/sdk/generated/open_checkout_mandate.py | OpenCheckoutMandate LineItems |
| A2A 扩展标识 | code/samples/python/src/common/a2a_extension_utils.py | EXTENSION_URI |
| v2 样例 mandate 工具 | code/samples/python/src/roles/shopping_agent_v2/shopping_agent/mandate_tools.py | (uses ap2.sdk) |
| 规范 vct 版本控制 | docs/ap2/specification.md | (Mandate Versioning) |