Schema 组合与扩展机制
概述
UCP 的 Schema 组合机制是其可扩展性的核心。通过 JSON Schema 的 allOf 组合和扩展模式,UCP 实现了模块化的 Schema 定义,使得基础能力可以独立演进,而扩展可以灵活地增强功能。
设计原则
1. 自描述 Schema
扩展 Schema 必须是自描述的。每个扩展 Schema 必须声明它引入的类型以及如何使用 allOf 组合修改基础类型。
2. 客户端 Schema 解析
平台必须通过获取和组合基础 Schema 与激活的扩展 Schema 来客户端解析 Schema。传输定义(OpenAPI/OpenRPC)必须只引用基础 Schema,不得内联枚举字段或定义 payload 形状。
3. 确定性解析
扩展 Schema 的 $defs 键必须使用完整的父能力名称(反向域名格式),以实现确定性 Schema 解析。
扩展 Schema 模式
基本结构
扩展 Schema 使用 allOf 定义组合类型。$defs 键必须使用完整的父能力名称:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://ucp.dev/2026-01-23/schemas/shopping/discount.json",
"name": "dev.ucp.shopping.discount",
"title": "Discount Extension",
"description": "Adds discount support to checkout and cart",
"$defs": {
"discounts_object": {
"type": "object",
"properties": {
"discounts": {
"type": "array",
"items": { "$ref": "#/$defs/discount_item" }
}
}
},
"dev.ucp.shopping.checkout": {
"title": "Checkout with Discount",
"allOf": [
{ "$ref": "https://ucp.dev/2026-01-23/schemas/shopping/checkout.json" },
{
"type": "object",
"properties": {
"discounts": {
"$ref": "#/$defs/discounts_object"
}
}
}
]
},
"dev.ucp.shopping.cart": {
"title": "Cart with Discount",
"allOf": [
{ "$ref": "https://ucp.dev/2026-01-23/schemas/shopping/cart.json" },
{
"type": "object",
"properties": {
"discounts": {
"$ref": "#/$defs/discounts_object"
}
}
}
]
}
}
}关键要求
$defs键匹配:$defs键必须与extends中声明的父能力名称完全匹配extends: "dev.ucp.shopping.checkout"→$defs["dev.ucp.shopping.checkout"]extends: ["checkout", "cart"]→$defs["dev.ucp.shopping.checkout"]和$defs["dev.ucp.shopping.cart"]
自文档化:Schema 明确声明它扩展了哪些父能力
确定性解析:
extends值直接映射到$defs键可验证:构建时检查可以确认每个
extends条目都有匹配的$defs键
Schema 解析约定
解析流程
要验证 payload,实现按以下方式解析扩展 Schema:
确定根能力:从操作确定根能力
- 结账操作 →
dev.ucp.shopping.checkout - 购物车操作 →
dev.ucp.shopping.cart - 订单操作 →
dev.ucp.shopping.order
- 结账操作 →
应用扩展:对于每个激活的扩展,解析并应用其
$defs[{root_capability}]
解析示例
场景:结账响应包含折扣扩展
- 根能力:
dev.ucp.shopping.checkout - 激活扩展:
dev.ucp.shopping.discount - 扩展 Schema:
discount.json - 解析路径:
discount.json#/$defs/dev.ucp.shopping.checkout
组合过程:
// 1. 基础 Schema
{
"$ref": "checkout.json"
}
// 2. 扩展 Schema 的 $defs
{
"allOf": [
{ "$ref": "checkout.json" },
{
"properties": {
"discounts": { ... }
}
}
]
}
// 3. 最终组合 Schema
{
"allOf": [
{ "$ref": "checkout.json" },
{
"properties": {
"discounts": { ... }
}
}
]
}完整解析流程
平台必须按照以下顺序解析 Schema:
步骤 1:发现
从 /.well-known/ucp 获取商家 Profile。
{
"ucp": {
"capabilities": {
"dev.ucp.shopping.checkout": [...],
"dev.ucp.shopping.discount": [...]
}
}
}步骤 2:协商
计算能力交集(参见 能力发现与协商机制)。
结果:
dev.ucp.shopping.checkout(v2026-01-23)dev.ucp.shopping.discount(v2026-01-23)
步骤 3:获取 Schema
获取基础 Schema 和所有激活的扩展 Schema。
const baseSchema = await fetch('https://ucp.dev/.../checkout.json');
const discountSchema = await fetch('https://ucp.dev/.../discount.json');步骤 4:版本兼容性
对于每个获取的扩展 Schema,如果存在 requires,验证协商的协议版本和能力版本满足声明的约束。
扩展 Schema 的 requires 示例:
{
"name": "dev.ucp.shopping.discount",
"requires": {
"protocol": { "min": "2026-01-23" },
"capabilities": {
"dev.ucp.shopping.checkout": { "min": "2026-01-23" }
}
}
}验证逻辑:
def verify_version_compatibility(extension_schema, negotiated_protocol_version, negotiated_capability_versions):
if 'requires' not in extension_schema:
return True # 无约束,假设兼容
requires = extension_schema['requires']
# 验证协议版本
if 'protocol' in requires:
protocol_constraint = requires['protocol']
if not satisfies_version_constraint(negotiated_protocol_version, protocol_constraint):
return False
# 验证能力版本
if 'capabilities' in requires:
for capability_name, capability_constraint in requires['capabilities'].items():
if capability_name not in negotiated_capability_versions:
return False
negotiated_version = negotiated_capability_versions[capability_name]
if not satisfies_version_constraint(negotiated_version, capability_constraint):
return False
return True
def satisfies_version_constraint(version, constraint):
"""验证版本是否满足约束"""
if 'min' in constraint:
if version < constraint['min']:
return False
if 'max' in constraint:
if version > constraint['max']:
return False
return True不兼容的扩展从激活能力集中排除,并重新修剪孤立扩展。
步骤 5:组合
基于激活的扩展,通过 allOf 链合并 Schema。
组合算法:
def compose_schema(root_capability, base_schema_url, active_extensions):
"""
组合基础 Schema 和扩展 Schema
Args:
root_capability: 根能力名称(如 "dev.ucp.shopping.checkout")
base_schema_url: 基础 Schema URL
active_extensions: 激活的扩展列表,每个包含 schema_url 和 schema_content
"""
# 开始组合链
all_of_chain = [
{ "$ref": base_schema_url }
]
# 为每个激活的扩展添加组合
for extension in active_extensions:
extension_def_key = f"{extension['schema_url']}#/$defs/{root_capability}"
all_of_chain.append({ "$ref": extension_def_key })
# 返回组合后的 Schema
return {
"allOf": all_of_chain
}示例组合结果:
{
"allOf": [
{ "$ref": "https://ucp.dev/.../checkout.json" },
{ "$ref": "https://ucp.dev/.../discount.json#/$defs/dev.ucp.shopping.checkout" },
{ "$ref": "https://ucp.dev/.../fulfillment.json#/$defs/dev.ucp.shopping.checkout" }
]
}步骤 6:验证
根据组合后的 Schema 验证请求和响应。
import jsonschema
def validate_payload(payload, composed_schema):
"""根据组合后的 Schema 验证 payload"""
try:
jsonschema.validate(payload, composed_schema)
return True, None
except jsonschema.ValidationError as e:
return False, str(e)多父扩展
扩展可以扩展多个父能力:
{
"dev.ucp.shopping.discount": [
{
"version": "2026-01-23",
"extends": ["dev.ucp.shopping.checkout", "dev.ucp.shopping.cart"]
}
]
}多父扩展的 Schema 定义
扩展 Schema 可以为它扩展的每个能力定义不同的字段:
{
"$defs": {
"dev.ucp.shopping.checkout": {
"allOf": [
{ "$ref": "checkout.json" },
{
"properties": {
"discounts": { ... },
"loyalty_earned": { ... } // checkout 特定字段
}
}
]
},
"dev.ucp.shopping.cart": {
"allOf": [
{ "$ref": "cart.json" },
{
"properties": {
"discounts": { ... },
"loyalty_preview": { ... } // cart 特定字段
}
}
]
}
}
}多父扩展的协商
在多父扩展的情况下:
- 扩展可以为它扩展的每个能力定义不同的字段
- 参见 交集算法 了解协商规则
- 对于多父扩展(
extends: ["a", "b"]),至少一个父能力必须在交集中
版本依赖
requires 字段
扩展 Schema 应该声明 requires 对象(与 name、title、description 一起)来指示正确操作所需的协议和能力版本:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://acme.com/ucp/schemas/loyalty.json",
"name": "com.acme.shopping.loyalty",
"title": "Acme Loyalty Points",
"requires": {
"protocol": { "min": "2026-01-23" },
"capabilities": {
"dev.ucp.shopping.checkout": { "min": "2026-06-01" }
}
},
"$defs": {
"dev.ucp.shopping.checkout": { ... }
}
}约束格式
每个约束是一个对象,包含必需的 min(包含)和可选的 max(包含)版本:
"requires": {
"protocol": { "min": "2026-01-23", "max": "2026-09-01" },
"capabilities": {
"dev.ucp.shopping.checkout": { "min": "2026-06-01" }
}
}当 max 缺失时,没有上限。
验证要求
requires.capabilities 中的键必须是扩展的 $defs 键的子集。如果 requires 存在,平台和商家必须在 Schema 解析期间验证协商的协议版本和能力版本满足声明的约束。
不兼容的扩展从激活能力集中排除(参见 解析流程)。
如果 requires 缺失,扩展假设与 Profile 声明的版本兼容。
传输定义的瘦身要求
要求
传输定义(OpenAPI/OpenRPC)必须是瘦的:它们只声明方法名并引用基础 Schema。它们不得枚举字段或内联定义 payload 形状。
正确示例(OpenAPI)
paths:
/checkout-sessions:
post:
operationId: create_checkout
requestBody:
content:
application/json:
schema:
$ref: 'https://ucp.dev/.../schemas/shopping/checkout.json'
responses:
'200':
content:
application/json:
schema:
$ref: 'https://ucp.dev/.../schemas/shopping/checkout.json'错误示例
# ❌ 错误:内联定义字段
paths:
/checkout-sessions:
post:
requestBody:
content:
application/json:
schema:
type: object
properties:
line_items:
type: array
items:
type: object
properties:
item: { ... }
quantity: { ... }为什么错误:
- 内联定义使得扩展无法通过 Schema 组合添加字段
- 违反了"瘦传输定义"原则
- 导致 Schema 重复和不一致
实际应用示例
示例 1:结账 + 折扣 + 履约
激活的能力:
dev.ucp.shopping.checkout(v2026-01-23)dev.ucp.shopping.discount(v2026-01-23)dev.ucp.shopping.fulfillment(v2026-01-23)
Schema 组合:
{
"allOf": [
{ "$ref": "checkout.json" },
{ "$ref": "discount.json#/$defs/dev.ucp.shopping.checkout" },
{ "$ref": "fulfillment.json#/$defs/dev.ucp.shopping.checkout" }
]
}结果:结账对象包含:
- 基础结账字段(
id、status、line_items等) - 折扣字段(
discounts) - 履约字段(
fulfillment、fulfillment_options)
示例 2:购物车 + 折扣
激活的能力:
dev.ucp.shopping.cart(v2026-01-23)dev.ucp.shopping.discount(v2026-01-23)
Schema 组合:
{
"allOf": [
{ "$ref": "cart.json" },
{ "$ref": "discount.json#/$defs/dev.ucp.shopping.cart" }
]
}结果:购物车对象包含:
- 基础购物车字段
- 折扣字段(
discounts、loyalty_preview)
优势与挑战
优势
- 模块化:基础能力和扩展可以独立演进
- 可组合性:通过组合实现功能增强
- 向后兼容:新扩展不影响现有实现
- 类型安全:通过 JSON Schema 提供类型验证
- 自描述:Schema 明确声明依赖关系
挑战
- 解析复杂度:需要客户端 Schema 解析和组合
- 版本管理:需要仔细管理版本依赖
- 性能:多次 Schema 获取可能影响性能
- 工具支持:需要工具支持 Schema 组合和验证
解决方案
- 缓存:缓存获取的 Schema
- 预组合:在构建时预组合常见 Schema
- 工具链:提供 Schema 解析和验证工具
- 文档:提供清晰的 Schema 组合指南
总结
UCP 的 Schema 组合与扩展机制实现了:
- 模块化设计:通过扩展机制实现功能组合
- 确定性解析:通过命名约定实现确定性 Schema 解析
- 版本管理:通过
requires字段管理版本依赖 - 类型安全:通过 JSON Schema 提供类型验证
- 灵活扩展:支持单父和多父扩展
这种设计使得 UCP 能够:
- 保持核心 Schema 简洁
- 支持渐进式功能增强
- 确保类型安全和验证
- 实现灵活的扩展机制