本文面向需要把 GPT 结果接入数据库、队列、API 或 Agent 执行器的后端开发者与数据工程师,按数据抽取、分类路由、工具参数、界面数据和生产流水线拆解 Structured Outputs 的正确用法。文章同时覆盖拒绝、截断、Schema 版本化、业务校验与回归测试,帮助团队避免把“结构合法”误判成“业务正确”。
OpenAI Structured Outputs 的正确用法是:在需要稳定结构时启用 json_schema 与 strict: true,而不是只依赖提示词或普通 JSON mode;同时处理 Schema 支持范围、refusal、截断和业务语义验证。 这样可以约束 JSON 的结构,但不能保证日期、金额、身份关系或分类内容一定真实。
这篇 OpenAI Structured Outputs 指南适合三类读者:
- 后端开发者:需要把 GPT API 返回值稳定解析并写入数据库。
- 数据工程师:需要把模型结果接入队列、批处理或数据管道。
- Agent 工程师:需要同时约束最终响应格式和工具调用参数。
先确定 Structured Outputs 解决的边界
普通提示词只能“要求模型输出 JSON”,无法从协议层阻止模型遗漏字段、增加未知字段或改变字段类型。JSON mode 的目标主要是生成可解析的 JSON,它并不保证结果符合指定的 JSON Schema;OpenAI 官方也建议,在需要匹配特定 Schema 时优先使用 Structured Outputs。(help.openai.com)
OpenAI 对 Structured Outputs 的实现包含 Schema 约束和受限解码。官方公告中的复杂 Schema 评测显示,特定模型配置在该测试中达到 100% 的 Schema 匹配率,但这不是所有模型、所有任务或字段语义都达到 100% 正确 的承诺。(openai.com)
因此,生产系统需要把结果拆成三层:
- 格式层:是否是完整 JSON,是否符合 Schema。
- 内容层:字段值是否来自输入,是否存在幻觉或误抽取。
- 业务层:权限、数据库状态、金额范围和关联关系是否允许继续执行。
只处理第一层,仍然可能把格式正确但业务错误的数据写入正式系统。
数据抽取先设计可演进的 Schema
数据抽取是最适合使用 Structured Outputs 的场景之一,例如把合同、会议纪要或工单转成数据库记录。Schema 不应追求一次覆盖所有可能字段,而应先定义下游真正需要的最小对象。
一个最小抽取结构可以是:
{
"type": "object",
"properties": {
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": { "type": "string" },
"due_date": { "type": "string" },
"owner": { "type": "string" }
},
"required": ["name", "due_date", "owner"],
"additionalProperties": false
}
}
},
"required": ["items"],
"additionalProperties": false
}
这里有三个关键决定:
- 根对象明确声明
type: object,避免把数组、字符串误当成完整结果。 - 数组元素再次定义对象结构,避免每一项出现不同字段。
required与additionalProperties: false同时使用,使缺字段和多余字段更容易在接口层暴露。
仅在提示词中写“请返回 name、due_date 和 owner”时,模型仍可能输出说明文字、额外字段或不同命名。启用严格 Schema 后,结构约束会更明确,但日期是否真的在原文中出现,仍需要应用层核对原始文本。
OpenAI 如何保证输出符合 JSON Schema?
更准确的说法是:在支持的模型和请求配置下,strict: true 会让生成结果遵循所提供 Schema 的结构;前提是 Schema 使用了 Structured Outputs 支持的 JSON Schema 子集。Schema 不支持或不符合严格模式要求时,请求可能直接返回错误,而不是自动替开发者修改设计。(help.openai.com)
如果 Schema 变更频繁,建议在数据中保存 schema_version,并为每个版本保留输入样本、原始响应和解析结果。这样字段升级后,历史数据仍可以按旧版本回放。
分类路由使用枚举,但必须保留未知状态
分类任务经常被误写成一个自由文本字段:
{ "category": "请根据内容自行判断" }
这种设计会产生“技术支持”“技术问题”“需要支持”等语义相近但无法稳定路由的结果。更适合下游分支判断的做法,是使用有限枚举:
{
"type": "object",
"properties": {
"category": {
"type": "string",
"enum": ["billing", "technical", "account", "unknown"]
},
"needs_human_review": {
"type": "boolean"
}
},
"required": ["category", "needs_human_review"],
"additionalProperties": false
}
unknown 不是失败字段,而是生产系统必须保留的安全出口。当输入信息不足、类别重叠或模型无法判断时,系统应把记录送入人工复核,而不是通过提示词逼迫 GPT API 选择一个看似合理的类别。
Structured Outputs 和 JSON mode 有什么区别?
JSON mode 更像“输出必须是可解析 JSON”的约束;Structured Outputs 则进一步要求输出匹配指定 Schema。前者适合结构尚未固定的实验性场景,后者更适合数据库写入、固定 API 请求和自动路由。即使使用 Structured Outputs,分类标签的定义仍应足够清晰,并对 unknown 或人工复核路径做显式设计。(help.openai.com)
工具参数要严格,执行权限不能交给模型
Agent 工具调用需要区分两件事:
- 工具参数是否符合参数 Schema;
- 应用是否允许执行这次工具调用。
例如,模型可以生成符合 Schema 的退款参数,但这不代表订单存在,也不代表当前用户有退款权限。严格参数只能减少参数格式错误,不能代替权限系统、资源锁、库存检查或事务控制。
工具定义通常应包含:
{
"type": "function",
"function": {
"name": "create_ticket",
"description": "创建客户服务工单",
"strict": true,
"parameters": {
"type": "object",
"properties": {
"customer_id": { "type": "string" },
"priority": {
"type": "string",
"enum": ["low", "normal", "high"]
},
"summary": { "type": "string" }
},
"required": ["customer_id", "priority", "summary"],
"additionalProperties": false
}
}
}
OpenAI 的 Function Calling 帮助文档明确说明,严格模式要求 Schema 使用受支持的子集,并满足严格模式条件;应用仍需自行验证工具执行条件。(help.openai.com)
在执行器中,至少应按以下顺序处理:
- 解析工具名称和参数。
- 再次使用本地 Schema 验证库校验参数。
- 查询用户身份、资源状态和权限。
- 对金额、删除、发送消息等高风险动作增加确认或人工审批。
- 执行事务并记录工具调用、参数、结果和错误。
此外,Structured Outputs 与并行工具调用存在兼容限制。若工具执行器无法安全处理多个同时调用,应按照官方文档关闭并行工具调用,而不是假设每次只会出现一个动作。(openai.com)
界面数据和下游 API 必须增加语义校验
字段类型正确,不代表字段内容可用。以下情况都可能通过基础 Schema 验证,却在业务上失败:
due_date是合法日期字符串,但早于合同签署日期。amount是数字,但币种与订单币种不一致。customer_id格式正确,但数据库中不存在。start_date与end_date都合法,却出现结束日期早于开始日期。status属于枚举,但当前状态不允许直接跳转到目标状态。
因此,建议把验证分成两次。第一次验证 JSON 结构,第二次执行业务规则和数据库约束;只有两次都通过,才允许进入正式写入或自动执行流程。
JSON Schema 验证通过后还要做业务校验吗?
需要。JSON Schema 主要验证类型、字段、枚举和嵌套结构,无法自动知道业务系统中的权限、资源存在性、时间先后关系或金额归属。生产服务应把 Schema 验证视为“进入业务校验的门槛”,而不是最终验收结果。
对于界面生成和下游 API,字段描述也不能省略。描述应明确日期格式、单位、是否允许空值,以及“无法从输入确认时应返回什么状态”。描述越含糊,结构可能越稳定,但内容越容易漂移。
拒绝、截断和解析失败分别处理
Structured Outputs 遇到 refusal 怎么处理?
不要把 refusal 当成普通 JSON 解析错误,也不要对所有拒绝请求无限重试。OpenAI 官方说明,模型仍可能因安全原因拒绝请求,此时响应会提供拒绝信息,而不是符合业务 Schema 的对象。(openai.com)
建议将结果分成以下几类:
refusal:记录拒绝原因,按安全或人工处理流程结束。- 正常结束但本地解析失败:检查 SDK、响应路径和 Schema 版本。
- 因长度限制提前结束:保留原始响应,判断是否需要扩大输出预算或拆分任务。
- API 错误:记录请求标识、模型、参数和重试次数。
- 业务校验失败:进入人工复核或补充信息流程。
官方公告指出,生成在达到 max_tokens 或其他停止条件前被截断时,结果可能无法完成 Schema。(openai.com) 这类情况不应简单地把半截 JSON 拼接后入库,也不宜不加区分地自动重试,否则可能造成重复写入或重复调用外部工具。
每次处理至少保存以下信息:
- 原始响应;
finish_reason或对应结束状态;refusal状态;- Schema 名称与版本;
- 模型和 SDK 版本;
- 本地解析错误与业务校验错误。
新 Schema 第一次请求还可能出现额外延迟,因为服务需要预处理 Schema;官方说明典型 Schema 的首次处理通常低于 10 秒,复杂 Schema 可能达到 1 分钟,后续请求可复用处理结果。(openai.com) 因此,批处理任务应尽量复用稳定 Schema,避免每条数据动态生成一份结构不同的定义。
用回归样例保护生产数据管道
Structured Outputs 的上线验收不应只拿一条“正常输入”测试。建议建立四类固定样例:
- 正常样例:字段齐全、表达清楚、无需人工判断。
- 边界样例:缺日期、多个负责人、空数组、超长文本或歧义分类。
- 恶意样例:输入中包含要求改变格式、泄露提示词或绕过业务规则的内容。
- 升级样例:旧 Schema 生成的数据能否被新服务读取,新增字段是否影响旧消费者。
可把每次请求的结果分成“结构通过”“业务通过”“人工复核”“拒绝”“截断”几个状态,而不是只统计 API 成功率。这样才能区分是 Schema 设计问题、模型拒答问题、输出长度问题,还是下游数据库约束过严。
如果团队需要持续批处理、运行本地验证脚本或接入苹果开发流水线,可以参考 nuvcloud 的帮助文档 先确认远程运行、权限和文件传输方式;需要长期运行的任务,则应进一步比较 Mac 方案与按月使用成本,不要只按单次 API 调用费用判断总成本。
上线前按这份清单验收
- [ ] 已使用
json_schema或工具定义中的strict: true,没有只依赖提示词。 - [ ] Schema 明确声明对象、数组元素、
required和additionalProperties。 - [ ] 分类字段包含
unknown、人工复核或其他合法兜底状态。 - [ ] 已确认当前模型和 SDK 支持所使用的 Schema 子集。
- [ ] 本地代码会区分正常内容、
refusal、截断和 API 错误。 - [ ] Schema 验证后仍会执行权限、资源状态、日期、金额和关联字段校验。
- [ ] 工具执行前存在二次校验、幂等控制和高风险动作审批。
- [ ] 已保存原始响应、结束状态、Schema 版本和错误类型。
- [ ] 已准备正常、边界、恶意和升级回归样例。
- [ ] Schema 变更已经版本化,并通知所有下游消费者。
生产方案如何选择
不同用途不应套用同一套输出配置。最终回答需要可读性时,响应 Schema 可以保留面向界面的字段;工具参数则应尽量短小、枚举清晰,并由执行器承担权限和状态判断。
| 使用场景 | 推荐配置 | 必须补充的校验 | 主要失败处理 |
|---|---|---|---|
| 数据抽取写入数据库 | json_schema + strict: true |
字段来源、关联记录、数据库约束 | 业务失败进入复核 |
| 分类与队列路由 | 枚举 + unknown |
分类置信规则、队列权限 | 未知状态不自动分流 |
| Agent 工具参数 | 工具 Schema + strict: true |
权限、资源状态、幂等和审批 | 拒绝执行,不盲目重试 |
| 界面或下游 API | 响应 Schema | 日期、金额、标识符和跨字段关系 | 语义错误回退人工 |
| 批量生产管道 | 固定版本 Schema | 回归样例、日志和版本兼容 | 按错误类型重试或暂停 |
如果当前方案只是提示词加 JSON mode,真实缺点通常是:字段结构无法稳定演进、解析失败与业务失败混在一起、截断后容易误入库,并且工具参数格式正确时仍可能绕过应用层的权限判断。对于需要持续批处理或长期运行验证脚本的团队,租赁 nuvcloud 的 Mac 环境可以减少本地机器占用、临时配置和开发流水线迁移成本;但若任务是长期满负载运行、必须连接特定物理设备,或需要完全控制硬件,直接自购 Mac 可能更合适。需要临时算力或测试环境时,可先通过 nuvcloud 控制中心 验证实际运行流程,再决定采用临时还是常驻方案。
为结构化输出搭建稳定的远程 Mac 环境
使用 nuvcloud 独享裸金属 Mac,快速搭建 JSON Schema 调试、接口联调与生产流水线验证环境。
支持 SSH 与 VNC 双入口,既能运行自动化脚本和测试任务,也能按需接入完整远程桌面。