Поделиться через


Структурированные выходные данные

Структурированные выходные данные позволяют модели соответствовать определению схемы JSON, предоставленному в рамках вызова API вывода. Это отличается от более старой функции режима JSON, которая гарантирует, что будет создано допустимое JSON, но не удалось обеспечить строгое соблюдение предоставленной схемы. Структурированные выходные данные рекомендуется использовать для вызова функций, извлечения структурированных данных и создания сложных многофакторных рабочих процессов.

Замечание

В настоящее время структурированные выходные данные не поддерживаются:

Поддерживаемые модели

  • codex-mini версия 2025-05-16
  • o3-pro версия 2025-06-10
  • gpt-4.5-preview версия 2025-02-27
  • o3-mini версия 2025-01-31
  • o1 Версия: 2024-12-17
  • gpt-4o-mini Версия: 2024-07-18
  • gpt-4o Версия: 2024-08-06
  • gpt-4o Версия: 2024-11-20
  • gpt-4.1 версия 2025-04-14
  • gpt-4.1-nano версия 2025-04-14
  • gpt-4.1-mini Версия: 2025-04-14
  • o4-mini Версия: 2025-04-16
  • o3 Версия: 2025-04-16

поддержка API

Поддержка структурированных выходных данных была добавлена в версию 2024-08-01-previewAPI. Он доступен в последних предварительных версиях API, а также в последней версии API общедоступной версии: 2024-10-21

Начало работы

Можно использовать Pydantic для определения схем объектов в Python. В зависимости от версии OpenAI и Pydantic библиотек , которые вы используете, может потребоваться обновить до более новой версии. Эти примеры были проверены на openai 1.42.0 и pydantic 2.8.2.

pip install openai pydantic --upgrade

Если вы не знакомы с использованием идентификатора Microsoft Entra для проверки подлинности, см. инструкции по настройке Azure OpenAI в моделях Azure AI Foundry с проверкой подлинности Microsoft Entra ID.

import os
from pydantic import BaseModel
from openai import AzureOpenAI
from azure.identity import DefaultAzureCredential, get_bearer_token_provider

token_provider = get_bearer_token_provider(
    DefaultAzureCredential(), "https://cognitiveservices.azure.com/.default"
)

client = AzureOpenAI(
  azure_endpoint = os.getenv("AZURE_OPENAI_ENDPOINT"), 
  azure_ad_token_provider=token_provider,
  api_version="2024-10-21"
)


class CalendarEvent(BaseModel):
    name: str
    date: str
    participants: list[str]

completion = client.beta.chat.completions.parse(
    model="MODEL_DEPLOYMENT_NAME", # replace with the model deployment name of your gpt-4o 2024-08-06 deployment
    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

print(event)
print(completion.model_dump_json(indent=2))

Выходные данные

name='Science Fair' date='Friday' participants=['Alice', 'Bob']
{
  "id": "chatcmpl-A1EUP2fAmL4SeB1lVMinwM7I2vcqG",
  "choices": [
    {
      "finish_reason": "stop",
      "index": 0,
      "logprobs": null,
      "message": {
        "content": "{\n  \"name\": \"Science Fair\",\n  \"date\": \"Friday\",\n  \"participants\": [\"Alice\", \"Bob\"]\n}",
        "refusal": null,
        "role": "assistant",
        "function_call": null,
        "tool_calls": [],
        "parsed": {
          "name": "Science Fair",
          "date": "Friday",
          "participants": [
            "Alice",
            "Bob"
          ]
        }
      }
    }
  ],
  "created": 1724857389,
  "model": "gpt-4o-2024-08-06",
  "object": "chat.completion",
  "service_tier": null,
  "system_fingerprint": "fp_1c2eaec9fe",
  "usage": {
    "completion_tokens": 27,
    "prompt_tokens": 32,
    "total_tokens": 59
  }
}

Вызов функции со структурированными выходными данными

Структурированные выходные данные для вызова функций можно включить с одним параметром, предоставив strict: true.

Замечание

Структурированные выходные данные не поддерживаются параллельными вызовами функций. При использовании структурированных выходных данных установите parallel_tool_calls в false.

from enum import Enum
from typing import Union
from pydantic import BaseModel
import openai
from openai import AzureOpenAI

client = AzureOpenAI(
  azure_endpoint = os.getenv("AZURE_OPENAI_ENDPOINT"), 
  api_key=os.getenv("AZURE_OPENAI_API_KEY"),  
  api_version="2024-10-21"
)


class GetDeliveryDate(BaseModel):
    order_id: str

tools = [openai.pydantic_function_tool(GetDeliveryDate)]

messages = []
messages.append({"role": "system", "content": "You are a helpful customer support assistant. Use the supplied tools to assist the user."})
messages.append({"role": "user", "content": "Hi, can you tell me the delivery date for my order #12345?"}) 

response = client.chat.completions.create(
    model="MODEL_DEPLOYMENT_NAME", # replace with the model deployment name of your gpt-4o 2024-08-06 deployment
    messages=messages,
    tools=tools
)

print(response.choices[0].message.tool_calls[0].function)
print(response.model_dump_json(indent=2))

Начало работы

Добавьте в проект следующие пакеты для работы с Azure OpenAI:

dotnet add package Azure.AI.OpenAI --prerelease
dotnet add package Azure.Identity
dotnet add package Newtonsoft.Json.Schema

Если вы не знакомы с использованием идентификатора Microsoft Entra для проверки подлинности, см. инструкции по настройке Azure OpenAI в моделях Azure AI Foundry с проверкой подлинности Microsoft Entra ID.

using Azure.AI.OpenAI;
using Azure.Identity;
using Newtonsoft.Json.Schema.Generation;
using OpenAI.Chat;
using System.ClientModel;

// Create the clients
string endpoint = GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT");

AzureOpenAIClient openAIClient = new(
    new Uri(endpoint),
    new DefaultAzureCredential());

var client = openAIClient.GetChatClient("gpt-4o");

// Create a chat with initial prompts
var chat = new List<ChatMessage>()
    {
         new SystemChatMessage("Extract the event information and projected weather."),
         new UserChatMessage("Alice and Bob are going to a science fair in Seattle on June 1st, 2025.")
    };

// Get the schema of the class for the structured response
JSchemaGenerator generator = new JSchemaGenerator();
var jsonSchema = generator.Generate(typeof(CalendarEvent)).ToString();

// Get a completion with structured output
var chatUpdates = client.CompleteChatStreamingAsync(
        chat,
        new ChatCompletionOptions()
        {
            ResponseFormat = ChatResponseFormat.CreateJsonSchemaFormat(
                        "calenderEvent",
                        BinaryData.FromString(jsonSchema))
        });

// Write the structured response
await foreach (var chatUpdate in chatUpdates)
{
    foreach (var contentPart in chatUpdate.ContentUpdate)
    {
        Console.Write(contentPart.Text);
    }
}

// The class for the structured response
public class CalendarEvent()
{
    public string Name { get; set; }
    public string Date { get; set; }
    public List<string> Participants { get; set; }
}

Вызов функции со структурированными выходными данными

Структурированные выходные данные для вызова функций можно включить с одним параметром, предоставив strict: true.

using Azure.AI.OpenAI;
using Newtonsoft.Json.Schema.Generation;
using OpenAI.Chat;
using System.ClientModel;

// Create the clients
string endpoint = GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT");

AzureOpenAIClient openAIClient = new(
    new Uri(endpoint),
    new DefaultAzureCredential());

var chatClient = openAIClient.GetChatClient("gpt-4o");

// Local function to be used by the assistant tooling
string GetTemperature(string location, string date)
{
    // Placeholder for Weather API
    if(location == "Seattle" && date == "2025-06-01")
    {
        return "75";
    }

    return "50";
}

// Create a tool to get the temperature
ChatTool GetTemperatureTool = ChatTool.CreateFunctionTool(
    functionName: nameof(GetTemperature),
    functionSchemaIsStrict: true,
    functionDescription: "Get the projected temperature by date and location.",
    functionParameters: BinaryData.FromBytes("""
        {
            "type": "object",
            "properties": {
                "location": {
                    "type": "string",
                    "description": "The location of the weather."
                },
                "date": {
                    "type": "string",
                    "description": "The date of the projected weather."
                }
            },
            "required": ["location", "date"],
            "additionalProperties": false  
        }
        """u8.ToArray())
);

// Create a chat with prompts
var chat = new List<ChatMessage>()
    {
         new SystemChatMessage("Extract the event information and projected weather."),
         new UserChatMessage("Alice and Bob are going to a science fair in Seattle on June 1st, 2025.")
    };

// Create a JSON schema for the CalendarEvent structured response
JSchemaGenerator generator = new JSchemaGenerator();
var jsonSchema = generator.Generate(typeof(CalendarEvent)).ToString();

// Get a chat completion from the AI model
var completion = chatClient.CompleteChat(
        chat,
        new ChatCompletionOptions()
        {
            ResponseFormat = ChatResponseFormat.CreateJsonSchemaFormat(
                "calenderEvent",
                BinaryData.FromString(jsonSchema)),
            Tools = { GetTemperatureTool }
        });

Console.WriteLine(completion.Value.ToolCalls[0].FunctionName);

// Structured response class
public class CalendarEvent()
{
    public string Name { get; set; }
    public string Date { get; set; }
    public string Temperature { get; set; }
    public List<string> Participants { get; set; }
}

Начало работы

response_format имеет значение json_schema, при этом strict: true задано.

curl -X POST  https://YOUR_RESOURCE_NAME.openai.azure.com/openai/deployments/YOUR_MODEL_DEPLOYMENT_NAME/chat/completions?api-version=2024-10-21 \
  -H "api-key: $AZURE_OPENAI_API_KEY" \
  -H "Content-Type: application/json" \
    -d '{
        "messages": [
                {"role": "system", "content": "Extract the event information."},
                {"role": "user", "content": "Alice and Bob are going to a science fair on Friday."}
            ],
            "response_format": {
                "type": "json_schema",
                "json_schema": {
                    "name": "CalendarEventResponse",
                    "strict": true,
                    "schema": {
                        "type": "object",
                        "properties": {
                            "name": {
                              "type": "string"
                            },
                            "date": {
                                "type": "string"
                            },
                            "participants": {
                                "type": "array",
                                "items": {
                                    "type": "string"
                                }
                            }
                        },
                        "required": [
                            "name",
                            "date",
                            "participants"
                        ],
                        "additionalProperties": false
                    }
                }
          }
  }'

Выходные данные:

{
  "id": "chatcmpl-A1HKsHAe2hH9MEooYslRn9UmEwsag",
  "object": "chat.completion",
  "created": 1724868330,
  "model": "gpt-4o-2024-08-06",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "{\n  \"name\": \"Science Fair\",\n  \"date\": \"Friday\",\n  \"participants\": [\"Alice\", \"Bob\"]\n}"
      },
      "logprobs": null,
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 33,
    "completion_tokens": 27,
    "total_tokens": 60
  },
  "system_fingerprint": "fp_1c2eaec9fe"
}

Вызов функции со структурированными выходными данными

curl -X POST  https://YOUR_RESOURCE_NAME.openai.azure.com/openai/deployments/YOUR_MODEL_DEPLOYMENT_NAME/chat/completions?api-version=2024-10-21 \
  -H "api-key: $AZURE_OPENAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "messages": [
    {
      "role": "system",
      "content": "You are a helpful assistant. The current date is August 6, 2024. You help users query for the data they are looking for by calling the query function."
    },
    {
      "role": "user",
      "content": "look up all my orders in may of last year that were fulfilled but not delivered on time"
    }
  ],
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "query",
        "description": "Execute a query.",
        "strict": true,
        "parameters": {
          "type": "object",
          "properties": {
            "table_name": {
              "type": "string",
              "enum": ["orders"]
            },
            "columns": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "id",
                  "status",
                  "expected_delivery_date",
                  "delivered_at",
                  "shipped_at",
                  "ordered_at",
                  "canceled_at"
                ]
              }
            },
            "conditions": {
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "column": {
                    "type": "string"
                  },
                  "operator": {
                    "type": "string",
                    "enum": ["=", ">", "<", ">=", "<=", "!="]
                  },
                  "value": {
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "number"
                      },
                      {
                        "type": "object",
                        "properties": {
                          "column_name": {
                            "type": "string"
                          }
                        },
                        "required": ["column_name"],
                        "additionalProperties": false
                      }
                    ]
                  }
                },
                "required": ["column", "operator", "value"],
                "additionalProperties": false
              }
            },
            "order_by": {
              "type": "string",
              "enum": ["asc", "desc"]
            }
          },
          "required": ["table_name", "columns", "conditions", "order_by"],
          "additionalProperties": false
        }
      }
    }
  ]
}'

Поддерживаемые схемы и ограничения

Структурированные выходные данные Azure OpenAI поддерживают то же подмножество схемы JSON, что и OpenAI.

Поддерживаемые типы

  • Струна
  • Номер
  • Boolean
  • Целое число
  • Объект
  • Массив
  • Перечисление
  • любой из

Замечание

Корневые объекты не могут быть типом anyOf .

Все поля должны быть обязательными

Все поля или параметры функции должны быть включены по мере необходимости. В приведенном ниже locationпримере и unit оба указаны в разделе "required": ["location", "unit"].

{
    "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объединения. В этом примере это достигается с помощью строки "type": ["string", "null"],.

{
    "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 свойств объекта и иметь до пяти уровней вложенности.

additionalProperties: значение false всегда должно быть задано в объектах

Это свойство определяет, может ли объект иметь дополнительные пары значений ключей, которые не были определены в схеме JSON. Чтобы использовать структурированные выходные данные, необходимо задать для этого значения значение false.

Порядок ключей

Структурированные выходные данные упорядочены так же, как и указанная схема. Чтобы изменить порядок вывода, измените порядок схемы, которую вы отправляете в рамках запроса вывода.

Неподдерживаемые ключевые слова, специфичные для типа

Тип Неподдерживаемое ключевое слово
Струна минимальная длина
максимальная длина
шаблон
формат
Номер минимум
максимум
multipleOf
Объекты свойства шаблона
неоценённые свойства
Имена свойств
минимальные свойства
максимальное количество свойств
Массивы неоцененныеЭлементы
Содержит
minContains
МаксимальноеСодержание
минимальное количество элементов
максимальное количество элементов
уникальныеЭлементы

Вложенные схемы, использующие anyOf, должны соответствовать общему набору подмножеств схемы JSON

Пример поддерживаемой anyOf схемы:

{
    "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"
    ]
}

Поддерживаются определения

Поддерживаемый пример:

{
    "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
}

Поддерживаются рекурсивные схемы

Пример использования #для рекурсии корня:

{
        "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
        }
    }

Пример явной рекурсии:

{
    "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"
    ]
}