O agente disse «pronto», mas a funcionalidade não funciona: por que acontece e como verificar o trabalho do agente de IA
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 defeito | Exemplo | Por que o teste unitário não pega |
|---|---|---|
| Incompatibilidade de interfaces | Um componente devolve caminho relativo, outro espera caminho absoluto | Cada um é testado com um mock que aceita qualquer formato |
| Propagação de estado | O esquema do banco mudou, mas o cache do ORM ainda tem a versão antiga | No teste unitário não existem banco real nem cache |
| Ciclo de vida de recursos | Descritores de arquivo e conexões não são liberados; a memória vaza com arquivos grandes | O teste usa dados pequenos e termina antes que os recursos se esgotem |
| Ambiente e configuração | Variável de ambiente ausente, SMTP não configurado, migration não aplicada | O 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.
- Diagnóstico (segundos): sintaxe, tipos, linter. Pega erros de digitação e falhas grosseiras.
- Bancada (minutos): testes unitários e de integração, a aplicação sobe. O motor funciona, mas o carro ainda está parado.
- 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.
Este tema é abordado em profundidade, com prática, no curso “Harness Engineering: инженерия надёжности AI-агентов”.
- Агент написал «готово», а фича не работает: verification gap и Definition of Done
- Все тесты зелёные, а фича сломана: E2E-тесты и архитектурные правила для AI-агента