教程
AI 生成 JSON 为什么还是会出错?2026 Structured Output、JSON Schema 与 Validation 完整指南
团队上了 json_schema + strict,CI 仍红。原因通常不是「模型又胡来了」,而是把 Structured Output 当成万能保险——它锁得住键名和类型,锁不住分类对错、数值真实和截断后的残 JSON。
把大模型接到订单抽取、风控打标或 Agent 工具参数里,JSON 出错是常态而不是例外。2026 年主流栈已经提供 Structured Output:OpenAI 的 json_schema + strict、Gemini 的 responseSchema、Anthropic 的 output_config.format、Apple 的 @Generable——它们比「请只返回 JSON」可靠一个数量级,但工程事故仍频繁发生。差别在于:很多失败发生在 Structured Output 的保障范围之外,或者 Schema 本身不可被该模型编译,或者校验链只做了 JSON.parse 就入库。这篇按「为什么还会错」组织,把 2026 能力边界、Schema 写法与本地 Validation 串成一条可落地的排查路径,并接上站内 Structured Output、Gemini、DeepSeek 与 MCP 相关文章。
Structured Output 解决了什么、没解决什么
先对齐预期。Structured Output(或各家的 JSON Schema 模式)在解码阶段限制下一 token 的合法集合,因此输出大概率同时满足:可被 JSON.parse 的语法;键名、必填、类型、枚举与 additionalProperties 等 Schema 声明的形状。OpenAI 文档把这一点与 JSON mode 明确区分:后者只保证 JSON 语法,前者保证你的 Schema。
它不能保证:分类标签在业务上是否正确;金额是否与原文一致;长数组是否因 max_tokens 被截断成半截对象;Tool arguments 执行后外部 API 是否成功。第三层语义与第四层工程仍 100% 是你的代码负责。团队里常见的挫败感来自「明明开了 strict 为什么还错」——错的可能根本不是形状层,而是把语义失败当成 Schema 失败去调 prompt。
另一个误区是把 Tool Calling 的 arguments JSON 与最终答复的 Structured Output 混为一谈。MCP tools/call、OpenAI function.arguments、DeepSeek strict tools 都涉及 JSON 字符串,但约束发生在不同 hop:有的只有 json_object,有的 Beta strict 只覆盖参数 Schema 子集。需要分 hop 看保障级别,而不是假设「全链路 Schema 化」。
四层错误:语法、形状、语义、工程
用四层 taxonomy 定位失败,比笼统说「JSON 坏了」更快:
| 层级 | 典型症状 | Structured Output 能否防 |
|---|---|---|
| 语法 | JSON.parse 抛错、尾逗号、截断字符串 | JSON mode / Schema 模式大多能防;token 不足时仍可能截断 |
| 形状 | 缺键、错类型、enum 外取值、多余字段 | json_schema + strict 可防;Schema 超子集或 strict 未开则不行 |
| 语义 | 字段齐但分类错、幻觉 ID、与源文本矛盾 | 不能;需抽检、双模型、规则引擎或检索 grounding |
| 工程 | 双 parse、编码 BOM、流式拼接丢括号、缓存旧 Schema | 不能;需统一校验入口与版本化 Schema |
语法层失败最好查:是否仍走纯 prompt;是否 max_tokens 过小;是否从 markdown 代码块里 regex 抠 JSON 而不是拿 API 的 structured 字段。形状层失败先查:strict 是否 true;required 是否列全所有键(OpenAI strict 要求);是否用了该模型不支持的 Schema 关键字。
语义层失败应接受「Schema 校验通过 ≠ 可入库」。工程上常见做法:Schema 校验作为 hard gate;业务规则(范围、外键存在性、与输入 hash 对比)作为 second gate;高风险场景人工或二次模型审核。
2026 各家 API 实际保证什么
2026 年没有「一个 Schema 走天下」,只有「按供应商文档选子集」。下面这张表是工程选型用的粗粒度对照,细节以各文档为准:
| 供应商 / 模式 | 配置入口 | 实际保证 |
|---|---|---|
| OpenAI json_schema + strict | response_format 或 Responses text.format | 形状 + 类型 + 必填;refusal 字段可表示拒答 |
| Gemini responseSchema | generationConfig.responseSchema | JSON 语法 + Schema 字段;关键字子集与 OpenAI 略有差异 |
| DeepSeek json_object | response_format.type = json_object | 语法 JSON;形状靠 prompt + 本地校验 |
| Tool arguments strict (Beta) | tools[].strict 或各家等价开关 | 参数 JSON 形状;Beta 子集限制 |
OpenAI 在 2026 年把 Structured Output 作为 Responses API 一等公民:Assistants sunset 后,json_schema 挂在 text.format 下,规则与 Chat Completions 相同。迁移时 Schema 本体可复用,变的是外壳字段——见Assistants → Responses 迁移文。Gemini 侧 responseJsonSchema 与 property ordering 更新见Gemini API JSON 输出教程。DeepSeek V4-Flash 仍停在 json_object,见DeepSeek V4-Flash JSON 文。
Apple 生态用 @Generable 在 Swift 侧做结构化,落地到 HTTP 或持久化前仍要 encode 成 JSON 再校验。Agent 工具链路上,Remote MCP 协议 JSON 的无状态化不替代 arguments 校验——见Stateless MCP 解析。
JSON Schema 写给模型看的五条规则
Schema 既是文档也是编译输入。写给模型时与写给人类 REST API 文档的侧重点不同:
- 扁平优先:深度嵌套与大量 $ref 增加编译失败率;能扁平的对象就扁平,重复结构用 $defs 但控制深度。
- additionalProperties: false 与 required 列全:OpenAI strict 下 object 的所有 properties 键都要出现在 required 里,否则模型可能合法地省略你「以为默认」的字段。
- 用 description 写清枚举语义:enum 只限制取值,description 帮助模型在边界 case 选对值;不要把业务规则只写在 prompt 里而 Schema 里空着。
- 慎用 oneOf 大矩阵:多分支联合体是常见编译失败源;能拆成多次调用或顶层 category + 子 Schema 就不要一个巨型 oneOf。
- 与本地校验器共用同一份 Schema:CI、运行时、JSONVue 手动排查应 pin 同一 JSON 文件,避免「线上 strict 用 A、本地校验用 B」。
官方 JSON Schema 教程见Understanding JSON Schema。注意「规范允许的关键字」与「某模型 API 实际支持的关键字」不是同一集合——集成测试应包含故意违规样本,而不只 happy path。
站内AI Structured Output 教程从 OpenAI json_schema 示例出发;本篇补的是失败模式与校验链,不重复 SDK 字段名清单。
Validation 流水线:parse → Schema → 业务
推荐固定三步,任何 hop(模型最终答复、Tool arguments、MCP 结果)都走同一套:
- Parse:JSON.parse 或等效;失败则记录 raw text、请求 id、模型版本,不要 silent retry 改字符串(易掩盖截断)。
- Schema:用 Draft 2020-12 或供应商要求的 dialect 校验;失败则输出 path 与 keyword,方便和 JSON Diff 对照预期 fixture。
- 业务:自定义规则——数值范围、跨字段一致、与源文档对齐、外键存在——这一步捕获语义错误。
Antidote 文档把 Structured Outputs 描述为减少格式错误;社区实践仍强调 client-side validation,因为供应商也明示 Schema 子集与拒答场景。把校验放在网关统一做,比在每个 Agent 分支里 copy paste 更不易 drift。
版本化 Schema:Breaking 改字段时 bump schema_version 或 tool 名,避免客户端缓存旧 Tool 列表(MCP 文里强调过 stale cache 会把错误 shape 写进 arguments)。响应用 metadata 带回 schema_version,便于日志聚合「哪版 Schema 开始失败率上升」。
排查清单与 JSONVue 实操
线上 JSON 报错时按顺序过一遍:
- 确认 hop:是最终 structured 字段、message.content 还是 tool_calls[].function.arguments?
- 看 token:finish_reason 是否 length;JSON 模式是否从中间截断。
- 对照 strict 与 Schema 文件:required 是否齐全;是否用了不支持的关键字。
- 准备三份 fixture:valid、missing-field、wrong-enum,在 CI 里对同一 Schema 跑校验。
- 语义抽检:Schema 过仍随机人工或规则复核,尤其金融、医疗、权限类字段。
JSONVue 适合在中间步骤人工复现:把 raw 响应贴进JSON 格式化看 parse 是否过;把 Schema 与实例贴进JSON Schema 校验看 shape 哪条失败;预期 vs 实际用JSON Diff对比键级差异。Agent 集成里可把这三步当作 on-call 手顺,比直接改 prompt 省时间。
若错误只在某一供应商出现,先缩小是 Schema 编译问题还是模型行为:同一 Schema 换 OpenAI strict 与 Gemini responseSchema 各跑一次,Diff 结果若 shape 一致而仅 enum 不同,多半是语义层或 description 不足,而不是解析器 bug。
延伸阅读:AI Structured Output 教程、Gemini API JSON 输出、DeepSeek V4-Flash JSON、Assistants → Responses 迁移。
常见问题 FAQ
开了 strict 为什么 Schema 校验还失败?
常见原因:本地校验 Schema 与请求里 Schema 不一致;strict 未 true;required 未列全 properties;或使用了该 API 不支持的 keyword。先用同一份文件在 JSONVue 校验通过,再对照请求体。
Structured Output 能替代 JSON Schema 校验吗?
不能替代。Structured Output 在模型侧提高形状合规概率;本地校验防供应商变更、防截断、防非模型 hop 的 JSON,并承载业务规则。两层叠加才稳。
json_object 和 json_schema 怎么选?
要固定字段、类型、枚举走 json_schema + strict;只要求合法 JSON、字段可漂移时用 json_object 并必须本地 Schema。DeepSeek 等仅支持 json_object 的栈只能后者。
Tool arguments 谁校验?
模型侧可能有 strict Beta;执行层必须 JSON.parse 后再 Schema 校验,再调真实 API。MCP tools/call 同理——协议无状态不等于参数自动正确。
总结与下一步
AI 生成 JSON 在 2026 年仍出错,因为 Structured Output 只覆盖语法与形状,语义与工程层仍依赖你;且各 API 支持的关键字与 strict 语义不同,Schema 写得太满会编译失败,写得太松则形状漂移。用四层 taxonomy 定位问题,用 parse → Schema → 业务三步校验入库,用版本化 Schema 与 fixture 防回归。
下一步:选一条生产失败样本,在 JSONVue 走格式化、Schema 校验与 Diff;对照本篇清单看落在哪一层。需要 API 接入细节时阅读 Structured Output 基础教程、Gemini JSON 输出、DeepSeek V4-Flash 与 Assistants 迁移文;需要 Agent 工具参数链路时阅读 Stateless MCP 解析。