TodayAI

GuidesModels

用 Structured Outputs 拿到可解析的模型结果

用 json_schema + strict 约束 Chat Completions 输出,让结果可以直接解析进入应用逻辑。

基于 OpenAI Cookbook 整理 · 官方资料 ↗

把模型接到产品里时,最常见的失败不是“答案不对”,而是“答案长得不对”。你要填表单、写库、驱动下一步工作流,却拿到一段看起来像 JSON、实际少字段、多字段、或夹着解释文字的字符串。

Structured Outputs 解决的就是这件事:你先声明真正需要的数据结构,再让 API 按 schema 约束返回。启用 strict 后,结果形状由协议保证,而不是靠提示词碰运气。

这篇按 OpenAI Cookbook 的数学辅导示例,走完定义 schema、调用 Chat Completions、再直接 json.loads 的最短路径。

为什么“让模型返回 JSON”还不够

只在 system prompt 里写“请返回 JSON”,模型通常会配合,但应用侧仍然脆弱。字段可能缺失,类型可能漂移,还可能夹带 Markdown 代码围栏或额外说明。

JSON mode 能把输出约束成合法 JSON,仍然不保证键名、嵌套结构和必填项与你的业务对象一致。你还得写一堆校验、重试和兜底解析。

Structured Outputs 更进一步:你把 JSON Schema 交给 API,并打开 strict。请求要么按 schema 返回,要么在 schema 本身不合法时被拒绝。应用代码可以按契约读取字段,而不是先猜字符串形态。

先定义你真正需要的数据结构

先别急着调模型。先想清楚下游代码要读什么。Cookbook 的数学辅导示例要两块数据:分步推理解释,以及最终答案。

schema 名叫 math_reasoning。根对象包含 steps 数组和 final_answer 字符串;steps 里每一项都有 explanation 与 output。每个 object 都要设 additionalProperties: false,并在 json_schema 上设 strict: true,这样不能偷偷多塞字段。

升级 openai SDK(官方 Cookbook)

bash
pip install -U openai

math_reasoning schema

python
response_format = {
  "type": "json_schema",
  "json_schema": {
    "name": "math_reasoning",
    "strict": True,
    "schema": {
      "type": "object",
      "properties": {
        "steps": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "explanation": {"type": "string"},
              "output": {"type": "string"}
            },
            "required": ["explanation", "output"],
            "additionalProperties": False
          }
        },
        "final_answer": {"type": "string"}
      },
      "required": ["steps", "final_answer"],
      "additionalProperties": False
    }
  }
}

把 schema 交给模型

把上面的 response_format 直接传给 chat.completions.create。Cookbook 示例模型是 gpt-4o-2024-08-06;你账号若不可用同名模型,需换成同样支持 Structured Outputs 的版本。

python
from openai import OpenAI
from textwrap import dedent

client = OpenAI()
MODEL = "gpt-4o-2024-08-06"

response = client.chat.completions.create(
    model=MODEL,
    messages=[
        {"role": "system", "content": dedent("You are a helpful math tutor.")},
        {"role": "user", "content": "solve 8x + 7 = -23"},
    ],
    response_format=response_format,
)

这里没有再写“请用 JSON 返回”。形状约束来自 response_format 里的 json_schema,而不是额外提示词。

直接拿到可解析结果

成功时,message.content 就是一段符合 schema 的 JSON 字符串。直接 json.loads,再读 final_answer 和 steps。

python
import json

data = json.loads(response.choices[0].message.content)
print(data["final_answer"])
print(len(data["steps"]))
for step in data["steps"]:
    print(step["explanation"], "->", step["output"])

这一步的验收标准很具体:能解析、有 final_answer、steps 每项都有 explanation 与 output。若这些都成立,就可以把同一段解析结果接到 UI 或数据库写入逻辑上。

schema 不匹配时会发生什么

有两类“不匹配”,表现完全不同。

第一类是你提交的 schema 本身不合 Structured Outputs 规则——例如 required 不完整、object 漏了 additionalProperties: false。这时 API 会直接拒绝请求,你根本拿不到模型输出。

第二类是 schema 合法且 strict: true。此时模型输出会被约束到该 schema:不会悄悄少必填字段,也不会多出未声明键。你仍然要处理业务语义是否正确,但不需要再为“JSON 长得不对”写大量防御代码。

Structured Outputs 什么时候真正有价值

它真正值钱的地方,是输出马上要进入确定性代码路径:渲染分步 UI、写入表结构、组装下游工具参数、驱动状态机。字段名和嵌套关系一旦稳定,应用边界就清晰了。

开放式长文、探索性草稿、或你本来就要人工阅读的回复,通常没必要硬套 schema。Cookbook 也同时覆盖了 function calling 的 strict 模式;当你需要的是“选工具 + 填参数”,那是另一条同族能力。

实用建议:先把业务对象写成 schema,再让模型填它。不要先拿自由文本,再在应用层用正则硬拆。

最容易踩的坑

API 拒绝 schema

检查每个 object 是否都有 additionalProperties: false、required 是否覆盖全部 properties,以及模型是否支持 Structured Outputs。

仍得到不合 schema 的内容

确认走的是 response_format.type = "json_schema" 且 json_schema.strict = True,而不是只开了 JSON mode 或只在 prompt 里要求 JSON。

json.loads 失败或字段对不上

先打印 message.content;确认没有包在代码围栏里。再核对你读的键是否与 schema 一致(steps / final_answer)。

模型名不可用

Cookbook 使用 gpt-4o-2024-08-06。若账号无此模型,换成当前支持 Structured Outputs 的可用模型后再试。

官方资料

OpenAI Cookbook

Introduction to Structured Outputs