Sorties structurées
Les sorties structurées font en sorte qu’un modèle suive une définition de Schéma JSON que vous fournissez dans le cadre de votre appel à l’API d’inférence. Cela contraste avec l’ancienne fonctionnalité du mode JSON, qui garantissait la génération d’un JSON valide, mais n’était pas en mesure d’assurer le respect strict du schéma fourni. Les sorties structurées sont recommandées pour l’appel de fonctions, l’extraction de données structurées et l’élaboration de workflows complexes en plusieurs étapes.
Remarque
Actuellement, les sorties structurées ne sont pas prises en charge avec ce qui suit :
- Scénarios Apportez vos propres données
- Assistants ou Azure AI Agent Service
- Version
gpt-4o-audio-preview
etgpt-4o-mini-audio-preview
:2024-12-17
.
Modèles pris en charge
-
o3-mini
version2025-01-31
- Version
o1
:2024-12-17
- Version
gpt-4o-mini
:2024-07-18
- Version
gpt-4o
:2024-08-06
Prise en charge des API
La prise en charge des sorties structurées a été ajoutée pour la première fois dans la version 2024-08-01-preview
de l’API. Il est disponible dans les dernières API en préversion, ainsi que dans la dernière API en disponibilité générale : 2024-10-21
.
Mise en route
Pour définir des schémas d’objet en Python, vous pouvez utiliser Pydantic
. Selon la version d’OpenAI et des Pydantic
bibliothèques que vous exécutez, il peut être nécessaire d’effectuer une mise à niveau vers une version plus récente. Ces exemples ont été testés par rapport à openai 1.42.0
et pydantic 2.8.2
.
pip install openai pydantic --upgrade
Si vous utilisez pour la première fois Microsoft Entra ID pour l’authentification, consultez Comment configurer Azure OpenAI Service avec l’authentification Microsoft Entra ID.
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))
Sortie
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
}
}
Appel de fonction avec sorties structurées
Les sorties structurées pour l’appel de fonctions peuvent être activées avec un seul paramètre, en fournissant strict: true
.
Remarque
Les sorties structurées ne sont pas prises en charge avec les appels de fonctions parallèles. Lorsque vous utilisez des sorties structurées, définissez parallel_tool_calls
sur 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))
Mise en route
Ajoutez les packages suivants à votre projet pour utiliser Azure OpenAI :
- Azure.AI.OpenAI : fournit un client Azure OpenAI avec des fonctionnalités spécifiques à Azure qui s’appuient sur la dépendance de bibliothèque OpenAI standard.
- Azure.Identity : fournit une prise en charge de l’authentification par jeton Microsoft Entra ID dans les bibliothèques de SDK Azure.
- Newtonsoft.Json.Schema : fournit des utilitaires utiles pour utiliser des schémas JSON.
dotnet add package Azure.AI.OpenAI --prerelease
dotnet add package Azure.Identity
dotnet add package Newtonsoft.Json.Schema
Si vous utilisez pour la première fois Microsoft Entra ID pour l’authentification, consultez Comment configurer Azure OpenAI Service avec l’authentification 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; }
}
Appel de fonction avec sorties structurées
Les sorties structurées pour l’appel de fonctions peuvent être activées avec un seul paramètre, en fournissant 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; }
}
Mise en route
response_format
est défini sur json_schema
avec strict: true
défini.
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
}
}
}
}'
Sortie :
{
"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"
}
Appel de fonction avec sorties structurées
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
}
}
}
]
}'
Schémas pris en charge et limitations
Les sorties structurées Azure OpenAI prennent en charge le même sous-ensemble du Schéma JSON que OpenAI.
Types pris en charge
- Chaîne
- Number
- Boolean
- Integer
- Object
- Array
- Enum
- anyOf
Remarque
Les objets racines ne peuvent pas être le type anyOf
.
Tous les champs doivent être obligatoires
Tous les champs ou paramètres de fonction doivent être inclus si nécessaire. Dans l’exemple ci-dessous, location
et unit
sont tous les deux spécifiés sous "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"]
}
Si nécessaire, il est possible d’émuler un paramètre optionnel en utilisant un type d’union avec null
. Dans cet exemple, la ligne "type": ["string", "null"],
permet d’atteindre cet objectif.
{
"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"
]
}
}
Profondeur d’imbrication
Un schéma peut avoir jusqu’à 100 propriétés d’objet au total, avec jusqu’à cinq niveaux d’imbrication
additionalProperties : false doit toujours être défini dans les objets
Cette propriété permet de contrôler si un objet peut avoir des paires clé-valeur supplémentaires qui n’ont pas été définies dans le Schéma JSON. Pour utiliser les sorties structurées, vous devez définir cette valeur sur false.
Classement des clés
Les sorties structurées sont ordonnées de la même manière que le schéma fourni. Pour modifier l’ordre de sortie, modifiez l’ordre du schéma que vous envoyez dans le cadre de votre demande d’inférence.
Mots clés spécifiques à un type non pris en charge
Type | Mot clé non pris en charge |
---|---|
Chaîne | minlength maxLength pattern format |
Number | minimum maximum multipleOf |
Objets | patternProperties unevaluatedProperties propertyNames minProperties maxProperties |
Tableaux | unevaluatedItems contains minContains maxContains minItems maxItems uniqueItems |
Les schémas imbriqués qui utilisent anyOf doivent respecter le sous-ensemble de Schéma JSON général.
Exemple de schéma anyOf
pris en charge :
{
"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"
]
}
Les définitions sont prises en charge
Exemple pris en charge :
{
"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
}
Les schémas récursifs sont pris en charge
Exemple d’utilisation de # pour la récursivité racine :
{
"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
}
}
Exemple de récursivité explicite :
{
"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"
]
}