Saída estruturada é uma resposta em uma forma que você decidiu de antemão: um objeto JSON com chaves nomeadas, uma tabela com colunas fixas ou um modelo com os mesmos títulos toda vez. Você precisa dela sempre que um programa lê a resposta, e ela ajuda quando uma pessoa lê também, porque todas as respostas ficam com a mesma cara e são fáceis de percorrer ou comparar.
O segredo é descrever a forma com tanta precisão que o modelo não tenha mais nada para escolher. A palavra "JSON" sozinha não é uma descrição. O bloco abaixo extrai detalhes de um relato de bug. Compare um pedido pelos "detalhes principais" com um pedido que detalha o esquema.
Aqui estão os detalhes principais do relato de bug:
- Problema: o app trava ao exportar projetos
- Gatilho: tocar em Exportar em projetos com mais de 50 fotos
- Início: depois da atualização mais recente
- Plataforma: Android 14
- Versão do app: 3.2.0
- Impacto: alto, já que está travando uma entrega para um cliente
Projetos menores parecem exportar sem problemas.
A resposta em texto livre é correta e legível, mas nenhuma execução vai usar os mesmos rótulos que a outra, a gravidade é uma frase em vez de um valor e um programa teria de adivinhar onde cada campo começa. A resposta em JSON pode ir direto para um sistema de bugs. Repare que a resposta ainda veio dentro de um bloco de código: os apps de chat costumam envolver o JSON assim, o que importa quando você faz o parse no código.
Como pedir JSON
Um bom pedido de JSON responde a todas as perguntas que o modelo responderia por você:
- Cada chave, escrita exatamente. Escreva os nomes das chaves entre aspas do jeito que devem aparecer. Diga "exatamente estas chaves" para que o modelo não acrescente outras.
- O tipo de cada valor. String, número, booleano, array de strings, objeto aninhado.
- Os valores permitidos para qualquer campo com um conjunto fixo, como gravidade ou categoria. Sem a lista, você recebe "High", "high", "severe" e "P1" em quatro execuções.
- O que fazer quando a entrada não tem um valor. Se você não disser "null se não for informado", o modelo tende a preencher a lacuna com um palpite plausível, e uma versão do app inventada tem exatamente a mesma cara de uma real.
- Nada em volta. "Retorne apenas o JSON, sem nenhum texto antes ou depois" elimina a frase simpática de abertura.
Quando a forma é aninhada ou incomum, mostrar um objeto de exemplo completo funciona melhor do que descrevê-lo. Isso é prompt few-shot aplicado ao formato. Deixe os valores do exemplo bem diferentes da entrada real para que o modelo não os copie.
Um prompt de extração reutilizável
Este bloco tem o mesmo tipo de pedido dividido em partes. Desligue a parte de formato e o modelo ainda retorna JSON, porque as restrições pedem isso, mas escolhe os próprios nomes de chave, como title em vez de job_title, e o código que espera as suas chaves quebra. A parte de restrições é o que o impede de completar um telefone pela metade ou deduzir a empresa a partir do domínio do e-mail. Cole uma assinatura real no campo de entrada para testar.
{
"name": "string",
"job_title": "string ou null",
"company": "string ou null",
"email": "string ou null",
"phone": "string ou null"
}{
"name": "Priya Nair",
"job_title": "Head de Dados",
"company": "Northwind Labs",
"email": "priya.nair@northwind.example",
"phone": null
}
Tabelas e modelos fixos
A saída estruturada não serve só para programas. Quando você mesmo lê a resposta, uma tabela em markdown ou um modelo fixo dá o mesmo benefício: você sabe onde cada informação vai estar antes de olhar.
| Tipo | Ordenado | Mutável | Aceita duplicatas | Uso típico |
|---|---|---|---|---|
| list | Sim | Sim | Sim | Uma sequência à qual você adiciona, remove ou ordena itens |
| tuple | Sim | Não | Sim | Um grupo fixo de valores, como coordenadas |
| set | Não | Sim | Não | Remover duplicatas e verificar pertencimento rapidamente |
Um modelo funciona do mesmo jeito para textos mais longos: dê os títulos na ordem e diga o que vai embaixo de cada um. "Responda com três rótulos em negrito: Causa, Correção, Como conferir" produz os mesmos três rótulos toda vez, o que deixa fácil comparar um lote de respostas.
JSON mode na API
Várias APIs de modelos têm um ajuste que força um JSON sintaticamente válido. No SDK Python da OpenAI, ele é o response_format. O JSON mode exige que a palavra "JSON" apareça em algum lugar das suas mensagens, então o prompt de sistema abaixo a menciona e lista as chaves.
import json
from openai import OpenAI
client = OpenAI()
MODEL = "your-model-id" # e.g. from your provider's model list
report_text = "Since yesterday's update the app crashes when I tap Export..."
response = client.chat.completions.create(
model=MODEL,
response_format={"type": "json_object"},
messages=[
{
"role": "system",
"content": (
"Extract the bug report into JSON with the keys "
"summary (string), severity (one of low, medium, high, critical) "
"and steps_to_reproduce (array of strings)."
),
},
{"role": "user", "content": report_text},
],
)
data = json.loads(response.choices[0].message.content)
O JSON mode garante que o texto seja parseável (a não ser que a resposta seja cortada no limite de tokens), não que ele siga o seu esquema. Uma chave ainda pode faltar, ou a gravidade ainda pode vir como "urgent". Vários fornecedores também aceitam um JSON Schema completo, como formato de saída ou como esquema de entrada de uma definição de ferramenta (função), e alguns desses modos restringem a resposta ao esquema. Confira a documentação do seu fornecedor para saber o parâmetro exato, já que esses recursos variam entre as APIs.
Valide o resultado no código
Trate o JSON do modelo como você trata qualquer entrada vinda de fora do programa: faça o parse e depois confira. O parse pega sintaxe quebrada. A verificação pega um objeto válido com o conteúdo errado.
ALLOWED_SEVERITIES = {"low", "medium", "high", "critical"}
def problems(data):
if not isinstance(data, dict):
return ["the answer must be a JSON object"]
found = []
if not isinstance(data.get("summary"), str):
found.append("summary must be a string")
if data.get("severity") not in ALLOWED_SEVERITIES:
found.append("severity must be low, medium, high or critical")
steps = data.get("steps_to_reproduce")
if not isinstance(steps, list) or not all(isinstance(s, str) for s in steps):
found.append("steps_to_reproduce must be an array of strings")
return found
Quando a verificação falha, uma única nova tentativa muitas vezes resolve: mande ao modelo a própria saída dele junto com a lista de problemas e peça o JSON corrigido. Limite o número de tentativas e registre as falhas, porque um campo que falha com frequência é sinal de que o prompt não está claro. Em projetos maiores, uma biblioteca de validação como o Pydantic ou um validador de JSON Schema substitui a função escrita à mão.
Mais dois hábitos evitam erros silenciosos. Defina o limite de tokens de saída alto o bastante para a maior resposta que você espera, porque um objeto truncado nunca passa no parse. E quando a entrada for longa ou vier de usuários, separe-a das suas instruções com delimitadores ou tags XML para que o texto de dentro tenha menos chance de ser lido como instrução. Se um único prompt precisa raciocinar e produzir JSON ao mesmo tempo, considere o encadeamento de prompts: deixe uma etapa pensar em texto livre e uma segunda etapa transformar o resultado na estrutura.
Perguntas frequentes
Como faço o ChatGPT responder em JSON?
Diga que a resposta precisa ser JSON, liste cada chave com o tipo e dê os valores permitidos para qualquer campo com um conjunto fixo. Acrescente "Retorne apenas o JSON, sem nenhum texto antes ou depois". Na API, ative também o JSON mode com response_format={"type": "json_object"}, que exige que a palavra JSON apareça nas suas mensagens.
Por que o modelo coloca texto em volta do JSON?
Os modelos de chat são treinados para conversar, então muitas vezes começam com uma frase como "Aqui está o JSON" ou colocam o objeto dentro de um bloco de código em markdown. Peça só o JSON e, no código, use o JSON mode da API ou remova o bloco de código em volta antes de fazer o parse.
O que é JSON mode?
JSON mode é um ajuste da API que faz o modelo produzir um JSON sintaticamente válido, desde que a resposta não seja cortada pelo limite de tokens de saída. Ele não faz o modelo seguir o seu esquema: chaves ainda podem faltar, vir com erro de digitação ou com o tipo errado. Alguns fornecedores também oferecem um modo mais rígido, que recebe um JSON Schema completo e restringe a saída a ele.
Um LLM consegue sempre retornar um JSON válido?
Não só com um prompt. Mesmo uma instrução clara falha de vez em quando, e uma resposta cortada pelo limite de tokens de saída é sempre inválida. Faça o parse de cada resposta com um parser de JSON de verdade, confira os campos de que você precisa e tente de novo ou gere um erro claro quando a verificação não passar.