Saídas estruturadas
As saídas estruturadas fazem com que um modelo siga uma definição de esquema JSON que você fornece como parte de sua chamada à API de inferência. Isso contrasta com o recurso de modo JSON mais antigo, que garantia que o JSON válido seria gerado, mas não podia garantir a adesão estrita ao esquema fornecido. As saídas estruturadas são recomendadas para chamada de função, extração de dados estruturados e criação de fluxos de trabalho complexos de várias etapas.
Observação
- Atualmente, saídas estruturadas não são suportadas no cenário traga seus próprios dados.
Modelos com suporte
- Versão
o1
:2024-12-17
- Versão
gpt-4o-mini
:2024-07-18
- Versão
gpt-4o
:2024-08-06
Suporte a API
O suporte para saídas estruturadas foi adicionado pela primeira vez na versão 2024-08-01-preview
da API. Está disponível nas APIs de visualização mais recentes, bem como na API em GA mais recente: 2024-10-21
.
Introdução
Você pode usar o Pydantic
para definir esquemas de objeto no Python. Dependendo da versão das bibliotecas do OpenAI e Pydantic
que você está executando, talvez seja necessário fazer upgrade para uma versão mais recente. Estes exemplos foram testados em openai 1.42.0
e pydantic 2.8.2
.
pip install openai pydantic --upgrade
Se você é novo no uso do Microsoft Entra ID para autenticação, veja Como configurar o Serviço OpenAI do Azure com autenticação do 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))
Saída
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
}
}
Chamada de função com saídas estruturadas
As saídas estruturadas para chamada de função podem ser habilitadas com um único parâmetro, fornecendo strict: true
.
Observação
Não há suporte para saídas estruturadas com chamadas de função paralelas. Ao usar saídas estruturadas, defina parallel_tool_calls
como 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))
Esquemas e limitações com suporte
As saídas estruturadas do OpenAI do Azure dão suporte ao mesmo subconjunto do esquema JSON que o OpenAI.
Tipos com suporte
- String
- Número
- Booliano
- Inteiro
- Objeto
- Array
- Enum
- anyOf
Observação
Objetos raiz não podem ser do tipo anyOf
.
Todos os campos devem ser necessários
Todos os campos ou parâmetros de função devem ser incluídos conforme necessário. No exemplo abaixo, tanto location
quanto unit
são especificados em "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"]
}
Se necessário, é possível emular um parâmetro opcional usando um tipo de união com null
. Neste exemplo, isso é alcançado com a linha "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"
]
}
}
Profundidade de aninhamento
Um esquema pode ter até 100 propriedades de objeto no total, com até cinco níveis de aninhamento
additionalProperties: false deve sempre ser definido em objetos
Esta propriedade controla se um objeto pode ter pares chave-valor adicionais que não foram definidos no esquema JSON. Para usar saídas estruturadas, você deve definir este valor como false.
Ordenação de chave
As saídas estruturadas são ordenadas da mesma forma que o esquema fornecido. Para alterar a ordem de saída, modifique a ordem do esquema que você envia como parte da sua solicitação de inferência.
Palavras-chave específicas de tipo sem suporte
Tipo | Palavra-chave sem Suporte |
---|---|
String | minlength maxLength pattern format |
Número | mínimo maximum multipleOf |
Objetos | patternProperties unevaluatedProperties propertyNames minProperties maxProperties |
matrizes | unevaluatedItems contains minContains maxContains minItems maxItems uniqueItems |
Esquemas aninhados usando anyOf devem aderir ao subconjunto geral do Esquema JSON
Exemplo de esquema anyOf
com suporte:
{
"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"
]
}
Há suporte para definições
Exemplo com suporte:
{
"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
}
Há suporte para esquemas recursivos
Exemplo usando # para recursão raiz:
{
"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
}
}
Exemplo de recursão explícita:
{
"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"
]
}