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

Saídas estruturadas do modelo

Garanta que as respostas de texto do modelo sigam um esquema JSON definido por você.

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

Saídas estruturadas é um recurso que garante que o modelo sempre gere respostas que sigam o JSON Schema fornecido por você, para que você não precise se preocupar com o modelo omitindo uma chave obrigatória ou inventando um valor inválido para uma enumeração.

Alguns benefícios das saídas estruturadas incluem:

  1. Segurança de tipos confiável: Não é necessário validar respostas com formatação incorreta nem repetir solicitações para corrigi-las
  2. Recusas explícitas: Agora é possível detectar programaticamente as recusas do modelo por motivos de segurança
  3. Criação de prompts mais simples: Não é necessário usar prompts enfáticos para obter uma formatação consistente

Além de oferecer suporte a JSON Schema na API REST, as bibliotecas da OpenAI para Python e JavaScript também permitem definir esquemas de objetos usando pydantic.BaseModel e z.object, respectivamente. A seguir, veja como extrair informações de um texto não estruturado e organizá-las conforme um esquema definido em código.

O SDK Ruby oferece suporte a esquemas definidos com T::Struct do Sorbet e retorna resultados tipados da análise sintática.

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 em nossos modelos de linguagem de grande porte mais recentes, a partir do GPT-4o. Para novos projetos, comece com gpt-6-astra. Modelos mais antigos, como gpt-4-turbo e anteriores, podem usar o modo JSON como alternativa.

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

O recurso de saídas estruturadas está disponível de duas formas na API da OpenAI:

  1. Ao usar chamada de função
  2. Ao usar um formato de resposta json_schema

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

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

Já as saídas estruturadas via response_format são mais adequadas quando você quer especificar um esquema estruturado para as respostas do modelo ao usuário, em vez de usá-lo nas chamadas do modelo a ferramentas.

Por exemplo, se você estiver desenvolvendo um aplicativo de tutoria de matemática, talvez queira que o assistente responda ao usuário usando um JSON Schema específico, para que você possa gerar uma interface que exiba diferentes partes da saída do modelo de maneiras distintas.

Na prática:

  • Se você está conectando o modelo a ferramentas, funções, dados etc. no seu sistema, deve usar chamada de função - Se quiser estruturar a saída do modelo quando ele responde ao usuário, deve usar text.format com um formato estruturado

O restante deste guia se concentra em casos de uso sem chamada de função na Responses API. Para saber mais sobre como usar saídas estruturadas com chamada de função, consulte

Chamada de função

no guia.

Saídas estruturadas versus modo JSON

O recurso de saídas estruturadas é a evolução do modo JSON. Embora ambos garantam a geração de JSON válido, apenas as saídas estruturadas garantem a conformidade com o esquema. Tanto as saídas estruturadas quanto o modo JSON são compatíveis com a Responses API, a API chat completions, a API Assistants, a API de ajuste fino e a API de processamento em lote.

Recomendamos usar sempre saídas estruturadas em vez do modo JSON, quando possível.

No entanto, as saídas estruturadas com response_format: {type: "json_schema", ...} só são compatíveis com as versões dos modelos gpt-4o-mini, gpt-4o-mini-2024-07-18, gpt-4o-2024-08-06 e posteriores.

Saídas estruturadasModo JSON
Gera JSON válidoSimSim
Segue o esquemaSim (veja os esquemas compatíveis)Não
Modelos compatíveisgpt-4o-mini, gpt-4o-2024-08-06 e posterioresgpt-3.5-turbo, gpt-4-*, gpt-4o-* e modelos GPT-5 compatíveis
Ativaçãotext: { format: { type: "json_schema", "strict": true, "schema": ... } }text: { format: { type: "json_object" } }

Exemplos

Cadeia de pensamento

Você pode pedir ao modelo que apresente uma resposta estruturada, passo a passo, para guiar o usuário pela solução.

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

Recusas com saídas estruturadas

Ao usar saídas estruturadas com entradas geradas por usuários, os modelos da OpenAI podem ocasionalmente se recusar a atender à solicitação por motivos de segurança. Como uma recusa não segue necessariamente o esquema fornecido em response_format, a resposta da API incluirá 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ê poderá exibir a recusa na interface ou incluir lógica condicional no código que consome a resposta para tratar a solicitação recusada.

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)

A resposta da API em caso de recusa 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 práticas recomendadas

Como lidar com entradas geradas por usuários

Se o seu aplicativo usa entradas geradas por usuários, certifique-se de que o prompt inclua instruções sobre como lidar com situações em que a entrada não pode resultar em uma resposta válida.

O modelo sempre tentará seguir o esquema fornecido, o que pode resultar em alucinações se a entrada não tiver nenhuma relação com o esquema.

Você pode incluir instruções no prompt para especificar que deseja retornar parâmetros vazios ou uma frase específica caso o modelo detecte que a entrada é incompatível com a tarefa.

Como lidar com erros

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

Evite divergências no esquema JSON

Para evitar divergências entre seu JSON Schema e os tipos correspondentes na sua linguagem de programação, recomendamos fortemente usar os recursos auxiliares nativos dos SDKs para esquemas, quando disponíveis.

Se preferir especificar o esquema JSON diretamente, você pode adicionar regras de CI que sinalizem alterações no esquema JSON ou nos objetos de dados subjacentes, ou adicionar uma etapa de CI que gere automaticamente o JSON Schema a partir das definições de tipos (ou vice-versa).

Streaming

Você pode usar streaming para processar respostas do modelo ou argumentos de chamadas de função à medida que são gerados e interpretá-los como dados estruturados.

Assim, você não precisa esperar a resposta inteira ser concluída para começar a processá-la. Isso é especialmente útil se você quiser exibir os campos JSON um por um ou processar os argumentos de chamadas de função assim que estiverem disponíveis.

Recomendamos usar os SDKs para lidar com 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)

Esquemas compatíveis

As saídas estruturadas oferecem suporte a um subconjunto da linguagem JSON Schema.

Tipos compatíveis

Os seguintes tipos são compatíveis com saídas estruturadas:

  • Cadeia de caracteres
  • Número
  • Booleano
  • Inteiro
  • Objeto
  • Array
  • Enum
  • anyOf

Propriedades compatíveis

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

Propriedades compatíveis com string:

  • pattern — Uma expressão regular à qual a cadeia de caracteres deve corresponder.
  • format — Formatos predefinidos para cadeias de caracteres. Atualmente, há suporte para:
    • date-time
    • time
    • date
    • duration
    • email
    • hostname
    • ipv4
    • ipv6
    • uuid

Propriedades compatíveis com number:

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

Propriedades compatíveis com array:

  • minItems — O array deve ter, no mínimo, esta quantidade de itens.
  • maxItems — O array deve ter, no máximo, esta quantidade de itens.

Veja alguns exemplos de como usar essas restrições de tipo:

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

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

Observe que o elemento raiz de um esquema deve ser um objeto e não pode usar anyOf. Um padrão encontrado no Zod, por exemplo, é o uso de uma união discriminada, que gera um anyOf no nível superior. Por isso, um código como o seguinte não funcionará:

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 definidos como required

Para usar saídas estruturadas, todos os campos ou parâmetros de função devem ser especificados 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"]
    }
}

Embora todos os campos devam ser obrigatórios (e o modelo retorne um valor para cada parâmetro), é possível simular um parâmetro opcional usando um tipo de união com 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"
        ]
    }
}

Os objetos têm limites de profundidade de aninhamento e tamanho

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

Limites para o tamanho total das cadeias de caracteres

Em um esquema, o comprimento total das cadeias de caracteres 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 para o tamanho de enum

Um esquema pode ter até 1.000 valores de enum no total, considerando todas as propriedades enum.

Para uma única propriedade enum com valores do tipo cadeia de caracteres, o comprimento total das cadeias de caracteres de todos os valores de enum não pode ultrapassar 15.000 caracteres quando houver mais de 250 valores de enum.

É necessário sempre definir additionalProperties: false nos objetos

additionalProperties controla se um objeto pode conter chaves e valores adicionais que não foram definidos no JSON Schema.

As saídas estruturadas permitem gerar apenas as chaves e os valores especificados. Por isso, exigimos que os desenvolvedores definam additionalProperties: false para usar saídas estruturadas.

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

Ao usar saídas estruturadas, os resultados serão gerados na mesma ordem das chaves no esquema.

Algumas palavras-chave específicas de tipos ainda não são compatíveis

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

Para modelos que passaram por ajuste fino, também não oferecemos suporte ao seguinte:

  • 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 fornecendo strict: true e chamar a API com um esquema JSON Schema não compatível, receberá um erro.

Para anyOf, cada esquema aninhado deve ser válido de acordo com este subconjunto de JSON Schema

Veja um exemplo de esquema 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"
    ]
}

Há suporte a definições

Você pode usar definições para definir subesquemas referenciados ao longo do seu esquema. Veja um exemplo simples a seguir.

{
    "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 a esquemas recursivos

Exemplo de esquema recursivo que usa # para indicar recursão na 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 esquema recursivo que usa 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. Enquanto o modo JSON garante que a saída do modelo seja um JSON válido, as saídas estruturadas garantem, de forma confiável, que a saída do modelo siga o esquema especificado. Recomendamos usar saídas estruturadas se houver suporte para o seu caso de uso.

Quando o modo JSON está ativado, a saída do modelo tem garantia de ser um JSON válido, exceto em alguns casos extremos que você deve detectar e tratar adequadamente.

Para ativar o modo JSON com a Responses API, você pode definir text.format como { "type": "json_object" }. Se você estiver usando chamada de função, o modo JSON estará sempre ativado.

Observações importantes:

  • Ao usar o modo JSON, você deve sempre instruir o modelo a produzir JSON por meio de alguma mensagem na conversa, por exemplo, a mensagem do sistema. Se você não incluir uma instrução explícita para gerar JSON, o modelo poderá gerar um fluxo interminável de espaços em branco, e a solicitação poderá continuar em execução até atingir o limite de tokens. Para ajudar a evitar esse esquecimento, a API retornará um erro se a string "JSON" não aparecer em algum lugar do contexto.
  • O modo JSON não garante que a saída siga um esquema específico, apenas que seja válida e possa ser analisada sem erros. Você deve usar saídas estruturadas para garantir que a saída siga seu esquema ou, se isso não for possível, usar uma biblioteca de validação e, possivelmente, novas tentativas para garantir que a saída siga o esquema desejado.
  • Seu aplicativo deve detectar e tratar os casos extremos que podem fazer com que a saída do modelo não seja um objeto JSON completo (veja abaixo)

Recursos

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