Skip to content

Structured Outputs ​

Structured Outputs 是 OpenAI API 提供的一项能力,它能保证模型的输出严格符合你定义的 JSON Schema。这解决了 LLM 应用开发中一个长期痛点:模型返回的 JSON 格式不可靠,导致下游解析频繁出错。

在实际开发中,我们经常需要模型以特定的 JSON 结构返回数据 —— 比如提取文章中的实体信息、对用户评论进行分类、或者生成前端组件需要的数据格式。传统的做法是在 Prompt 中描述期望的格式,但模型可能遗漏字段、搞错类型,甚至返回不合法的 JSON。Structured Outputs 通过在解码层面施加约束,从根本上消除了这些问题。

工作原理 ​

ℹ️Schema 约束解码

Structured Outputs 的核心原理是约束解码(Constrained Decoding)。当你提供一个 JSON Schema 后,OpenAI 会在首次请求时将其编译为一个高效的状态机。在模型生成每个 token 时,这个状态机会屏蔽所有不符合 Schema 的 token 选择,确保最终输出 100% 匹配你的 Schema 定义。

这意味着:

  • 每个必填字段一定会出现
  • 字段类型一定正确(string 不会变成 number)
  • 枚举值一定在你定义的范围内
  • 不会出现多余的字段(当设置 additionalProperties: false)

这不是"尽力而为"的 Prompt 约束,而是数学意义上的保证。

使用方式 ​

Structured Outputs 有两种使用方式:通过 text.format 参数直接约束文本输出格式,或者通过 Function Calling 的 Strict Mode。两种方式都能提供 100% 的 Schema 合规保证,区别在于使用场景不同。

方式一:response_format(文本输出约束) ​

当你希望模型直接返回结构化的文本数据时,使用 text.format 参数。适用于数据提取、内容分类、格式化输出等场景。

python
from openai import OpenAI

client = OpenAI()

response = client.responses.create(
    model="gpt-4.1",
    input="列出三个历史事件",
    text={
        "format": {
            "type": "json_schema",
            "name": "historical_events",
            "schema": {
                "type": "object",
                "properties": {
                    "events": {
                        "type": "array",
                        "items": {
                            "type": "object",
                            "properties": {
                                "name": {"type": "string"},
                                "year": {"type": "integer"},
                                "significance": {"type": "string"}
                            },
                            "required": ["name", "year", "significance"],
                            "additionalProperties": False
                        }
                    }
                },
                "required": ["events"],
                "additionalProperties": False
            },
            "strict": True
        }
    }
)

import json
events = json.loads(response.output_text)
for e in events["events"]:
    print(f"{e['year']}年 - {e['name']}: {e['significance']}")
javascript
import OpenAI from "openai";

const client = new OpenAI();

const response = await client.responses.create({
  model: "gpt-4.1",
  input: "列出三个历史事件",
  text: {
    format: {
      type: "json_schema",
      name: "historical_events",
      schema: {
        type: "object",
        properties: {
          events: {
            type: "array",
            items: {
              type: "object",
              properties: {
                name: { type: "string" },
                year: { type: "integer" },
                significance: { type: "string" }
              },
              required: ["name", "year", "significance"],
              additionalProperties: false
            }
          }
        },
        required: ["events"],
        additionalProperties: false
      },
      strict: true
    }
  }
});

const events = JSON.parse(response.output_text);
events.events.forEach(e => {
  console.log(`${e.year}年 - ${e.name}: ${e.significance}`);
});

在这个示例中,我们定义了一个包含 events 数组的 Schema,每个事件有 name、year 和 significance 三个字段。无论模型生成什么内容,输出一定会严格遵循这个结构。additionalProperties: false 确保不会出现意料之外的字段。

方式二:Function Calling Strict Mode ​

当你需要模型调用工具并生成结构化的调用参数时,在工具定义中设置 "strict": true 即可启用 Structured Outputs。这种方式在 Function Calling 章节中已有详细介绍。

python
from openai import OpenAI

client = OpenAI()

tools = [{
    "type": "function",
    "name": "create_event",
    "description": "创建一个日历事件",
    "parameters": {
        "type": "object",
        "properties": {
            "title": {"type": "string", "description": "事件标题"},
            "date": {"type": "string", "description": "日期,格式 YYYY-MM-DD"},
            "duration_minutes": {"type": "integer", "description": "时长(分钟)"},
            "participants": {
                "type": "array",
                "items": {"type": "string"},
                "description": "参与者邮箱列表"
            }
        },
        "required": ["title", "date", "duration_minutes", "participants"],
        "additionalProperties": False
    },
    "strict": True
}]

response = client.responses.create(
    model="gpt-4.1",
    input="帮我创建一个明天下午的团队周会,时长1小时,参与者有 alice@co.com 和 bob@co.com",
    tools=tools
)
javascript
import OpenAI from "openai";

const client = new OpenAI();

const tools = [{
  type: "function",
  name: "create_event",
  description: "创建一个日历事件",
  parameters: {
    type: "object",
    properties: {
      title: { type: "string", description: "事件标题" },
      date: { type: "string", description: "日期,格式 YYYY-MM-DD" },
      duration_minutes: { type: "integer", description: "时长(分钟)" },
      participants: {
        type: "array",
        items: { type: "string" },
        description: "参与者邮箱列表"
      }
    },
    required: ["title", "date", "duration_minutes", "participants"],
    additionalProperties: false
  },
  strict: true
}];

const response = await client.responses.create({
  model: "gpt-4.1",
  input: "帮我创建一个明天下午的团队周会,时长1小时,参与者有 alice@co.com 和 bob@co.com",
  tools
});

通过 "strict": true,模型生成的函数参数一定包含所有 required 字段,且类型完全匹配。你可以放心地直接解析参数而不需要编写大量的防御性代码。

Structured Outputs vs JSON Mode ​

在 Structured Outputs 推出之前,OpenAI 已经提供了 JSON Mode(text.format.type = "json_object")。两者都能让模型输出 JSON,但能力差异很大。

特性 ⇅Structured Outputs ⇅JSON Mode ⇅
Schema 保证100% 符合定义的 Schema仅保证有效 JSON
Schema 定义需要提供 JSON Schema不需要
适用场景需要严格类型的应用灵活的 JSON 输出
出错率极低可能结构不匹配
配置方式text.format.type = json_schematext.format.type = json_object

简单来说:如果你只需要"模型返回一个有效的 JSON",JSON Mode 足够了;如果你需要"模型返回的 JSON 严格符合特定结构",则必须使用 Structured Outputs。在生产环境中,我们强烈推荐使用 Structured Outputs,因为它能从根本上消除格式解析错误。

JSON Mode 的一个常见问题是,模型可能返回一个合法的 JSON,但结构与你预期的完全不同。例如你期望 {"name": "...", "age": 30},模型可能返回 {"user_name": "...", "user_age": "30"}。字段名不同、类型也变成了字符串 —— 这在 JSON Mode 下是"正确"的输出,但对你的代码来说是错误的。Structured Outputs 彻底解决了这个问题。

何时使用 Structured Outputs vs Function Calling ​

这两个功能有一定的重叠 —— 它们都能产出结构化数据。选择哪个取决于你的具体使用场景。

场景 ⇅推荐方案 ⇅原因 ⇅
提取/分类数据Structured Outputs直接获取结构化文本输出
调用外部 APIFunction Calling天然适合工具调用场景
多步骤工作流Function Calling支持多工具编排
数据库写入Structured Outputs严格 Schema 防止写入错误
UI 渲染Structured Outputs输出直接绑定前端组件

一个实用的判断原则是:如果你需要模型"做某件事"(调用 API、执行操作),用 Function Calling;如果你只需要模型"按格式回答"(提取信息、生成结构化数据),用 Structured Outputs。

当然,这两种方式也可以组合使用。例如,你可以通过 Function Calling 让模型调用搜索 API 获取原始数据,然后在第二次请求中使用 Structured Outputs 让模型将搜索结果整理成固定格式。

支持的 JSON Schema 特性 ​

Structured Outputs 支持 JSON Schema 的一个子集。了解哪些特性被支持、哪些不被支持,可以帮助你设计出正确的 Schema。

支持的特性 ​

特性说明
type支持 string、number、integer、boolean、array、object、null
properties定义对象的字段
required指定必填字段(建议列出所有字段)
additionalProperties必须设为 false
items定义数组元素的类型
enum限制可选值范围
$ref / $defs引用和递归 Schema
anyOf联合类型(Union Type)
description字段描述(不影响约束,但帮助模型理解语义)

不支持的特性 ​

以下 JSON Schema 特性在 Structured Outputs 中不被支持,使用它们会导致 API 报错:

  • patternProperties — 正则匹配的属性名
  • if / then / else — 条件 Schema
  • oneOf — 互斥联合类型(请用 anyOf 替代)
  • not — 否定约束
  • minLength / maxLength — 字符串长度约束
  • minimum / maximum — 数值范围约束
  • pattern — 正则表达式约束
  • format — 格式约束(如 email、uri)

这些约束需要在你的应用层自行实现。例如,如果你需要验证邮箱格式,可以在收到模型输出后用正则表达式检查。

重要规则 ​

编写 Schema 时有几条必须遵守的规则:

  1. 所有字段必须设为 required。 如果某个字段是可选的,将其类型设为 anyOf: [{"type": "string"}, {"type": "null"}],让模型在不需要时填入 null。
  2. 必须设置 additionalProperties: false。 这在每一层 object 类型中都要设置,包括嵌套对象。
  3. 根级别必须是 object 类型。 不能直接使用 array 或原始类型作为 Schema 的根。
  4. Schema 名称只能包含字母、数字和下划线。 不支持中文或特殊字符。

最佳实践 ​

💡Structured Outputs 实践建议

以下建议可以帮助你更好地使用 Structured Outputs:

  1. Schema 尽量扁平化。 避免过深的嵌套结构。如果 Schema 超过 3-4 层嵌套,考虑拆分成多次请求,或使用 $ref 引用来组织结构。

  2. 善用 description 字段。 虽然 description 不参与格式约束,但它会影响模型填入的内容质量。在 description 中说明字段的含义、格式要求和示例值,能显著提高输出质量。

  3. 在 Prompt 中配合说明期望的输出。 即使 Schema 已经定义了结构,在 Prompt 中简要说明你期望的输出风格(如"每个 significance 字段用一句话概括")仍然有助于提高内容质量。

  4. 缓存 Schema 编译结果。 首次使用某个 Schema 时,API 会有额外的编译延迟(通常几秒)。之后的请求会使用缓存的编译结果,延迟恢复正常。相同的 Schema 只会编译一次。

  5. 处理 refusal 场景。 如果模型认为请求违反内容政策,它可能拒绝生成输出。此时返回的 refusal 字段会包含拒绝原因,而结构化内容为空。你的代码应该检查这一情况。

  6. 控制数组长度。 JSON Schema 的 minItems / maxItems 不被支持,但你可以在 Prompt 中指定数量要求(如"列出恰好 5 个事件"),模型通常能很好地遵循。

实际应用场景 ​

Structured Outputs 在以下场景中特别有价值:

数据提取与标注。 从非结构化文本中提取结构化信息,例如从简历中提取教育经历、工作经验等字段。传统做法需要大量正则表达式和规则引擎,现在只需定义一个 Schema 就能可靠地完成。

内容分类与情感分析。 让模型对输入内容进行多维度分类,返回包含分类标签、置信度和理由的结构化结果。由于输出格式固定,可以直接存入数据库或传给下游系统处理。

API 响应生成。 当你的后端需要返回固定格式的 JSON 给前端时,Structured Outputs 可以确保模型生成的内容一定能被前端正确解析和渲染,不需要额外的格式转换层。

测试数据生成。 批量生成符合特定格式要求的测试数据。定义好 Schema 后,模型可以生成大量多样化但格式统一的测试数据,大幅提高测试覆盖率。