Structured Outputs em APIs OpenAI-compatible: guia JSON Schema
Guia prático de JSON Schema estrito com Responses e Chat Completions, validação em camadas, falhas e testes de compatibilidade da rota.
Structured Outputs permite solicitar JSON conforme a um Schema definido, em vez de apenas pedir ao modelo para “retornar JSON”. Em uma API OpenAI-compatible, o mesmo JSON Schema pode ser usado com Responses ou Chat Completions, mas o wrapper muda e o suporte deve ser verificado no modelo e na rota exatos.
O padrão seguro de production tem três camadas: usar Schema strict quando suportado, fazer parse e validação novamente na aplicação e, só então, aplicar regras de negócio determinísticas. Conformidade estrutural reduz erros de formato; não prova correção semântica ou factual.
Structured Outputs não é JSON mode
| Método | JSON válido | Impõe o Schema | Uso típico |
|---|---|---|---|
| Somente Prompt | Não garantido | Não | Protótipos que toleram falha de parsing |
| JSON mode | Sim, quando suportado e concluído | Não | JSON flexível validado pela aplicação |
Structured Outputs com strict: true |
Sim, com tratamento de conclusão e refusal | Sim, no subset suportado | Extração tipada e workflows de aplicação |
O guia OpenAI Structured Outputs recomenda essa opção em vez de JSON mode quando modelo e endpoint suportam. Formato estruturado serve para devolver um objeto previsível; Function Calling serve para solicitar uma ação da aplicação. Os Schemas podem ser semelhantes, mas Call IDs e mensagens de resultado pertencem a outro contrato.
Defina o Schema antes de escolher o endpoint
Uma triagem de suporte precisa de quatro campos:
{
"type": "object",
"properties": {
"category": { "type": "string", "enum": ["billing", "technical", "account"] },
"priority": { "type": "integer", "minimum": 1, "maximum": 3 },
"requires_human": { "type": "boolean" },
"summary": { "type": "string" }
},
"required": ["category", "priority", "requires_human", "summary"],
"additionalProperties": false
}
Tornar tudo required e usar additionalProperties: false cria um objeto estável. Isso não torna todo keyword de JSON Schema portátil. OpenAI documenta um subset, enquanto outro provedor pode implementar um subset diferente ou não oferecer Strict Schema Mode.
Envie o Schema com Responses API
Responses coloca o Schema em text.format:
{
"model": "<MODEL_WITH_VERIFIED_STRUCTURED_OUTPUT_SUPPORT>",
"input": "The customer was charged twice and wants a refund.",
"text": {
"format": {
"type": "json_schema",
"name": "support_ticket",
"schema": {
"type": "object",
"properties": {
"category": { "type": "string", "enum": ["billing", "technical", "account"] },
"priority": { "type": "integer", "minimum": 1, "maximum": 3 },
"requires_human": { "type": "boolean" },
"summary": { "type": "string" }
},
"required": ["category", "priority", "requires_human", "summary"],
"additionalProperties": false
},
"strict": true
}
}
}
Envie para POST https://modelflare.dev/v1/responses com Authorization: Bearer <YOUR_API_KEY>. O conteúdo aparece em typed output items. SDKs podem oferecer helpers, mas um cliente HTTP deve localizar o texto completo e fazer parse; não assuma um objeto tipado no top level.
Envie o mesmo Schema com Chat Completions
Chat Completions usa response_format.json_schema:
{
"model": "<MODEL_WITH_VERIFIED_STRUCTURED_OUTPUT_SUPPORT>",
"messages": [
{ "role": "user", "content": "The customer was charged twice and wants a refund." }
],
"response_format": {
"type": "json_schema",
"json_schema": {
"name": "support_ticket",
"schema": {
"type": "object",
"properties": {
"category": { "type": "string", "enum": ["billing", "technical", "account"] },
"priority": { "type": "integer", "minimum": 1, "maximum": 3 },
"requires_human": { "type": "boolean" },
"summary": { "type": "string" }
},
"required": ["category", "priority", "requires_human", "summary"],
"additionalProperties": false
},
"strict": true
}
}
}
O JSON normalmente é uma string em choices[0].message.content e ainda precisa de parse.
| Objetivo | Responses API | Chat Completions |
|---|---|---|
| Container | text.format |
response_format.json_schema |
| Tipo | text.format.type |
response_format.type |
| Nome | text.format.name |
response_format.json_schema.name |
| Schema | text.format.schema |
response_format.json_schema.schema |
| Strict | text.format.strict |
response_format.json_schema.strict |
| Resultado | Output items / helper | choices[0].message.content |
A camada de compatibilidade do Modelflare converte os wrappers em rotas OpenAI/Codex suportadas, mas não cria Structured Outputs em um modelo upstream sem o recurso. Em famílias Raw Chat Completions Pass-through, o contrato do provedor continua sendo a fonte de verdade.
Valide estrutura e significado separadamente
{
"category": "billing",
"priority": 2,
"requires_human": true,
"summary": "Customer reports a duplicate charge and requests a refund."
}
Validação estrutural
Um JSON Schema validator confirma properties required, ausência de extras, tipos, enum e limites. Mesmo com Strict Adherence, a validação protege contra rota incompatível, erro de integração, truncation e mudança futura.
Validação semântica e de negócio
O Schema não sabe se a cobrança duplicada existiu, se priority: 2 está correto ou quem pode aprovar reembolso. Trate o objeto como classificação proposta, compare com dados autoritativos e imponha permissão e limite financeiro em código determinístico. JSON válido nunca deve contornar autorização, billing ou segurança.
Trate saídas incompletas e excepcionais
Refusals
Verifique Status e representação do refusal antes de procurar JSON. Recusa de segurança não é erro de parsing.
Truncation e Output Limits
Se a geração para antes do fechamento, o Schema não recupera o sufixo. Confira Completion Status e Stop Reason e use Output Limit suficiente, porém limitado.
Keywords não suportados
Comece com Objects, Arrays, Primitive Types, Enums, Required e Additional Properties explícitos. Consulte a documentação antes de References, recursion, Unions complexas ou validação avançada.
Latência do primeiro Schema
Alguns provedores preprocessam e armazenam um Schema novo em cache. Reutilize Schemas estáveis e versionados e meça First Use separadamente do Steady State.
Modelo ou rota incompatível
Um endpoint pode aceitar Chat Completions, rejeitar json_schema ou ignorar strict. Um 200 com JSON não prova Strict Adherence. Inclua inputs inválidos e casos-limite.
Execute uma matriz de compatibilidade
| Teste | Evidência |
|---|---|
| Objeto required mínimo | Status, modelo, rota, objeto e validação |
| Cada enum | Produção e parse de todos os valores |
| Informação ausente | Valor limitado ou pedido de esclarecimento |
| Input de segurança | Formato de refusal e tratamento |
| Output Limit baixo | Completion Status e truncation |
| Feature não suportada | Erro explícito ou constraint ignorada |
| Wrappers Responses e Chat | Objetos equivalentes na aplicação |
| Schema estável repetido | First Use contra Steady State |
Mantenha Schema, Prompt, Region, Streaming Mode e Output Limit constantes e registre a data.
Escolha o endpoint após testar o workflow
Responses é adequado para output items tipados, streaming events e workflows maiores de Tools. Chat Completions é adequado para integração por messages quando a rota suporta response_format. Veja Responses API vs Chat Completions.
Em qualquer opção:
- mantenha uma Source of Truth versionada para JSON Schema;
- altere apenas o wrapper, não o significado;
- valide a resposta completa;
- aplique regras determinísticas depois da estrutura;
- monitore parsing, refusal, truncation e compatibilidade separadamente.
Para migração, use o guia OpenAI-Compatible API; para streams incompletos, o guia AI API Streaming. Consulte Models & Pricing e teste Structured Outputs na rota exata da API Key.
Structured Outputs torna a resposta uma interface mais confiável, mas não substitui verificação factual, autorização ou lógica de billing. “OpenAI-compatible” continua sendo uma afirmação a testar recurso por recurso.