Saídas estruturadas em LLMs de produção
JSON mode garante texto parseável, não campos corretos. Saídas estruturadas em produção precisam de contratos de esquema, validação no servidor, loops de reparo com retries limitados e conjuntos de eval que pontuem forma e semântica — não apenas sintaxe válida.
O pipeline de extração retornou JSON válido. O parser de faturas aceitou. Finanças conciliou contra um nome de fornecedor que nunca apareceu no documento — o modelo inventou uma string plausível dentro de um objeto perfeitamente tipado. Saídas estruturadas em LLMs de produção falham em duas camadas: a sintaxe passa enquanto a semântica deriva, e as equipes descobrem a lacuna só quando sistemas downstream tratam o JSON parseado como verdade absoluta. JSON mode e geração guiada por esquema reduzem erros de formato. Não substituem validação, observabilidade nem a disciplina de eval que define o que significa "extração correta" antes do tráfego chegar.
Modelos de linguagem grandes emitem texto. Sistemas de produção precisam de objetos — campos tipados, valores enumerados, registros aninhados, arrays com restrições de cardinalidade. A lacuna entre geração de tokens e contratos de aplicação é onde a maioria dos incidentes de integração com IA se origina. Tratar o modelo como serializador sem fronteira de validação é a mesma categoria de erro que confiar em validação de formulários no cliente para valores de pagamento.
JSON mode vs schema contracts: what each layer actually guarantees
Recursos do provedor caem em um espectro de frouxo a estrito:
| Mechanism | Guarantees | Does not guarantee |
|---|---|---|
| Instrução no prompt ("retorne apenas JSON") | Nada exigível | JSON válido, chaves corretas, tipos corretos |
| JSON mode | Objeto/array JSON sintaticamente válido | Conformidade com esquema, campos obrigatórios, valores enum |
| JSON Schema / restrição de formato de resposta | Saída corresponde à forma do esquema declarado na camada API | Correção de negócio, valores de campo alucinados |
| Decodificação restrita (gramática, guiada por esquema) | Fluxo de tokens não pode violar a gramática | Precisão semântica de campos string |
| Validação pós-parse (Zod, Pydantic, AJV) | Aplicação rejeita payloads inválidos | Modelo produz saída válida na primeira tentativa |
JSON mode resolve o problema de vírgulas finais e fences de markdown. Não resolve o modelo preenchendo confidence: 0.99 em um palpite ou mapeando "status": "approved" quando o texto fonte diz rejeitado. Contratos de esquema na camada API reduzem o espaço de tokens — propriedades obrigatórias, restrições de tipo, pertencimento a enum — o que melhora materialmente presença de campos e correção de tipos. Contratos de esquema ainda permitem strings erradas em campos tipados como string.
JSON válido é preocupação de transporte. Extração correta é preocupação de produto. Só validação e evals as separam.
Arquitetura de produção trata suporte de esquema do provedor como o primeiro portão, não o último. O contrato durável vive em código de aplicação:
import { z } from "zod";
const InvoiceExtraction = z.object({
vendor_name: z.string().min(1).max(200),
invoice_number: z.string().regex(/^[A-Z0-9-]{4,32}$/),
total_cents: z.number().int().positive(),
line_items: z.array(
z.object({
description: z.string(),
quantity: z.number().positive(),
unit_price_cents: z.number().int().nonnegative(),
})
).min(1),
currency: z.enum(["USD", "EUR", "GBP"]),
});
type InvoiceExtraction = z.infer<typeof InvoiceExtraction>;Falhas de parse, incompatibilidades de tipo e violações de regras de negócio surgem aqui — com mensagens de erro adequadas para loops de reparo e logging adequado para dashboards.
Where schema contracts belong in the request path
Um fluxo típico de produção:
User input → prompt assembly → LLM call (schema mode) → raw JSON parse
→ schema validation → business rules → persist / downstream API
↓ fail ↓ fail
repair loop (bounded) human review queue
Prompt assembly inclui exemplos, definições de campos e tratamento explícito de null ("usar null quando ausente, nunca inventar"). LLM call usa o modo mais estrito suportado — response schema, tool schema ou restrição de gramática — alinhado à capacidade do provedor. Validation roda no servidor independentemente das promessas do provedor; provedores mudam comportamento entre versões de modelo.
Business rules ficam acima da validação de esquema: total_cents deve igualar a soma de line items dentro de tolerância de arredondamento; invoice_number não deve duplicar registro existente salvo se marcado como revisão. Esquema valida forma; regras de negócio validam significado.
Equipes que debatem fine-tuning como último recurso para extração costumam ter lacunas corrigíveis de prompt e esquema primeiro. Fine-tuning em tarefas com formato estável pode ajudar em escala — mas só depois que infraestrutura de eval prova que o modo de falha restante é comportamento do modelo, não validação ausente ou prompts fracos.
Repair loops: bounded recovery when the model misses the contract
Conformidade de esquema na primeira passagem em produção raramente atinge 100%. Loops de reparo retentam com feedback estruturado:
MAX_REPAIR_ATTEMPTS = 2
def extract_invoice(document_text: str) -> InvoiceExtraction:
messages = build_initial_messages(document_text)
for attempt in range(MAX_REPAIR_ATTEMPTS + 1):
raw = call_llm(messages, response_schema=INVOICE_SCHEMA)
try:
parsed = json.loads(raw)
validated = InvoiceExtraction.model_validate(parsed)
validate_business_rules(validated, document_text)
return validated
except (json.JSONDecodeError, ValidationError, BusinessRuleError) as exc:
if attempt >= MAX_REPAIR_ATTEMPTS:
raise ExtractionFailed(str(exc)) from exc
messages.append({"role": "assistant", "content": raw})
messages.append({
"role": "user",
"content": f"Validation failed: {format_errors(exc)}. "
f"Return corrected JSON matching the schema. "
f"Do not invent fields not supported by the source.",
})
raise ExtractionFailed("unreachable")Loops de reparo eficazes compartilham propriedades:
Bounded attempts. Três tentativas com feedback superam loops de retry infinitos que queimam orçamentos de latência e amplificam custo em inputs patológicos.
Error messages the model can act on. "Validation failed at line_items[2].quantity: expected positive number, got 0" supera "invalid schema".
Assistant turn preservation. Incluir a saída falha no contexto dá ao modelo um alvo de diff; omitir força regeneração completa do zero.
Escalation path. Após falha limitada, rotear para revisão humana, heurística de fallback ou extração degradada de campo único — nunca passagem silenciosa de objetos parciais.
Metrics per attempt. Rastrear taxa de sucesso na primeira passagem, taxa de sucesso pós-reparo e taxa de falha terminal separadamente. Taxa de reparo crescente com taxa de primeira passagem estável sinaliza deriva de prompt ou esquema, não degradação do modelo.
Loops de reparo adicionam latência. Para caminhos síncronos voltados ao usuário, limitar timeout total incluindo reparos. Para pipelines assíncronos, reparos são aceitáveis se profundidade de fila for monitorada.
Streaming, partial JSON, and parser hardening
Streaming de saída estruturada melhora latência percebida mas complica o parse. Fluxos de tokens podem entregar JSON incompleto até o chunk final. Estratégias:
Buffer-until-complete. O mais simples: acumular tokens, parsear uma vez no fim do stream. Perde UX incremental mas evita bugs de parse parcial.
Incremental parsers. Bibliotecas de reparo JSON e parsers de stream conscientes do esquema emitem objetos parciais conforme campos completam — útil para indicadores de progresso na UI. Exigem fuzz testing em limites de chunks.
Never execute on partial state. Efeitos colaterais downstream — escritas no banco, início de pagamentos — devem esperar objetos completos validados. Preview parcial na UI é aceitável; persistência parcial não.
Mesmo com JSON mode, stripping defensivo de fences markdown lida com regressões do provedor sem derrubar o pipeline.
Evals for structured output: shape is necessary, semantics is sufficient
Qualidade de saída estruturada exige dimensões de eval além do sucesso de JSON.parse:
| Eval dimension | Example check | Why it matters |
|---|---|---|
| Schema pass rate | Valida contra Zod/AJV | Detecta falhas de tipo e enum |
| Field presence | Campos obrigatórios não nulos quando fonte contém dados | Detecta tradeoff omissão vs invenção |
| Hallucination rate | Campos string coincidem com spans fonte ou sobreposição NER | Detecta valores plausíveis mas errados |
| Numeric accuracy | Valores parseados dentro de tolerância da verdade rotulada | Detecta erros de ordem de magnitude |
| Repair rate | Primeira passagem vs passagem pós-reparo | Sinaliza saúde de prompt/esquema |
| Latency p99 | End-to-end incluindo reparos | Sinaliza viabilidade em produção |
O enquadramento de evals de produto IA aplica diretamente: donos de produto definem o que significa extração correta por campo — quando null é aceitável, quando inferência é permitida, quando revisão humana é obrigatória. Engenharia implementa validadores; produto possui o conjunto rotulado e limiares de aprovação.
Testes de regressão para saídas estruturadas rodam a cada mudança de prompt, upgrade de modelo e revisão de esquema. Upgrade de modelo que melhora qualidade de prosa enquanto degrada aderência a enum é regressão silenciosa comum — só conjunto de eval com casos específicos de enum detecta antes do deploy.
Observability and failure taxonomy in production
Registrar falhas de saída estruturada com taxonomia, não 500s genéricos:
extraction_outcome: success | schema_fail | business_rule_fail | repair_success | terminal_fail
schema_violation_path: $.line_items[0].quantity
repair_attempt: 1
model_version: gpt-4.x-2025-04-14
latency_ms: 1840
Dashboards segmentam por versão de modelo, tipo de documento e caminho de violação. Alertar sobre taxa de falha terminal e aumentos de taxa de reparo. Amostrar payloads falhos em fila de revisão para ampliar conjunto de eval. Armazenar saída bruta do modelo junto a objetos validados para replay de auditoria.
How should teams ship structured LLM outputs?
Decisões de implementação que sobrevivem upgrades de modelo e crescimento de tráfego.
When is JSON mode enough?
JSON mode basta para ferramentas internas de baixo risco onde um humano sempre revisa a saída, esquemas são planos e retry em falha de parse é manual. É insuficiente para escritas downstream automatizadas, campos financeiros ou pipelines de alto volume sem validação.
Should validation run client-side or server-side?
Sempre no servidor. Checagens de esquema no cliente melhoram UX em fluxos interativos; a fronteira autoritativa é o servidor antes de persistir. Nunca confiar em saída LLM validada no navegador para mutações.
How many repair attempts are reasonable?
Dois reparos após a tentativa inicial (três gerações totais) é default comum de produção para tarefas de extração. Ajustar contra SLO de latência e custo por documento. Registrar falhas terminais para expansão de eval.
Does streaming require a different validation strategy?
Sim. Separar streaming de exibição de validação de commit. Fazer commit só após validação de objeto completo passar. Documentar comportamento de UX quando stream completa mas validação falha — mostrar erro, oferecer retry, nunca salvamento parcial.
A common argument runs the other way
A visão oposta sustenta que esquemas estritos e loops de reparo adicionam complexidade desnecessária — modelos frontier com JSON mode são confiáveis o suficiente para parsear uma vez e seguir.
Confiabilidade depende da tarefa e da cauda. JSON em caso mediano de modelos frontier atuais parece excelente em demos. Tráfego de produção inclui PDFs escaneados, tabelas ambíguas, cabeçalhos multilíngues e inputs adversariais. Taxa de passagem de esquema em conjunto demo de 50 exemplos não prediz comportamento p99 em dez mil faturas diárias. Loops de reparo e validação custam milissegundos e centavos; IDs de fornecedor alucinados custam horas de reconciliação e risco de auditoria. A complexidade é proporcional ao custo de errar — por isso pipelines automatizados nunca pulam a camada de validação mesmo quando o modelo "quase sempre funciona".
Key takeaways
- JSON mode garante sintaxe parseável, não campos corretos nem significado de negócio.
- Contratos de esquema na camada API mais validação no servidor formam a fronteira durável de produção.
- Loops de reparo com tentativas limitadas e feedback de erro acionável recuperam falhas de formato sem retry infinito.
- Regras de negócio acima da validação de esquema detectam extrações semanticamente válidas mas factualmente erradas.
- Conjuntos de eval devem pontuar precisão em nível de campo e alucinação — não apenas taxa de passagem de esquema.
- Fazer stream para UX; validar e fazer commit só em objetos completos e validados.
- Taxonomia de observabilidade (caminho de esquema, tentativa de reparo, versão de modelo) transforma falhas em melhorias de eval.
Conclusion
Saídas estruturadas transformam modelos de linguagem em componentes que sistemas downstream podem consumir — mas só quando garantias de sintaxe se sobrepõem com validação, reparo limitado e correção definida por eval. JSON mode e recursos de esquema do provedor reduzem a taxa de falha na primeira passagem; não eliminam a obrigação de rejeitar objetos ruins antes de chegarem ao banco.
O checklist de produção: response schema habilitado, validador no servidor definido, loop de reparo limitado, regras de negócio documentadas, conjunto de eval cobre forma e semântica, saída bruta registrada para auditoria. Itens faltantes mapeiam para categorias de incidente — campos inventados, coerção silenciosa de tipos, tempestades de retry sem limite — que a arquitetura de saída estruturada existe para prevenir.