El agente escribió «listo», pero la funcionalidad no funciona: por qué pasa y cómo verificar el trabajo de un agente de IA
El agente escribe «listo» porque evalúa su propio trabajo y solo ve lo que ha comprobado: normalmente pruebas unitarias, que no ven las uniones entre módulos, las migraciones, la configuración ni el escenario de extremo a extremo. Se arregla con estructura, no con el prompt: una definición de Done en AGENTS.md, una ejecución E2E obligatoria, un script de verificación que sea el único que puede poner el estado en passing y un evaluador independiente con contexto limpio.
Por qué ocurre
Cualquiera que trabaje con agentes conoce la situación: el agente escribió «listo», pero la funcionalidad no funciona. No miente a propósito. Informa de lo que llegó a comprobar y está sinceramente convencido de que eso basta. La brecha entre la afirmación y la realidad es una propiedad del esquema «el ejecutor evalúa su propio trabajo», no un defecto de un modelo concreto.
Hay tres razones. La primera es que los modelos tienden a la confianza excesiva: la seguridad de una respuesta supera a su precisión. La segunda es que el agente evalúa su trabajo en el mismo contexto en el que lo hizo: ya se ha convencido de que la solución es correcta, y pedirle «sé más objetivo» no cambia el contexto. La tercera, la más práctica, es que al agente solo le llegan comprobaciones parciales: las pruebas unitarias se ponen en verde rápidamente, mientras que el escenario de extremo a extremo nunca se ejecutó.
Crónica de un «listo» falso típico
Al agente se le encargó implementar el restablecimiento de contraseña. Cambia el esquema de la base de datos, añade un endpoint, conecta una plantilla de correo y ejecuta las pruebas unitarias: todo en verde. En el chat aparece un «Listo» muy seguro.
En realidad ocurrió lo siguiente:
- la migración falló a mitad y dejó el esquema en un estado inconsistente;
- la plantilla de correo existe, pero el servicio de correo (SMTP) no está configurado en el entorno: no sale ningún correo;
- el escenario «el usuario pulsa el enlace → introduce una nueva contraseña → inicia sesión» nunca se ejecutó.
Las pruebas unitarias no podían detectar ninguno de estos problemas: la base de datos estaba simulada, el envío de correo estaba simulado y no hubo ninguna ejecución de extremo a extremo.
Por qué las pruebas en verde no demuestran que la funcionalidad funciona
Una prueba unitaria comprueba un módulo de forma aislada: las dependencias se sustituyen por simulaciones (mocks) y el mundo exterior queda desconectado. Eso da velocidad y precisión, pero también una ceguera sistemática. Esa ceguera no es un fallo de las pruebas, sino consecuencia de su diseño. Las pruebas unitarias en verde solo demuestran que cada pieza funciona por separado, no que las piezas funcionen juntas.
Hay cuatro clases de defectos que una prueba unitaria no ve en absoluto:
| Clase de defecto | Ejemplo | Por qué una prueba unitaria no lo detecta |
|---|---|---|
| Interfaces que no encajan | Un componente devuelve una ruta relativa y el otro espera una absoluta | Cada uno se prueba con su propia simulación, que «acepta» cualquier formato |
| Propagación del estado | El esquema de la base cambió, pero la caché del ORM conserva el antiguo | En la prueba unitaria no hay base de datos real ni caché |
| Ciclo de vida de los recursos | Descriptores de archivo y conexiones que no se liberan, fugas de memoria con archivos grandes | La prueba trabaja con datos pequeños y termina antes de que se agoten los recursos |
| Entorno y configuración | Falta una variable de entorno, SMTP no está configurado, la migración no se aplicó | La prueba no arranca la aplicación con su configuración real |
Los niveles de verificación están anidados: todo lo que detectan las pruebas unitarias lo detectan también las de integración, y todo lo que detectan las de integración lo detecta una ejecución de extremo a extremo (E2E). Cada nivel superior ve estrictamente más. Por eso el derecho a decir «listo» debe tenerlo solo el nivel más alto.
Medir el problema: la brecha de verificación
Antes de arreglar nada, cuente. Tome las últimas 10 tareas cerradas por el agente. En cada una responda a dos preguntas: ¿dijo el agente «listo» y lo confirmó una verificación independiente (usted recorrió el escenario, la prueba E2E está en verde)? La proporción de discrepancias es su brecha de verificación (verification gap).
Esta cifra es útil por dos motivos. Muestra la magnitud del problema sin discusiones del tipo «bueno, parece que funciona bastante bien». Y ofrece una línea base: tras cada cambio en el proceso, la recalcula sobre un conjunto parecido de tareas y ve si ha ayudado.
Verificar de verdad: tres capas de validación
Una comparación útil es la del taller mecánico. Un coche pasa por tres estaciones, y ninguna se puede saltar.
- Diagnóstico (segundos): sintaxis, tipos, linter. Detecta erratas y errores graves.
- Banco de pruebas (minutos): pruebas unitarias y de integración, la aplicación arranca. El motor funciona, pero el coche sigue parado.
- Prueba de carretera: un escenario de extremo a extremo tal como lo recorre un usuario, a través de la API, el navegador o la CLI, con base de datos y configuración reales.
La regla de avance: no se sale a la prueba de carretera mientras el diagnóstico tenga errores, y no se entrega el coche sin haber hecho la prueba. Las pruebas unitarias en verde son la segunda estación superada, no la tercera.
Conviene reunir todo detrás de un único punto de 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
Aquí el E2E levanta una base de datos real y un servidor de correo de pruebas, aplica las migraciones y recorre el escenario completo. Una migración que falla o un correo sin configurar se ven de inmediato, y no en casa del cliente.
Definición de Done en AGENTS.md
El agente no debe decidir por sí mismo qué significa «listo». El criterio se escribe en el archivo de instrucciones y se formula de forma verificable:
## Definition of Done
Una funcionalidad está lista solo si se cumplen TODOS los puntos:
1. make check terminó con código de salida 0
2. make e2e terminó con código 0, y el escenario de la funcionalidad existe en tests/e2e
3. Las migraciones se aplican sobre una base limpia y se pueden revertir
4. Las nuevas variables de entorno están descritas en .env.example
5. PROGRESS.md registra qué se hizo y cómo se comprobó
NO son señal de que esté lista:
- «el código está escrito y tiene buena pinta»
- «las pruebas unitarias pasan»
- «en mi máquina funciona»
Una lista explícita de lo que no cuenta como terminado funciona mejor de lo que parece: el agente se detiene con menos frecuencia en la primera ejecución en verde.
Quién tiene derecho a poner «passing»
El siguiente paso es quitar por completo el estado de «listo» de las manos del agente. Las funcionalidades se guardan en una lista (por ejemplo feature_list.json) con los campos «comportamiento», «comando de verificación» y «estado». Solo un script puede pasar una funcionalidad a passing: ejecuta el make check común, después el comando de verificación de la funcionalidad concreta y, si todo sale bien, registra el estado junto con el hash del commit como prueba. Si falla, la funcionalidad sigue activa; no existe el «casi funcionó».
{
"id": "F04",
"behavior": "El usuario recibe un correo con un enlace y cambia su contraseña",
"verification": "pytest tests/e2e/test_password_reset.py",
"state": "active"
}
Aquí hay una trampa: el script solo comprueba lo que está escrito en el campo verification. Si una comprobación de arquitectura forma parte de la definición de Done, pero no del comando de la funcionalidad, el hueco sigue invisible. Por eso el make check común debe incluir todas las comprobaciones obligatorias, no solo las pruebas.
Un evaluador independiente
La autoevaluación del ejecutor nunca es una fuente independiente de la verdad. La solución estructural consiste en separar los roles:
- el planificador descompone el requisito en funcionalidades con criterios de aceptación y no escribe código;
- el generador implementa una funcionalidad y ejecuta las pruebas, pero no decide si está terminada;
- el evaluador emite un veredicto según una lista de criterios.
El evaluador no debe ver el razonamiento del generador, o heredará su confianza. Recibe solo el requisito, el diff y los resultados de las ejecuciones. En Claude Code es fácil montarlo como subagente, que tiene su propio contexto limpio. Mejor aún si el evaluador es otro modelo: sus puntos ciegos son distintos. La valoración «parece bien» está prohibida: cada hallazgo debe remitir a un archivo, una línea o un registro.
Mensajes de error que el agente puede corregir por sí mismo
Una comprobación es útil solo si su salida lleva a una corrección. «Test failed» o un AssertionError desnudo no le dicen nada al agente. Un buen mensaje indica qué se rompió, por qué existe la regla y cómo corregirlo:
ERROR arch-check: app/services/refund.py importa app/db/session.py
POR QUÉ: los servicios no acceden a la base directamente, solo a través de repositorios
CORRECCIÓN: use RefundRepository de app/repositories/refund.py
Este formato convierte la comprobación de una prohibición en una pista: el agente corrige la infracción sin que usted intervenga.
La verificación manual: dónde sigue siendo necesaria
La automatización no sustituye a la persona, pero reduce su papel. La verificación manual sigue siendo necesaria cuando el criterio es difícil de formalizar: el aspecto de la interfaz, los textos, la comodidad de un flujo. Hágala con una lista de verificación basada en los criterios de aceptación, no «probando por encima». Si escribe por segunda vez el mismo comentario al agente, es señal de convertirlo en una comprobación automática: una prueba, una regla de linter o un guardia grep en make check.
Cómo el E2E cambia el comportamiento del agente antes de la ejecución
El valor de una prueba de extremo a extremo no está solo en los errores que atrapa. Si el agente sabe de antemano que su trabajo lo revisará un escenario E2E, escribe el código de otra manera: piensa en el contrato con el componente vecino y no solo en su propia función, trata las rutas de error y no cruza los límites de la arquitectura, porque una infracción saldría a la luz en la ejecución. La comprobación actúa como restricción a la entrada, y no solo como filtro a la salida.
Hay una condición: el E2E tiene sentido cuando el sistema tiene límites claros. En una arquitectura de espagueti, una prueba de extremo a extremo solo demostrará que el espagueti funciona, y no mostrará dónde se rompió el diseño. Por eso primero se establecen las capas y las reglas de dependencia, y solo después los escenarios de extremo a extremo. Los agentes copian con gusto los patrones existentes de un repositorio, también los malos, así que es mejor introducir las restricciones desde el primer día.
Errores frecuentes
- Pedirle al agente que «revise con más cuidado». El contexto es el mismo, la evaluación seguirá inflada.
- Tomar el 100 % de cobertura como garantía. La cobertura por pruebas unitarias no cubre las uniones.
- Refactorizar antes de confirmar la funcionalidad. El orden es estricto: corrección → rendimiento → estilo.
- Simular en E2E lo que más se rompe. En la prueba de extremo a extremo, la base de datos, las migraciones y la configuración deben ejecutarse de verdad; las API de pago externas pueden sustituirse por un servidor de pruebas.
Resumen
Cuando el agente ha escrito «listo» y la funcionalidad no funciona, el problema casi siempre está en el proceso de verificación, no en el modelo. Calcule la brecha de verificación, fije la definición de Done en AGENTS.md, haga obligatorio el E2E, entregue el estado de finalización a un script y añada un evaluador independiente. Este esquema se trabaja en la práctica en el curso de harness engineering.
Preguntas frecuentes
¿Puedo hacer que el agente ejecute él mismo las pruebas E2E?
Sí, si el comando figura en AGENTS.md como parte obligatoria de la definición de Done y el agente tiene las herramientas necesarias. Es más fiable además lanzarlo mediante un hook o en CI, para no depender de la memoria del agente.
Las pruebas E2E son lentas, ¿hay que ejecutarlas tras cada cambio?
No. Lint, tipos y pruebas unitarias siempre; E2E cuando los cambios afectan a más de una capa, y antes de declarar la funcionalidad terminada.
¿Cómo se verifica el frontend que hizo el agente?
Con una prueba de extremo a extremo en el navegador (por ejemplo Playwright) que haga clic en la interfaz real, mientras que el aspecto se revisa manualmente con una lista de verificación o con capturas que examina el evaluador.
¿Basta con una revisión de código de una persona en lugar del evaluador?
La revisión humana es útil, pero es cara y no escala a horas de trabajo autónomo. Un evaluador con una lista de criterios elimina los problemas evidentes antes de que lleguen a una persona, y esa persona decide las cuestiones dudosas.
Este tema se explica a fondo y con práctica en el curso «Harness Engineering: инженерия надёжности AI-агентов».
- Агент написал «готово», а фича не работает: verification gap и Definition of Done
- Все тесты зелёные, а фича сломана: E2E-тесты и архитектурные правила для AI-агента