Desenvolvedores agora podem manter o custo de correção de JSON malformado proveniente de modelos de linguagem de grande escala (LLMs) sob controle, limitando as tentativas de reparo a duas, como mostra um simples loop em Python. O padrão — gerar, analisar, validar, tentar novamente, e então aceitar ou falhar — transforma um "JSON possivelmente válido" em uma taxa de sucesso mensurável e evita o uso desenfreado de tokens.
Por que um loop de reparo é importante
LLMs são frequentemente instruídos a "retornar JSON", mas a saída frequentemente contém erros de sintaxe, chaves ausentes ou valores que quebram regras de negócio. Um sistema subsequente que espera um payload estrito irá falhar ou produzir resultados incorretos se receber tal saída. Sem uma maneira sistemática de capturar e corrigir esses problemas, as equipes acabam aceitando uma alta taxa de falha ou desperdiçando dinheiro solicitando o modelo repetidamente até que ele acerte.
As três camadas de validação
Um loop confiável separa a validação em:
- Sintaxe – a string pode ser analisada como JSON?
- Estrutura – o objeto de nível superior contém exatamente as chaves esperadas?
- Semântica – os valores respeitam as restrições de domínio (por exemplo, uma "categoria" deve pertencer a um conjunto predefinido)?
Ao parar na primeira camada que falhar, o loop pode fornecer ao modelo uma mensagem de erro precisa, o que aumenta drasticamente as chances de uma correção correta na próxima tentativa.
Exemplo mínimo em Python
O código a seguir utiliza apenas a biblioteca padrão. Ele define um esquema minúsculo (um dicionário com as chaves category e summary) e uma lista fixa de categorias permitidas.
import json
CATEGORIES = {"bug", "feature", "question"}
def validate(raw: str):
try:
value = json.loads(raw)
except json.JSONDecodeError as exc:
return None, f"syntax: {exc.msg}"
if not isinstance(value, dict):
return None, "shape: root must be an object"
if set(value) != {"category", "summary"}:
return None, "shape: expected category and summary"
if value["category"] not in CATEGORIES:
return None, "semantic: invalid category"
if not isinstance(value["summary"], str) or not value["summary"].strip():
return None, "semantic: summary must be text"
return value, None
O próprio loop de reparo impõe um teto rígido de tentativas:
MAX_ATTEMPTS = 2
raw = model_response # initial LLM output
for attempt in range(MAX_ATTEMPTS + 1):
value, error = validate(raw)
if error is None:
break
if attempt == MAX_ATTEMPTS:
raise RuntimeError(f"failed after {MAX_ATTEMPTS} retries: {error}")
# Send the error and schema back to the model for a fix
raw = ask_model_to_repair(raw, error)
Se o JSON passar na validação na primeira tentativa, o loop encerra imediatamente. Caso contrário, ele tenta novamente, enviando ao modelo um payload conciso: a saída original, a string de erro exata e o esquema esperado. Após duas tentativas fracassadas, o processo é abortado com uma exceção clara.
Duas regras para reparos econômicos
Mantenha os prompts de reparo concisos – Inclua apenas o JSON bruto, a mensagem de erro e o esquema. Adicionar um histórico de chat longo infla o uso de tokens e pode confundir o modelo, aumentando o custo por tentativa sem melhorar a precisão.
Separe a validação da verificação de fatos – O parser pode detectar problemas estruturais, mas não pode verificar se uma afirmação de "summary" é verdadeira. A verificação de fatos pertence a uma etapa posterior; tentar "reparar" uma afirmação falsa apenas faz com que a mentira pareça mais limpa.
Medindo o sucesso
Registrar o tipo de erro de cada tentativa (sintaxe, estrutura, semântica) e se o loop teve sucesso fornece uma métrica concreta: a proporção de respostas de LLM que se tornam utilizáveis dentro das tentativas limitadas. As equipes podem acompanhar isso ao longo do tempo, comparar estilos de prompt ou experimentar diferentes definições de esquema.
Possíveis desvantagens
Limitar o loop significa que algumas saídas malformadas serão descartadas após o limite ser atingido, o que pode aumentar a taxa de falha geral se a qualidade bruta do modelo for baixa. Em ambientes onde cada resposta é crítica, os desenvolvedores podem preferir um teto de tentativas mais alto, às custas de tokens extras. O equilíbrio é explícito: mais custo para maior cobertura, ou orçamentos mais apertados com um teto previsível.
O que observar a seguir
- Engenharia de prompt para reparo – Refinar o formato da mensagem de erro pode reduzir o número de tentativas necessárias.
- Parsers alternativos – Algumas bibliotecas oferecem um parsing tolerante que pode autocorrigir problemas menores; comparar o custo delas versus um loop limitado vale a pena.
- Atualizações de modelos – Versões mais novas de LLMs podem produzir JSON mais limpo nativamente, potencialmente permitindo que o limite de tentativas seja reduzido ainda mais.
Um loop de reparo de JSON limitado oferece aos desenvolvedores uma ferramenta prática para transformar saídas imprecisas de LLM em dados confiáveis sem custos crescentes. Ao tratar a validação como uma etapa de primeira classe e limitar as tentativas, as equipes podem manter tanto os orçamentos quanto os sistemas subsequentes satisfeitos.
