Skip to main content

1.介绍

基本介绍

JSON 是世界上应用最广泛的数据交换格式之一。Structured Outputs 是一个功能,可以确保模型始终生成符合你提供的 JSON Schema 的响应,因此你无需担心模型遗漏必要的键,或生成无效的枚举值。 Structured Outputs 的一些好处包括:
  • 可靠的类型安全性:无需验证或重试格式错误的响应
  • 明确拒绝:基于安全的模型拒绝现在可以程序化检测
  • 简化提示:无需使用强烈措辞的提示即可实现一致的格式化
除了在 REST API 中支持 JSON Schema 之外,OpenAI 的 Python 和 JavaScript SDK 还分别使用 Pydantic 和 Zod 使得定义对象模式变得容易。下面你可以看到如何从符合代码中定义的模式的无结构文本中提取信息。
from openai import OpenAI
from pydantic import BaseModel

MODEL_NAME = "Qwen/Qwen2.5-7B-Instruct"
API_KEY = "您的 APIKEY", # 从https://cloud.siliconflow.cn/account/ak获取
client = OpenAI(api_key=API_KEY, base_url="https://api.siliconflow.cn/v1")
class Step(BaseModel):
    explanation: str
    output: str
class MathReasoning(BaseModel):
    steps: list[Step]
    final_answer: str
json_schema = MathReasoning.model_json_schema()
completion = client.chat.completions.create(
    model=MODEL_NAME,
    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={
        "type": "json_schema",
        "json_schema": {
            "name": "math_response",
            "schema": json_schema
    }
  }
)
math_reasoning = completion.choices[0].message.content
print(math_reasoning)
###2.支持模型列表 目前线上提供的大语言类模型都支持上述参数。
注意:支持的模型情况可能会发生变化,请查阅本文档了解最新支持的模型列表。
你的应用必须检测并处理可能导致模型输出不完整JSON对象的边缘案例。
请合理设置max_tokens,防止JSON字符串被中断。

3.使用场景

在 OpenAI API 中,Structured Outputs 有两种形式:
  • 使用函数调用时
  • 当使用 json_schema 响应格式时
函数调用在构建连接应用程序模型和功能的应用程序时很有用。 例如,你可以给模型访问查询数据库的功能,以便构建一个能帮助用户处理订单的 AI 助手,或者可以与 UI 交互的功能。 相反,通过 response_format 可以更适当地表示结构化输出,当模型响应用户时,你希望表明一个结构化的方案,而不是当模型调用工具时。 例如,如果你正在开发一个数学辅导应用,你可能希望助手使用特定的 JSON Schema 来响应用户,以便你可以生成一个 UI,以不同的方式显示模型输出的各个部分。 简单来说:
  • 如果你将模型连接到系统中的工具、函数、数据等,那么你应该使用函数调用。
  • 如果你希望在模型回应用户时结构化其输出,那么你应该使用一个结构化的 response_format
    本指南的其余部分将专注于 Chat Completions API 中的非函数调用用例。如需了解如何使用结构化输出进行函数调用,请参阅函数调用指南。

4. Structured Outputs vs JSON mode

Structured Outputs 是 JSON 模式的进化。虽然两者都能确保生成有效的 JSON,但只有 Structured Outputs 能确保遵循模式。在语言模型中和语言微调模型中,都支持 Structured Outputs 和 JSON 模式。 我们建议在可能的情况下始终使用结构化输出,而不是 JSON 模式。

示例

你可以让模型以结构化、分步骤的方式输出答案,引导用户解决问题。
链式思维数学辅导的结构化输出
    from openai import OpenAI
    from pydantic import BaseModel

    MODEL_NAME = "Qwen/Qwen2.5-7B-Instruct"
    API_KEY = "您的 APIKEY", # 从https://cloud.siliconflow.cn/account/ak获取
    client = OpenAI(api_key=API_KEY, base_url="https://api.siliconflow.cn/v1")
    class Step(BaseModel):
        explanation: str
        output: str
    class MathReasoning(BaseModel):
        steps: list[Step]
        final_answer: str
    json_schema = MathReasoning.model_json_schema()
    completion = client.chat.completions.create(
        model=MODEL_NAME,
        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={
            "type": "json_schema",
            "json_schema": {
                "name": "math_response",
                "schema": json_schema
        }
    }
    )
    math_reasoning = completion.choices[0].message.content
    print(math_reasoning)
输出示例:
    {
    "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"
}
您可以定义结构化字段以从无结构输入数据中提取信息,例如研究论文。
从研究论文中提取数据使用结构化输出
    from openai import OpenAI
    from pydantic import BaseModel

    MODEL_NAME = "Qwen/Qwen2.5-7B-Instruct"
    API_KEY = "您的 APIKEY", # 从https://cloud.siliconflow.cn/account/ak获取
    client = OpenAI(api_key=API_KEY, base_url="https://api.siliconflow.cn/v1")
    class ResearchPaperExtraction(BaseModel):
        title: str
        authors: list[str]
        abstract: str
        keywords: list[str]
    json_schema = ResearchPaperExtraction.model_json_schema()
    completion = client.chat.completions.create(
        model=MODEL_NAME,
        messages=[
            {"role": "system", "content": "You are an expert at structured data extraction. You will be given unstructured text from a research paper and should convert it into the given structure."},
            {"role": "user", "content": "..."}
        ],
        response_format={
            "type": "json_schema",
            "json_schema": {
                "name": "math_response",
                "schema": json_schema
            }
        }
    )
    math_reasoning = completion.choices[0].message.content
    print(math_reasoning)
输出示例:
{
    "title": "Application of Quantum Algorithms in Interstellar Navigation: A New Frontier",
    "authors": [
        "Dr. Stella Voyager",
        "Dr. Nova Star",
        "Dr. Lyra Hunter"
    ],
    "abstract": "This paper investigates the utilization of quantum algorithms to improve interstellar navigation systems. By leveraging quantum superposition and entanglement, our proposed navigation system can calculate optimal travel paths through space-time anomalies more efficiently than classical methods. Experimental simulations suggest a significant reduction in travel time and fuel consumption for interstellar missions.",
    "keywords": [
        "Quantum algorithms",
        "interstellar navigation",
        "space-time anomalies",
        "quantum superposition",
        "quantum entanglement",
        "space travel"
    ]
    }
你可以通过将 HTML 表示为具有约束条件的递归数据结构(如枚举)来生成有效的 HTML。
使用结构化输出生成 HTML
    from openai import OpenAI
    from pydantic import BaseModel

    MODEL_NAME = "Qwen/Qwen2.5-7B-Instruct"
    API_KEY = "您的 APIKEY", # 从https://cloud.siliconflow.cn/account/ak获取
    client = OpenAI(api_key=API_KEY, base_url="https://api.siliconflow.cn/v1")
    class UIType(str, Enum):
        div = "div"
        button = "button"
        header = "header"
        section = "section"
        field = "field"
        form = "form"

    class Attribute(BaseModel):
        name: str
        value: str

    class UI(BaseModel):
        type: UIType
        label: str
        children: List["UI"]
        attributes: List[Attribute]
    UI.model_rebuild() # This is required to enable recursive types
    class Response(BaseModel):
        ui: UI

    json_schema = Response.model_json_schema()
    completion = client.chat.completions.create(
        model= MODEL_NAME,
        messages=[
            {"role": "system", "content": "You are a UI generator AI. Convert the user input into a UI."},
            {"role": "user", "content": "Make a User Profile Form"}
        ],
            response_format={
                "type": "json_schema",
                "json_schema": {
                    "name": "ui schema",
                    "schema": json_schema
                }
            }
    )
    math_reasoning = completion.choices[0].message.content
    print(math_reasoning)
{
    "type": "form",
    "label": "User Profile Form",
    "children": [
        {
            "type": "div",
            "label": "",
            "children": [
                {
                    "type": "field",
                    "label": "First Name",
                    "children": [],
                    "attributes": [
                        {
                            "name": "type",
                            "value": "text"
                        },
                        {
                            "name": "name",
                            "value": "firstName"
                        },
                        {
                            "name": "placeholder",
                            "value": "Enter your first name"
                        }
                    ]
                },
                {
                    "type": "field",
                    "label": "Last Name",
                    "children": [],
                    "attributes": [
                        {
                            "name": "type",
                            "value": "text"
                        },
                        {
                            "name": "name",
                            "value": "lastName"
                        },
                        {
                            "name": "placeholder",
                            "value": "Enter your last name"
                        }
                    ]
                }
            ],
            "attributes": []
        },
        {
            "type": "button",
            "label": "Submit",
            "children": [],
            "attributes": [
                {
                    "name": "type",
                    "value": "submit"
                }
            ]
        }
    ],
    "attributes": [
        {
            "name": "method",
            "value": "post"
        },
        {
            "name": "action",
            "value": "/submit-profile"
        }
    ]
}
你可以根据多个类别对输入进行分类,这是一种常见的审核方式。
使用结构化输出进行内容审核
    from openai import OpenAI
    from pydantic import BaseModel

    MODEL_NAME = "Qwen/Qwen2.5-7B-Instruct"
    API_KEY = "您的 APIKEY", # 从https://cloud.siliconflow.cn/account/ak获取
    client = OpenAI(api_key=API_KEY, base_url="https://api.siliconflow.cn/v1")
    class Category(str, Enum):
        violence = "violence"
        sexual = "sexual"
        self_harm = "self_harm"

    class ContentCompliance(BaseModel):
        is_violating: bool
        category: Optional[Category]
        explanation_if_violating: Optional[str]

    json_schema = ContentCompliance.model_json_schema()
    completion = client.chat.completions.create(
        model=MODEL_NAME,
        messages=[
            {"role": "system", "content": "Determine if the user input violates specific guidelines and explain if they do."},
            {"role": "user", "content": "How do I prepare for a job interview?"},
        ],
                response_format={
                    "type": "json_schema",
                    "json_schema": {
                        "name": "ContentCompliance",
                        "schema": json_schema
                    }
                }
    )
    math_reasoning = completion.choices[0].message.content
    print(math_reasoning)
{
    "is_violating": false,
    "category": null,
    "explanation_if_violating": null
}

如何使用

如何使用response_format输出结构化数据

您可以使用新 SDK 辅助程序中的结构化输出来将模型的输出解析为您所需的格式,或者可以直接指定 JSON 方案。 注意:使用任何模式进行首次请求时,由于我们的 API 需要处理模式,因此会有多余的延迟,但使用相同模式的后续请求将不会有多余的延迟。
  • 步骤 1: 定义你的对象 首先,您必须定义一个对象或数据结构来表示模型应遵循的 JSON Schema。请参阅本指南顶部的示例以获取参考。 Structured Outputs 支持大部分 JSON Schema,但出于性能或技术原因,有些功能是不可用的。更多详情请见此处。 为了最大化模型生成的质量,我们建议如下:
    • 名称键要清晰直观
    • 为结构中的重要键创建清晰的标题和描述
    • 创建并使用 evals 来确定最适合您用例的结构
  • 步骤 2: 在 API 调用中提供您的对象 你可以使用 create 方法将 JSON 响应自动解析为你定义的对象。SDK 内部会处理提供与您的数据结构对应的 JSON 模式,然后将响应解析为对象。
  • 步骤 3: 处理边缘情况 在某些情况下,模型可能不会生成符合提供的 JSON schema 的有效响应。这可能发生在拒绝的情况下,如果模型出于安全原因拒绝回答,或者例如您达到了最大 tokens 限制,导致响应不完整。
try:
  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=MathResponse,
      max_tokens=50
  )
  math_response = completion.choices[0].message
  if math_response.parsed:
      print(math_response.parsed)
  elif math_response.refusal:
      # handle refusal
      print(math_response.refusal)
except Exception as e:
  # Handle edge cases
  if type(e) == openai.LengthFinishReasonError:
      # Retry with a higher max tokens
      print("Too many tokens: ", e)
      pass
  else:
      # Handle other exceptions
      print(e)
      pass

提示和最佳实践

####处理用户生成的输入 如果您的应用程序使用用户生成的输入,请确保提示中包含如何处理输入无法生成有效响应的情况的说明。 该模型会始终尝试遵循提供的结构,如果输入与结构完全无关,则可能会产生幻觉。 您可以在提示中加入语言来指定希望模型在检测到输入与任务不兼容时返回空参数,或特定句子。 ####处理错误 为了防止您的 JSON Schema 和相应编程语言中的类型出现偏差,我们强烈建议使用原生的 Pydantic/zod SDK 支持。 如果您更喜欢直接指定 JSON 方案,可以添加 CI 规则以标记当 JSON 方案或底层数据对象被编辑时的情况,或者添加一个 CI 步骤来自动生成 JSON 方案(反之亦然)。

避免 JSON 模式偏差

为了防止您的 JSON Schema 和相应编程语言中的类型出现偏差,我们强烈建议使用原生的 Pydantic/zod SDK 支持。 如果您更喜欢直接指定 JSON 方案,可以添加 CI 规则以标记当 JSON 方案或底层数据对象被编辑时的情况,或者添加一个 CI 步骤来自动生成 JSON 方案(反之亦然)。 ##流式传输 你可以使用流式处理来处理模型响应或函数调用参数,边生成边解析为结构化数据。 这样,你无需等待整个响应完成就可以处理它。这在你希望逐个显示 JSON 字段或一有可用的函数调用参数就进行处理时特别有用。 我们建议依赖于 SDK 来处理带有结构化输出的流式传输。您可以在函数调用指南中找到如何在不使用 SDK stream 辅助的情况下流式传输函数调用参数的示例。
from openai import OpenAI
from pydantic import BaseModel
from enum import Enum
from typing import Optional
from typing import List

MODEL_NAME = "Qwen/Qwen2.5-7B-Instruct"
API_KEY = "you api_key"
client = OpenAI(api_key=API_KEY, base_url="https://api.siliconflow.cn/v1")
class Step(BaseModel):
    explanation: str
    output: str
class MathReasoning(BaseModel):
    steps: list[Step]
    final_answer: str
json_schema = MathReasoning.model_json_schema()
completion = client.chat.completions.create(
    model=MODEL_NAME,
    stream=True,
    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={
        "type": "json_schema",
        "json_schema": {
            "name": "math_response",
            "schema": json_schema
    }
  }
)
print(completion)
li = ""
for chunk in completion:
    li += chunk.choices[0].delta.content
print(li)

支持的模式

支持的结构化输出类型有: String 字符串 Number 数字 Boolean 布尔 Integer 整数 Object 对象 Array 数组 Enum 枚举 anyOf Root 对象必须不要是 anyOf