Erro

O agente disse «pronto», mas a funcionalidade não funciona: por que acontece e como verificar o trabalho do agente de IA

Atualizado: · Skillaria.Top

O agente diz «pronto» porque avalia o próprio trabalho e enxerga apenas o que conseguiu checar, em geral testes unitários, que não veem as junções entre módulos, as migrations, a configuração e o cenário de ponta a ponta. A solução é estrutural, e não um prompt: a definição de pronto no AGENTS.md, a execução obrigatória do E2E, um script de verificação que sozinho pode marcar o passing e um avaliador independente com contexto limpo.

Por que isso acontece

Quem trabalha com agentes conhece a situação: o agente diz «pronto», mas a funcionalidade não funciona. Ele não mente de propósito. Ele reporta o que conseguiu verificar e acredita sinceramente que isso basta. A distância entre o relato e a realidade vem da própria arquitetura em que o executor avalia o próprio trabalho, e não de um defeito de um modelo específico.

Há três motivos. O primeiro é que modelos tendem à confiança excessiva: a certeza da resposta é maior que a sua precisão. O segundo é que o agente avalia o trabalho no mesmo contexto em que o fez: já se convenceu de que a solução é correta, e pedir «seja mais objetivo» não muda o contexto. O terceiro, e mais prático, é que a verificação que o agente consegue fazer costuma ser parcial. Os testes unitários ficam verdes rapidamente, enquanto o cenário de ponta a ponta ninguém rodou.

Um caso típico de «pronto» falso

O agente recebeu a tarefa de implementar a redefinição de senha. Ele altera o esquema do banco, acrescenta um endpoint, liga o template do e-mail, roda os testes unitários: tudo verde. Aparece no chat um «Pronto!» confiante.

Na prática, aconteceu o seguinte:

  • a migration falhou no meio e deixou o esquema inconsistente;
  • o template do e-mail existe, mas o serviço de e-mail (SMTP) não está configurado no ambiente, então nada é enviado;
  • o fluxo «o usuário clica no link → digita a nova senha → consegue entrar» nunca foi executado.

Nenhum desses problemas poderia ser pego pelos testes unitários: o banco foi substituído por mocks, o envio do e-mail também, e nenhuma execução de ponta a ponta aconteceu.

Por que testes verdes não provam que a funcionalidade funciona

Um teste unitário examina um módulo isolado: as dependências são trocadas por mocks e o mundo externo é desligado. Isso dá velocidade e precisão, mas também uma cegueira sistemática. Essa cegueira não é um defeito dos testes, e sim o seu desenho. Testes unitários verdes provam que cada peça funciona sozinha, e não que as peças funcionam juntas.

Há quatro classes de defeitos que um teste unitário não enxerga por princípio:

Classe do defeitoExemploPor que o teste unitário não pega
Incompatibilidade de interfacesUm componente devolve caminho relativo, outro espera caminho absolutoCada um é testado com um mock que aceita qualquer formato
Propagação de estadoO esquema do banco mudou, mas o cache do ORM ainda tem a versão antigaNo teste unitário não existem banco real nem cache
Ciclo de vida de recursosDescritores de arquivo e conexões não são liberados; a memória vaza com arquivos grandesO teste usa dados pequenos e termina antes que os recursos se esgotem
Ambiente e configuraçãoVariável de ambiente ausente, SMTP não configurado, migration não aplicadaO teste não sobe a aplicação com a configuração real

Os níveis de verificação são aninhados: tudo o que os testes unitários pegam também é pego pelos de integração, e tudo o que os de integração pegam também é pego pelo teste de ponta a ponta (E2E). Cada nível enxerga estritamente mais que o anterior. Por isso, o direito de dizer «pronto» deve ficar apenas com o nível mais alto.

Como medir o problema: verification gap

Antes de consertar, meça. Pegue as últimas 10 tarefas concluídas pelo agente. Para cada uma, responda a duas perguntas: o agente disse «pronto»? A verificação independente confirmou (você mesmo percorreu o cenário, ou o teste E2E ficou verde)? A proporção de discordância é o seu verification gap.

Esse número é útil por dois motivos. Ele mostra a dimensão do problema sem discussão do tipo «acho que funciona». E dá uma linha de base: depois de cada mudança no processo, recalcule-o em um conjunto parecido de tarefas e veja se melhorou.

A verificação de verdade: três camadas

Uma analogia útil é a oficina mecânica. O carro passa por três estações, e nenhuma pode ser pulada.

  1. Diagnóstico (segundos): sintaxe, tipos, linter. Pega erros de digitação e falhas grosseiras.
  2. Bancada (minutos): testes unitários e de integração, a aplicação sobe. O motor funciona, mas o carro ainda está parado.
  3. Test drive: o cenário de ponta a ponta, do jeito que o usuário faz, via API, navegador ou CLI, com banco e configuração reais.

A regra de ordem é: não se sai para o test drive enquanto o diagnóstico tiver erros, e não se entrega o carro sem ter passado pelo test drive. Passar nos testes unitários verdes significa ter vencido a segunda estação, não a terceira.

Dá para reunir tudo em uma única entrada:

# Makefile
check:
	ruff check .
	mypy app
	pytest tests/unit tests/integration

e2e:
	docker compose up -d db mailhog
	alembic upgrade head
	pytest tests/e2e

verify: check e2e

Aqui o E2E sobe um banco real e um servidor de e-mail de teste, aplica as migrations e percorre o cenário inteiro. Uma migration quebrada ou um e-mail não configurado aparecem na hora, e não na mão do cliente.

Definição de pronto no AGENTS.md

O agente não deve decidir sozinho o que significa «pronto». O critério é escrito no arquivo de instruções, de forma verificável:

## Definition of Done (definição de pronto)
A funcionalidade só está pronta se TODOS os itens forem atendidos:
1. make check terminou com código 0
2. make e2e terminou com código 0, e o cenário da funcionalidade está em tests/e2e
3. As migrations são aplicadas em um banco limpo e podem ser revertidas
4. Novas variáveis de ambiente estão descritas em .env.example
5. PROGRESS.md registra o que foi feito e como foi verificado

NÃO são evidência de pronto:
- «o código está escrito e parece correto»
- «os testes unitários passam»
- «na minha máquina funciona»

Listar explicitamente o que não conta como pronto funciona melhor do que parece: o agente deixa de parar na primeira execução verde.

Quem tem o direito de marcar «passing»

O passo seguinte é tirar de vez o status de conclusão das mãos do agente. As funcionalidades ficam em uma lista (por exemplo, feature_list.json), cada uma com «comportamento», «comando de verificação» e «estado». Só um script pode mudar uma funcionalidade para passing: ele roda o make check comum, depois o comando de verificação específico da funcionalidade e, em caso de sucesso, grava o status junto com o hash do commit como prova. Se falhar, a funcionalidade continua active. Não existe «quase passou».

{
  "id": "F04",
  "behavior": "O usuário recebe um e-mail com link e troca a senha",
  "verification": "pytest tests/e2e/test_password_reset.py",
  "state": "active"
}

Há uma armadilha aqui: o script só verifica o que está escrito no campo verification. Se uma checagem arquitetural faz parte da definição de pronto, mas não está no comando de verificação da funcionalidade, a falha fica escondida. Por isso o make check comum deve incluir todas as verificações obrigatórias, e não apenas os testes.

Avaliador independente

O relato do executor nunca é uma fonte independente de verdade. A solução estrutural é separar os papéis:

  • planejador transforma o requisito em funcionalidades com critérios de aceite e não escreve código;
  • gerador implementa uma funcionalidade e roda os testes, mas não decide se ela está pronta;
  • avaliador dá o veredito conforme uma lista de critérios.

O avaliador não deve ver o raciocínio do gerador, senão herda a confiança dele. Recebe apenas o requisito, o diff e os resultados das execuções. No Claude Code, isso pode ser feito com um subagente, que tem contexto próprio e limpo. Melhor ainda se o avaliador for outro modelo, porque cada modelo tem pontos cegos diferentes. Um veredito do tipo «parece razoável» não é aceito: cada conclusão precisa citar arquivo, linha ou log.

Mensagens de erro que o agente consegue corrigir sozinho

A verificação só é útil se a saída leva à correção. «Test failed» ou um AssertionError seco não dizem nada ao agente. Uma boa mensagem diz o que quebrou, por que a regra existe e como corrigir:

ERROR arch-check: app/services/refund.py importa app/db/session.py
WHY: a camada de serviços não acessa o banco diretamente, só pelos repositórios
FIX: use RefundRepository de app/repositories/refund.py

Esse formato transforma a verificação de uma proibição em uma dica: o agente corrige a violação sem a sua intervenção.

Onde a verificação manual ainda é necessária

A automação não elimina a pessoa, mas reduz o papel dela. A verificação manual é necessária onde o critério é difícil de formalizar: aparência da interface, textos, facilidade do fluxo. Faça-a com a lista de critérios de aceite, e não como um «dar uma olhada». Se você escreve ao agente o mesmo comentário pela segunda vez, é sinal de transformá-lo em verificação automática: um teste, uma regra de linter ou um guarda grep no make check.

Como o E2E muda o comportamento do agente antes mesmo de rodar

O valor de um teste de ponta a ponta não está só nos bugs que ele pega. Se o agente sabe de antemão que o trabalho será checado por um cenário E2E, ele escreve o código de outra forma: pensa no contrato com os componentes vizinhos, e não só na própria função; trata os caminhos de erro; e não viola as fronteiras da arquitetura, porque a violação vai aparecer na execução. A verificação passa a funcionar como restrição na entrada, e não apenas como filtro na saída.

Há uma condição: o E2E só faz sentido quando o sistema tem fronteiras claras. Em uma arquitetura espaguete, um teste de ponta a ponta prova apenas que o espaguete funciona, e não mostra onde o desenho foi quebrado. Por isso, primeiro se fixam as camadas e as regras de dependência, e só depois se constroem os cenários de ponta a ponta. Agentes gostam de copiar os padrões que já existem no repositório, inclusive os ruins, então as restrições são melhores quando entram desde o primeiro dia.

Erros comuns

  • Pedir ao agente «verifique com mais atenção».O contexto é o mesmo, e a avaliação continua inflada.
  • Tratar 100% de cobertura como garantia.A cobertura de unidades não cobre as junções entre módulos.
  • Refatorar antes de confirmar a funcionalidade.A ordem é rígida: corretude → desempenho → estilo.
  • Mockar no E2E justamente o que mais quebra.Banco, migrations e configuração devem rodar de verdade no teste de ponta a ponta; apenas APIs externas pagas podem ser trocadas por um servidor de teste.

Conclusão

Quando o agente diz «pronto» e a funcionalidade não funciona, o problema quase sempre está no processo de verificação, e não no modelo. Calcule o verification gap, fixe a definição de pronto no AGENTS.md, torne o E2E obrigatório, entregue a decisão de conclusão ao script e acrescente um avaliador independente. Essa abordagem é explicada na prática, em um projeto real, no curso de harness engineering.

Perguntas frequentes

Dá para fazer o agente rodar os testes E2E sozinho?

Sim, se o comando estiver escrito no AGENTS.md como parte obrigatória da definição de pronto e o agente tiver as ferramentas necessárias. Ainda assim, é mais confiável rodá-lo também por um hook ou pelo CI, para não depender da memória do agente.

Os testes E2E são lentos. Preciso rodá-los a cada alteração?

Não. O linter, a checagem de tipos e os testes unitários rodam sempre; o E2E roda em alterações que tocam mais de uma camada e antes de declarar a funcionalidade pronta.

Como verificar um frontend que o agente fez?

Com um teste E2E no navegador (como o Playwright), que clica na interface real, e com a aparência verificada manualmente pela lista de critérios ou por capturas de tela revisadas pelo avaliador.

Uma revisão de código humana não substitui o avaliador?

A revisão humana é valiosa, mas cara e não escala para horas de trabalho autônomo. Um avaliador com rubrica tira os problemas evidentes antes da pessoa, e a pessoa decide os pontos controversos.