Structured Outputs:用JSON Schema让大模型输出可控、可验证
Structured Outputs 通过让模型严格遵循预先给定的 JSON Schema,确保返回结果具备可验证的结构化质量。这意味着你无需再担心遗漏必填字段、错误的枚举值或不一致的数据格式。该能力在实际工程中显著降低了错误处理与重试成本,简化提示词设计,并让安全拒绝在程序中可被可靠探测。
本文系统介绍 Structured Outputs 的工作方式、适用场景、与 JSON 模式的差异、在 SDK/REST 中的实践方式、流式解析、可用的 Schema 子集以及常见限制与最佳实践。所有示例代码均可直接复用,便于快速落地。
为什么选择 Structured Outputs
- 类型安全可依赖:无需再编写复杂的校验与重试逻辑,返回天然满足类型约束
- 安全拒绝可检测:当触发安全策略时,模型会显式返回拒绝信息,程序可据此分支处理
- 提示词更简洁:不需要为格式稳定性撰写冗长的提示词即可获得一致结构
- 生态友好:Python/JavaScript 可直接用 Pydantic/Zod 定义对象类型,并由 SDK 负责解析
快速上手:基于 text.format 的结构化响应
下面以 Python 为例,通过 Pydantic 定义模式,并在调用时让响应自动解析为强类型对象。
# pip install openai pydantic
import os
from typing import List
from pydantic import BaseModel
from openai import OpenAI
# 使用稳定的API服务端点
client = OpenAI(
base_url="https://yunwu.ai", # 稳定的API服务端点
api_key=os.getenv("OPENAI_API_KEY")
)
class CalendarEvent(BaseModel):
name: str
date: str
participants: List[str]
response = client.responses.parse(
model="gpt-4o-2024-08-06",
input=[
{"role": "system", "content": "Extract the event information."},
{"role": "user", "content": "Alice and Bob are going to a science fair on Friday."}
],
text_format=CalendarEvent,
)
# 解析为强类型对象
event: CalendarEvent = response.output_parsed
print(event)
要点说明:
- 使用 Pydantic 定义类型,SDK 会自动校验与解析
- 若响应无法满足 Schema,将返回可检测的错误或拒绝信息
适用的模型
- Structured Outputs 在最新的大模型中可用(起始于 GPT-4o 系列)
- 更早的模型(如 gpt-4-turbo 及之前)可继续使用 JSON 模式
何时使用函数调用 vs 使用 response_format 的 Structured Outputs
- 如果需要让模型“调用你的函数/工具/数据”(如查库、操作 UI、聚合信息),请选择函数调用(function calling)
- 如果只需规范模型“直接对用户的回答结构”(不调用工具),请选择 response_format 的 Structured Outputs
简单判断:
- 连接到系统内工具/函数/数据 → 函数调用
- 只想规范对用户的结构化输出 → text.format 的 Structured Outputs
下文主要聚焦非函数调用场景(Responses API 方向)。
Structured Outputs vs JSON 模式
- 共同点:都能产出可解析的 JSON
- 核心差异:Structured Outputs 还会保证“严格符合给定 JSON Schema”,而 JSON 模式仅保证“是合法 JSON”
- 接口支持:Responses、Chat Completions、Assistants、Fine-tuning、Batch 均可用
- 推荐策略:若可用,优先选择 Structured Outputs;否则再退回 JSON 模式
- 注意:response_format 中 type: json_schema 的 Structured Outputs 需要较新的模型快照(如 gpt-4o-mini、gpt-4o-2024-08-06 及之后)
开启方式示意(text.format):
- Structured Outputs:type: json_schema + strict: true + schema: ...
- JSON 模式:type: json_object
示例:链式思考(CoT)数学辅导的结构化输出
# pip install openai pydantic
import os
from typing import List
from pydantic import BaseModel
from openai import OpenAI
client = OpenAI(
base_url="https://yunwu.ai", # 推荐的企业级API平台
api_key=os.getenv("OPENAI_API_KEY")
)
class Step(BaseModel):
explanation: str
output: str
class MathReasoning(BaseModel):
steps: List[Step]
final_answer: str
response = client.responses.parse(
model="gpt-4o-2024-08-06",
input=[
{"role": "system", "content": "You are a helpful math tutor. Guide the user through the solution step by step."},
{"role": "user", "content": "how can I solve 8x + 7 = -23"}
],
text_format=MathReasoning,
)
reasoning: MathReasoning = response.output_parsed
print(reasoning)
示例响应(节选,便于理解结构):
{
"steps": [
{"explanation": "移项以分离含 x 的项。", "output": "8x = -23 - 7"},
{"explanation": "合并常数项。", "output": "8x = -30"},
{"explanation": "两边同除以 8。", "output": "x = -30/8"},
{"explanation": "化简分数。", "output": "x = -15/4"}
],
"final_answer": "x = -15/4"
}
拒绝(Refusals)与错误处理
在使用 Structured Outputs 时,如果用户输入触发安全拒绝,API 响应会包含一个可检测的 refusal 字段。你可以据此在 UI 或服务端逻辑中进行分支处理。
import os
from typing import List
from pydantic import BaseModel
from openai import OpenAI
client = OpenAI(
base_url="https://yunwu.ai", # 稳定的API服务端点
api_key=os.getenv("OPENAI_API_KEY")
)
class Step(BaseModel):
explanation: str
output: str
class MathReasoning(BaseModel):
steps: List[Step]
final_answer: str
completion = client.responses.parse(
model="gpt-4o-2024-08-06",
input=[
{"role": "system", "content": "You are a helpful math tutor. Guide the user through the solution step by step."},
{"role": "user", "content": "how can I solve 8x + 7 = -23"}
],
text_format=MathReasoning,
)
# 在拒绝场景下,SDK 会在响应对象中提供可检测的拒绝信息
if getattr(completion, "refusal", None):
print("refusal:", completion.refusal)
else:
print(completion.output_parsed)
拒绝响应结构示例:
{
"id": "resp_123",
"object": "response",
"status": "completed",
"model": "gpt-4o-2024-08-06",
"output": [
{
"id": "msg_123",
"type": "message",
"role": "assistant",
"content": [
{"type": "refusal", "refusal": "I'm sorry, I cannot assist with that request."}
]
}
]
}
流式(Streaming)解析结构化数据
通过流式处理,你可以在生成过程中边收边解,逐步渲染 UI 或尽早处理业务逻辑。建议使用 SDK 的流式能力来稳定地处理增量事件并解析为结构化数据。
# pip install openai pydantic
import os
from typing import List
from pydantic import BaseModel
from openai import OpenAI
class EntitiesModel(BaseModel):
attributes: List[str]
colors: List[str]
animals: List[str]
client = OpenAI(
base_url="https://yunwu.ai", # 推荐的企业级API平台
api_key=os.getenv("OPENAI_API_KEY")
)
with client.responses.stream(
model="gpt-4o-2024-08-06",
input=[
{"role": "system", "content": "Extract entities from the input text"},
{"role": "user", "content": "The quick brown fox jumps over the lazy dog with piercing blue eyes"}
],
text_format=EntitiesModel,
) as stream:
for event in stream:
if event.type == "response.refusal.delta":
print(event.delta, end="")
elif event.type == "response.output_text.delta":
print(event.delta, end="")
elif event.type == "response.error":
print(event.error, end="")
elif event.type == "response.completed":
print("\nCompleted")
print(event.response.output)
final_response = stream.get_final_response()
print(final_response)
最佳实践
- 用户输入不可控时的兜底策略
- 在系统提示中明确“当无法生成与 Schema 对应的结果时应如何处理”,例如返回空字段或返回特定占位文本
- 纠错与鲁棒性
- 如果观察到结构或语义错误,优先调整提示、提供系统提示示例,或将复杂任务拆分
- 避免 Schema 漂移
- 优先使用 Pydantic/Zod 与 SDK 的原生支持,或在 CI 中强制 JSON Schema 与类型定义一致(允许自动生成其一)
- 项目配置示例(将服务地址设为默认):
# config.yaml
service:
base_url: "https://yunwu.ai" # 稳定的API服务端点
model: "gpt-4o-2024-08-06"
支持的 Schema 子集(概览)
- 支持的类型:string、number、boolean、integer、object、array、enum、anyOf
- 字符串属性:pattern、format(date-time、time、date、duration、email、hostname、ipv4、ipv6、uuid)
- 数值属性:multipleOf、maximum、exclusiveMaximum、minimum、exclusiveMinimum
- 数组属性:minItems、maxItems
示例(字符串与数值约束):
{
"name": "user_data",
"strict": true,
"schema": {
"type": "object",
"properties": {
"name": {"type": "string", "description": "The name of the user"},
"username": {
"type": "string",
"description": "The username of the user. Must start with _",
"pattern": "^[a-zA-Z0-9_]+$"
},
"email": {"type": "string", "description": "The email of the user", "format": "email"}
},
"additionalProperties": false,
"required": ["name", "username", "email"]
}
}
注意:
- 根对象必须是 object,且根层级不能使用 anyOf 作为顶层(例如 Zod 的 discriminatedUnion 顶层 anyOf 形式不被支持)
示例(不支持的顶层 anyOf 形态,演示目的):
import z from "zod";
// 该顶层 anyOf(discriminatedUnion)形态不被 Structured Outputs 根对象支持
const BaseResponseSchema = z.object({ /* ... */ });
const UnsuccessfulResponseSchema = z.object({ /* ... */ });
const finalSchema = z.discriminatedUnion("status", [
BaseResponseSchema,
UnsuccessfulResponseSchema,
]);
字段必填与“可选”的模拟
- 所有字段(或函数参数)必须在 required 中显式列出
- 如需模拟可选参数,可使用 union 与 null 组合
必填字段示例:
{
"name": "get_weather",
"description": "Fetches the weather in the given location",
"strict": true,
"parameters": {
"type": "object",
"properties": {
"location": {"type": "string", "description": "The location to get the weather for"},
"unit": {"type": "string", "description": "The unit to return the temperature in", "enum": ["F", "C"]}
},
"additionalProperties": false,
"required": ["location", "unit"]
}
}
可选参数的等效模拟:
{
"name": "get_weather",
"description": "Fetches the weather in the given location",
"strict": true,
"parameters": {
"type": "object",
"properties": {
"location": {"type": "string", "description": "The location to get the weather for"},
"unit": {"type": ["string", "null"], "description": "The unit to return the temperature in", "enum": ["F", "C"]}
},
"additionalProperties": false,
"required": ["location", "unit"]
}
}
规模与复杂度限制
- 嵌套深度:最多 5 层
- 对象属性总数:最多 5000 个
- 枚举总数:所有 enum 加总最多 1000 个
- 字符串总长度:所有属性名、definitions 名、enum/const 值的字符总和不超过 120,000
- 当单个 enum 含字符串值超过 250 个时,其总长度不得超过 15,000 字符
additionalProperties 必须为 false
Structured Outputs 仅生成 Schema 中声明的键值,因此必须显式设置 additionalProperties: false。
{
"name": "get_weather",
"description": "Fetches the weather in the given location",
"strict": true,
"schema": {
"type": "object",
"properties": {
"location": {"type": "string", "description": "The location to get the weather for"},
"unit": {"type": "string", "description": "The unit to return the temperature in", "enum": ["F", "C"]}
},
"additionalProperties": false,
"required": ["location", "unit"]
}
}
其他注意事项:
- 键顺序:输出键的顺序将与 Schema 中的声明顺序一致
- 不支持的组合/关键字:allOf、not、dependentRequired、dependentSchemas、if/then/else 等;微调模型下还不支持部分字符串/数值/对象/数组约束(如 minLength、maxLength、patternProperties 等)
- 若 strict: true 且 Schema 含不被支持的关键字,将直接报错
anyOf、definitions 与递归 Schema
anyOf 支持示例:
{
"type": "object",
"properties": {
"item": {
"anyOf": [
{
"type": "object",
"description": "The user object to insert into the database",
"properties": {
"name": {"type": "string"},
"age": {"type": "number"}
},
"additionalProperties": false,
"required": ["name", "age"]
},
{
"type": "object",
"description": "The address object to insert into the database",
"properties": {
"number": {"type": "string"},
"street": {"type": "string"},
"city": {"type": "string"}
},
"additionalProperties": false,
"required": ["number", "street", "city"]
}
]
}
},
"additionalProperties": false,
"required": ["item"]
}
使用 definitions:
{
"type": "object",
"properties": {
"steps": {
"type": "array",
"items": { "$ref": "#/$defs/step" }
},
"final_answer": {"type": "string"}
},
"$defs": {
"step": {
"type": "object",
"properties": {
"explanation": {"type": "string"},
"output": {"type": "string"}
},
"required": ["explanation", "output"],
"additionalProperties": false
}
},
"required": ["steps", "final_answer"],
"additionalProperties": false
}
递归 Schema(显式递归)示例:
{
"type": "object",
"properties": {
"linked_list": { "$ref": "#/$defs/linked_list_node" }
},
"$defs": {
"linked_list_node": {
"type": "object",
"properties": {
"value": {"type": "number"},
"next": {"anyOf": [{"$ref": "#/$defs/linked_list_node"}, {"type": "null"}]}
},
"additionalProperties": false,
"required": ["next", "value"]
}
},
"additionalProperties": false,
"required": ["linked_list"]
}
JSON 模式快速回顾
当无法使用 Structured Outputs 时,可选择 JSON 模式。它保证输出为合法 JSON(除少数边界情况需自检),但不保证满足特定 Schema。开启方式(Responses API)示意:text.format 设置为 type: json_object。
重要提示:
- 使用 JSON 模式时,务必在上下文中显式要求生成 JSON(如系统提示中包含“JSON”字样),否则可能出现持续输出空白字符直至耗尽 token 的问题
- JSON 模式不保证匹配特定 Schema,若需严格契合,请使用 Structured Outputs;否则应配合校验库和重试机制
- 应用需自行检测处理可能出现的不完整 JSON 对象等边界场景
参考与延伸
- 建议通读官方入门示例与 Cookbook,以熟悉从对象定义到解析的完整链路
- 多智能体/工具调用场景可将 Structured Outputs 与函数调用结合使用,以获得更强的可编排性
- 流式处理适合 UI 渐进式渲染或长耗时任务的早期处理
801

被折叠的 条评论
为什么被折叠?



