English
主导航

旧版 API

结构化模型输出

确保模型生成的文本响应遵循您定义的 JSON schema。

JSON 是世界上应用程序交换数据最广泛使用的格式之一。

结构化输出是一项功能,可确保模型始终生成遵循您提供的 JSON Schema,因此你不必担心模型会遗漏必需的键,或凭空产生无效的枚举值。

结构化输出的部分优势包括:

  1. 可靠的类型安全: 无需验证或重试格式不正确的响应
  2. 明确的拒绝: 基于安全考虑的模型拒绝现在可通过编程方式检测
  3. 更简单的提示词: 无需使用措辞强烈的提示词来实现一致的格式

除了在 REST API 中支持 JSON Schema 之外,适用于 Python and JavaScript 的 OpenAI SDK 也让使用 Pydantic and Zod 。在下文中,你可以了解如何从非结构化文本中提取符合代码中所定义 Schema 的信息。

获取结构化响应
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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-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 = response.output_parsed

支持的模型

Structured Outputs 可在我们的 最新大型语言模型中使用,从 GPT-4o 开始支持。较早的模型如 gpt-4-turbo and earlier may use JSON 模式 instead.

何时使用通过函数调用实现 Structured Outputs 与通过 text.format

Structured Outputs 在 OpenAI API 中提供两种形式:

  1. 当使用 函数调用
  2. 当使用 json_schema 响应格式

当你正在构建一个连接模型与应用功能的应用程序时,函数调用会非常有用。

例如,你可以让模型访问查询数据库的函数,从而构建一个能帮助用户处理订单的 AI 助手;或者让它访问可以与 UI 交互的函数。

相反,通过 response_format 当你希望指定一个结构化 schema,用于模型响应用户而非调用工具时,更为适合。

例如,如果你正在构建一个数学辅导应用程序,你可能希望助手使用特定的 JSON Schema 来响应用户,以便你能生成一个 UI,以不同的方式展示模型输出的不同部分。

简而言之:

  • 如果你要在系统中将模型连接到工具、函数、数据等,则应使用函数调用 - 如果你希望对模型响应用户时的输出进行结构化,则应使用结构化 text.format

本指南的其余部分将重点介绍 Responses API 中的非函数调用用例。要了解有关如何在函数调用中使用 Structured Outputs 的更多信息,请查看

函数调用

guide.

Structured Outputs 与 JSON 模式

Structured Outputs 是以下功能的演进: JSON 模式。虽然两者都能确保生成有效的 JSON,但只有结构化输出能确保严格遵循 schema。结构化输出和 JSON 模式在 Responses API、Chat Completions API、Assistants API、Fine-tuning API 和 Batch API 中均受支持。

我们建议在可能的情况下,始终使用 Structured Outputs 而非 JSON 模式。

然而,带有以下功能的 Structured Outputs response_format: {type: "json_schema", ...} 仅受 gpt-4o-mini, gpt-4o-mini-2024-07-18,且 gpt-4o-2024-08-06 模型快照及更高版本支持。

结构化输出JSON 模式
输出有效的 JSON
遵循 schema是 (参见 受支持的 schema)No
兼容模型gpt-4o-mini, gpt-4o-2024-08-06及后续gpt-3.5-turbo, gpt-4-* and gpt-4o-* 视觉
启用text: { format: { type: "json_schema", "strict": true, "schema": ... } }text: { format: { type: "json_object" } }

示例

思维链

你可以要求模型以结构化、分步的方式输出答案,从而引导用户理清解决方案。

用于数学辅导思维链的结构化输出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from openai import OpenAI
from pydantic import BaseModel

client = OpenAI()

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,
)

math_reasoning = response.output_parsed

示例响应

{
  "steps": [
    {
      "explanation": "Start with the equation 8x + 7 = -23.",
      "output": "8x + 7 = -23"
    },
    {
      "explanation": "Subtract 7 from both sides to isolate the term with the variable.",
      "output": "8x = -23 - 7"
    },
    {
      "explanation": "Simplify the right side of the equation.",
      "output": "8x = -30"
    },
    {
      "explanation": "Divide both sides by 8 to solve for x.",
      "output": "x = -30 / 8"
    },
    {
      "explanation": "Simplify the fraction.",
      "output": "x = -15 / 4"
    }
  ],
  "final_answer": "x = -15 / 4"
}

如何将结构化输出与 text.format

结构化输出中的拒绝

在将结构化输出用于用户生成的输入时,出于安全原因,OpenAI 模型可能会偶尔拒绝处理该请求。由于拒绝的响应不一定会遵循你在 response_format,API 响应将包含一个名为 refusal 中提供的 schema,因此我们会在响应中添加一个额外的字符串属性,以指示模型拒绝处理该请求。

refusal 属性出现在你的输出对象中时,你可以在 UI 中展示该拒绝信息,或者在使用该响应的代码中加入条件逻辑,以处理请求被拒绝的情况。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Step(BaseModel):
    explanation: str
    output: str

class MathReasoning(BaseModel):
steps: list[Step]
final_answer: str

completion = client.chat.completions.parse(
model="gpt-4o-2024-08-06",
messages=[
{"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"},
],
response_format=MathReasoning,
)

math_reasoning = completion.choices[0].message

# If the model refuses to respond, you will get a refusal message

if math_reasoning.refusal:
print(math_reasoning.refusal)
else:
print(math_reasoning.parsed)

拒绝的 API 响应大概如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
{
  "id": "resp_1234567890",
  "object": "response",
  "created_at": 1721596428,
  "status": "completed",
  "completed_at": 1721596429,
  "error": null,
  "incomplete_details": null,
  "input": [],
  "instructions": null,
  "max_output_tokens": null,
  "model": "gpt-4o-2024-08-06",
  "output": [{
    "id": "msg_1234567890",
    "type": "message",
    "role": "assistant",
    "content": [
      {
        "type": "refusal",
        "refusal": "I'm sorry, I cannot assist with that request."
      }
    ]
  }],
  "usage": {
    "input_tokens": 81,
    "output_tokens": 11,
    "total_tokens": 92,
    "output_tokens_details": {
      "reasoning_tokens": 0,
    }
  },
}

技巧与最佳实践

处理用户生成的输入

如果你的应用使用了用户生成的输入,请确保你的提示词包含有关如何处理输入无法生成有效响应情况的说明。

模型将始终尝试遵循提供的 schema,如果输入与 schema 完全无关,则可能会导致幻觉。

你可以在提示词中加入指令,指定如果模型检测到输入与任务不兼容,则返回空参数或特定的句子。

处理错误

结构化输出仍可能包含错误。如果你发现错误,请尝试调整指令、在系统指令中提供示例,或将任务拆分为更简单的子任务。有关如何调整输入的更多指导,请参阅 提示词工程指南 有关如何调整输入的更多指导。

避免 JSON schema 偏差

为了防止 JSON Schema 与编程语言中相应的类型发生偏差,我们强烈建议使用原生的 Pydantic/zod SDK 支持。

如果你倾向于直接指定 JSON schema,可以添加 CI 规则,在 JSON schema 或底层数据对象被修改时发出警告,或者添加一个 CI 步骤,从类型定义自动生成 JSON Schema(或反向操作)。

流式传输

你可以使用流式传输来处理正在生成的模型响应或函数调用参数,并将其解析为结构化数据。

这样,你无需等待整个响应完成即可对其进行处理。如果你想逐个显示 JSON 字段,或者在函数调用参数可用时立即对其进行处理,此功能尤为有用。

我们建议依赖 SDK 来处理结构化输出的流式传输。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
from typing import List

from openai import OpenAI
from pydantic import BaseModel

class EntitiesModel(BaseModel):
attributes: List[str]
colors: List[str]
animals: List[str]

client = OpenAI()

with client.responses.stream(
model="gpt-4.1",
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("Completed") # print(event.response.output)

    final_response = stream.get_final_response()
    print(final_response)

支持的 schema

结构化输出支持以下的子集 JSON Schema language.

支持的类型

结构化输出支持以下类型:

  • 字符串
  • 数字
  • 布尔值
  • 整数
  • 对象
  • 数组
  • 枚举
  • anyOf

支持的属性

除了指定属性的类型外,你还可以指定一系列附加约束:

支持 string properties:

  • pattern — 字符串必须匹配的正则表达式。
  • format — 字符串的预定义格式。目前支持:
    • date-time
    • time
    • date
    • duration
    • email
    • hostname
    • ipv4
    • ipv6
    • uuid

支持 number properties:

  • multipleOf — 该数字必须是此值的倍数。
  • maximum — 该数字必须小于或等于此值。
  • exclusiveMaximum — 该数字必须小于此值。
  • minimum — 该数字必须大于或等于此值。
  • exclusiveMinimum — 该数字必须大于此值。

支持 array properties:

  • minItems — 该数组必须至少包含此数量的项。
  • maxItems — 该数组最多包含此数量的项。

以下是关于如何使用这些类型限制的一些示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
{
    "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"
        ]
    }
}

请注意,这些约束 尚不支持微调模型.

根对象不得为 anyOf 并且必须是一个对象

请注意,schema 的根级别对象必须是一个对象,并且不得在顶层使用 anyOf。在 Zod(作为一个示例)中出现的一种模式是使用可辨识联合(discriminated union),它会产生一个 anyOf 。因此,如下所示的代码将无法运行:

1
2
3
4
5
6
7
8
9
10
11
12
13
import { z } from 'zod';
import { zodResponseFormat } from 'openai/helpers/zod';

const BaseResponseSchema = z.object({/* ... */});
const UnsuccessfulResponseSchema = z.object({/* ... */});

const finalSchema = z.discriminatedUnion('status', [
BaseResponseSchema,
UnsuccessfulResponseSchema,
]);

// Invalid JSON Schema for Structured Outputs
const json = zodResponseFormat(finalSchema, 'final_schema');

所有字段必须为 required

要使用 Structured Outputs,所有字段或函数参数必须指定为 required.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
    "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"]
    }
}

尽管所有字段都必须是必需的(并且模型会为每个参数返回一个值),但可以通过使用带有以下类型的联合类型来模拟可选参数: null.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
    "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"
        ]
    }
}

对象在嵌套深度和大小上有限制

一个 schema 最多可包含 5000 个对象属性,且嵌套层级最多为 10 层。

总字符串大小的限制

在一个 schema 中,所有属性名称、定义名称、枚举值和 const 值的总字符串长度不能超过 120,000 个字符。

枚举大小的限制

一个 schema 中的所有枚举属性最多可包含 1000 个枚举值。

对于包含字符串值的单个枚举属性,当枚举值超过 250 个时,所有枚举值的总字符串长度不能超过 15,000 个字符。

additionalProperties: false 必须在对象中设置

additionalProperties 控制是否允许对象包含未在 JSON Schema 中定义的额外键/值。

结构化输出仅支持生成指定的键/值,因此我们要求开发者设置 additionalProperties: false 以选择启用结构化输出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
    "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"
        ]
    }
}

键名排序

使用结构化输出时,输出的顺序将与模式中键名的顺序保持一致。

暂不支持某些特定类型的关键字

  • Composition: allOf, not, dependentRequired, dependentSchemas, if, then, else

对于微调模型,我们还不支持以下功能:

  • For strings: minLength, maxLength, pattern, format
  • For numbers: minimum, maximum, multipleOf
  • For objects: patternProperties
  • For arrays: minItems, maxItems

如果您通过提供 strict: true 并使用不受支持的 JSON Schema 调用 API,您将收到一个错误。

For anyOf,嵌套模式必须各自符合此子集的有效 JSON Schema

以下是一个受支持的 anyOf 模式示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
{
    "type": "object",
    "properties": {
        "item": {
            "anyOf": [
                {
                    "type": "object",
                    "description": "The user object to insert into the database",
                    "properties": {
                        "name": {
                            "type": "string",
                            "description": "The name of the user"
                        },
                        "age": {
                            "type": "number",
                            "description": "The age of the user"
                        }
                    },
                    "additionalProperties": false,
                    "required": [
                        "name",
                        "age"
                    ]
                },
                {
                    "type": "object",
                    "description": "The address object to insert into the database",
                    "properties": {
                        "number": {
                            "type": "string",
                            "description": "The number of the address. Eg. for 123 main st, this would be 123"
                        },
                        "street": {
                            "type": "string",
                            "description": "The street name. Eg. for 123 main st, this would be main st"
                        },
                        "city": {
                            "type": "string",
                            "description": "The city of the address"
                        }
                    },
                    "additionalProperties": false,
                    "required": [
                        "number",
                        "street",
                        "city"
                    ]
                }
            ]
        }
    },
    "additionalProperties": false,
    "required": [
        "item"
    ]
}

支持使用定义

您可以使用定义来定义在整个模式中被引用的子模式。以下是一个简单的示例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
{
    "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
}

支持递归模式

使用以下方式的递归模式示例 # to indicate root recursion.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
{
    "name": "ui",
    "description": "Dynamically generated UI",
    "strict": true,
    "schema": {
        "type": "object",
        "properties": {
            "type": {
                "type": "string",
                "description": "The type of the UI component",
                "enum": ["div", "button", "header", "section", "field", "form"]
            },
            "label": {
                "type": "string",
                "description": "The label of the UI component, used for buttons or form fields"
            },
            "children": {
                "type": "array",
                "description": "Nested UI components",
                "items": {
                    "$ref": "#"
                }
            },
            "attributes": {
                "type": "array",
                "description": "Arbitrary attributes for the UI component, suitable for any element",
                "items": {
                    "type": "object",
                    "properties": {
                        "name": {
                            "type": "string",
                            "description": "The name of the attribute, for example onClick or className"
                        },
                        "value": {
                            "type": "string",
                            "description": "The value of the attribute"
                        }
                    },
                    "additionalProperties": false,
                    "required": ["name", "value"]
                }
            }
        },
        "required": ["type", "label", "children", "attributes"],
        "additionalProperties": false
    }
}

使用显式递归的递归模式示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
{
    "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 模式

JSON 模式是结构化输出功能的一个更基础的版本。虽然 JSON 模式能确保模型输出有效的 JSON,但结构化输出能可靠地将模型的输出与您指定的模式相匹配。如果在您的用例中支持结构化输出,我们推荐您使用它。

当开启 JSON 模式时,模型的输出将被确保为有效的 JSON,但在某些边缘情况下可能并非如此,您需要自行检测并妥善处理。

要开启 Responses API 的 JSON 模式,您可以设置 text.format to { "type": "json_object" }。如果您正在使用函数调用,JSON 模式将始终处于开启状态。

重要提示:

  • 在使用 JSON 模式时,你必须始终通过对话中的某条消息(例如系统消息)指示模型生成 JSON。如果不包含生成 JSON 的明确指令,模型可能会生成无休止的空格流,并且请求可能会持续运行,直到达到 token 限制。为了帮助你避免遗忘,如果上下文中没有出现字符串“JSON”,API 将会抛出错误。
  • JSON 模式不能保证输出符合任何特定 schema,只能保证它是有效的并且能无错解析。你应该使用结构化输出来确保其匹配你的 schema;如果无法做到,则应使用验证库并视情况配合重试,以确保输出符合所需的 schema。
  • 你的应用必须检测并处理模型输出不是完整 JSON 对象的边缘情况(见下文)

资源

如需了解更多关于结构化输出的信息,我们建议浏览以下资源: