response-format-json-schema-strictness · ZH · 2026-11-04

在一把 key 上向六个后端要严格 JSON:schema 约束在哪一层开始松动

同一份 JSON schema 经聚合站发往不同后端(Claude、GPT、DeepSeek、Qwen、GLM、Kimi)时,约束强度按「提示词→JSON 模式→schema 约束解码」三层逐级递减,响应漂移主要出现在多余包裹文字、必填字段缺失和类型漂移三类。本文给出可复现的对照方法、每类漂移的成因,以及客户端必须兜底的三条校验线。

为什么「一把 key 六个后端」会放大 JSON 问题

聚合站的卖点是一个 API key 调多个模型,但结构化输出恰恰是最不适合「无差别转发」的场景。原因很简单:JSON 约束不是一个统一开关,而是至少三层机制叠起来的。

  • 提示词层:在 system 或 user 消息里写「只返回 JSON」。所有后端都支持,但都只是软约束。
  • JSON 模式层:请求里声明「输出必须是合法 JSON」。多数后端支持,但只保证语法可解析,不保证符合你的字段。
  • schema 约束解码层:把完整 schema 交给后端,在解码阶段屏蔽不合法的 token。支持与否、支持到什么子集,各家差异最大。

同一份 schema 从聚合站发出时,能不能落到第三层,取决于背后那条路由最终命中的是谁。于是你写的是一份期望,收到的却是六种不同的实现。

三层约束的判断方法

不要凭文档猜,用请求本身去测。一个最小对照实验:

  1. 固定同一份 schema(例如 user 对象:name 字符串、age 整数、tags 字符串数组,全部 required,additionalProperties: false)。
  2. 固定同一段业务文本作为输入,不要换 prompt。
  3. 分别向聚合站指定不同后端(或让它路由),记录原始响应体,而不是解析后的对象。
  4. 对每个响应回答三个问题:能否 JSON.parse?是否符合 schema?是否符合语义(字段值是否真的是那个意思)?

只有第三层通过,才说明背后那条路径真的在做 schema 约束解码。第一层通过只说明模型听话,第二层通过只说明语法正确。

漂移的三种形态

多余文字与包裹层

最常见。模型返回了合法 JSON,但外面包了 markdown 代码围栏,或前面加一句「好的,结果如下」。也有把结果再包一层的:{"result": {...}} 或 {"data": {...}}。

这类漂移通常出现在只有提示词约束、或 JSON 模式但不带 schema 的路径上。它对 JSON.parse 是致命的,但对 schema 校验反而是「幸运」的——一旦剥掉包裹就能通过。

必填字段缺失

schema 里标了 required,响应里却少了字段。常见于:模型认为某字段值为空就省略、把空数组直接不输出、或在长输出后段「忘记」结构。

这类漂移最危险,因为它不报错。下游代码读到 undefined 后在很远的地方才炸。JSON 模式层通常无法拦住它,只有 schema 约束解码才能从候选集里排除缺字段的序列。

类型漂移

age 声明为整数,返回 "18";或 tags 声明为数组,返回逗号分隔的字符串;布尔字段返回 "true"。这是最隐蔽的一类——JSON 语法合法、字段齐全,但类型错了。

类型漂移在弱 schema 约束下高频出现,尤其是数字、布尔、枚举这三类。枚举还常出现大小写或同义词替换。

一个实用的分级兜底策略

既然漂移不可避免,就把校验放在客户端,并按代价分级:

  • 第一道:结构剥离。拿到原始文本后,先去掉代码围栏,定位第一个 { 或 [ 到最后一个配对的括号,再 JSON.parse。这一步能吃掉大部分「多余文字」。
  • 第二道:schema 校验。用 JSON Schema 校验器(如 Ajv 一类)跑一遍,区分「缺字段」「类型错」「多余字段」三类错误并分别记录。不要只做 parse 就完事。
  • 第三道:语义与业务校验。schema 管不到的东西——枚举值是否在允许集内、数字是否在合理区间、ID 是否真实存在——必须单独写。

更稳的做法是自动修复 + 有限重试:对可安全转换的漂移(字符串数字转数字、单值转单元素数组)做规范化;对不可修复的(缺 required 字段、枚举越界)直接重试,并在重试时把上一次的错误摘要回填进 prompt。

路由与后端选择上的取舍

  • 如果你必须用严格结构化输出,就把请求显式绑定到已知支持 schema 约束的后端,不要依赖自动路由。自动路由在可用性和一致性之间,永远优先可用性。
  • 把 schema 复杂度压到最低:字段越少、嵌套越浅、枚举越短,任何一层的漂移概率都越低。扁平对象远比深嵌套可靠。
  • 在响应里保留后端标识(如果聚合站返回了实际命中的模型),把漂移按后端做统计。这样你能知道哪条路径需要更严的客户端校验,而不是对所有后端一刀切。
  • 对成本敏感的长批量任务,接受「更宽松的层 + 更强客户端校验」是合理取舍;对在线低延迟的关键路径,宁可换到约束更硬的后端。

结论

「一把 key 调六个模型」省的是接入成本,不是一致性成本。schema 约束在哪一层松动,取决于请求最终落到哪个后端、以及那个后端对 schema 的支持子集。把 JSON 解析、schema 校验、语义校验三道线都建在客户端,是唯一与后端无关的可靠做法。

补充一条计费上的事实,便于做成本核算:使用聚合站按官方价 ×1.3 扣费;贡献 key 按官方价 ×1.1(优质 ×1.2)返 USDC。