跳到主要内容

AP2 — Mandate 与角色(核心概念)

这一章讲 AP2 的两块基石:mandate 的开/闭两态,和谁来签、谁来验。把这两件事搞清,后面所有密码学细节都只是「怎么实现它」。

1. Mandate 是什么

一句话: mandate(授权书)是一份人类批准的、密码学签名的凭证,告诉验证方「这个 agent 被授权做这件事」。

AP2 的核心不是发明新的加密,而是发明一种结构:把「授权」拆成两个状态(agent_authorization.md:392-399):

状态含义由谁签cnf(下一跳公钥)?
开 (Open)还没绑到具体交易,只带一组约束 + 授权某个 agent 使用用户(经 Trusted Surface)(绑定允许使用它的 agent)
闭 (Closed)绑死到一笔具体交易,授权 agent 对某验证方执行一个动作用户本人,或 agent 用开 mandate 里 cnf 指定的钥匙否(终端,不再往下委托)

为什么要拆成两态

因为 agent 是非确定性的,你不能给它一张「随便花」的空白支票。开 mandate 解决的就是:

「人不在场时,怎么既让 agent 自主成交,又把它框住?」(agent_authorization.md:400-402)

用户签的是规则(开),agent 成交后补签事实(闭),验证方检查「事实是否落在规则内」。这样:

  • 用户不必为每笔交易在场点确认;
  • agent 的越权(超预算、换商家)会在验证时被当场抓住;
  • 整条链是自证的——验证方只信任根(用户/银行的钥匙),不必信任 agent。

2. 五种角色,各管一段

AP2 把一笔交易拆给五个角色,核心区分是**「谁是 LLM(潜在攻击者)、谁必须是确定性代码」**(specification.md:59-98):

┌──────────────┐
用户 ───▶│ Trusted │ 必须非 agentic:唯一能签「用户 mandate」的地方
│ Surface (TS) │ 拿到知情同意 → 用 user_sk 签名
└──────┬───────┘
│ 把签好的 mandate 交回

┌──────────────┐ 谈货/组车/出示 mandate ┌───────────┐
│ Shopping │──────────────────────────────▶│ Merchant │
│ Agent (SA) │ └───────────┘
│ 预期 agentic │── 拿支付凭证 ──▶┌──────────────┐
└──────────────┘ │ Credential │
│ Provider(CP)│
└──────────────┘
│ 处理支付

┌──────────────┐
│ Merchant Pay │
│ Processor MPP│
└──────────────┘

怎么读这张图: 唯一能签出「用户授权」的是 Trusted Surface,且它必须是确定性代码(specification.md:78-80)。其余三个验证方(M/CP/MPP)可以是 agentic 也可以不是,但它们的验证逻辑必须跑在确定性代码里(specification.md:96-98)——这是协议最硬的一条安全约束。

3. 两种模式:人在场 vs 人不在场

这是 AP2 最常被问到的区别(specification.md:166-190)。验证方收到的永远是闭 mandate,差别只在「信任的根是谁」:

Human Present(直接)Human Not Present(自主)
用户批准什么 mandate(看到真实成交,亲自批准) mandate(只批准一组约束)
闭 mandate 谁签用户(用户钥匙直接签)agent(用开 mandate cnf 授权的 agent 钥匙)
验证方信任链直接验用户签名验开 mandate(用户签)→ 再验闭 mandate(agent 签)落在约束内
链长1 跳(根即闭)≥2 跳(开根 + agent 闭)

巧妙处: 人在场时,因为用户亲自批了闭 mandate,这一步常常可以退化成传统电商流程——商家和 Trusted Surface 直接通信即可(specification.md:217-219)。AP2 不是要取代电商,而是只在「agent 自主」时才加那层密码学。

防双花的硬规则: 自主模式下,agent 必须先收到上一份开 mandate 的拒绝收据,才能出示下一份开 mandate(specification.md:237-240),防止用同一份开 mandate 批准多个不同 checkout。

4. 三类 / 两类 mandate(命名陷阱预警)

这里有个容易把人绕晕的地方,先讲清:

  • 协议规范 v0.2 只定义两类 mandate:Checkout Mandate(保护「买什么」)和 Payment Mandate(保护「怎么付」),都用 SD-JWT 承载(specification.md:106-107)。
  • 旧版 Pydantic 模型 还在用三类:IntentMandate(意图)、CartMandate(购物车,商家签 JWT)、PaymentMandate(支付)(code/sdk/python/ap2/models/mandate.py:32,107,167)。

这两套并存于同一仓库。为什么会这样、哪套用在哪个样例,见 04-two-model-generations.md。这里你只要记住一个映射直觉:

旧三类 v0.2 两类(SD-JWT-VC)
IntentMandate (用户意图) ─┐
CartMandate (商家签购物车) ─┴──▶ Checkout Mandate(开/闭)
PaymentMandate(支付授权) ───▶ Payment Mandate (开/闭)

CartMandate:一个值得看的真实片段

旧模型里 CartMandate 把「商家对购物车的担保」做成一个嵌套的商家签名 JWT(code/sdk/python/ap2/models/mandate.py:107-136):

# code/sdk/python/ap2/models/mandate.py:113-135(节选)
class CartMandate(BaseModel):
contents: CartContents # 购物车真实内容
merchant_authorization: str | None # 商家签名的 JWT

那个 merchant_authorization JWT 的 payload 里藏着一个关键字段 cart_hash——CartContents 的规范 JSON 算的哈希,外加短时 exp(5-15 分钟)和防重放的 jti。这就是「商家担保此购物车在限定时间内价格/库存不变」的密码学落点。PaymentMandate.user_authorization 同理,是一个对 cart+payment 双哈希签名的可验证表述(mandate.py:184-203)。

注意这套旧模型的绑定是字段里塞 JWT/哈希,而新 SD-JWT 运行时是整条链用 sd_hash 串起来——这是两代设计最本质的差异。

5. 边界与诚实

  • AP2 不是电商协议本身。 目录 API、checkout 更新、角色间具体 API 都明确在范围外(specification.md:20-24)。它只管「在已有 commerce 协议上加一层支付安全」,并声明与 UCP(Universal Commerce Protocol)兼容。
  • mandate 选择机制在范围外。 agent 怎么从一堆开 mandate 里挑出适用的那张,规范明说是实现细节(flows.md:140-141agent_authorization.md:489-491)。
  • Agent 间委托(一个 SA 把 mandate 转授给另一个 SA)规范承认「概念上可行」但当前版本不覆盖(specification.md:256-260)。

下一章进入实现:这些「开/闭、委托、绑定」到底用 SD-JWT 怎么拼出来。→ 02-sdjwt-delegation-chain.md

6. 代码地图

主题文件符号名
角色与模式定义docs/ap2/specification.md(Roles / Modes / Agentic vs Non-Agentic)
开/闭两态、委托框架docs/ap2/agent_authorization.md(Mandate Structure)
旧版意图 mandatecode/sdk/python/ap2/models/mandate.pyIntentMandate
旧版购物车 mandate(商家签)code/sdk/python/ap2/models/mandate.pyCartMandate CartContents
旧版支付 mandatecode/sdk/python/ap2/models/mandate.pyPaymentMandate PaymentMandateContents