跳到主要内容

03 — 能力协商与扩展框架

这一章讲 ACP 怎么让「能力各异的 agent 和商家」在一次往返里谈拢,以及怎么在不动核心的前提下加可选功能。源头:rfcs/rfc.capability_negotiation.mdrfcs/rfc.extensions.mdrfcs/rfc.discount_extension.md

3.1 它要解决的小问题

agent 和商家各有各的本事:这个 agent 能弹 3DS 弹窗,那个不能;这家店要求强认证,那家不要。如果没有协商机制,双方只能靠「试错」或「线下约定」,新功能一加就破兼容(rfcs/rfc.capability_negotiation.md:13-23)。

3.2 核心巧思:单一 capabilities 对象 + 交集语义

ACP 不搞「请求里叫 agent_capabilities、响应里叫 seller_capabilities」那套对称结构。它用同一个 capabilities 对象,靠上下文(在请求里 = agent 声明;在响应里 = 商家回应)区分角色(rfcs/rfc.capability_negotiation.md:60、变更记录 :609)。

最关键的一步:商家在响应里只回「双方都支持的交集」(rfcs/rfc.capability_negotiation.md:63-69)。

为什么交集这么妙? 因为它把「兼容性计算」从客户端彻底搬走了——agent 不用自己去比对两个集合,响应里就是最终能用的东西,单一真相源(:298)。

原理演示:

# 示意,非源码:商家侧的交集计算
def negotiate(agent_caps, seller_supports):
agent_interventions = set(agent_caps["interventions"]["supported"])
# 只回双方都支持的那部分
mutually = agent_interventions & set(seller_supports)
return {
"payment_methods": seller_payment_methods, # 商家侧才有的字段
"interventions": {
"supported": sorted(mutually), # ← 交集
"required": seller_required, # 商家要求的
"enforcement": "conditional",
},
}
# 例:agent 声明 [3ds, biometric, address_verification],
# 商家内部支持 [3ds, address_verification]
# → 响应 supported = [3ds, address_verification](去掉 biometric)

对照真实例子:rfcs/rfc.capability_negotiation.md:283-287 的交集示例,与 §5.1 完整往返(:347-393)。

3.3 interventions:协商「需要用户介入的动作」

interventions 是协商的主角(rfcs/rfc.capability_negotiation.md:172-205)。它在请求和响应里字段不同:

字段出现在含义
supported双方agent 能处理的 / 响应里是交集
required仅响应商家要求的介入类型(如 3ds)
enforcement仅响应always / conditional(默认,按风险) / optional
display_context仅请求agent 怎么呈现(native/webview/modal/redirect)
max_redirects / max_interaction_depth仅请求agent 能处理的重定向数 / 嵌套深度

兼容性判定(:288-298):交集要么覆盖了所有 required,要么 required 为空——并且预期交互深度 ≤ agent 的 max_interaction_depth。如果商家 required: ["3ds"] 但交集为空且 enforcement: always,会回一条 intervention_required 错误消息(:312-337)。

3.4 安全:协商不是降级的后门

这是协商机制最该警惕的地方。攻击者可能伪造 agent 能力来绕过安全控制(降级攻击)。RFC 的硬规则(rfcs/rfc.capability_negotiation.md:455-462):

  • 商家必须无视 agent 声明的能力来强制认证要求;
  • 服务端必须自己校验支付方式兼容性;
  • 绝不把 agent 声明的能力当作安全决策的唯一依据;
  • 算交集归算交集,但 required 介入照样强制。

一句话:协商用来优化体验,不用来决定安全。 安全由服务端独立兜底。

另一条贯穿全协议的前向兼容铁律:双方必须优雅忽略未知能力值(:339-341),否则新功能一上线就会因为「不认识」而 DoS。

3.5 Extensions 框架:可组合地加功能

extensions 框架(rfcs/rfc.extensions.md)让商家声明「我支持哪些可选能力」,且这些能力不改核心 schema就能挂上去。它复用同一套 capabilities 协商(:46-56)。

最妙的设计——extends 用 JSONPath 精确声明扩展点(rfcs/rfc.extensions.md:123-168):

// 商家在响应里声明 discount 扩展加了哪些字段
{
"name": "discount",
"extends": [
"$.CheckoutSessionCreateRequest.discounts",
"$.CheckoutSessionUpdateRequest.discounts",
"$.CheckoutSession.discounts"
],
"schema": "https://agenticcommerce.dev/schemas/discount/2026-01-27.json"
}

为什么不只写个扩展名,还要 JSONPath?因为它让扩展机器可解析:工具/SDK 能据此知道「discounts 字段被加到了 create 请求、update 请求和会话响应这三个确切位置」,从而自动做 schema 组合与校验(rfcs/rfc.extensions.md:123-131)。

组合规则(防止扩展破坏核心)(rfcs/rfc.extensions.md:316-322):扩展不得改已有必填字段、不得改已有字段语义;可以加新可选字段、可以messages[].code 枚举加新值。第三方扩展用 reverse-domain 命名避免冲突(:236-245)。

3.6 第一个扩展:Discount

discount 扩展(rfcs/rfc.discount_extension.md)是框架的首个落地实现,把原来简单的 coupons 数组升级成富结构:支持应用结果明细分配明细(折扣落到哪些行/哪些金额)、自动折扣(商家主动给的促销)、以及完整的拒绝原因码(rfcs/rfc.extensions.md:57-65)。

它正是 §3.5 那个 extends 例子所声明的扩展——在 create/update 请求和会话响应里加一个 discounts 对象。Schema 落在 spec/2026-04-17/json-schema/schema.discount.json

3.7 本章要带走的三句话

  • 一个 capabilities 对象双向用,商家只回交集——客户端零兼容性计算。
  • 协商不是安全边界:required 介入由服务端强制,绝不信 agent 自报的能力。
  • extensions 用 JSONPath extends 精确声明扩展点,可组合加功能而不动核心。