OpenAI API 结构化输出(Structured Outputs)怎么写:json_schema、Pydantic / Zod、refusal 和 incomplete 处理

想让 OpenAI 模型的回答严格符合你定义的 JSON 结构?在 Responses 请求的 text.format 里写 json_schema 并开启 strict,或用 Pydantic、Zod 加 parse。本文讲官方写法、schema 子集与上限,以及 refusal 与 incomplete 的处理。

NNathaniel bigo··原创首发·AI 辅助撰写
9 分钟读完
资料核对于 2026-11-05 · 依据官方文档与公开资料整理 其他 账号
本文根据 OpenAI 官方《Structured model outputs》整理,资料核对于 2026-11-05。配图为本站整理的示意图,不是界面截图。代码里的模型名取自官方当天示例,请以官方模型页为准。本站(bigolab.com)不是 OpenAI 官方网站。API 是按用量计费的独立服务,与 ChatGPT 订阅分开计算,Key 的获取见 /guides/openai-api-key。

适用于谁

  • 要把模型回答直接交给程序处理的开发者:信息抽取、分类打标、生成表单或界面描述;
  • 以前靠提示词“请只输出 JSON”、总是偶尔多一句废话或少一个字段的人;
  • 想知道 Structured Outputs 和 JSON 模式有什么区别的人。

如果你想了解跨厂商的思路(只靠提示词、JSON 模式、结构化输出三种做法对比),先看 /guides/llm-json-structured-output-reliable。本文只写 OpenAI 的具体写法。

结论先说

  1. Structured Outputs 保证输出符合你给的 JSON Schema:不会漏掉必填字段,也不会冒出不存在的枚举值。JSON 模式只保证是合法 JSON,不保证符合 schema。
  2. 官方建议能用就用 Structured Outputs,不用 JSON 模式。
  3. Responses API 里的写法:text.format 设成 type: "json_schema",并设 strict: true。
  4. 有现成辅助方法:Python 用 Pydantic 加 responses.parse,Node 用 Zod 加 responses.parse,结果在 output_parsed。
  5. 仍然要处理两种意外:模型拒绝回答(refusal),以及输出被 token 上限截断(incomplete)。这两种情况输出不一定符合 schema。

Structured Outputs 与 JSON 模式的区别

图:本站根据 OpenAI 官方《Structured model outputs》 整理的示意图

一、和函数调用怎么选

官方说 Structured Outputs 有两种形式:一是用在函数调用上,二是用 json_schema 响应格式。区分很简单:

  • 模型要连接你的工具、函数、数据:用函数调用,见 /guides/openai-api-function-calling;
  • 你想规定模型回答用户时的结构:用 text.format,比如数学辅导应用想让答案分成“步骤”和“最终答案”,好在界面上分别展示。

二、最省事的写法:Pydantic 和 Zod

Python(需要先 pip install openai pydantic)。下面按官方“抽取日程”例子改写:

python
from openai import OpenAI
from pydantic import BaseModel

client = OpenAI()

class CalendarEvent(BaseModel):
    name: str
    date: str
    participants: list[str]

response = client.responses.parse(
    model="gpt-6-astra",  # 以官方模型页当天为准
    input=[
        {"role": "system", "content": "从用户的话里提取日程信息。"},
        {"role": "user", "content": "小明和小红周五要去参加科学展。"},
    ],
    text_format=CalendarEvent,
)

event = response.output_parsed
print(event.name, event.date, event.participants)

Node.js(需要 npm install openai zod,保存为 .mjs 运行):

javascript
import OpenAI from "openai";
import { zodTextFormat } from "openai/helpers/zod";
import { z } from "zod";

const openai = new OpenAI();

const CalendarEvent = z.object({
  name: z.string(),
  date: z.string(),
  participants: z.array(z.string()),
});

const response = await openai.responses.parse({
  model: "gpt-6-astra", // 以官方模型页当天为准
  input: [
    { role: "system", content: "从用户的话里提取日程信息。" },
    { role: "user", content: "小明和小红周五要去参加科学展。" },
  ],
  text: {
    format: zodTextFormat(CalendarEvent, "event"),
  },
});

console.log(response.output_parsed);

官方推荐尽量用 SDK 的这类辅助方法,理由是避免“JSON schema 和代码里的类型逐渐不一致”。如果你坚持手写 schema,官方建议在 CI 里加规则:只要 schema 或对应的数据类被改了就报警,或者让 CI 从类型定义自动生成 schema。

三、自己写 JSON Schema

不用辅助方法时,直接在 text.format 里写。下面是官方“数学辅导”例子的 Python 结构:

python
response = client.responses.create(
    model="gpt-6-astra",  # 以官方模型页当天为准
    input=[
        {"role": "system", "content": "你是数学老师,一步一步引导用户解题。"},
        {"role": "user", "content": "8x + 7 = -23 怎么解?"},
    ],
    text={
        "format": {
            "type": "json_schema",
            "name": "math_response",
            "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,
            },
        },
    },
)

print(response.output_text)

注意 JSON 文本在 output_text 里,是字符串,需要自己 json.loads。官方还说:第一次使用某个 schema 会有额外延迟,之后相同 schema 的请求没有额外延迟。

官方给 schema 的三条建议:键名起得清楚直观;给重要的键写清楚标题和描述;用评测(evals)来决定哪种结构最适合你的场景。

四、schema 的限制

官方说 Structured Outputs 只支持 JSON Schema 的一个子集,要点:

  • 支持的类型:字符串、数字、布尔、整数、对象、数组、枚举、anyOf;
  • 根必须是对象,且不能是 anyOf。官方特别提醒,用 Zod 的判别联合(discriminated union)会在顶层产生 anyOf,这样写不行;
  • 所有字段必须 required。想表示可选,把类型写成 ["string", "null"] 这样的联合,模型会对每个字段都给值,没有就给 null;
  • 每个对象必须 additionalProperties: false;
  • 规模上限:最多 5000 个对象属性、最多 10 层嵌套;所有属性名、定义名、枚举值的字符总长不超过 120,000;枚举值总数最多 1000;
  • 不支持的关键字:allOf、not、dependentRequired、dependentSchemas、if、then、else;
  • 输出里的键顺序和 schema 里的顺序一致;
  • 支持 $defs 定义和递归 schema。

如果开了 strict: true 却传了不支持的 schema,你会收到错误。字符串还能用 pattern 和部分 format(例如 email、date-time、uuid),数字能用 minimum、maximum 等,数组能用 minItems、maxItems,但官方说这些约束对微调模型暂不支持。

五、两种边界情况必须处理

结构化输出拿不到结果怎么办:schema 不支持、缺少 required、可选字段、incomplete、refusal

图:本站根据 OpenAI 官方《Structured model outputs》 整理的示意图,处理办法一栏为本站补充

官方强调:模型有时不会生成符合 schema 的有效响应,原因有两个:拒绝和不完整。官方示例的判断结构,改写成 Python 如下:

python
response = client.responses.create(
    model="gpt-6-astra",  # 以官方模型页当天为准
    input=[{"role": "user", "content": "8x + 7 = -23 怎么解?"}],
    max_output_tokens=1024,
    text={"format": {"type": "json_schema", "name": "math_response",
                     "strict": True, "schema": schema}},  # schema 同上
)

if (response.status == "incomplete"
        and response.incomplete_details.reason == "max_output_tokens"):
    raise RuntimeError("输出被截断,结果不完整")

message = next(i for i in response.output if i.type == "message")
part = message.content[0]
if part.type == "refusal":
    print("模型拒绝:", part.refusal)
elif part.type == "output_text":
    print(part.text)
  • 达到 max_output_tokens 时,status 是 incomplete,JSON 可能是半截的;
  • 安全拒绝时,内容项的 type 是 refusal 而不是 output_text,官方把它称为“可以用程序检测的拒绝”。

另外官方还提醒两件事:一是用户输入与 schema 完全不相关时,模型仍会努力凑出符合 schema 的内容,可能编造,所以提示词里要写明“输入不适用时返回空值或一句固定话”;二是 Structured Outputs 仍然可能出错,内容错了就调整指令、在系统指令里加示例,或把任务拆成更简单的子任务。

常见问题

Q:必须用 gpt-6-astra 吗?

不是。官方说 Structured Outputs 在 gpt-4o 起的最新模型上可用,新项目建议从当天官方推荐的模型起步;json_schema 格式需要 gpt-4o-mini、gpt-4o-2024-08-06 及之后的快照。

Q:可以和流式一起用吗?

可以。官方页面有 SDK 的 stream 方法示例,会收到文字增量和拒绝增量事件,基础流式写法见 /guides/openai-api-streaming。

Q:它会让请求更贵吗?

官方这一页没有写额外费用,价格以官方价格页当天为准,见 /guides/openai-api-price-official-table。schema 本身会占输入 token 这一点,是本站的推断,实际用量请看账单。

Q:第一个请求很慢?

正常,官方说明首次使用新 schema 需要处理时间。

参考资料

  • OpenAI 官方:Structured model outputs — https://developers.openai.com/api/docs/guides/structured-outputs

Nathaniel 的更多内容

  1. 01

    Claude Code 在 Windows 上的常见坑:Git Bash 还是 PowerShell、路径反斜杠、假报错、WSL 怎么选

    Windows 上用 Claude Code,装好之后最容易踩的是:用哪个 shell、路径写法不一致、grep 没匹配就报错、钩子没生效、WSL 和原生怎么选。本文逐条按官方文档整理原因和处理办法,附三段提示词和一张对照表。

    Claude0
  2. 02

    Claude Code Hooks 实战「提交前自动跑测试」:PreToolUse 拦住 git commit,测试不过就退出码 2,附 Windows 注意点

    只写一个完整例子:让 Claude Code 每次要执行 git commit 之前,先自动跑项目测试,没通过就拦下并把输出交给它修。本文按官方 Hooks 文档给出脚本、settings.json 配置、测试方法、排错和 Windows 的两处坑。

    Claude0
  3. 03

    Claude Code 自定义斜杠命令与 Skill 实战:从零写一个「生成更新日志」的 /release-notes,含传参、预执行命令和调试

    概念看过了,还是不知道从哪下手?本文按 Claude Code 官方「Extend Claude with skills」写一个完整可用的例子:用 /release-notes 根据 git 记录生成更新日志,覆盖目录、frontmatter、传参、预执行命令、预授权和调试。

    Claude0
  4. 04

    Claude Code 部署网站:先写 Dockerfile 和 compose 在本机跑通,再按清单上云服务器,别让它直接改线上

    想让 Claude Code 帮你把网站部署到云服务器。本文只写通用做法,按 Docker 官方文档讲:让它写 Dockerfile、.dockerignore 和 compose,在本机跑通,端口和数据卷怎么设,再给出上线清单,并说明为什么不要让它直接动线上机器。

    Claude0
  5. 05

    Claude Code 写爬虫与数据清洗:先查 robots.txt 和网站条款,控制频率,原始数据留一份再清洗

    想让 Claude Code 写爬虫抓数据再清洗。本文先讲合规:robots.txt 不是授权也不是安全措施、网站条款要自己读;再讲怎么用 Python 标准库检查规则、小样本先试、保存原始数据,以及用 pandas 清洗,附四段提示词。

    Claude0
  6. 06

    Claude Code 写 Chrome 扩展:按 Manifest V3 起步,加载未打包的扩展,改了哪些文件要重新加载

    想让 Claude Code 帮你写一个 Chrome 扩展。本文按 Chrome 官方扩展文档讲:manifest.json 的最小写法、在 chrome://extensions 加载未打包的扩展、哪些改动要刷新、怎么看控制台和报错,再给四段提示词,以及权限要尽量少。

    Claude0

同产品的其他教程

  1. 01

    OpenAI API 的 token 怎么算、怎么估算费用:input_tokens 计数接口、tiktoken 和隐藏的输出 token

    发请求前想知道会用多少 token?官方有 Responses 的 input_tokens 计数接口,能把文字、图片、文件、工具定义一起算准;本地 tiktoken 只适合纯文本。本文给 Python 和 Node 示例,讲输出 token 为什么可能比看到的多,以及怎么估算费用。

    ChatGPT0
  2. 02

    OpenAI API Node.js / TypeScript 快速上手:npm install openai、example.mjs 第一个请求,到流式、函数调用、联网搜索

    用 Node.js 调 OpenAI API:设置 OPENAI_API_KEY 环境变量,npm install openai,新建 example.mjs,用 client.responses.create 发出第一个请求。并列出图片、文件、流式、函数调用、联网搜索和错误捕获的 Node 写法。

    ChatGPT0
  3. 03

    OpenAI API 用量层级(usage tiers)与速率限制怎么提升:Free、Build、Launch、Grow 和后台 Limits 页

    OpenAI API 的限速不是固定的:官方按累计购买额度把组织分成 Free、Build、Launch、Grow 四个层级,达到门槛自动升级。本文讲 RPM、TPM 等指标、怎么在后台查自己的限额、响应头怎么读、花费上限和限速的区别,以及怎么让限额变得够用。

    ChatGPT0
  4. 04

    OpenAI API 报错 400、401、403、404、429、500、503 怎么排查:按官方错误码页逐个看原因和解决办法

    调用 OpenAI API 返回 400、401、403、404、429、500、503,各自什么意思?本文按官方 Error codes 页,每个状态码一节,写清官方给的原因和解决办法,并说明 404 在官方页面里的位置、429 的六种情形,附 Python 和 Node 的错误捕获写法。

    ChatGPT0
  5. 05

    OpenAI Realtime API 是什么:语音到语音会话、WebRTC 与 WebSocket、临时密钥和事件机制

    Realtime API 让模型直接处理音频,做可以随时打断的语音助手。本文按官方文档讲它是什么、什么时候用、浏览器接入的流程、session 与事件、可用声音、60 分钟会话上限、安全标识,以及它和转写、文字转语音的分工。

    ChatGPT0
  6. 06

    OpenAI 图像生成 API 怎么用:gpt-image 系列、images.generate 与 Responses 工具、尺寸质量和遮罩编辑

    用 OpenAI API 生成或编辑图片,有 Image API 和 Responses 里的 image_generation 工具两条路。本文按官方文档给 Python 和 Node 示例,讲 gpt-image 模型选择、尺寸质量格式参数、遮罩编辑、被拦截时的错误处理,以及需要组织验证的提醒。

    ChatGPT0

0 条评论

登录 后参与评论

还没有评论,来抢沙发~