直接回答:结构化输出经常失败,通常不是因为模型完全不会JSON,而是因为只要求“输出JSON”却没有严格Schema、字段描述和程序校验。稳定方案应包含五层:明确JSON Schema、启用平台支持的严格结构化输出、程序端校验、按错误类型自动修复、超过次数后人工或业务兜底。Structured Outputs用于约束最终答案格式,Function Calling用于让模型请求应用执行操作,两者不要混淆。[1][2]
API文档核验日期:2026年8月5日。不同模型支持的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设计原则
- 顶层明确
type: object; - 所有生产必需字段放入
required; - 不允许任意扩展时设置
additionalProperties: false; - 分类字段使用
enum; - 数字设置minimum、maximum;
- 字符串写清日期、邮箱、货币和单位格式;
- 数组设置items与合理的minItems/maxItems;
- 描述字段业务含义,而不只是写“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"}
]
}然后明确要求:只修复这些字段,不改变已经通过校验的内容,不输出解释。
生产闭环
- 请求前检查模型和Schema是否兼容;
- 生成结构化结果;
- JSON解析和Schema校验;
- 执行业务规则校验;
- 轻量本地修复;
- 带错误反馈重试一次;
- 仍失败则进入降级模板、人工队列或备用模型;
- 记录模型版本、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、提示词、截断还是模型能力。系统性错误应修设计,偶发错误才考虑重试或路由。
结构化输出能保证事实正确吗?
不能。它保证“长得像要求的结构”,不保证字段内容真实,事实仍需工具、数据库和业务规则验证。
参考资料
© 版权声明
文章版权归作者所有,未经允许请勿转载。