Ontem descobri que metade de um módulo do meu app estava baseado em dados inventados. Não por um júnior distraído. Pela minha IA.
O pior não é que tenha inventado. O pior é que tudo compilava e os 90 testes passaram.
A ficção coerente
Estou construindo BFClaude-9000, um app de barra de menu para macOS que monitora a cota do Claude Max. Parte da funcionalidade requer distinguir se uma conta do Claude é paga ou gratuita, chamando a API do claude.ai.
Pedi ao Claude Code que implementasse a detecção. Ele fez. Me entregou:
- Um DTO
OrganizationInfocom um campoactiveFlags: [String] - Uma propriedade computada
isPaidque verifica seactiveFlagsnão está vazio - Um enum
OrganizationSelectionque classifica as organizações em pagas e gratuitas - Testes com fixtures que verificam que tudo funciona
Bonito. Limpo. Bem estruturado. Tudo inventado.
O campo active_flags não existe na API real do Claude. Ou se existe, não funciona como o código assumia. Quando fiz login com minha conta paga, o app me disse que minha conta era gratuita.
O padrão do castelo de cartas
O insidioso não é que mentiu sobre um campo da API. É o sistema completo que construiu ao redor dessa mentira:
| |
Você vê? Não é um campo mal colocado. É um castelo de cartas: o DTO define um campo falso, a lógica depende desse campo, os testes validam que a lógica funciona com fixtures que também são falsas. Cada peça confirma as outras. Tudo se encaixa. Nada é real.
IEEE Spectrum tem um nome para isso: silent failure. O código não crasha, não lança erros, não acende alarmes. Simplesmente faz o incorreto em silêncio.
Não é um caso isolado
Acontece que a comunidade já tem nome para quando um LLM inventa pacotes e dependências: package hallucination. Um estudo da Snyk encontrou que entre 5% e 20% das recomendações de pacotes dos principais LLMs são inventadas. Pacotes que não existem, publicados.
Mas o caso dos pacotes é o caso fácil. Você executa npm install pacote-inventado, falha, você fica sabendo. Um campo inventado em um DTO que faz parse de JSON com try? e degradação graciosa… isso não falha. Funciona. Retorna nil ou um array vazio. E seu código segue adiante, operando sobre dados fantasma.
A própria Anthropic, em sua documentação sobre redução de alucinações, diz sem rodeios:
“Claude can sometimes generate responses that contain fabricated information… presented in a confident, authoritative manner.”
Apresentado de forma “autoritativa”. Essa é a chave. Não é que duvida e se equivoca. É que afirma com total segurança algo que acabou de inventar.
Por que os testes não te salvam
Aqui é onde dói. Eu tinha testes. Bons testes. 90 testes em 12 suítes. Todos no verde. E daí?
O problema é que os testes validam consistência interna, não correspondência com a realidade. Se o DTO diz que o campo se chama active_flags, o fixture tem um active_flags, e o teste verifica que o DTO faz parse do fixture… tudo passa. Ficção contra ficção. Verde fosforescente.
É como se um estudante inventasse uma fórmula de física, escrevesse um exame baseado nessa fórmula, e se desse nota 10. Cada passo é internamente coerente. O resultado não tem nenhum contato com a realidade.
Realidade: campo X não existe na API
↓ (invisível)
DTO: define campo X ← inventado
Fixture: inclui campo X ← inventado para validar o DTO
Teste: fixture faz parse bem ← valida invenção contra invenção
Resultado: ✅ Tudo verde ← ficção coerente
Não há nenhum ponto nesta cadeia onde se verifica contra a API real. E este é o buraco.
Todas as medidas atuais são preventivas
Se você busca o que pode fazer para evitar isso, a literatura e a experiência oferecem uma lista de medidas. Todas são preventivas:
| Medida | Tipo | Problema |
|---|---|---|
| Instruções no CLAUDE.md: “não invente” | Preventiva | Executadas pelo mesmo agente que mente |
| Chain of thought: “cite suas fontes” | Preventiva | Pode citar fontes inventadas |
| Temperatura baixa | Preventiva | Reduz criatividade, não elimina invenção |
| Grounding com documentos | Preventiva | Só se você tem o documento correto |
| Proibições explícitas | Preventiva | O LLM pode “racionalizar” exceções |
| RAG (Retrieval Augmented Generation) | Preventiva | Depende de que a base de dados seja completa |
Nota o padrão? Todas tentam evitar que a IA invente. Nenhuma detecta quando já inventou.
É como colocar uma placa de “proibido roubar” numa loja sem câmeras, sem alarmes e sem segurança. Pode funcionar. Pode não funcionar. Você não tem como saber até contar o caixa.
O que falta: detecção reativa
O que precisamos e hoje não existe são medidas reativas: sistemas que detectem a invenção depois que ocorre, idealmente antes que chegue à produção.
Imagine:
Contract testing contra APIs reais: um teste que chame a API de verdade (com credenciais de teste) e compare o schema real com o DTO. Se o DTO tem campos que a API não retorna, alerta.
Fixture validation: um linter que verifique que os fixtures de teste correspondem a dados reais capturados, não a dados escritos à mão (ou gerados pela IA). Algo como snapshot testing mas contra respostas reais de produção.
Smoke tests com dados reais: antes de mergear, um passo de CI que execute as chamadas contra um sandbox da API e verifique que os DTOs fazem parse de dados reais sem perda silenciosa.
Anomaly detection em parsing: se um campo opcional retorna
nil100% das vezes em produção, algo cheira mal. Um monitor que detecte campos que sempre são nil e os reporte como suspeitos de serem inventados.Diff semântico pós-geração: um segundo modelo (ou o mesmo com um prompt diferente) que revise o código gerado e sinalize campos ou estruturas que não consegue verificar contra documentação conhecida.
Nada disso existe hoje como produto. Algumas equipes implementam partes manualmente (contract testing é uma prática conhecida, por exemplo). Mas não há um HallucinationTracker que você conecte ao seu CI e te diga “ei, esse campo active_flags não aparece em nenhuma documentação nem resposta real da API”.
E sim, há um paper da Universidade de Washington (HallucinationTracker) que propõe métricas para detectar confabulações. Mas está em fase de pesquisa, não é algo que você possa brew install.
O problema de fundo
O problema de fundo é profundamente desconfortável: as regras são executadas pelo mesmo sistema que as viola.
Quando você coloca “não invente dados” no seu CLAUDE.md, está dizendo para o mesmo modelo que vai inventar dados. É como pedir ao acusado que seja também o juiz. Pode funcionar, mas você não tem garantias.
As medidas preventivas (boas instruções, temperatura baixa, grounding) reduzem a probabilidade de invenção. Mas não a eliminam. E quando ocorre, não há sirene que toque.
O que precisamos é que a detecção seja feita por algo externo ao modelo: um teste contra dados reais, um linter de schemas, um monitor em produção. Algo que o modelo não possa racionalizar nem esquivar, porque não é o modelo quem executa.
Até que isso exista como algo maduro e fácil de usar, estamos na mesma situação que a segurança informática antes dos firewalls: sabemos que há um problema, temos medidas parciais, e confiamos que “comigo não vai acontecer”.
O que eu faço enquanto isso
Sendo honesto, estas são as medidas que funcionam para mim hoje. Nenhuma é perfeita:
Ler o código gerado como se fosse de um desconhecido. Não assumir que está correto porque compila. Isso é exaustivo, mas é o que há.
Perguntar “de onde você tirou isso?” Especialmente para campos de API, nomes de pacotes, e qualquer dado que não posso verificar olhando o código.
Contract tests manuais. Antes de dar como boa um DTO, fazer uma chamada real à API e comparar. É tedioso. É necessário.
Desconfiar de testes que passam de primeira. Se a IA gera código e testes e tudo passa de primeira, isso não é bom sinal — é sinal de que provavelmente validou ficção contra ficção.
Capturar respostas reais como fixtures. Em vez de deixar a IA escrever os fixtures, salvar as respostas reais da API e usá-las como fixture. Se o DTO não faz parse da resposta real, quebra imediatamente.
Estas medidas são manuais, lentas, e dependem da minha disciplina. Não escalam. Mas hoje são o melhor que tenho.
O que deveria existir amanhã
Se alguém está buscando um problema real para resolver, aqui há um:
Um sistema de verificação pós-geração que seja externo ao modelo, automático, e que se integre em CI/CD.
Não precisa ser perfeito. Precisa existir. Que alguém construa o equivalente a um linter para alucinações: algo que analise o código gerado, o cruze com fontes verificáveis (documentação de APIs, schemas OpenAPI, respostas capturadas), e sinalize o que não bate.
Hoje, se sua IA inventa um campo de API e o envolve em testes coerentes, a única defesa é você lendo o código com olho clínico. Amanhã, deveria haver uma máquina que fizesse isso por você.
Mas hoje ela não existe. E isso é o mais preocupante de tudo.
Relacionado: Este post é o terceiro capítulo de uma série involuntária. Primeiro foi os 44 emails inventados (a IA que age sem permissão). Depois MEMORY.md (a IA que esquece o que aprendeu). Agora, a IA que inventa dados e os envolve numa ficção que passa nos testes. Três falhas diferentes, um denominador comum: confiamos demais num sistema que não entende o que faz.