<!-- title: 嵌套Schema深度税：三层即糊底 -->

# 嵌套Schema深度税：三层即糊底

## 概述

把一个工具的参数写成"对象套对象套并集（oneOf/anyOf）再套引用（$ref）"，是工程上最自然、却最致命的工具设计失误。本报告基于 arXiv 2501.10868（JSONSchema Benchmark，6 个 SOTA 模型 × 官方 JSON Schema Test Suite）、DeepJSONEval（arXiv 2509.25922，专为深嵌套设计）、以及 Requesty 对 244 个模型 / 23 个供应商 / 10 种高频 Schema 形态的实测，拆解一条被多数团队忽略的断层线：**Schema 的"形状"本身就在抽税**。结论是，嵌套深度突破 3 层、或引入并集（union）分支后，受约束解码（constrained decoding）虽能保证"合法"，却保证不了"正确"——模型会在合法的结构里把语义填错、把分支选反、把深层字段漏掉。这条税比"选错工具"更隐蔽，因为它永远以 100% 合法 JSON 的姿态通过校验。

## 一、为什么"形状"会抽税：受约束解码的两副面孔

工具调用的可靠性建立在两条独立链路上：**结构合法**（输出能通过 JSON Schema 校验）与**语义正确**（输出的值是真的）。受约束解码（OpenAI 严格模式、Outlines、xgrammar、llama.cpp GBNF）只负责第一条——它在每个 token 位置屏蔽掉所有会让输出脱离 Schema 的 token，所以"合法性"几乎是免费的。但代价是：约束越复杂，合法路径的歧义越大。

```python
# 一个"形状"危险、却完全合法的工具定义
book_flight = {
    "name": "book_flight",
    "strict": True,                 # OpenAI 严格模式：保证合法
    "parameters": {
        "type": "object",
        "additionalProperties": False,   # 严格模式强制
        "required": ["passenger", "legs"],
        "properties": {
            "passenger": {
                "type": "object",
                "additionalProperties": False,
                "required": ["name", "docs"],
                "properties": {
                    "name": {"type": "string"},
                    "docs": {"type": "string"}
                }
            },
            "legs": {                          # 第 3 层：数组套对象
                "type": "array",
                "items": {
                    "type": "object",
                    "additionalProperties": False,
                    "required": ["from", "to", "cabin"],
                    "properties": {
                        "from": {"type": "string"},
                        "to":   {"type": "string"},
                        "cabin": {                 # 第 4 层：并集分支
                            "anyOf": [
                                {"type": "string", "enum": ["eco", "biz"]},
                                {"type": "object",
                                 "required": ["class", "seat"],
                                 "properties": {
                                     "class": {"type": "string"},
                                     "seat":  {"type": "string"}}}
                            ]
                        }
                    }
                }
            }
        }
    }
}
```

上面这个 Schema 在 OpenAI 严格模式下会被接受，结构合法性 100%。但 `cabin` 在第 4 层、且是 `anyOf` 并集——这正是 arXiv 2501.10868 与 Requesty 报告共同标记的高失败形态：模型容易"选对分支却填错字段"，或干脆把 `cabin` 退化为枚举值，把后半段结构丢掉。**结构合法与语义正确之间的鸿沟，随深度与分支数指数级拉宽**，这就是"形状税"。

## 二、深度断层线：三层是个隐性门槛

DeepJSONEval（arXiv 2509.25922）专门为"多层嵌套"设计了评测集，结论被多篇工程实践反复印证：嵌套深度突破 3 层后，模型对深层字段的**召回率**和**赋值正确率**出现断崖。Toward Data Science 的工程复盘直接点出："deeply nested schemas with many object properties may yield API errors — there is a limitation on the depth of nesting"。

| 嵌套深度 | 典型表现 | 合法性 | 语义正确率（综合） |
|----------|----------|--------|------------------|
| 1 层（扁平） | 全部字段平铺 | ≈100% | 高（基线） |
| 2 层 | 1 个内嵌对象 | ≈100% | 接近基线 |
| 3 层 | 对象套对象 / 数组套对象 | ≈100% | 开始松动 |
| 4–5 层 | 并集 + 引用混入 | ≈100% | 明显塌方 |
| >5 层或递归 | 多数供应商报错或截断 | 受限 | 不可控 |

值得注意的是：**合法性几乎不掉，掉的是正确率**。这意味着所有依赖"JSON 能 parse 就放行"的护栏会全部失守——错误以最致命的方式伪装成成功。OpenAI 早期严格模式文档曾把嵌套深度上限设在 5 层、字段数上限 100、并要求所有 `$ref` 必须在 `additionalProperties:false` 下展开，正是为了在 API 层把这条断层线挡住。这条规则至今仍是"3 层"这条经验法则的工程注脚。

## 三、并集税：oneOf/anyOf 是合法却易崩的灰色地带

如果说"深度"是慢性病，"并集（union）"就是急症。Requesty 在 10 种高频 Schema 形态的横评里，把含 `anyOf`/`oneOf` 的并集形态列为跨供应商最不一致的一类。问题出在受约束解码对"分支选择"的处理：解码器必须在生成早期就锁定走哪条子 Schema，一旦锁定，后续 token 只能在该分支内活动。

```typescript
// Zod v4：z.union() 生成的 anyOf 会被部分供应商拒收
const Cabin = z.union([
  z.enum(["eco", "biz"]),
  z.object({ class: z.string(), seat: z.string() }),
]);
// → JSON Schema: { "anyOf": [ {enum...}, {object...} ] }
```

GitHub 上 colinhacks/zod#5807 记录了一个被广泛踩中的坑：Zod v4 的 `z.union()` 默认生成 `anyOf`，而 Gemini 的 Function Calling 对嵌套 `anyOf` 解析不稳，直接导致工具定义被拒或被静默拍平。OpenAI 社区也反复出现 "Responses API doesn't like my nested anyOf" 的报错（community.openai.com 帖 #1276965），根因同样是"嵌套的 anyOf 让约束解码的分支状态机失稳"。

**变现场景评估**：这恰恰是一个可以包装成"工具 Schema 体检 / 自动拍平"训练营或 SaaS 的痛点——团队通常意识不到自己的 Zod/Pydantic 模型生成出了危险并集。一个能扫描 Schema、检测"深嵌套 + 并集 + 递归"组合、并自动给出拍平建议的 lint 工具，是清晰的付费点。

## 四、四类"形状"的实测税基

把不同 Schema 形态映射到风险，可以归纳为一张形状风险矩阵。下表综合 arXiv 2501.10868 的 JSON Schema Test Suite 通过率与 Requesty 的 244 模型横评：

| Schema 形态 | 跨供应商一致 | 语义风险 | 典型失败 | 工程对策 |
|------------|-------------|---------|----------|---------|
| 扁平对象 | 高 | 低 | 几乎无 | 作为基准 |
| 单层内嵌对象 | 高 | 低 | 偶发漏字段 | 可接受 |
| 数组套对象（≤3层） | 中 | 中 | 索引/字段错位 | 加 `description` |
| 并集 anyOf/oneOf | **低** | **高** | 选错分支/截断 | 加判别字段 |
| $ref 递归 | 很低 | 很高 | 部分供应商拒收 | 拆成扁平工具 |
| patternProperties / if-then-else | 几乎不支持 | N/A | API 报错 | 避免使用 |

最后两行尤其值得警惕：OpenAI 严格模式明确**不支持** `patternProperties`、`if/then/else`、`dependentSchemas` 等 JSON Schema 2020-12 高阶关键字，`$ref` 支持但不允许循环引用。把一个为人类文档设计的富 Schema 原样塞进工具定义，等于在 API 层赌运气。

## 五、深层字段召回：越深越像"系统提示被裁掉"

把"形状税"放大到一次真实的多步工具调用里：当工具返回的对象本身又深又嵌套（比如一个 REST 列表接口返回 500 条、每条 30 字段、嵌套 4 层），问题就从"生成端"转移到"理解端"。dev.to 与 Medium 上多篇复盘指出，模型在面对深度嵌套的工具结果时，会出现"只读前几层、深层当噪声"的现象——这和长上下文里的"中间遗忘"是同一类机制：注意力对深层、尾部 token 的覆盖天然衰减。

```mermaid
flowchart TD
    A[工具定义 Schema] --> B{形状判定}
    B -->|扁平 / ≤2层| C[高保真<br/>合法性≈100% 正确率高]
    B -->|3层 / 数组套对象| D[中风险<br/>偶发漏字段 / 错位]
    B -->|≥4层 或 并集| E[高风险<br/>选错分支 / 深层截断]
    B -->|递归 / 高阶关键字| F[供应商拒收<br/>API 报错]
    E --> G[护栏假通过<br/>JSON 合法但语义错]
    G --> H[下游静默失败]
    D --> I[加 description + 判别字段可缓解]
    E --> I
    I --> C
```

这张图的核心论点：**形状风险最终汇聚到"护栏假通过"这一个出口**。因为受约束解码兜底了合法性，所有形状税都以"合法 JSON"的形式落地，绕过绝大多数基于校验的护栏。

## 六、四大供应商的形状承受力差异

同一份危险 Schema，在不同供应商手里的命运截然不同。Requesty 的横评把这种差异量化成了"兼容性矩阵"，可以归纳为四档：

1. **OpenAI 严格模式**：形状最可控，但代价是规则最硬——`additionalProperties:false` 全强制、5 层深度墙、100 字段墙、高阶关键字一律拒收。好处是"要么拒收、要么合法"，几乎没有中间态。坏处是复杂工具根本定义不进去。
2. **Anthropic（Claude 工具调用）**：对 Schema 的语法更宽容，允许更丰富的结构，但宽容换来了更多"合法却不正确"的灰色地带——并集和深层嵌套的语义风险需要调用方自己用 Pydantic 兜底。
3. **Gemini（Function Calling）**：对 `anyOf` 解析不稳（zod#5807），嵌套并集容易被静默拍平，"结构被悄悄改写"是 Gemini 形状税的主要形态。
4. **开源 + Outlines/xgrammar/llama.cpp GBNF**：受约束解码把合法性做到极致，但分支越多的并集，推理延迟越高、且对长尾关键字（`patternProperties` 等）支持参差。

结论很现实：**没有供应商在"形状复杂度"上全胜**，差异在于"硬拒收"（OpenAI）还是"软塌方"（Claude/Gemini）。软塌方更危险，因为它不报错。

## 七、拍平优先：把形状税退回基线的工程配方

形状税的根治办法不是"换更强的模型"，而是**在工具设计层就把 Schema 拍平**。这是 JSONSchema Benchmark 与各路最佳实践共同指向的方向，可以浓缩为五条：

```python
# 反模式（危险）：一个工具扛三层 + 并集
search_order = {"parameters": {"properties": {
    "query": {"type": "string"},
    "filters": {"type": "object", "properties": {
        "date": {"type": "object", "properties": {"from": {}, "to": {}}},
        "status": {"anyOf": [...]}
    }}}}}}

# 正解（拍平）：拆成多个扁平工具 + 判别字段
search_order_by_date = {"parameters": {"properties": {
    "query": {"type": "string"},
    "date_from": {"type": "string", "description": "ISO 8601"},
    "date_to":   {"type": "string", "description": "ISO 8601"},
}}}
search_order_by_status = {"parameters": {"properties": {
    "query":  {"type": "string"},
    "status": {"type": "string", "enum": ["open", "closed", "shipped"],
               "description": "选一个，勿自创"}
}}}
```

五条配方：**(1)** 深度压到 ≤3 层，超出就拆工具；**(2)** 用判别字段（`enum`/`const`）替代 `anyOf` 分支，让模型先"选类型"再填值；**(3)** 给每个字段写 `description`，深度越深描述越要具体——描述是抵消"形状歧义"最便宜的杠杆；**(4)** 禁用递归与高阶关键字，工具结果若天然深嵌套，先用一道"摘要拍平"中间件再喂回模型；**(5)** 在调用方用 Pydantic/严格校验兜底"语义"，不能只信"能 parse"。

## 八、推荐方案与落地优先级

按场景给出形状策略，按"先止血、再优化"排序：

- **生产 Agent（金融/医疗/法律）**：一律扁平 + 判别字段，深度硬上限 3 层，调用方双层校验（结构 + 语义）。OpenAI 严格模式作为形状护栏，Pydantic 作为语义护栏。
- **通用 Copilot / 客服**：允许 2–3 层，但任何 `anyOf`/`oneOf` 必须配判别字段；上线前跑一遍 Schema 形状体检。
- **内部工具 / 原型**：可放宽到 4 层，但接受"语义偶发错误"的代价，并在监控里埋点"结构合法但字段缺失率"。
- **跨供应商产品**：以最严供应商（OpenAI 严格模式）为形状基准设计 Schema，向宽松供应商迁移时几乎零成本，反向则必然返工。

**变现/训练营评估**：形状税是一个被严重低估的细分赛道。"工具 Schema 体检 + 自动拍平 + 跨供应商形状兼容矩阵"可以同时服务三类客户：搞 Function Calling 的应用团队（买 lint/SaaS）、做 RAG/Agent 的咨询客户（买体检与重构）、以及想卖工具的企业（买"工具上架前的形状合规"）。它的优势在于需求真实、可量化（深度、分支数、字段召回率）、且和具体模型解耦——一个长期主义的训练营选题。

## 结论

工具调用最大的断层，从来不在"模型会不会选工具"，而在"工具的形状会不会把模型逼到合法却错误的一隅"。受约束解码用 100% 的合法性，掩盖了深度与并集带来的语义塌方。三层是隐性门槛、并集是灰色地带、递归与高阶关键字是供应商拒收区。把 Schema 拍平、用判别字段替代并集、把语义校验从"能 parse"升级到"字段齐且值对"——这三步能把形状税退回基线，且不依赖任何新模型。在工具调用这件事上，**形状是债，扁平是息**。

---

## 参考来源

1. [Generating Structured Outputs from Language Models: Benchmark and Studies (arXiv 2501.10868)](https://arxiv.org/abs/2501.10868v2) — JSONSchema Benchmark，6 个 SOTA 模型配对官方 JSON Schema Test Suite，量化 Schema 形态与通过率关系
2. [DeepJSONEval: Benchmarking Complex Nested JSON Data Mining (arXiv 2509.25922)](https://arxiv.org/abs/2509.25922) — 专为多层嵌套结构设计的评测，提供深嵌套召回/赋值正确率基线
3. [Structured Outputs Across LLM Providers: 244 Models Tested (Requesty, 2026)](https://www.requesty.ai/blog/structured-outputs-across-llm-providers-the-compatibility-mess) — 跨 244 模型/23 供应商/10 种高频 Schema 形态的兼容性实测，标记 anyOf/oneOf 为最不一致形态
4. [Zod v4 anyOf Incompatibility with Gemini Function Calling (colinhacks/zod#5807)](https://github.com/colinhacks/zod/issues/5807) — z.union() 生成 anyOf 在 Gemini 工具调用中被拒/拍平的工程实录
5. [Diving Deeper with Structured Outputs (Towards Data Science)](https://towardsdatascience.com/diving-deeper-with-structured-outputs-b4a5d280c208/) — 工程复盘，记录深嵌套触发 API 报错与深度上限的实战经验

> [!tip] 51学通信知识星球
> **星主：爱卫生**（微信：gprshome201101）——公众号「51学通信」创办者，19 年通信行业运维与讲师经验，著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书（各大电商平台有售）。
> **星球定位**：深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域，主打运维岗位与信令分析。
> **当前规模**：5200 篇主题 / 990 个回答，198 元/年。
> **加入福利**：加入星球后请加爱卫生微信，拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题，另有群内分享。
> **AI 加持**：51学通信知识星球已作为 skill 接入 WorkBuddy，使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例，做总结、找文档和 PPT 等场景全覆盖。

![51学通信知识星球海报-扫码加入](https://wiki.500nian.com/images/zhixingqiu_qrcode.png)
