Skip to content
目录

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必须使用完整的父能力名称:

json
{
  "$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"
            }
          }
        }
      ]
    }
  }
}

关键要求

  1. $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"]
  2. 自文档化:Schema 明确声明它扩展了哪些父能力

  3. 确定性解析extends 值直接映射到 $defs

  4. 可验证:构建时检查可以确认每个 extends 条目都有匹配的 $defs

Schema 解析约定

解析流程

要验证 payload,实现按以下方式解析扩展 Schema:

  1. 确定根能力:从操作确定根能力

    • 结账操作 → dev.ucp.shopping.checkout
    • 购物车操作 → dev.ucp.shopping.cart
    • 订单操作 → dev.ucp.shopping.order
  2. 应用扩展:对于每个激活的扩展,解析并应用其 $defs[{root_capability}]

解析示例

场景:结账响应包含折扣扩展

  • 根能力dev.ucp.shopping.checkout
  • 激活扩展dev.ucp.shopping.discount
  • 扩展 Schemadiscount.json
  • 解析路径discount.json#/$defs/dev.ucp.shopping.checkout

组合过程

json
// 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。

json
{
  "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。

javascript
const baseSchema = await fetch('https://ucp.dev/.../checkout.json');
const discountSchema = await fetch('https://ucp.dev/.../discount.json');

步骤 4:版本兼容性

对于每个获取的扩展 Schema,如果存在 requires,验证协商的协议版本和能力版本满足声明的约束。

扩展 Schema 的 requires 示例

json
{
  "name": "dev.ucp.shopping.discount",
  "requires": {
    "protocol": { "min": "2026-01-23" },
    "capabilities": {
      "dev.ucp.shopping.checkout": { "min": "2026-01-23" }
    }
  }
}

验证逻辑

python
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。

组合算法

python
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
    }

示例组合结果

json
{
  "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 验证请求和响应。

python
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)

多父扩展

扩展可以扩展多个父能力:

json
{
  "dev.ucp.shopping.discount": [
    {
      "version": "2026-01-23",
      "extends": ["dev.ucp.shopping.checkout", "dev.ucp.shopping.cart"]
    }
  ]
}

多父扩展的 Schema 定义

扩展 Schema 可以为它扩展的每个能力定义不同的字段:

json
{
  "$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 对象(与 nametitledescription 一起)来指示正确操作所需的协议和能力版本:

json
{
  "$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(包含)版本:

json
"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)

yaml
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'

错误示例

yaml
# ❌ 错误:内联定义字段
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 组合

json
{
  "allOf": [
    { "$ref": "checkout.json" },
    { "$ref": "discount.json#/$defs/dev.ucp.shopping.checkout" },
    { "$ref": "fulfillment.json#/$defs/dev.ucp.shopping.checkout" }
  ]
}

结果:结账对象包含:

  • 基础结账字段(idstatusline_items 等)
  • 折扣字段(discounts
  • 履约字段(fulfillmentfulfillment_options

示例 2:购物车 + 折扣

激活的能力

  • dev.ucp.shopping.cart (v2026-01-23)
  • dev.ucp.shopping.discount (v2026-01-23)

Schema 组合

json
{
  "allOf": [
    { "$ref": "cart.json" },
    { "$ref": "discount.json#/$defs/dev.ucp.shopping.cart" }
  ]
}

结果:购物车对象包含:

  • 基础购物车字段
  • 折扣字段(discountsloyalty_preview

优势与挑战

优势

  1. 模块化:基础能力和扩展可以独立演进
  2. 可组合性:通过组合实现功能增强
  3. 向后兼容:新扩展不影响现有实现
  4. 类型安全:通过 JSON Schema 提供类型验证
  5. 自描述:Schema 明确声明依赖关系

挑战

  1. 解析复杂度:需要客户端 Schema 解析和组合
  2. 版本管理:需要仔细管理版本依赖
  3. 性能:多次 Schema 获取可能影响性能
  4. 工具支持:需要工具支持 Schema 组合和验证

解决方案

  1. 缓存:缓存获取的 Schema
  2. 预组合:在构建时预组合常见 Schema
  3. 工具链:提供 Schema 解析和验证工具
  4. 文档:提供清晰的 Schema 组合指南

总结

UCP 的 Schema 组合与扩展机制实现了:

  1. 模块化设计:通过扩展机制实现功能组合
  2. 确定性解析:通过命名约定实现确定性 Schema 解析
  3. 版本管理:通过 requires 字段管理版本依赖
  4. 类型安全:通过 JSON Schema 提供类型验证
  5. 灵活扩展:支持单父和多父扩展

这种设计使得 UCP 能够:

  • 保持核心 Schema 简洁
  • 支持渐进式功能增强
  • 确保类型安全和验证
  • 实现灵活的扩展机制

MIT License.