For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to the page URL.
Navegação principal
Theme

Saídas estruturadas do modelo

Garanta que as respostas de texto do modelo sigam o schema JSON que você definir.

JSON é um dos formatos mais usados no mundo para a troca de dados entre aplicativos.

O recurso de saídas estruturadas garante que o modelo sempre gere respostas que sigam o JSON Schema que você fornecer. Assim, você não precisa se preocupar com a possibilidade de o modelo omitir uma chave obrigatória ou inventar um valor inválido para uma enumeração.

Algumas vantagens das saídas estruturadas:

  1. Segurança de tipos: você não precisa validar as respostas nem repetir solicitações por erros de formatação
  2. Recusas explícitas: agora é possível detectar por código quando o modelo recusa uma solicitação por motivos de segurança
  3. Prompts mais simples: você não precisa insistir no formato no prompt para obter uma formatação consistente

Além do suporte a JSON Schema na API REST, as bibliotecas da OpenAI para Python e JavaScript permitem definir schemas de objetos com pydantic.BaseModel e z.object, respectivamente. O exemplo a seguir mostra como extrair informações de um texto não estruturado e retorná-las de acordo com um schema definido no código.

O SDK para Ruby aceita schemas definidos com T::Struct do Sorbet e retorna os resultados da análise como objetos tipados.

Como obter uma resposta estruturada
from openai import OpenAI
from pydantic import BaseModel

client = OpenAI()


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


response = client.responses.parse(
    model="gpt-6-astra",
    input=[
        {"role": "system", "content": "Extract the event information."},
        {
            "role": "user",
            "content": "Alice and Bob are going to a science fair on Friday.",
        },
    ],
    text_format=CalendarEvent,
)

event = response.output_parsed

Modelos compatíveis

O recurso de saídas estruturadas está disponível nos nossos modelos de linguagem de grande porte mais recentes, a partir do GPT-4o. Para novos projetos, comece com gpt-6-astra. Em modelos mais antigos, como gpt-4-turbo e versões anteriores, você pode usar o modo JSON como alternativa.

Quando usar saídas estruturadas com chamada de função ou com text.format

Na API da OpenAI, há duas formas de usar saídas estruturadas:

  1. Com chamada de função
  2. Com o formato de resposta json_schema

A chamada de função é útil quando você desenvolve um aplicativo que conecta os modelos às funcionalidades do próprio aplicativo.

Por exemplo, para criar um assistente de IA que ajude os usuários com seus pedidos, você pode dar ao modelo acesso a funções que consultam um banco de dados. Também pode dar acesso a funções que interagem com a interface.

Já as saídas estruturadas com response_format são mais indicadas quando você quer definir um schema para a resposta que o modelo envia ao usuário, em vez de definir um schema para a chamada de uma ferramenta.

Por exemplo, se você estiver desenvolvendo um aplicativo de tutoria em matemática, pode fazer o assistente responder ao usuário seguindo um schema JSON específico. Assim, você pode gerar uma interface que exiba cada parte da saída do modelo de uma forma diferente.

Na prática:

  • Se você quer conectar o modelo a ferramentas, funções, dados ou outros recursos do seu sistema, use a chamada de função. Se você quer estruturar a saída do modelo quando ele responde ao usuário, use um text.format estruturado.

O restante deste guia aborda casos de uso da API Responses sem chamada de função. Consulte o guia de

chamada de função

para saber como usar saídas estruturadas com esse recurso.

Saídas estruturadas e modo JSON

As saídas estruturadas são uma evolução do modo JSON. Ambos os recursos garantem a geração de JSON válido, mas apenas as saídas estruturadas garantem que a saída siga o schema. As APIs Responses, chat completions, Assistants, Fine-tuning e Batch oferecem suporte tanto a saídas estruturadas quanto ao modo JSON.

Sempre que possível, recomendamos usar saídas estruturadas em vez do modo JSON.

No entanto, o recurso de saídas estruturadas com response_format: {type: "json_schema", ...} só está disponível nas versões gpt-4o-mini, gpt-4o-mini-2024-07-18 e gpt-4o-2024-08-06 dos modelos e nas versões posteriores.

Saídas estruturadas Modo JSON
Gera JSON válido Sim Sim
Segue o schema Sim (consulte os schemas compatíveis) Não
Modelos compatíveis gpt-4o-mini, gpt-4o-2024-08-06 e versões posteriores gpt-3.5-turbo, gpt-4-*, gpt-4o-* e modelos GPT-5 compatíveis
Como ativar text: { format: { type: "json_schema", "strict": true, "schema": ... } } text: { format: { type: "json_object" } }

Exemplos

Cadeia de pensamento

Você pode pedir ao modelo que organize a resposta em etapas para orientar o usuário passo a passo na resolução do problema.

Saídas estruturadas para tutoria de matemática com cadeia de pensamento
from openai import OpenAI
from pydantic import BaseModel

client = OpenAI()


class Step(BaseModel):
    explanation: str
    output: str


class MathReasoning(BaseModel):
    steps: list[Step]
    final_answer: str


response = client.responses.parse(
    model="gpt-6-astra",
    input=[
        {
            "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"},
    ],
    text_format=MathReasoning,
)

math_reasoning = response.output_parsed

Exemplo de resposta

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

Como usar saídas estruturadas com text.format

Como alterar o formato da resposta durante a conversa

Para passar a usar um novo schema entre uma resposta e outra, acrescente um item configuration_update a input antes da próxima mensagem do usuário. Em cada solicitação, mantenha o valor original de text.format no nível superior para que o prefixo anterior do prompt continue elegível para o cache de prompts.

Você pode atualizar o formato da resposta nos modelos compatíveis a partir do GPT-6, no modo padrão com um único agente.

Suponha que o turno anterior tenha descrito, em texto simples, uma remessa com 17 caixas vermelhas e 26 caixas azuis. Continue a conversa com previous_response_id e passe o array a seguir como input.

Passe do texto simples para uma contagem estruturada de caixas
from openai import OpenAI
from pydantic import BaseModel, ConfigDict


class CrateCount(BaseModel):
    model_config = ConfigDict(extra="forbid")
    total_crates: int


client = OpenAI()
model = "gpt-6-astra"

first = client.responses.create(
    model=model,
    input="Summarize this shipment in one sentence: 17 red crates and 26 blue crates.",
    store=True,
)

second = client.responses.parse(
    model=model,
    previous_response_id=first.id,
    input=[
        {
            "type": "configuration_update",
            "text": {
                "format": {
                    "type": "json_schema",
                    "name": "crate_count",
                    "schema": CrateCount.model_json_schema(),
                    "strict": True,
                }
            },
        },
        {"role": "user", "content": "How many crates are in the shipment?"},
    ],
    parse_as=CrateCount,
)

if second.status == "completed" and second.output_parsed is not None:
    print(second.output_parsed.total_crates)

A resposta esperada é {"total_crates":43}. No objeto de resposta, o campo text.format continua a indicar o formato original definido no nível da solicitação. Depois de verificar se houve recusa ou se a resposta está incompleta, interprete a resposta com o schema da atualização de formato mais recente.

Cada atualização substitui o formato por completo. O novo formato continua em vigor nos turnos seguintes, inclusive quando você atualiza apenas reasoning.effort. Para voltar ao texto simples, acrescente {"type":"configuration_update","text":{"format":{"type":"text"}}}. As atualizações aceitam apenas json_schema e text.

Se você reenviar o histórico manualmente, mantenha cada atualização na posição original. Após uma compactação explícita, especifique novamente cada configuração que você alterou. Consulte a seção sobre compatibilidade das atualizações de configuração.

Recusas com saídas estruturadas

Quando você usa saídas estruturadas com dados fornecidos pelos usuários, os modelos da OpenAI podem, ocasionalmente, recusar uma solicitação por motivos de segurança. Como a recusa nem sempre segue o schema fornecido em response_format, a resposta da API inclui um novo campo chamado refusal para indicar que o modelo se recusou a atender à solicitação.

Quando a propriedade refusal aparecer no objeto de saída, você pode exibir a recusa na interface ou incluir uma condição no código que consome a resposta para tratar solicitações recusadas.

class Step(BaseModel):
    explanation: str
    output: str


class MathReasoning(BaseModel):
    steps: list[Step]
    final_answer: str


response = client.responses.parse(
    model="gpt-6-astra",
    input=[
        {
            "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"},
    ],
    text_format=MathReasoning,
)

for output in response.output:
    if output.type != "message":
        continue

    for item in output.content:
        if item.type == "refusal":
            # If the model refuses to respond, you will get a refusal message
            print(item.refusal)
            continue

        if not item.parsed:
            raise Exception("Could not parse response")

        print(item.parsed)

Em caso de recusa, a resposta da API será semelhante a esta:

{
  "id": "resp_1234567890",
  "object": "response",
  "created_at": 1721596428,
  "status": "completed",
  "completed_at": 1721596429,
  "error": null,
  "incomplete_details": null,
  "input": [],
  "instructions": null,
  "max_output_tokens": null,
  "model": "gpt-4o-2024-08-06",
  "output": [{
    "id": "msg_1234567890",
    "type": "message",
    "role": "assistant",
    "content": [
      {
        "type": "refusal",
        "refusal": "I'm sorry, I cannot assist with that request."
      }
    ]
  }],
  "usage": {
    "input_tokens": 81,
    "output_tokens": 11,
    "total_tokens": 92,
    "output_tokens_details": {
      "reasoning_tokens": 0,
    }
  },
}

Dicas e boas práticas

Como tratar dados fornecidos pelos usuários

Se o seu aplicativo usa dados fornecidos pelos usuários, inclua no prompt instruções sobre como proceder quando não for possível gerar uma resposta válida a partir desses dados.

O modelo sempre tenta seguir o schema fornecido. Isso pode gerar alucinações quando a entrada não tem nenhuma relação com o schema.

Você pode especificar no prompt que o modelo deve retornar parâmetros vazios ou uma frase específica se detectar que a entrada é incompatível com a tarefa.

Como lidar com erros

As saídas estruturadas ainda podem conter erros. Se encontrar algum, tente ajustar as instruções, incluir exemplos nas instruções do sistema ou dividir as tarefas em subtarefas mais simples. Consulte o guia de engenharia de prompt para saber como ajustar as entradas.

Como evitar divergências no schema JSON

Para evitar divergências entre o schema JSON e os tipos correspondentes na sua linguagem de programação, recomendamos fortemente usar os recursos nativos do SDK para definir schemas sempre que estiverem disponíveis.

Se preferir especificar o schema JSON diretamente, você pode adicionar regras de integração contínua (CI) que sinalizem alterações no schema JSON ou nos objetos de dados correspondentes. Outra opção é adicionar uma etapa de CI que gere automaticamente o schema JSON a partir das definições de tipos ou vice-versa.

Streaming

Com o streaming, você pode processar as respostas do modelo ou os argumentos de uma chamada de função à medida que o modelo os gera e interpretá-los como dados estruturados.

Assim, você pode começar a processar a resposta antes de recebê-la por completo. Isso é especialmente útil para exibir os campos JSON um a um ou processar os argumentos de uma chamada de função assim que estiverem disponíveis.

Recomendamos usar os SDKs para lidar com o streaming de saídas estruturadas.

from openai import OpenAI
from pydantic import BaseModel


class EntitiesModel(BaseModel):
    attributes: list[str]
    colors: list[str]
    animals: list[str]


client = OpenAI()

with client.responses.stream(
    model="gpt-6-astra",
    input=[
        {"role": "system", "content": "Extract entities from the input text"},
        {
            "role": "user",
            "content": "The quick brown fox jumps over the lazy dog with piercing blue eyes",
        },
    ],
    text_format=EntitiesModel,
) as stream:
    for event in stream:
        if event.type == "response.refusal.delta":
            print(event.delta, end="")
        elif event.type == "response.output_text.delta":
            print(event.delta, end="")
        elif event.type == "response.error":
            print(event.error, end="")
        elif event.type == "response.completed":
            print("Completed")  # print(event.response.output)

    final_response = stream.get_final_response()
    print(final_response)

Schemas compatíveis

O recurso de saídas estruturadas oferece suporte a um subconjunto da linguagem JSON Schema.

Tipos compatíveis

As saídas estruturadas aceitam os seguintes tipos:

  • String
  • Número
  • Booleano
  • Inteiro
  • Objeto
  • Array
  • Enumeração
  • anyOf

Propriedades compatíveis

Além de definir o tipo de uma propriedade, você pode especificar restrições adicionais:

Propriedades aceitas para string:

  • pattern — Expressão regular à qual a string deve corresponder.
  • format — Formato predefinido para strings. Os formatos aceitos atualmente são:
    • date-time
    • time
    • date
    • duration
    • email
    • hostname
    • ipv4
    • ipv6
    • uuid

Propriedades aceitas para number:

  • multipleOf — O número deve ser múltiplo desse valor.
  • maximum — O número deve ser menor ou igual a esse valor.
  • exclusiveMaximum — O número deve ser menor que esse valor.
  • minimum — O número deve ser maior ou igual a esse valor.
  • exclusiveMinimum — O número deve ser maior que esse valor.

Propriedades aceitas para array:

  • minItems — Quantidade mínima de itens que o array deve conter.
  • maxItems — Quantidade máxima de itens que o array pode conter.

Veja como aplicar essas restrições a diferentes tipos:

{
    "name": "user_data",
    "strict": true,
    "schema": {
        "type": "object",
        "properties": {
            "name": {
                "type": "string",
                "description": "The name of the user"
            },
            "username": {
                "type": "string",
                "description": "The username of the user. Must start with @",
                "pattern": "^@[a-zA-Z0-9_]+$"
            },
            "email": {
                "type": "string",
                "description": "The email of the user",
                "format": "email"
            }
        },
        "additionalProperties": false,
        "required": [
            "name", "username", "email"
        ]
    }
}

A raiz deve ser um objeto e não pode usar anyOf

A raiz do schema deve ser um objeto e não pode usar anyOf. No Zod, por exemplo, é comum usar uma união discriminada, que gera um anyOf na raiz. Por isso, um código como o seguinte não funciona:

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");

Todos os campos devem ser marcados como required

Para usar saídas estruturadas, marque todos os campos ou parâmetros de função como required.

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

Todos os campos são obrigatórios, e o modelo retorna um valor para cada parâmetro. Ainda assim, você pode simular um parâmetro opcional usando um tipo de união que inclua 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"
        ]
    }
}

Limites de tamanho e profundidade de aninhamento dos objetos

Um schema pode ter até 5.000 propriedades de objetos no total, com até 10 níveis de aninhamento.

Limite de tamanho total das strings

Em um schema, a soma dos comprimentos de todos os nomes de propriedades, nomes de definições, valores de enum e valores de const não pode ultrapassar 120.000 caracteres.

Limites de tamanho das enumerações

Um schema pode ter até 1.000 valores de enum no total, somando todas as propriedades com enum.

Se uma propriedade com enum tiver mais de 250 valores do tipo string, a soma dos comprimentos dessas strings não pode ultrapassar 15.000 caracteres.

Todo objeto deve definir additionalProperties: false

additionalProperties determina se um objeto pode conter pares de chave e valor além dos definidos no JSON Schema.

Como o recurso de saídas estruturadas gera apenas os pares de chave e valor especificados, você precisa definir additionalProperties: false para usá-lo.

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

Ordem das chaves

Nas saídas estruturadas, as chaves aparecem na mesma ordem em que foram definidas no schema.

Algumas palavras-chave específicas de cada tipo ainda não são aceitas

  • Composição: allOf, not, dependentRequired, dependentSchemas, if, then, else

Os modelos com ajuste fino também não aceitam as seguintes palavras-chave:

  • Para strings: minLength, maxLength, pattern, format
  • Para números: minimum, maximum, multipleOf
  • Para objetos: patternProperties
  • Para arrays: minItems, maxItems

Se você ativar as saídas estruturadas com strict: true e chamar a API com um JSON Schema incompatível, a API retornará um erro.

Em anyOf, cada schema aninhado deve ser um JSON Schema válido dentro do subconjunto aceito

Veja um exemplo de schema compatível que usa 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"
    ]
}

Suporte a definições

Com as definições, você pode criar subschemas e referenciá-los em qualquer parte do schema. Veja um exemplo simples:

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

Suporte a schemas recursivos

Exemplo de schema recursivo que usa # para referenciar a 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 schema com 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"
    ]
}

Modo JSON

O modo JSON é uma versão mais básica do recurso de saídas estruturadas. O modo JSON garante que o modelo gere JSON válido. Com saídas estruturadas, a saída do modelo também segue de forma confiável o schema que você especificar. Recomendamos usar saídas estruturadas se houver suporte ao recurso no seu caso de uso.

Com o modo JSON ativado, o modelo gera JSON válido, exceto em alguns casos especiais que o aplicativo deve detectar e tratar adequadamente.

Para ativar o modo JSON na API Responses, você pode definir text.format como { "type": "json_object" }. Quando você usa chamada de função, o modo JSON está sempre ativado.

Observações importantes:

  • Ao usar o modo JSON, sempre instrua o modelo a gerar JSON em alguma mensagem da conversa, como a mensagem do sistema. Sem uma instrução explícita para gerar JSON, o modelo pode produzir uma sequência contínua de caracteres em branco, e a solicitação pode continuar em execução até atingir o limite de tokens. Para ajudar você a lembrar dessa instrução, a API retorna um erro se a string “JSON” não aparecer em nenhum ponto do contexto.
  • O modo JSON garante apenas que a saída seja JSON válido e possa ser analisada sem erros. Ele não garante que a saída siga um schema específico. Use saídas estruturadas para garantir que a saída siga o seu schema. Se isso não for possível, use uma biblioteca de validação e, se necessário, repita a solicitação para garantir que a saída corresponda ao schema desejado.
  • Seu aplicativo deve detectar e tratar os casos especiais em que o modelo pode gerar um objeto JSON incompleto (veja abaixo).

Recursos

Para saber mais sobre saídas estruturadas, recomendamos os seguintes recursos: