Menu

Saída estruturada: JSON confiável de um LLM

Saída estruturada (structured output) é pedir ao modelo uma resposta em uma forma fixa, como JSON, uma tabela ou um modelo, para que um programa ou uma pessoa possa usá-la sem reformatar. Dê o esquema, diga o que fazer com valores ausentes e valide o resultado no código.

Você pode editar cada prompt desta página e depois abri-lo no ChatGPT, no Claude ou em outro app de IA.

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.

Extraia os detalhes principais deste relato de bug: "Desde a atualização de ontem o app trava quando eu toco em Exportar em um projeto com mais de 50 fotos. Projetos menores exportam normalmente. Estou no Android 14, versão 3.2.0 do app. Isso está travando a entrega para o meu cliente."
Try it
Example replyReplies vary between models and runs.

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.

Extrair dados de contato em JSON
Fill in
Parts
Extraia os dados de contato da assinatura de e-mail abaixo.
Retorne um objeto JSON com exatamente estas chaves: { "name": "string", "job_title": "string ou null", "company": "string ou null", "email": "string ou null", "phone": "string ou null" }
Use null para qualquer valor que não esteja no texto. Não adivinhe nem complete valores parciais. Retorne apenas o JSON.
Priya Nair | Head de Dados, Northwind Labs | priya.nair@northwind.example
Try it
Example replyReplies vary between models and runs.
{
  "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.

Tabela
Compare listas, tuplas e conjuntos em Python em uma tabela markdown com estas colunas: Tipo, Ordenado, Mutável, Aceita duplicatas, Uso típico. Uma linha por tipo. Nenhum texto fora da tabela.
Try it
Example replyReplies vary between models and runs.
TipoOrdenadoMutávelAceita duplicatasUso típico
listSimSimSimUma sequência à qual você adiciona, remove ou ordena itens
tupleSimNãoSimUm grupo fixo de valores, como coordenadas
setNãoSimNãoRemover 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.

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR