结构化输出
试用
在 Playground 中试用或生成现成的架构定义来试验结构化输出。
介绍
JSON 是世界上应用程序交换数据使用最广泛的格式之一。
结构化输出是一项功能,可确保模型始终生成符合您提供的 JSON 架构的响应,因此您无需担心模型会遗漏所需的键或产生无效的枚举值。
结构化输出的一些好处包括:
- 可靠的型式安全:无需验证或重试格式不正确的响应
- 明确拒绝:基于安全的模型拒绝现在可以以编程方式检测
- 更简单的提示:无需措辞强硬的提示即可实现一致的格式
除了在 REST API 中支持 JSON 架构外,适用于 Python 和 JavaScript 的 OpenAI SDK 还可以分别使用 Pydantic 和 Zod 轻松定义对象架构。下面,您可以看到如何从符合代码中定义的架构的非结构化文本中提取信息。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from pydantic import BaseModel
from openai import OpenAI
client = OpenAI()
class CalendarEvent(BaseModel):
name: str
date: str
participants: list[str]
completion = client.beta.chat.completions.parse(
model="gpt-4o-2024-08-06",
messages=[
{"role": "system", "content": "Extract the event information."},
{"role": "user", "content": "Alice and Bob are going to a science fair on Friday."},
],
response_format=CalendarEvent,
)
event = completion.choices[0].message.parsed
支持的型号
结构化输出在我们最新的大型语言模型中可用,从 GPT-4o 开始:
gpt-4o-mini-2024-07-18
及以后gpt-4o-2024-08-06
及以后
较早的模型(如 和 Earlier)可能会改用 JSON 模式。gpt-4-turbo
何时通过函数调用使用结构化输出,何时通过response_format
结构化输出在 OpenAI API 中以两种形式提供:
- 使用函数调用时
- 使用响应格式时
json_schema
当您构建一个桥接应用程序模型和功能的应用程序时,函数调用非常有用。
例如,您可以向模型提供对查询数据库的函数的访问权限,以便构建可以帮助用户处理订单的 AI 助手,或可以与 UI 交互的函数。
相反,当您想要指示结构化架构以便在模型响应用户时使用,而不是在模型调用工具时使用时,Structured Outputs via 更合适。response_format
例如,如果您正在构建数学辅导应用程序,您可能希望助手使用特定的 JSON 架构来响应您的用户,以便您可以生成一个 UI,以不同的方式显示模型输出的不同部分。
简单地说:
- 如果要将模型连接到系统中的工具、函数、数据等,则应使用函数调用
- 如果你想在响应用户时构建模型的输出,那么你应该使用结构化的
response_format
结构化输出与 JSON 模式
结构化输出是 JSON 模式的演变。虽然两者都可以确保生成有效的 JSON,但只有结构化输出才能确保架构合规性。结构化输出和 JSON 模式在聊天完成 API、助手 API、微调 API 和批处理 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 | 是的 | 是的 |
遵循架构 | 是(请参阅支持的架构) | 不 |
兼容型号 | gpt-4o-mini 、 和更高版本gpt-4o-2024-08-06 | gpt-3.5-turbo 和模型gpt-4-* gpt-4o-* |
使 | response_format: { type: "json_schema", json_schema: {"strict": true, "schema": ...} } | response_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
from pydantic import BaseModel
from openai import OpenAI
client = OpenAI()
class Step(BaseModel):
explanation: str
output: str
class MathReasoning(BaseModel):
steps: list[Step]
final_answer: str
completion = client.beta.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.parsed
响应示例
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
{
"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"
}
如何将结构化输出与 response_format 结合使用
您可以将结构化输出与新的 SDK 帮助程序结合使用,将模型的输出解析为所需的格式,也可以直接指定 JSON 架构。
结构化输出的 Refusals
当使用带有用户生成输入的结构化输出时,出于安全原因,OpenAI 模型有时可能会拒绝满足请求。由于拒绝不一定遵循您在 中提供的模式,因此 API 响应将包含一个新字段,该字段被调用以指示模型拒绝满足请求。response_format
refusal
当该属性出现在输出对象中时,您可以在 UI 中显示拒绝,或者在使用响应来处理拒绝请求情况的代码中包含条件逻辑。refusal
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Step(BaseModel):
explanation: str
output: str
class MathReasoning(BaseModel):
steps: list[Step]
final_answer: str
completion = client.beta.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
{
"id": "chatcmpl-9nYAG9LPNonX8DAyrkwYfemr3C8HC",
"object": "chat.completion",
"created": 1721596428,
"model": "gpt-4o-2024-08-06",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"refusal": "I'm sorry, I cannot assist with that request."
},
"logprobs": null,
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 81,
"completion_tokens": 11,
"total_tokens": 92,
"completion_tokens_details": {
"reasoning_tokens": 0,
"accepted_prediction_tokens": 0,
"rejected_prediction_tokens": 0
}
},
"system_fingerprint": "fp_3407719c7f"
}
提示和最佳实践
处理用户生成的输入
如果您的应用程序正在使用用户生成的输入,请确保您的提示包含有关如何处理输入无法产生有效响应的情况的说明。
模型将始终尝试遵循提供的架构,如果输入与架构完全无关,则可能会导致幻觉。
您可以在提示中包含语言以指定要返回空参数,或者如果模型检测到输入与任务不兼容,则包含特定句子。
处理错误
结构化输出仍可能包含错误。如果您发现错误,请尝试调整您的说明,在系统说明中提供示例,或将任务拆分为更简单的子任务。有关如何调整输入的更多指导,请参阅提示工程指南。
避免 JSON 架构发散
为了防止 JSON 架构和编程语言中的相应类型发散,我们强烈建议使用本机 Pydantic/zod sdk 支持。
如果您希望直接指定 JSON 架构,则可以添加 CI 规则,以便在编辑 JSON 架构或底层数据对象时进行标记,或者添加一个 CI 步骤,用于从类型定义自动生成 JSON 架构(反之亦然)。
支持的架构
结构化输出支持 JSON 架构语言的子集。
支持的类型
结构化输出支持以下类型:
- 字符串
- 数
- 布尔
- 整数
- 对象
- 数组
- 枚举
- anyOf 的
根对象不得为anyOf
请注意,架构的根级别对象必须是对象,并且不能使用 .Zod 中出现的一种模式(例如)正在使用可区分联合,这会在顶层生成一个。因此,如下所示的代码将不起作用:anyOf
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"]
}
}
尽管所有字段都必须是必需的(并且模型将为每个参数返回一个值),但可以通过使用 union 类型来模拟可选参数。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"
]
}
}
对象对嵌套深度和大小有限制
一个架构最多可以有 100 个对象属性,最多有 5 个嵌套级别。
对总字符串大小的限制
在 Schema 中,所有属性名称、定义名称、枚举值和 const 值的总字符串长度不能超过 15,000 个字符。
枚举大小的限制
一个架构在所有枚举属性中最多可以有 500 个枚举值。
对于具有字符串值的单个枚举属性,当枚举值超过 250 个时,所有枚举值的总字符串长度不能超过 7,500 个字符。
additionalProperties: false
必须始终在 Objects 中设置
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"
]
}
}
键排序
使用结构化输出时,输出的生成顺序将与架构中键的顺序相同。
尚不支持某些特定于类型的关键字
不支持的值得注意的关键字包括:
- 对于字符串:、、、
minLength
maxLength
pattern
format
- 对于数字: , ,
minimum
maximum
multipleOf
- 对于对象:、、
patternProperties
unevaluatedProperties
propertyNames
minProperties
maxProperties
- 对于数组:、、、、
unevaluatedItems
contains
minContains
maxContains
minItems
maxItems
uniqueItems
如果您通过使用不受支持的 JSON 架构提供和调用 API 来启用结构化输出,您将收到错误。strict: true
对于 ,每个嵌套架构都必须是此子集的有效 JSON 架构anyOf
下面是一个支持的 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
}
支持递归架构
示例递归架构 using 来指示根递归。#
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,但在某些边缘情况下,您应该适当地检测和处理。
要使用 Chat Completions 或 Assistants API 打开 JSON 模式,您可以将 设置为 .如果使用函数调用,则 JSON 模式始终处于打开状态。response_format
{ "type": "json_object" }
重要说明:
- 使用 JSON 模式时,您必须始终指示模型通过对话中的某些消息生成 JSON,例如通过您的系统消息。如果您不包含生成 JSON 的明确指令,则模型可能会生成无休止的空格流,并且请求可能会持续运行,直到达到令牌限制。为了帮助确保您不会忘记,如果字符串 “JSON” 未出现在上下文中的某个位置,API 将引发错误。
- JSON 模式不保证输出与任何特定架构匹配,只保证它是有效的并且解析没有错误。您应该使用结构化输出来确保它与您的架构匹配,或者如果无法匹配,则应使用验证库并可能重试,以确保输出与所需的架构匹配。
- 您的应用程序必须检测并处理可能导致模型输出不是完整 JSON 对象的边缘情况(请参阅下文)
资源
要了解有关结构化输出的更多信息,我们建议您浏览以下资源: