如何让大模型稳定输出 JSON?Prompt、JSON Mode 和 Structured Outputs 有什么区别?
简化版
三种手段保证强度递增:纯 Prompt 只能要求模型「尽量」输出 JSON,仍可能加解释、漏字段、语法错;JSON Mode 通常保证「语法上是合法 JSON」,但不保证字段符合你的 Schema;Structured Outputs / 严格模式 Function Calling 能约束输出匹配指定 JSON Schema(字段、类型、枚举、必填)。但无论哪种,字段值的业务正确性仍要程序校验——「解析成功」不等于「内容正确」。
详细版
| 方案 | 保证什么 | 不保证什么 |
|---|---|---|
| 文本 Prompt + 示例 | 尽量像 JSON | 可能多解释、漏字段、无效 JSON |
| JSON Mode | 可解析为 JSON(语法) | 字段名/类型/必填是否符合预期 |
| Structured Outputs | 匹配受支持的 JSON Schema | 值的业务正确性 |
| Function Calling (strict) | 工具参数符合 Schema | 操作是否已授权 |
Schema 要清晰描述字段语义、枚举、是否允许 null、附加属性。调用端还要处理拒答、截断、超时、Schema 不支持、语义错误、重试——别把「解析成功」等同「业务正确」。
完整版教学
一、为什么「只输出 JSON」不够(失败示例)
普通生成的目标是预测自然语言,所以模型经常在 JSON 上翻车:
你要的:{"name": "张三", "age": 30}
模型可能输出:
好的,这是结果: ← 多了前言
```json
{"name": "张三", "age": "30"} ← age 被写成字符串
``` ← 包在 markdown 代码块里
// 希望对你有帮助 ← 多了注释
即使语法合法,也可能数字写成字符串、漏必填字段、发明新字段。Few-shot 示例能改善格式,但给不了严格保证。
二、JSON Mode 的能力边界
JSON Mode 约束输出为有效 JSON,解决的是「语法可解析性」。但它通常不理解你的完整业务 Schema——所以「输出了一个 JSON 对象」和「精确符合预期结构」是两回事:
JSON Mode 保证:能 JSON.parse()
JSON Mode 不保证:{"naem": 30, "extra": true} ← 字段名拼错、类型错、多字段,但语法合法
如果平台支持 Structured Outputs,优先提供 Schema;不支持就用验证库检查 + 有限重试。
三、Structured Outputs 为什么更可靠
严格 Schema 约束的实现,会把 JSON Schema 转成解码约束——生成时只允许产生符合结构的 token 序列(constrained decoding):
Schema: { status: enum["pending","paid","failed"], amount: number }
约束解码:生成 status 时,只允许从三个枚举值里选;生成 amount 时只允许数字 token
→ 从源头杜绝字段名错、类型错、枚举越界
它能控制对象字段、类型、枚举、必填关系,显著减少格式错误。但注意边界:不同平台只支持 JSON Schema 的子集,Schema 过深或过于动态可能用不了;模型也可能拒答或因输出长度不足被截断,应用必须处理这些状态。
四、结构正确 ≠ 内容正确(最关键)
这是最容易被忽略的红线。Schema 能保证「形状」,但保证不了「内容」:
Schema 保证:amount 是数字 → 但不保证这个金额来自正确的发票
Schema 保证:status 是合法枚举 → 但不保证模型选对了状态
以下仍需业务层验证:数值范围和字段间关系、ID 是否真实存在、用户是否有权限、日期和单位是否合理、内容是否由证据支持。约束解码只管形状,求真靠程序。
五、Function Calling 与结构化回答的区别
- 结构化回答:返回程序可消费的数据;
- Function Calling:让模型建议调用某个工具及参数。
关键安全认知:即使开启严格模式、参数符合 Schema,“模型产生了调用”也不代表”操作已获授权”。应用仍要验证:工具名、参数语义、当前用户权限、风险等级,通过后才执行。而且工具返回值属于不可信外部数据,不能直接升级成高优先级指令(间接注入风险)。
六、Schema 设计与错误恢复
Schema 设计:字段名表达业务语义(别用模糊缩写)、用 description 说明来源和判定规则、能用 enum 就不用自由文本、明确 required 和 nullable、禁止多余附加字段、结构扁平少嵌套、给版本号防上下游不兼容。
错误恢复:先区分三类错误,只对可修复的重试:
解析错误(语法坏) → 可重试,把错误信息作为受控反馈
Schema 错误(缺字段)→ 可重试
语义错误(值不对) → 不盲目重试;先补证据/工具校验,必要时转人工或拒绝
+ 设最大重试次数,避免死循环
Schema 约束解决“输出能否被解析”,不会自动解决事实是否正确、字段语义是否合理以及动作是否获授权。
七、常见误区与追问
- 误区:JSON Mode 保证符合我的 Schema。 只保证语法合法,字段/类型/必填仍可能不符。
- 误区:Structured Outputs 保证内容对。 只保证形状(类型/枚举/必填),值的业务正确性要程序验。
- 误区:模型给了工具调用就能执行。 严格模式只保证参数形状,授权/权限/风险仍要应用校验。
- 追问:Structured Outputs 底层怎么实现? 把 Schema 转成解码约束,只允许生成符合结构的 token。
- 追问:为什么 Schema 正确还会错? 它管形状不管真值——金额是数字≠金额来自正确发票。
- 追问:工具返回值能直接信吗? 不能,是不可信外部数据,别升级为高优先级指令。
- 追问:什么错误该重试、什么不该? 解析/Schema 错可有限重试;语义错应先补证据或工具校验,不能让模型原样反复猜。
八、关键判断表
| 约束层 | 能保证什么 | 失败处理 |
|---|---|---|
| Prompt 示例 | 提高格式倾向 | 仍需解析校验 |
| JSON mode | 输出可解析 JSON | 不保证业务字段正确 |
| Schema constrained decoding | 结构符合 Schema | 语义仍需验证 |
| 业务校验 | 范围、权限、引用正确 | 拒绝或重试 |
九、加强记忆
三种强度记「口头要求 / 保证像合法表格 / 保证表头类型符合模板」:Prompt(尽量)< JSON Mode(语法合法)< Structured Outputs(匹配 Schema,常借助约束解码限制可生成结构)。最红线的一条钉死:结构正确 ≠ 内容正确——Schema 管形状(类型/枚举/必填),值是否真实、是否有权限仍要业务程序验。Function Calling 严格模式只保证参数形状、不代表已授权;工具返回值是不可信数据。Schema 要语义化字段 + enum + 版本号;格式错误可有限重试,语义错误先补证据或校验,不能原样反复猜。
项目实战落地
项目里怎么做的
《AI Agent智能会议纪要辅助系统》的纪要生成、纪要自检、说话人匹配三处模型调用都要拿到能直接入库的 JSON,项目在这里换过一次方案:
- 最早用
json_object。 只要求模型返回合法 JSON,字段靠提示词里的结构说明约束。拿真实 Key 端到端跑反思环时,重写步返回的纪要里有一条决策少了owner_suggestion,Pydantic 校验不过,整次自检记成失败;同样的请求单独跑几次又都正常。 - 换成
json_schema严格模式。 实测百炼 chat/completions 接口的严格模式,审查结果、说话人建议、纪要三份结构都能用,就把模型工厂切了过去:
# 严格结构化输出:模型返回的 JSON 必须符合 schema,必填字段一个都不能少
return model.bind(
response_format={
"type": "json_schema",
"json_schema": {"name": schema_name, "strict": True, "schema": schema},
}
)
结构定义写在代码常量里(如 MINUTES_SCHEMA),每一层对象都写了 additionalProperties: False 和完整的 required,风险级别用 enum 限定 LOW / MEDIUM / HIGH。拿到回复之后还有两层:
| 层 | 做什么 |
|---|---|
SchemaJsonParser | 继承 LangChain 的 JsonOutputParser,剥掉 ```json 围栏、忽略 JSON 后面多出来的文字;返回的不是 JSON 对象就抛异常。它只覆写了格式说明,换成中文「只返回一个 JSON 对象……必须符合下面的结构定义」 |
Pydantic MinutesResult | 在代码侧再校验一遍:字段缺失、类型不对、风险级别不在枚举里都会抛异常 |
《AI面试官与刷题训练平台》停在最基础的一层:请求里不设 response_format,JSON 结构全靠 Prompt 约束,再由代码宽容地解析。单题评分模板要求输出 score、level、advantage、problem、suggestion 五个字段,写明 score 是 0 到 100 的整数、level 可取优秀、良好、中等、待提升;动态追问要求「请只输出 JSON 对象,不要输出 Markdown 代码块或其他说明」,并给出 question、referenceAnswer 两个字段的示例。解析时剥掉代码围栏、截取第一个 { 到最后一个 },Jackson 关闭 FAIL_ON_TRAILING_TOKENS;截出来的仍不是合法 JSON,就报「AI评分结果不是合法JSON」。
为什么这样取舍
json_object只保证「是 JSON」。 字段缺失是偶发的,单测和少量手工测试很难发现,端到端跑整条反思环才暴露;严格模式把「必填字段一个不少、枚举只取枚举值」交给模型服务保证。- 严格模式之后仍保留 Pydantic。 结构约束在模型侧,校验在代码侧,两道关各管一头;解析失败为什么直接判失败、不填默认值,见「模型输出解析失败时,应该填默认值继续,还是直接报错?」。
- 格式说明继续写进提示词。 模型读的是中文提示词,结构说明也用中文附在后面;结构应该怎么设计,见「Prompt 输出契约应该如何设计?」。
面试官还会追问
- Pydantic 校验不通过时,这次模型调用在 AI 调用日志里记成成功还是失败?纪要本身落到什么状态?
- 纪要自检的重写步为什么直接复用纪要生成那份结构,而不是单独定义一份?
学完《AI Agent智能会议纪要辅助系统》,上面这些追问你都会迎刃而解。