结构化输出为什么经常失败?JSON Schema、校验与自动修复指南

直接回答:结构化输出经常失败,通常不是因为模型完全不会JSON,而是因为只要求“输出JSON”却没有严格Schema、字段描述和程序校验。稳定方案应包含五层:明确JSON Schema、启用平台支持的严格结构化输出、程序端校验、按错误类型自动修复、超过次数后人工或业务兜底。Structured Outputs用于约束最终答案格式,Function Calling用于让模型请求应用执行操作,两者不要混淆。[1][2]

API文档核验日期:2026年8月5日。不同模型支持的JSON Schema子集不同,迁移模型时必须重新验证。

结构化输出为什么经常失败?JSON Schema、校验与自动修复指南

五种最常见的失败

失败类型例子根因
语法失败少逗号、引号或括号只用文本提示,没有结构约束
类型失败数字字段返回字符串Schema缺失或校验未执行
字段失败漏必填项、增加额外项required与additionalProperties不明确
枚举失败状态返回“已完成”而系统只接受done字段描述与枚举不一致
语义失败格式合法但金额为负、日期矛盾JSON Schema无法覆盖全部业务规则

JSON模式不等于Schema遵循

旧式JSON mode通常只能保证输出是合法JSON,不能保证字段、类型和枚举完全符合你的结构。OpenAI文档建议对支持的模型优先使用json_schema结构化输出,并可设置strict;Gemini官方Structured Outputs也允许用JSON Schema限制最终响应。[1][2]

推荐的Schema设计原则

  1. 顶层明确type: object
  2. 所有生产必需字段放入required
  3. 不允许任意扩展时设置additionalProperties: false
  4. 分类字段使用enum
  5. 数字设置minimum、maximum;
  6. 字符串写清日期、邮箱、货币和单位格式;
  7. 数组设置items与合理的minItems/maxItems;
  8. 描述字段业务含义,而不只是写“string”。

程序校验是不可省略的一层

即使平台声称严格遵循Schema,也要在自己的应用中验证,因为:

  • 模型可能拒绝、截断或达到输出上限;
  • 某些模型只支持JSON Schema子集;
  • 工具调用、多模态或流式输出可能有额外边界;
  • 格式正确不代表业务数据正确;
  • 模型版本升级可能改变行为。

自动修复怎么做

错误本地修复需要模型重试
多余Markdown代码围栏删除围栏通常不需要
可安全转换的类型“12”转12视业务而定
字段名别名映射到标准字段可不重试
缺少关键字段只有默认值明确时补通常需要
语义冲突业务规则无法自动决定需要重试或人工

最稳定的错误反馈格式

不要只告诉模型“JSON不对,请重试”。应返回具体、机器可读的错误列表,例如:

{
  "validation_errors": [
    {"path":"$.age","error":"must be >= 0"},
    {"path":"$.email","error":"invalid email format"},
    {"path":"$.status","error":"must be one of: pending, done"}
  ]
}

然后明确要求:只修复这些字段,不改变已经通过校验的内容,不输出解释。

生产闭环

  1. 请求前检查模型和Schema是否兼容;
  2. 生成结构化结果;
  3. JSON解析和Schema校验;
  4. 执行业务规则校验;
  5. 轻量本地修复;
  6. 带错误反馈重试一次;
  7. 仍失败则进入降级模板、人工队列或备用模型;
  8. 记录模型版本、Schema版本、失败类型和原始输出。

怎样测试结构化输出

  • 空值、超长字符串、特殊字符和多语言;
  • 数组为空、数组过长和嵌套对象;
  • 枚举边界、日期时区和小数精度;
  • 超长输入导致输出截断;
  • 拒答和安全过滤;
  • 同一输入重复100次统计一次通过率。

最终指标不只是“JSON能解析”,还包括Schema一次通过率、业务规则通过率、平均重试次数和错误进入下游的比例。

Structured Outputs与Function Calling怎么选

需求更适合
最终答案必须按固定字段返回Structured Outputs
模型需要请求你的程序执行操作Function Calling
先搜索/计算,再返回固定结果工具调用+结构化输出

结论:提示词只能提高概率,Schema与校验才是工程约束。不要让未经校验的模型输出直接进入数据库、支付、邮件或自动发布流程。

一个最小Schema示例

{
  "type": "object",
  "properties": {
    "id": {"type": "string"},
    "status": {"type": "string", "enum": ["pending", "done"]},
    "amount": {"type": "number", "minimum": 0},
    "notes": {"type": "array", "items": {"type": "string"}}
  },
  "required": ["id", "status", "amount"],
  "additionalProperties": false
}

Schema只解决结构和基础约束。比如amount为100并不代表业务上正确,仍要核对币种、订单和总额。

截断、拒答和空输出怎么处理

  • 达到输出上限:标记为incomplete,不要尝试解析半个JSON;
  • 安全拒答:单独识别refusal状态,不要把拒答文本塞进字段;
  • 网络中断:保存流式缓冲,但重新请求前检查是否产生副作用;
  • 空输出:记录finish reason、模型状态和请求ID;
  • 工具调用:先完成工具循环,再校验最终结构化答案。

Schema也需要版本管理

给Schema设置版本号,例如invoice.v2。新增字段时考虑向后兼容;删除或改名字段前准备迁移;日志中同时记录模型版本和Schema版本。否则一次字段升级可能让旧客户端全部失败。

自动修复的边界

类型转换、去除代码围栏和别名映射适合本地修复;涉及业务含义时不要猜。例如“金额是-5”不能自动改为5,因为负数可能代表退款,也可能是模型错误。

补充常见问题

把示例JSON放进提示词够不够?

示例能帮助模型理解,但不能替代Schema和程序校验,尤其在长输入和边界值下。

是否应该每次失败都换模型?

先判断是Schema、提示词、截断还是模型能力。系统性错误应修设计,偶发错误才考虑重试或路由。

结构化输出能保证事实正确吗?

不能。它保证“长得像要求的结构”,不保证字段内容真实,事实仍需工具、数据库和业务规则验证。

参考资料

  1. OpenAI API:JSON Schema与Structured Outputs
  2. Google Gemini:Structured Outputs
  3. Google:Structured Outputs与Function Calling区别
© 版权声明

相关文章