Um assistente responde uma consulta jurídica ou técnica e anexa as fontes: para cada afirmação, a seção do documento e o trecho que a sustenta. O texto fica convincente. Só que algumas dessas citações estão erradas — e as erradas se dividem em dois tipos bem diferentes.
O primeiro tipo é a citação inexistente: o trecho não está no documento. O segundo é pior: o trecho está lá, palavra por palavra, mas o contexto dele diz o contrário da afirmação. Verificar isso à mão significa achar o documento, achar o trecho e ler o entorno. Numa resposta com oito citações, é meia hora.
Por que a solução ingênua falha
A tentação é pedir ao próprio LLM que confira as próprias citações. Isso tem um problema óbvio — quem errou está conferindo — e um menos óbvio: a resposta vem em texto, e alguém tem de interpretar “parece correta” para decidir se publica.
A segunda tentação é resolver tudo com Ctrl+F. Metade funciona: se o trecho não
está no documento, a citação é inventada, e nenhum modelo é necessário para
descobrir. A outra metade escapa inteira, porque a busca textual não sabe ler
contexto. Citação literal com contexto contrário passa no Ctrl+F com nota
máxima.
O desenho das perguntas
A receita separa o que é código do que é julgamento.
Etapa 1, em código. Normalize espaços e aspas curvas para o trecho casar mesmo com a quebra de linha do documento, e procure como substring. Se não achar, o veredicto é fabricada — sem chamada de API. Se achar, você ganha de brinde a seção de onde o trecho veio, que é o texto que a etapa 2 vai ler.
Etapa 2, uma pergunta. Um Choice com três opções cobre as três relações possíveis entre uma seção e uma afirmação:
from typesafe_sdk import Choice, TypeSafeClient
PERGUNTAS = {
"relacao": Choice(
instructions="Como a seção se relaciona com a afirmação?",
criteria={
"sustenta": "A seção afirma o que a afirmação diz, ou implica diretamente que é verdadeira",
"contradiz": "A seção afirma o oposto da afirmação, ou implica que ela é falsa",
"nada_diz": "A seção não trata do que a afirmação sustenta, nem a favor nem contra",
},
),
}
ACEITE_AUTOMATICO = 0.8
with TypeSafeClient() as client:
resposta = client.system_one(
state={"afirmacao": afirmacao, "secao": secao},
questions=PERGUNTAS,
)
relacao = resposta.answers["relacao"]
veredicto = {"sustenta": "verificada", "contradiz": "contradita", "nada_diz": "sem_suporte"}[relacao.choice]
automatico = relacao.confidence >= ACEITE_AUTOMATICO
Três decisões de projeto valem o registro. A opção nada_diz é a válvula de
escape que evita forçar a citação em “sustenta” ou “contradiz” quando a seção
simplesmente não trata do assunto. A confiança da própria resposta decide o que
vai para revisão humana, sem limiar inventado por fora. E o state carrega só a
afirmação e a seção — não o documento inteiro —, o que mantém a entrada enxuta.
O que o experimento oficial mediu
O cookbook usa a RFC 7519 (JSON Web Token) como fonte: 58.365 caracteres, 45
seções numeradas, em inglês. As oito citações foram escritas por um LLM contra
essa RFC; quatro são exatas e quatro foram alteradas de propósito para falhar. Os
números publicados pela TypeSafe, medidos no jev-1.12 em 16 de agosto de 2026:
| Citação | Etapa 1 | Relação | Confiança | Veredicto | Ação |
|---|---|---|---|---|---|
| Segundos de época | achada | sustenta | 0,93 | verificada | automática |
| Rejeição por audiência | achada | sustenta | 0,95 | verificada | automática |
| Relato de assinatura | não achada | — | — | fabricada | automática |
| Tolerância de relógio | achada | sustenta | 0,99 | verificada | automática |
| Campo obrigatório | achada | contradiz | 0,99 | contradita | automática |
| Criptografia de dados pessoais | achada | nada diz | 0,27 | sem suporte | revisão |
| Data de emissão futura | só seção | nada diz | 0,56 | sem suporte | revisão |
| Nomes duplicados | achada | sustenta | 0,99 | verificada | automática |
As quatro corretas voltaram verificadas com confiança alta, todas acima do limiar de 0,80. A fabricada nunca chegou ao modelo. A mais interessante é a contradita: o trecho está na RFC palavra por palavra, e a mesma seção diz que o uso daquele campo é opcional — então a afirmação de que seria obrigatório é falsa, com confiança 0,99.
E as duas que foram para revisão mostram o mecanismo funcionando na direção certa: 0,27 e 0,56 são o modelo dizendo que não tem certeza. Uma delas é o caso que justifica a receita inteira — o trecho está no documento palavra por palavra, e a seção de onde ele veio não trata da afirmação.
O próprio cookbook registra o limite da etapa 1: a comparação é exata depois de normalizar, então citação truncada ou levemente reescrita volta como fabricada. Quem aceita citação relaxada precisa de comparação aproximada no lugar.
O que muda em português
O documento do experimento é uma RFC em inglês. Em português, a receita encaixa bem em dois casos brasileiros comuns: conferir citação de norma (a seção citada sustenta mesmo a interpretação?) e conferir resumo de contrato contra o contrato.
Dois ajustes práticos. A etapa 1 precisa lidar com a nossa pontuação: normalize aspas curvas, travessões e espaços não separáveis, que aparecem muito em PDF de documento oficial. E a segmentação por seção precisa entender numeração de cláusula (“7.2.1”, “Parágrafo único”) em vez da numeração de RFC. Como a TypeSafe declara que o inglês é a língua principal de treino, teste a etapa 2 com as suas cláusulas antes de baixar o limiar de 0,80.
Onde isso quebra
- Leitura literal. A afirmação e a seção são lidas ao pé da letra. Afirmação com dupla negativa (“não é verdade que o campo não é obrigatório”) custa acurácia, como descrito nos limites do jev-1.13.
- Seção grande. Se a seção citada tem páginas, o state cresce e a acurácia cai. Corte para o entorno do trecho.
- Datas e números na afirmação. “A norma exige notificação em até 72 horas” mistura leitura com comparação numérica. Extraia o número com a receita de extrair valores e compare no código.
- Documento que muda. A verificação vale para a versão que você leu. Guarde qual revisão do documento foi usada, junto com a probabilidade e a confiança.
Se as citações vêm de um pipeline de RAG, a triagem anterior está em classificar passagens de RAG. O índice completo está em receitas.
Perguntas frequentes
Por que duas etapas em vez de uma?
Porque cada etapa resolve um erro diferente e a primeira é grátis. Comparação de texto acha a citação que não existe no documento, sem chamar modelo nenhum. O que sobra é citação literal, e aí a dúvida passa a ser se o contexto sustenta a afirmação — isso é julgamento.
Quais veredictos a receita produz?
Quatro: verificada, contradita, sem suporte e fabricada. Os três primeiros vêm da pergunta Choice sobre o contexto; o último vem da comparação de texto, sem custo de API.
O que o experimento oficial mediu?
Sobre oito citações geradas por um LLM contra a RFC 7519, quatro exatas e quatro alteradas de propósito: as quatro corretas voltaram verificadas com confiança de 0,93 a 0,99, a fabricada foi pega pela comparação de texto, uma voltou contradita com 0,99 e duas foram para revisão com 0,27 e 0,56.
Por que uma citação literal pode ser contradita?
Porque a frase citada pode existir palavra por palavra e a seção dizer o contrário do que a afirmação sustenta. No experimento, uma citação literal foi marcada contradita com confiança 0,99 porque a mesma seção diz que o uso do campo é opcional.
A comparação de texto tolera citação aproximada?
Não. O cookbook usa correspondência exata depois de normalizar espaços e aspas curvas, então citação truncada ou levemente reescrita volta como fabricada. Um sistema de produção que aceita citação relaxada precisa de comparação aproximada.