Como fazer um LLM devolver dados estruturados?
Você tem três formas de fazer um LLM devolver dados estruturados, em ordem crescente de garantia: pedir o formato no prompt, validar a resposta contra um schema e repetir quando falha, ou restringir a decodificação para que token inválido nunca seja amostrado. Só a terceira garante JSON sintaticamente válido. As duas primeiras são mais baratas e resolvem a maioria dos casos.
A escolha entre os três degraus é quase sempre uma escolha de custo. Forçar JSON válido descreve o caminho mais curto quando você só precisa que a resposta abra no parser; JSON Schema e Zod cobre a parte de validação; e parsing tolerante a falha trata do que fazer quando a resposta chega quebrada apesar de tudo.
Antes de escolher, vale entender por que o formato quebra. A causa está no jeito como o modelo gera texto, não em prompt mal escrito — e isso explica por que instrução mais enfática rende tão pouco aqui, ao contrário do que costuma valer em engenharia de prompts.
Por que o formato quebra
O modelo escolhe um token por vez, amostrando de uma distribuição sobre o
vocabulário inteiro. Nada nesse processo carrega a informação de que existe uma
chave aberta esperando fechamento, ou de que o campo total foi declarado como
número. Validade de JSON é uma propriedade da sequência inteira; a amostragem é
local. O modelo acerta o formato porque viu muito JSON no treino, não porque
alguma coisa o impede de errar.
Na prática a quebra aparece em quatro formas, e vale distinguir porque a correção de cada uma é diferente.
Cerca de markdown em volta. O modelo devolve o JSON certo, embrulhado em
```json. É a falha mais comum e a mais barata de consertar, porque o dado
está inteiro.
Vírgula final. Um , antes do } ou do ]. Legível para qualquer humano,
recusado por JSON.parse e por json.loads.
Aspas. Duas variantes distintas: aspas tipográficas (“ ”) no lugar das
retas, quando o texto do campo veio de conteúdo formatado; e aspas retas não
escapadas dentro de uma string, que fecham o valor no meio.
Truncamento. A mais traiçoeira. O JSON está impecável até o ponto em que os
tokens acabaram. O sintoma é falhar só nos casos longos, o que parece
aleatoriedade até você olhar o motivo de parada da resposta (stop_reason ou
finish_reason, conforme o provedor) e encontrar max_tokens.
Uma resposta com os quatro ao mesmo tempo, que é o caso realista:
Claro! Aqui está o JSON:
```json
{
"cliente": "Padaria do Zé",
"itens": [
{"sku": "PF-001", "qtd": 12, "obs": "farinha "tipo 1""},
{"sku": "PF-002", "qtd": 3,},
],
"total": 148.
Cerca de markdown com prosa antes, aspas não escapadas em obs, vírgula final em
dois lugares e truncamento no meio de total. Cada um pede um tratamento
diferente, e nenhum deles é “peça com mais firmeza”.
Degrau 1: pedir no prompt
O mais barato, e o suficiente para prototipar. Três coisas ajudam de verdade:
- Mostrar a saída exata, não descrevê-la. Um exemplo resolvido comunica nome de campo, tipo e formato de data de uma vez.
- Declarar o que não deve aparecer. “Responda apenas com o objeto JSON, sem cerca de código e sem texto antes ou depois” corta a maior parte das cercas.
- Deixar espaço. Metade das falhas de truncamento é
max_tokensapertado demais para o pior caso, não para o caso médio.
Um truque clássico morreu recentemente e vale registrar: preencher o começo da
resposta do assistente com { para forçar o modelo a continuar de dentro do
objeto. Em julho de 2026, os modelos atuais da Anthropic, da geração 4.6 em
diante, recusam a requisição com erro 400 quando a última mensagem é do
assistente. Onde o prefill ainda existe, ele funciona; onde não existe, o
substituto é a saída estruturada nativa.
Este degrau falha de um jeito específico: ele funciona nos seus dez casos de teste e quebra na primeira semana em produção, com uma entrada que tinha aspas no nome do cliente.
Degrau 2: validar depois
Aqui você aceita que a saída pode vir errada e trata isso como um caso normal, e não como exceção. O padrão tem três partes: extrair, validar, repetir.
Extrair é recortar o objeto de dentro do que veio. Um corte da primeira {
até a última } resolve cerca de markdown e prosa em volta sem nenhuma
inteligência.
Validar é conferir contra um schema, não só contra o parser. JSON.parse
aceita {"qtd": "doze"}; o seu schema não deveria.
import { z } from 'zod';
const Pedido = z.object({
cliente: z.string().min(1),
itens: z.array(z.object({ sku: z.string(), qtd: z.number().int().positive() })),
total: z.number(),
});
function extrair(bruto: string) {
const i = bruto.indexOf('{');
const j = bruto.lastIndexOf('}');
if (i < 0 || j < i) throw new Error('nenhum objeto na resposta');
return JSON.parse(bruto.slice(i, j + 1));
}
Repetir é a parte que quase todo mundo faz mal. Reenviar o mesmo prompt e torcer para o modelo acertar desta vez desperdiça a informação mais útil que você tem: a mensagem de erro. Devolva o erro do validador ao modelo, junto com a resposta anterior, e a taxa de acerto na segunda tentativa sobe muito.
const conserto = [
{ role: 'assistant', content: bruto },
{ role: 'user', content: `Isso falhou na validação: ${erro.message}\n` +
`Devolva apenas o objeto corrigido.` },
];
Uma passada de conserto assim é barata e mensurável. No estudo de Tam e colegas, pedir a um modelo pequeno que reformatasse saídas com erro de parse melhorou o resultado final em JSON e em YAML1.
Degrau 3: restringir a decodificação
O terceiro degrau muda a natureza do problema. Em vez de julgar o texto depois de gerado, você intervém no momento da amostragem: a cada passo, os tokens que levariam a uma sequência inválida recebem probabilidade zero. O modelo não é convencido a produzir JSON válido — ele fica incapaz de produzir outra coisa.
O mecanismo é uma máscara sobre os logits. O schema, ou a gramática, é compilado
em um autômato; o estado do autômato depois dos tokens já emitidos determina quais
tokens do vocabulário são aceitáveis agora, e o resto é zerado antes da amostragem.
Depois de {"qtd": só passam dígitos e sinal; depois de {"cliente": só passa
aspas.
A objeção óbvia é custo. Testar o vocabulário inteiro a cada token é caro: mesmo um vocabulário pequeno como o do GPT-2 tem 50.257 entradas, e implementações ingênuas percorrem isso a cada passo. Willard e Louf mostraram como construir um índice do vocabulário por estado do autômato antes da geração, o que reduz o trabalho por token a custo constante em média2. É por isso que a técnica saiu do papel e virou recurso de API.
A ideia se aplica além de JSON. Gramáticas livres de contexto descrevem o espaço de saída de uma classe grande de tarefas (SQL, chamadas de função, sequências de ação de agente), e o mesmo mecanismo de restrição serve para todas3.
Em julho de 2026, os provedores expõem isso de duas maneiras. Na API da Anthropic,
output_config.format com um json_schema restringe a resposta, e strict: true
na definição de uma ferramenta restringe os parâmetros dela; o schema precisa
declarar additionalProperties: false e listar os campos obrigatórios. Um schema
novo paga uma compilação na primeira chamada e fica em cache por 24 horas. Duas
consequências práticas: a primeira requisição de um schema é mais lenta que as
seguintes, e montar o schema de novo a cada chamada joga o cache fora.
O formato é só a primeira frente
Resolver o formato é a parte visível. Os três problemas seguintes aparecem em ordem, e cada um custa mais que o anterior: garantir que o conteúdo faz sentido, decidir o que fazer quando não faz, e sustentar isso em volume — lote, limite de requisições, streaming, idempotência de retry.
Onde isso falha
JSON válido não é resposta correta. A decodificação restrita garante que
{"categoria": "cobranca"} vai abrir no parser e que cobranca está no enum. Ela
não garante que aquele ticket era de cobrança. É a confusão mais cara do assunto,
porque o sistema para de falhar de forma barulhenta e passa a falhar em silêncio.
A ordem dos campos no schema muda o resultado. Tam e colegas encontraram o
caso mais claro disso: em 100% das respostas do GPT-3.5 Turbo em modo JSON, a
chave answer apareceu antes da chave reason, o que transformou uma tarefa de
chain-of-thought em resposta direta e derrubou o desempenho1. O modelo escreve
na ordem em que os campos aparecem; se o resultado vem antes do raciocínio, não há
raciocínio.
A perda não vem do parse. No mesmo trabalho, o LLaMA 3 8B teve 0,148% de erro de parse na tarefa Last Letter em JSON, e mesmo assim 38,15% de diferença de desempenho em relação à saída em texto livre1. Vale como aviso contra a explicação fácil: se estruturar piorou o seu resultado, o parser provavelmente não é o culpado.
Restrição mal implementada machuca de verdade. Beurer-Kellner e colegas mediram uma versão de GSM8K codificada em JSON com Mistral 7B e cinco exemplos: 41,5% de acerto sem restrição, 30,8% com restrição ingênua, 41,8% com restrição minimamente invasiva4. A causa é desalinhamento de subpalavra — a máscara força uma fragmentação de tokens que o modelo nunca viu no treino, e a distribuição seguinte fica fora de distribuição. A conclusão prática é que a qualidade da biblioteca importa, não só o fato de haver restrição.
A cobertura de JSON Schema é parcial. Um benchmark de 2025 avaliou seis
frameworks de decodificação restrita sobre 10 mil schemas reais e encontrou
diferenças relevantes de cobertura entre eles5. Na prática: restrições
numéricas (minimum, multipleOf), restrições de tamanho de string
(minLength) e schemas recursivos costumam ficar de fora. O schema é aceito, a
saída “obedece” e a sua regra de negócio não foi verificada por ninguém. Mantenha
o validador do degrau 2 mesmo usando o degrau 3.
Nem toda tarefa perde. Em classificação, restringir o espaço de respostas às vezes ajuda: no mesmo estudo, um dos modelos avaliados melhorou em um conjunto de diagnóstico com o modo JSON ligado1. Faz sentido: em classificação, o formato carrega a resposta.
Por onde começar
- Meça antes de decidir. Rode 100 entradas reais com o prompt mais simples e conte quantas abriram no parser. Se forem 98, o degrau 2 basta; se forem 60, olhe o que quebrou antes de subir de degrau.
- Separe as falhas por tipo. Cerca de markdown, vírgula, aspas e truncamento têm consertos diferentes, e três deles são de uma linha.
- Escreva o schema com o raciocínio primeiro. Se a tarefa exige pensar, o campo de raciocínio precisa vir antes do campo de resultado.
- Suba para decodificação restrita quando o custo da falha for alto, não quando a taxa de falha for alta. Ingestão em lote sem supervisão justifica; uma tela com um humano olhando geralmente não.
- Mantenha o validador. Ele deixa de ser o mecanismo principal e vira a rede de segurança contra truncamento e contra o pedaço do schema que a restrição não cobre.
Custo
Três estimativas grosseiras, para dimensionar a decisão.
Validar e repetir custa uma chamada extra na fração de casos que falha. Com 2% de falha e uma passada de conserto, o acréscimo fica em torno de 2% do custo total de saída — irrelevante. Com 30% de falha, você está pagando um terço a mais e deveria consertar o prompt antes de automatizar o conserto.
Restringir a decodificação custa pouco em tempo de geração quando a implementação pré-computa o índice: Willard e Louf reportam sobrecarga pequena sobre a geração livre2, e a abordagem minimamente invasiva de Beurer-Kellner e colegas chega a superar a geração sem restrição em throughput graças a decodificação especulativa4. O custo real está na primeira chamada de cada schema novo e no trabalho de manter os schemas estáveis o suficiente para o cache servir.
Não estruturar custa o parser que você vai escrever, os casos de borda que vai descobrir em produção e os dados errados que entraram no banco antes de alguém perceber. Esse é o único dos três que não aparece na fatura.
Footnotes
-
Tam et al. (2024) comparam decodificação restrita, instrução de formato e conversão em duas etapas, em tarefas de raciocínio e de classificação. ↩ ↩2 ↩3 ↩4
-
Willard e Louf (2023) reformulam a geração guiada como transições entre estados de um autômato finito e constroem um índice sobre o vocabulário do modelo, o que torna o custo por token constante em média. ↩ ↩2
-
Geng et al. (2023) mostram que gramáticas formais descrevem o espaço de saída de uma faixa ampla de tarefas de PLN, e propõem gramáticas dependentes da entrada para estruturas que variam com o caso. ↩
-
Beurer-Kellner et al. (2024) medem a perda de acurácia causada por desalinhamento entre subpalavras e restrição, e apresentam um algoritmo alinhado a subpalavras que a elimina. ↩ ↩2
-
Geng et al. (2025) avaliam seis frameworks de decodificação restrita sobre 10 mil schemas reais de JSON Schema, em eficiência, cobertura e qualidade. ↩
Perguntas frequentes
- Como faço um LLM devolver JSON?
- Descreva o formato no prompt, dê um exemplo da saída exata e extraia o primeiro objeto da resposta antes de chamar o parser. Isso resolve a maior parte dos casos. Se precisar de garantia dura, use o modo de saída estruturada do provedor, que restringe a decodificação e impede a geração de JSON inválido.
- Por que o JSON vem dentro de uma cerca de markdown?
- Porque o modelo viu milhões de blocos de código cercados durante o treino, e responder assim é o comportamento mais provável. Instrução no prompt reduz a frequência, mas não zera. O conserto barato é recortar o trecho entre a primeira chave e a última antes de fazer o parse.
- Saída estruturada garante que a resposta está correta?
- Não. Ela garante sintaxe e, em parte, o schema. O modelo continua livre para preencher os campos com valores errados, inventar um identificador que não existe ou escolher a categoria errada do enum. Validação de formato e validação de conteúdo são problemas separados, e a decodificação restrita resolve só o primeiro.
- Restringir a decodificação piora a qualidade da resposta?
- Pode piorar, dependendo de como o schema é escrito e de como a restrição é implementada. Um estudo de 2024 mediu queda grande em tarefas de raciocínio quando o campo da resposta vinha antes do campo do raciocínio no schema. Colocar o raciocínio primeiro recupera boa parte da diferença.
- Ainda preciso de retry se já uso decodificação restrita?
- Sim. A decodificação restrita não impede truncamento por limite de tokens, não cobre toda a especificação de JSON Schema e não protege contra recusa do modelo. Mantenha o validador e o caminho de retry; a diferença é que eles passam a disparar raramente, em vez de a cada dez chamadas.
- Qual a diferença entre JSON mode e schema estrito?
- JSON mode garante apenas que a saída é um JSON sintaticamente válido, com qualquer estrutura. Schema estrito compila o seu schema em uma restrição sobre a decodificação e garante também os campos, os tipos e os valores de enum. O segundo costuma custar uma compilação na primeira chamada.
Referências
- Willard, B. T. e Louf, R.. Efficient Guided Generation for Large Language Models (2023)arXiv:2307.09702
- Geng, S. et al.. Grammar-Constrained Decoding for Structured NLP Tasks without Finetuning (2023)arXiv:2305.13971
- Beurer-Kellner, L. et al.. Guiding LLMs The Right Way: Fast, Non-Invasive Constrained Generation (2024)arXiv:2403.06988
- Tam, Z. R. et al.. Let Me Speak Freely? A Study on the Impact of Format Restrictions on Performance of Large Language Models (2024)arXiv:2408.02442
- Geng, S. et al.. JSONSchemaBench: A Rigorous Benchmark of Structured Outputs for Language Models (2025)arXiv:2501.10868