需要固定字段、类型、必填项或枚举时,使用 Structured Outputs;只需要一段可被 JSON.parse 读取、并愿意自己校验字段时,才考虑 JSON mode。两者都不能保证事实正确。Structured Outputs 解决“形状可靠”,业务规则、数值、权限和来源仍由应用验证。
官方资料(2026-10-29 核对):Structured model outputs · Responses format reference · Chat Completions response_format · 迁移到 Responses API
Structured Outputs 与 JSON mode 核心对比
| 维度 | Structured Outputs | JSON mode |
|---|---|---|
| 能否输出有效 JSON | 是 | 是,但仍要检查中断或拒答等边界 |
| 是否保证符合 Schema | 在支持的 Schema 子集和成功完成条件内保证 | 不保证;字段、类型、枚举和必填项可能不符 |
| 主要类型 | json_schema,通常配合 strict: true | json_object |
| Responses API | text.format | text.format |
| Chat Completions | response_format | response_format |
| 提示词要求 | 仍需清楚说明任务和字段语义 | 必须在上下文中明确要求 JSON |
| 应用侧工作 | 处理拒答、不完整响应和业务校验 | 另加 Schema 校验、有限重试及全部边界处理 |
什么时候选择哪一种?
优先 Structured Outputs
- API 结果要直接写入数据库、表单、队列或类型化后端。
- 字段缺失、类型错误或非法枚举会造成业务事故。
- 需要前端稳定渲染卡片、表格、图表或多步骤 UI。
- 希望用 SDK 的 Zod、Pydantic 等辅助能力减少 Schema 与类型漂移。
JSON mode 仍有价值
- 所用旧模型或集成暂不支持 Structured Outputs。
- 输出结构非常松散,应用本来就需要兼容多种对象。
- 正在从纯文本逐步迁移,短期先取得有效 JSON,再补 Schema。
不要因为 JSON mode 配置更短就默认它更稳定。只要下游依赖具体字段,你仍要把验证、重试和失败隔离写回应用。
Responses API:使用 text.format
Responses 的 Structured Outputs 配置位于 text.format。下面示例把输出限制为一个订单摘要;模型名称应替换为你当前项目实际支持并验证过的模型。
import OpenAI from "openai";
const client = new OpenAI();
const response = await client.responses.create({
model: "YOUR_MODEL",
input: "订单 A-102,客户要求周五前送达。",
text: {
format: {
type: "json_schema",
name: "order_summary",
strict: true,
schema: {
type: "object",
properties: {
order_id: { type: "string" },
deadline: { type: ["string", "null"] }
},
required: ["order_id", "deadline"],
additionalProperties: false
}
}
}
});
const message = response.output.find(item => item.type === "message");
const content = message?.content?.[0];
if (response.status !== "completed" || content?.type !== "output_text") {
throw new Error("refused or incomplete");
}
const data = JSON.parse(content.text);如果使用 SDK 的解析辅助方法,可以从代码类型生成 Schema 并取得已解析对象;无论哪种方式,都要先判断响应是否完成、是否出现拒答,再把结果交给业务逻辑。
Chat Completions:使用 response_format
Chat Completions 的 Schema 位于 response_format.json_schema。它与 Responses 的字段层级不同,因此迁移时不能原样复制。
const completion = await client.chat.completions.create({
model: "YOUR_MODEL",
messages: [
{ role: "developer", content: "提取订单字段。" },
{ role: "user", content: "订单 A-102,客户要求周五前送达。" }
],
response_format: {
type: "json_schema",
json_schema: {
name: "order_summary",
strict: true,
schema: {
type: "object",
properties: {
order_id: { type: "string" },
deadline: { type: ["string", "null"] }
},
required: ["order_id", "deadline"],
additionalProperties: false
}
}
}
});
const message = completion.choices[0].message;
if (message.refusal || !message.content) {
throw new Error("refused or incomplete");
}
const data = JSON.parse(message.content);要迁移完整端点、Items、状态和 Streaming,请先看 Responses API 与 Chat Completions 区别;本页只解决输出格式选择和失败处理。
strict Schema 最常见的四个问题
- 根节点必须是 object:不能把顶层直接定义为数组,也不能在根节点使用
anyOf。 - 字段都要 required:需要“可选”时,可把字段类型写成包含
null的联合类型,但字段本身仍列入required。 - 关闭额外字段:对象通常需要
additionalProperties: false,避免模型返回 Schema 外的键。 - 只使用受支持子集:Structured Outputs 支持 JSON Schema 的一个子集;先用官方指南核对类型、嵌套、枚举、anyOf、约束和递归支持,不要把任意 Schema 验证器的全部能力直接搬进请求。
大型 Schema 还要关注名称清晰、字段描述、嵌套深度和首次 Schema 处理延迟。把同一 Schema 版本化,并用固定评测集测试,比频繁动态生成更容易排查。
拒答、截断和“不完整 JSON”怎么处理?
Structured Outputs 并不表示每个请求都能得到业务对象。安全拒答可能以 refusal 出现;达到 max_output_tokens 或内容过滤时,Responses 可能标记为 incomplete。网络错误、超时和服务端错误则属于请求层问题。
- 先检查 HTTP/SDK 错误,不把网络失败当成 JSON 错误。
- Responses 先检查
status与incomplete_details;Chat Completions 检查结束原因和消息内容。 - 检查输出内容是否为 refusal;拒答应进入明确 UI 或业务分支,而不是强行 JSON.parse。
- 只有完整输出才解析;随后再做订单号、金额、权限、数据库约束等业务校验。
- 按错误类别决定是否重试,并设置次数、退避与幂等保护;不要对固定 Schema 错误无限重试。
遇到 500、503、连接或超时,请看 API 5xx 与超时排查;遇到 429,按 速率限制指南处理。
JSON mode 的正确用法
Responses 使用 text: { format: { type: "json_object" } };Chat Completions 使用 response_format: { type: "json_object" }。同时必须在 developer、system 或 user 上下文中明确要求生成 JSON。
const response = await client.responses.create({
model: "YOUR_MODEL",
instructions: "只返回有效 JSON,包含 summary 和 risk 字段。",
input: "分析这段订单备注。",
text: { format: { type: "json_object" } }
});JSON mode 不知道你的业务 Schema。解析后必须用 Zod、JSON Schema validator、Pydantic 或等价方式验证;失败时保留原始响应、Schema 版本、模型、Request ID 和失败类别,有限重试或进入人工处理。
Structured Outputs 与 function calling 怎么选?
两者都能使用严格 Schema,但意图不同:
- 模型给用户或界面返回结构化结果:使用
text.format或response_format。 - 模型决定调用你的工具、数据库或业务动作:使用 function calling,并在函数定义中启用适用的严格参数 Schema。
不要为了取得 JSON 就假装调用一个无副作用函数;也不要把需要执行动作的工具参数当成普通文本返回。工具调用还要校验授权、幂等和副作用。
上线前验收清单
- 确认模型和端点支持所选格式;不要只凭模型家族名称推断。
- Responses 使用
text.format,Chat Completions 使用response_format。 - 根 Schema 是 object,required 与 properties 一致,额外属性已关闭。
- 正常、空值、长文本、多语言、恶意输入和拒答样本均通过测试。
- 应用区分网络错误、429、5xx、refusal、incomplete、Schema 错误和业务校验失败。
- 有限重试带指数退避、jitter 与幂等保护,不形成重试风暴。
- 日志记录模型、Schema 版本、状态、Request ID、延迟和 usage,但不记录 API Key 或敏感原文。
- 通过 API Usage观察 Token、成本和失败率;批量离线任务可评估 Batch API。
Schema 能约束输出格式,却不会自动判断有害内容。文本或图片审核方式见 OpenAI Moderation API 指南。
Structured Outputs 约束模型输出;文档上传、purpose 和 file_id 管理由 OpenAI Files API 指南解释。
常见问题
Structured Outputs 和 JSON mode 最大区别是什么?
Structured Outputs 在支持的模型和 Schema 子集内保证输出遵循给定 JSON Schema;JSON mode 只保证结果是有效 JSON,不保证字段、类型、枚举或必填项符合业务 Schema。
新项目应该优先使用哪一个?
只要所选模型和场景支持,优先使用 Structured Outputs。只有兼容旧模型、旧集成或暂时无法采用受支持 Schema 时,才考虑 JSON mode,并在应用侧校验与有限重试。
Responses API 和 Chat Completions 的配置字段相同吗?
不同。Responses API 把格式配置放在 text.format;Chat Completions 使用 response_format。迁移时不能只复制同一段请求体。
Structured Outputs 能保证业务事实正确吗?
不能。它约束输出形状,不保证事实、计算、引用或业务判断正确。仍应验证来源、权限、数值范围和业务规则。
为什么 strict 模式的 Schema 会被拒绝?
常见原因包括根节点不是 object、根节点使用 anyOf、字段没有全部列入 required、对象没有关闭额外属性,或使用了 Structured Outputs 不支持的 JSON Schema 关键字。
Structured Outputs 还需要处理解析失败吗?
需要处理拒答、max_output_tokens 或内容过滤导致的 incomplete、没有 message/output_text、网络错误和应用侧类型错误。只有确认响应 completed 且内容不是 refusal 后,才进入业务解析。
JSON mode 为什么可能一直输出空白?
使用 JSON mode 时,必须在上下文中明确要求生成 JSON。官方文档提示,如果没有明确 JSON 指令,模型可能持续输出空白直到达到 Token 上限;同时应用仍要检测不完整 JSON。