Ontem descobri que metade de um módulo da minha app estava baseado em dados inventados. Não por um júnior distraído. Pela minha IA.
O pior não é que inventou. O pior é que tudo compilava e os 90 testes passavam.
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 de claude.ai.
Pedi ao Claude Code que implementasse a detecção. Ele implementou. Me entregou:
- Um DTO
OrganizationInfocom um campoactiveFlags: [String] - Uma propriedade computada
isPaidque verifica seactiveFlagsnão está vazio - Um enum
OrganizationSelectionque classifica as orgs 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:
// DTO com campo inventado
struct OrganizationInfo: Decodable {
let uuid: String
let name: String
let activeFlags: [String] // ← Isso não existe
var isPaid: Bool { !activeFlags.isEmpty }
}
// Lógica que depende do campo inventado
enum OrganizationSelection {
case paid(id: String, name: String)
case noPaidOrg // ← Este estado não deveria existir
case noOrgs
}
// Testes com fixtures que validam a invenção
let paidOrg = """
{"uuid": "abc", "name": "Acme", "active_flags": ["pro"]}
"""
// Teste passa ✅ — mas valida ficção contra ficção
Está vendo? 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 trava, não lança erros, não acende alarmes. Simplesmente faz a coisa errada 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 descobriu 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ê roda npm install pacote-inventado, falha, você fica sabendo. Um campo inventado num DTO que faz parse de JSON com try? e degradação elegante… isso não falha. Funciona. Retorna nil ou um array vazio. E seu código continua, operando sobre dados fantasma.
A própria Anthropic, em sua documentação sobre reduzir alucinações, fala sem rodeios:
“Claude can sometimes generate responses that contain fabricated information… presented in a confident, authoritative manner.”
Apresentado de forma “autoritária”. Essa é a chave. Não é que duvida e se engana. É 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 verdes. 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, a fixture tem um active_flags, e o teste verifica que o DTO faz parse da fixture… tudo passa. Ficção contra ficção. Verde fosforescente.
É como se um estudante inventasse uma fórmula de física, escrevesse uma prova baseada nessa fórmula, e se desse nota 10. Cada passo é internamente coerente. O resultado não tem contato algum 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 nessa cadeia onde se verifica contra a API real. E este é o buraco.
Todas as medidas atuais são preventivas
Se você procurar o que pode fazer para evitar isso, a literatura e a experiência te oferecem uma lista de medidas. Todas são preventivas:
| Medida | Tipo | Problema |
|---|---|---|
| Instruções no CLAUDE.md: “não invente” | Preventiva | São 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ê tiver o documento correto |
| Proibições explícitas | Preventiva | O LLM pode “racionalizar” exceções |
| RAG (Retrieval Augmented Generation) | Preventiva | Depende de a base de dados ser completa |
Percebe o padrão? Todas tentam evitar que a IA invente. Nenhuma detecta quando ela já inventou.
É como colocar uma placa “proibido roubar” numa loja sem câmeras, sem alarmes e sem segurança. Pode funcionar. Pode não funcionar. Você só descobre quando conta 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 ela acontece, idealmente antes de chegar à 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, alarme.
Fixture validation: um linter que verifique se as 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 fazer merge, um passo de CI que execute as chamadas contra um sandbox da API e verifique se os DTOs fazem parse de dados reais sem perda silenciosa.
Anomaly detection no 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. Alguns times implementam peças manualmente (contract testing é uma prática conhecida, por exemplo). Mas não há um HallucinationTracker que você conecte ao seu CI e te diga “olha, esse campo active_flags não aparece em documentação alguma 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 isso ao 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 acontece, 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 contornar, 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 da informação 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, essas 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 tem.
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 por bom um DTO, fazer uma chamada real à API e comparar. É tedioso. É necessário.
Desconfiar dos 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 as fixtures, salvar as respostas reais da API e usá-las como fixture. Se o DTO não faz parse da resposta real, quebra imediatamente.
Essas 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á procurando um problema real para resolver, aqui está um:
Um sistema de verificação pós-geração que seja externo ao modelo, automático, e que se integre a 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, 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 não há. 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.
Este artigo foi escrito originalmente em espanhol e traduzido com a ajuda de IA.