主导航

遗留 API

结构化模型输出

确保模型输出的文本符合您定义的 JSON 架构。

JSON 是全球应用程序交换数据时最广泛使用的格式之一。

结构化输出(Structured Outputs)是一项确保模型始终生成符合您所提供 JSON 架构响应的功能,因此您不必担心模型遗漏必需的键或臆造出无效的枚举值。

结构化输出的一些优势包括

  1. 可靠的类型安全:无需验证或重试格式错误的响应
  2. 明确的拒绝:基于安全原因的模型拒绝现在可以通过编程方式检测到
  3. 更简单的提示词:无需使用强硬的提示词来实现一致的格式

除了在 REST API 中支持 JSON 架构外,OpenAI 的 PythonJavaScript SDK 也分别通过 PydanticZod 轻松定义对象架构。在下文中,您可以看到如何从非结构化文本中提取符合代码中定义架构的信息。

获取结构化响应
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

支持的模型

结构化输出适用于我们的最新大语言模型,从 GPT-4o 开始。像 gpt-4-turbo 及更早期的旧模型可能会改用 JSON 模式

何时通过函数调用与通过 text.format 使用结构化输出

OpenAI API 中有两种形式的结构化输出

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

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

例如,您可以为模型提供查询数据库的函数,从而构建一个能帮助用户处理订单的 AI 助手,或者提供能与 UI 交互的函数。

相反,当您希望在模型响应用户时(而不是调用工具时)指定一个结构化架构,通过 response_format 使用结构化输出更为合适。

例如,如果您正在构建一个数学辅导应用,您可能希望助手使用特定的 JSON 架构来回复用户,这样您就可以生成一个 UI,以不同的方式展示模型输出的不同部分。

简而言之

  • 如果您正在将模型连接到系统中的工具、函数、数据等,那么您应该使用函数调用 - 如果您想在模型响应用户时对其输出进行结构化,则应使用结构化的 text.format

本指南的其余部分将重点介绍 Responses API 中非函数调用的使用场景。要了解关于如何在函数调用中使用结构化输出的更多信息,请查看

函数调用

指南。

结构化输出与 JSON 模式对比

结构化输出是 JSON 模式的演进版。虽然两者都确保生成有效的 JSON,但只有结构化输出能确保遵循架构。结构化输出和 JSON 模式均在 Responses API、Chat Completions API、Assistants API、微调 API 和 Batch API 中得到支持。

我们建议尽可能始终使用结构化输出代替 JSON 模式。

然而,带有 response_format: {type: "json_schema", ...} 的结构化输出仅支持 gpt-4o-minigpt-4o-mini-2024-07-18gpt-4o-2024-08-06 模型快照及后续版本。

结构化输出JSON 模式
输出有效 JSON
遵循架构是(参见支持的架构
兼容模型gpt-4o-mini, gpt-4o-2024-08-06 及后续版本gpt-3.5-turbo, gpt-4-*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 的新字段,以指示模型拒绝满足请求。

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

提示与最佳实践

处理用户生成的输入

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

模型将始终尝试遵循提供的架构,如果输入与架构完全不相关,这可能导致幻觉。

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

处理错误

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

避免 JSON 架构分歧

为了防止您的 JSON 架构与编程语言中的对应类型产生分歧,我们强烈建议使用原生 Pydantic/Zod SDK 支持。

如果您倾向于直接指定 JSON 架构,您可以添加 CI 规则,在 JSON 架构或底层数据对象被编辑时进行标记,或添加一个从类型定义自动生成 JSON 架构(反之亦然)的 CI 步骤。

流式传输

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

这样,您就不必等待整个响应完成即可处理它。如果您想逐个显示 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)

支持的架构

结构化输出支持 JSON 架构语言的子集。

支持的类型

结构化输出支持以下类型

  • 字符串
  • Number (数字)
  • Boolean (布尔值)
  • Integer (整数)
  • Object (对象)
  • Array (数组)
  • Enum (枚举)
  • anyOf

支持的属性

除了指定属性的类型外,您还可以指定一系列额外的约束

支持的 string 属性

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

支持的 number 属性

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

支持的 array 属性

  • 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 且必须为对象

请注意,架构的根级别对象必须是对象,且不能使用 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 (必需的)

要使用结构化输出,必须将所有字段或函数参数指定为 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"
        ]
    }
}

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

一个架构最多可包含 5000 个对象属性,嵌套层级最多 10 层。

字符串总大小限制

在架构中,所有属性名、定义名、枚举值和常量值的总字符串长度不能超过 120,000 个字符。

枚举大小限制

一个架构在所有枚举属性中最多可包含 1000 个枚举值。

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

对象中必须始终设置 additionalProperties: false

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

结构化输出仅支持生成指定的键/值,因此我们要求开发者设置 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"
        ]
    }
}

键排序

使用结构化输出时,输出生成的顺序将与架构中键的顺序一致。

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

  • 组合: allOf, not, dependentRequired, dependentSchemas, if, then, else

对于微调模型,我们额外不支持以下内容

  • 对于字符串: minLength, maxLength, pattern, format
  • 对于数字: minimum, maximum, multipleOf
  • 对于对象: patternProperties
  • 对于数组: minItems, maxItems

如果您通过提供 strict: true 开启结构化输出并使用不受支持的 JSON 架构调用 API,将会收到错误。

对于 anyOf,嵌套的架构必须各自是符合此子集的有效 JSON 架构

这是一个支持的 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"
    ]
}

支持定义 (Definitions)

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

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
}

支持递归架构

使用 # 表示根递归的递归架构示例。

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 设置为 { "type": "json_object" }。如果您正在使用函数调用,JSON 模式始终处于开启状态。

重要说明

  • 使用 JSON 模式时,您必须始终通过对话中的某条消息(例如通过您的系统消息)指示模型生成 JSON。如果您未包含生成 JSON 的明确说明,模型可能会生成无休止的空白流,并且请求可能会持续运行直到达到 Token 限制。为了帮助确保您不会忘记,如果字符串 “JSON” 没有出现在上下文的某个位置,API 将抛出错误。
  • JSON 模式不能保证输出匹配任何特定架构,仅保证它是有效的且解析时不会出错。您应使用结构化输出以确保其匹配您的架构,如果无法做到,则应使用验证库并可能采取重试机制,以确保输出匹配您所需的架构。
  • 您的应用程序必须检测并处理可能导致模型输出不是完整 JSON 对象的边缘情况(见下文)

资源

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

© . This website operates independently and is not affiliated with or endorsed by OpenAI, Inc. All brand names, logos, and trademarks are the property of their respective owners.