Por Gustavo Paixão
Verificando o trabalho da IA com um agente que não o fez
Antes de eu marcar uma tarefa como concluída, um segundo agente a confere com o código real. A ideia veio de um artigo científico sobre como detectar IA enganosa, com um método que eu não consigo rodar. O que tirei dele, o que o agente encontrou e quanto custa.
O problema
Quando um agente de IA termina uma tarefa, ele me diz o que fez. Na maioria das vezes, isso está certo. Às vezes, não: um teste que não cobre o que diz cobrir, um caso que ninguém testou, um plano que já não bate com o código. No Ronne AI Marketplace, 143 das 1.522 afirmações que o agente de implementação fez sobre o próprio trabalho se revelaram verdadeiras só em parte, ou nem isso.
Então, antes de eu marcar uma tarefa como concluída, outro agente a confere. Ele não escreve o código, não o corrige e não acredita na palavra de ninguém: lê os arquivos e roda os comandos ele mesmo. Chamo esse agente de testemunha de estado (state witness), em referência ao artigo de onde veio a ideia. O registro da testemunha vai num WITNESS.md ao lado da especificação e do plano da funcionalidade, no mesmo commit da tarefa, e uma verificação no hook de pre-commit e na CI recusa uma tarefa marcada, a menos que a última rodada da testemunha tenha confirmado todas as afirmações.
Eu escrevo a especificação e marco quais tarefas são de risco; o agente de implementação inicia a testemunha e cola o registro dela no arquivo.
De onde veio a ideia
Ouvi falar do StateWitness pela primeira vez num Reel do Instagram do Vinicius Pasquantonio (@pasquadev). É um artigo científico (Chen et al., 2026) sobre como detectar engano em modelos de raciocínio. Um segundo modelo lê as ativações do modelo auditado (os estados ocultos dele) e responde a perguntas como "esta resposta esconde alguma coisa?". Em sete benchmarks de engano, o método chegou a um AUROCArea Under the Receiver Operating Characteristic Curve (área sob a curva ROC)Em IA e aprendizado de máquina, uma métrica de quão bem um modelo de classificação distingue duas classes, como:Commits com bug vs. limposTestes flaky vs. confiáveisPacotes maliciosos vs. segurosCódigo vulnerável vs. seguroRespostas de IA enganosas vs. honestas, como neste artigo médio de 0,916, contra 0,821 do melhor juiz que só lê o texto.
Eu não consigo rodar isso. O método só funciona num modelo cujas ativações você consiga ler:
Modelos de pesos abertos
- Exemplos
- gpt-oss-20b e Gemma, os dois do artigo
- O que você recebe
- Os pesos. Você mesmo roda o modelo, então consegue ler as ativações dele.
- Uma testemunha completa
- Possível, com um decodificador treinado para aquele modelo exato e GPUs: os autores relatam cerca de 190 horas de GPU A100 de treinamento.
Modelos comerciais
- Exemplos
- GPT, Gemini e Claude, o modelo por trás do Claude Code
- O que você recebe
- Texto, por meio de um app ou de uma API.
- Uma testemunha completa
- Não de fora. Só a empresa que roda o modelo vê as ativações dele.
As mesmas empresas fazem os dois tipos: o gpt-oss e o GPT são da OpenAI, o Gemma e o Gemini, do Google. Com o Claude Code, o leitor de ativações fica fora de alcance, e o que sobra são as ideias que funcionam com texto.
O artigo também responde a uma pergunta diferente. Ele pergunta se um modelo está enganando, e eu preciso saber se uma tarefa está concluída.
O que eu podia usar eram três descobertas sobre conferir o trabalho alheio, mais uma regra minha.
O que tirei dele
| Descoberta | No artigo | Na testemunha |
|---|---|---|
| A narrativa ancora o verificador | Um juiz de texto se saiu pior quando também leu o raciocínio do modelo: 0,789 contra 0,809 no gpt-oss-20b | Ela começa às cegas: o Done when da tarefa (a linha do plano que diz o que conta como concluído), as linhas da especificação e onde procurar, nunca as anotações do implementador |
| Dois verificadores diferentes deixam passar menos que um | Combinar o leitor de ativações com o juiz de texto elevou o recall, a uma taxa de 1% de falsos alarmes, de 0,430 para 0,562 | Tarefas de risco (login, papéis, migrações, exclusão de dados, qualquer coisa que rode como root) passam por uma segunda rodada, cuja única instrução é quebrar as afirmações |
| Um verificador pode parecer seguro com evidências fracas | Os autores dizem isso na seção de limitações | Toda afirmação precisa de evidência: o comando e o que ele mostrou, ou um arquivo e uma linha. Toda rodada indica o commit que conferiu |
A primeira descoberta é a mais fraca das três. No outro modelo do artigo, o Gemma, o juiz se saiu um pouco melhor com o raciocínio.
| Só a resposta final | Resposta e raciocínio | |
|---|---|---|
| gpt-oss-20b | 0,809 | 0,789 |
| Gemma 4 26B | 0,826 | 0,829 |
AUROC dos resultados principais do artigo: 1,0 detecta toda resposta enganosa sem nenhum falso alarme, 0,5 é o acaso. As barras começam no zero.
Mantive o começo às cegas mesmo assim. Não custa nada, e os meus próprios números, mais abaixo, apontam na mesma direção.
A regra que é minha, não do artigo: depois dos vereditos, a testemunha lê as anotações do implementador e marca quais afirmações elas fizeram. Uma afirmação reprovada que estava nas anotações é um erro do implementador. São essas as 143 do começo.
Como funciona uma rodada
Um agente novo recebe o Done when da tarefa, as linhas da especificação que ela cobre, onde procurar e o commit. Ele transforma isso em afirmações separadas, uma por linha, e confere cada uma lendo o código e rodando ele mesmo os testes, os builds e pequenas sondagens. Testes verdes, por si sós, não contam: ele verifica se um teste cobre a afirmação e, quando pode, quebra o código numa cópia descartável para ver o teste falhar.
Cada veredito é um entre quatro: confirmado, em parte, não atendido ou não dá para verificar aqui (precisa de Windows, do GitHub ou de um release). Estas linhas vêm de uma rodada adversarial real sobre uma mudança no resolvedor de dependências:
| # | Afirmação | Nas anotações? | Veredito | Evidência |
|---|---|---|---|---|
| 30 | A suíte principal passa, com o teste de muitos solicitantes | não | confirmado | 350 passaram |
| 31 | A mudança passa no pnpm lint | não | não atendido | noAssignInExpressions em resolve.ts:224 |
| 32 | O orçamento conta as verificações de intervalo, então muitos solicitantes não conseguem prolongar a execução | não | confirmado | N=100/300/500 → 454 / 1159 / 4391 ms |
| 33 | O novo teste falha quando as verificações contam +1 por versão | não | confirmado | → 11.543 ms, falha |
Os testes estavam verdes. O lint, não.
O que ela encontrou
Todos os números deste texto vêm dos registros como estavam no repositório em 11 de outubro às 22h21 (horário do Leste) (commit 149e14a (abre em uma nova aba)), oito dias depois do início da testemunha, e das transcrições do Claude Code até esse commit. O repositório é público, então os números dos registros podem ser recontados.
Nesses oito dias, a testemunha fez 408 rodadas sobre 3.490 afirmações: 3.122 confirmadas, 268 em parte, 89 não atendidas e 8 que ela não podia verificar ali (3 linhas antigas trazem um veredito que o formato já não permite). As 1.522 afirmações do começo são as que, entre as 3.490, as anotações do implementador tinham feito. As 408 rodadas vieram de 251 execuções da testemunha: uma reverificação em geral volta para o agente que fez a rodada, com as correções, em vez de ir para um agente novo.
Em parte é a falha mais comum, e cobre uma faixa ampla. No registro: um teste que existe, mas não verifica o campo de que a afirmação trata; um teste de condição de corrida que é flaky no SQLite e falha uma vez a cada doze execuções; uma seção da documentação numerada como "quarta" quando era a terceira. Algumas dessas são erros do implementador e outras são rigor da testemunha, e o registro não diz quais.
A primeira versão entregava à testemunha as anotações do implementador junto com a tarefa. Depois de ler as primeiras 55 rodadas, sobre 42 tarefas, mudei a regra para ela começar às cegas, e a proporção de tarefas cuja primeira rodada deixou pelo menos uma afirmação sem confirmar subiu muito.
| Tarefas | |
|---|---|
| Testemunha com as anotações (primeiras 42 tarefas) | 14% (6 de 42) |
| Testemunha começando às cegas (125 tarefas seguintes) | 58% (73 de 125) |
Um antes e depois, não um teste controlado: as funcionalidades seguintes, espaços de trabalho com papéis e permissões, também eram mais difíceis. Rodar as mesmas tarefas dos dois jeitos ainda está na minha lista.
A segunda rodada também justificou o seu lugar. Em 11 das 38 tarefas de risco, a rodada adversarial encontrou problemas depois que a rodada às cegas tinha dado tudo como atendido. Em termos gerais: informação vazando entre espaços de trabalho, um cache compartilhado entre administradores que deveria ser separado, e deadlocks em alguns servidores de banco de dados quando duas mudanças chegavam ao mesmo tempo. Cada problema foi corrigido antes de a tarefa dele ser marcada como concluída.
Não é rápido. Uma tarefa levou, em média, 2,4 rodadas, e a pior levou 17.
Quanto custa
Uso o Claude no Max plan, então a testemunha não aumenta nenhuma conta. Ela consome o plano: nesses oito dias, 23% de todo o uso do Claude no projeto foi para a testemunha. Isso é medido nas mesmas transcrições e aos mesmos preços: os tokens da testemunha contra tudo o que o Claude Code registrou para o projeto. Ela também leva tempo. Uma execução da testemunha leva 5 minutos na mediana, e o décimo mais lento, 29 minutos ou mais: 50 horas de agente no total. Uma tarefa não pode entrar num commit até a rodada dela voltar. As correções que ela provoca são trabalho do implementador e não entram nesta conta; eu as considero o objetivo do exercício, não o custo dele.
Se você paga por token, isto é o que as mesmas execuções custariam pelos preços da API do Claude Opus 5.5:
| Tipo de execução | Execuções | Custo | Por execução |
|---|---|---|---|
| Às cegas | 173 | US$ 186 | US$ 1,08 |
| Adversarial | 39 | US$ 107 | US$ 2,75 |
| Reverificação por um agente novo | 39 | US$ 26 | US$ 0,65 |
| Todas | 251 | US$ 319 |
O custo de uma execução inclui as reverificações que o mesmo agente fez em seguida. Uma tarefa de risco recebeu a reverificação adversarial de um agente novo, e não do mesmo, e é por isso que há 39 execuções adversariais para 38 tarefas de risco.
Como fiz o cálculo
Estes são os tokens que as transcrições do Claude Code registraram, com os preços do Claude Opus 5.5 na página de preços da Anthropic:
| Tipo de execução | Leituras de cache | Gravações de cache | Entrada | Saída (estimada) |
|---|---|---|---|---|
| Às cegas | 266,7M | 16,86M | 0,01M | 2,44M |
| Adversarial | 176,8M | 8,97M | 0,004M | 1,35M |
| Reverificação por um agente novo | 36,5M | 2,32M | 0,001M | 0,33M |
| Preço por milhão | US$ 0,20 | US$ 5 | US$ 4 | US$ 20 |
Multiplique cada coluna pelo preço dela e some tudo. Para as execuções às cegas:
266,7M de leituras de cache × US$ 0,20 + 16,86M de gravações de cache × US$ 5 + 0,01M de entrada × US$ 4 + 2,44M de saída × US$ 20 = US$ 186.
- As leituras de cache são a maior parte dos tokens, porque quase tudo o que um agente lê vem do cache. A US$ 0,20 por milhão, elas ainda custam menos que as gravações.
- As gravações de cache estão cotadas pelo cache de 5 minutos, US$ 5 por milhão em vez dos US$ 8 do cache de uma hora: as transcrições registram qual duração cada gravação comprou, e nenhuma das da testemunha usou a mais longa.
- A saída é uma estimativa. As transcrições não a registram para os agentes verificadores, então usei a taxa de tokens por caractere das minhas sessões principais, que a registram.
- As transcrições vão de 4 de outubro, o primeiro dia da testemunha, até o commit acima.
Valeu a pena?
Em todas as 38 tarefas de risco, as execuções adversariais custaram cerca de US$ 107 em preços de API e encontraram 51 afirmações que não se sustentavam e 104 que se sustentavam só em parte, entre elas os vazamentos entre espaços de trabalho. Para um software cuja função inclui manter o trabalho de um time longe do de outro, um único vazamento em produção custaria muito mais do que isso.
O que eu ainda não consigo medir é o que a testemunha deixa passar, ou com que frequência ela erra. O meu processo pede um teste a cada marco: plantar um bug que a tarefa deveria pegar e ver se uma rodada nova o encontra. Ainda não o rodei. É o próximo passo.
O que eu mudaria
- Um segundo verificador que seja diferente de verdade. O ganho do artigo veio de dois monitores lendo sinais diferentes. O meu lê o mesmo sinal duas vezes: o mesmo modelo, as mesmas instruções, um briefing diferente. Vou testar outro modelo na rodada adversarial, e também o teste de mutação, que altera um pouco o código e confere se os testes percebem.
- Rodadas adversariais além das tarefas que eu classifico como de risco. A rodada adversarial encontrou problemas em 11 de 38 tarefas de risco. Não sei a taxa no resto, e uma amostra aleatória me diria.
- Uma reverificação completa antes de marcar a tarefa. Hoje, uma reverificação pode cobrir só as afirmações que falharam, então as afirmações anteriores não são verificadas de novo no commit final.
- Um registro dos erros da própria testemunha. Nada numa rodada diz se um não atendido acabou se revelando um erro da testemunha, então não consigo informar a taxa de falsos alarmes dela. Bastaria uma coluna a mais.
- Gravidade em cada achado. Hoje, um deslize de redação e um vazamento de dados contam igual no registro.
As instruções da testemunha estão no repositório, com os motivos por trás de cada regra.
Leia as instruções da testemunha
Se você confere o trabalho dos seus agentes de outro jeito, eu gostaria de trocar experiências.