Structured outputs são contratos
Um schema transforma o modelo de gerador de texto em componente integrável. Trate com a seriedade de uma API.
A mudança que mais melhorou a confiabilidade das features com LLM que entrego não foi um prompt melhor nem um modelo melhor. Foi me recusar a aceitar texto livre. Toda chamada que alimenta um programa devolve um schema: campos tipados, valores enumerados, nulls explícitos. No momento em que a saída tem um formato, o modelo deixa de ser parceiro de conversa e vira um componente com contrato, e tudo o que sei sobre contratos passa a valer.
Um schema é uma promessa
Quando defino um schema de saída, faço uma promessa ao resto do sistema: estes campos vão existir, estes tipos vão se manter, estes enums vão ser um destes valores. Essa promessa é o ponto de integração. O código a jusante é escrito contra o schema, não contra o modelo, o que significa que posso mudar o prompt, trocar o provedor ou atualizar o modelo, e enquanto o schema for honrado, nada a jusante percebe. É o mesmo benefício que um contrato de API dá entre serviços. O modelo é só um serviço com uma implementação muito incomum.
Valide, não confie
Pedir um schema não é o mesmo que receber um. Mesmo com um provedor que garante o formato, os valores dentro dele continuam gerados: um campo de confiança vai ser preenchido com um número plausível, um enum vai ser escolhido mesmo quando nenhum se encaixa, uma data vai estar bem formada e errada. Então todo structured output passa pela mesma camada de validação de qualquer input externo: parsear, checar tipos, checar faixas, checar integridade referencial contra a fonte. Quando a validação falha, a falha é tratada explicitamente, com retry, fallback ou erro, nunca com um default silencioso. Trato o modelo exatamente como trataria uma API de terceiros que costuma acertar.
Projete o schema para o consumidor, não para o modelo
Um erro comum é desenhar o schema em torno do que o modelo acha fácil produzir. Desenhe em torno do que o código precisa consumir. Se a lógica a jusante precisa saber se um fato foi encontrado na fonte, dê a ela um boolean e um campo de citação, não uma explicação em texto livre. Se uma decisão tem três resultados, dê um enum com três valores e um quarto para "não foi possível determinar", porque a ausência de uma saída de emergência é como um modelo é obrigado a chutar. Torne obrigatório o que é obrigatório e opcional o que é opcional. O schema é onde você codifica os invariantes que o prompt só consegue sugerir.
Versionamento e evolução
Schemas mudam, e um schema usado por um modelo muda por dois motivos: o produto precisa de um campo novo, ou o modelo se sai melhor com outro formato. Os dois são migrações. Versiono schemas de saída como versiono respostas de API: mudanças aditivas são seguras, remoções e renomeações ganham período de transição, e o conjunto de evals roda de novo contra o formato novo antes de ir para produção. Prompt e schema viajam juntos no controle de versão, porque um prompt escrito para um schema degrada em silêncio contra outro.
O que quebra na prática
As falhas que encontro em auditorias são consistentes. Schemas profundamente aninhados que o modelo preenche de forma inconsistente, quando um plano teria funcionado. Campos que significam coisas diferentes em prompts diferentes porque ninguém escreveu a definição. Validação que existe num caminho do código e não no job em lote que usa o mesmo modelo. E o clássico: um schema que nunca foi imposto, então o código parseia texto livre com regex e torce. Cada um desses é um problema de contrato com solução conhecida. O modelo o tornou visível. A engenharia conserta.