El pipeline donde otra IA intenta refutar el código que arregló la primera
Si le pides al mismo modelo que revise su propio código, vuelve a aprobar su propia conclusión. La historia de un desarrollador en solitario, que escribe casi todo su código con IA, y de cómo convirtió en pipeline permanente un bucle de verificación cruzada en el que le pide a otra IA que "intente rebatir" el trabajo de la primera. El caso real en el que tres rondas seguidas sacaron bugs que la autorevisión había dejado pasar, y el diseño de los prompts.
Lo esencial
Una IA que juzga su propio trabajo es indulgente. Si le pides a otro modelo que "asuma que este arreglo está mal y lo refute", salen defectos que la autorevisión no vio. El caso en el que un solo bug de logout reveló defectos cada vez más profundos durante tres rondas seguidas, el diseño de prompts (encuadre de refutación, formato de evidencia) y el criterio de cuándo ejecutar este bucle.
En esta página
Quien haya puesto a un agente de IA a revisar código lo sabe. El código ajeno lo caza bastante bien. Pero si le pides que revise el código que él mismo acaba de escribir, se vuelve extrañamente indulgente. Te devuelve un "la implementación se ajusta a la intención" y, cuando lo ejecutas de verdad, el bug sigue ahí.
Es lo esperable. Dentro del mismo modelo y el mismo contexto, la confusión que cometió al escribir el código la comete igual al revisarlo. Es una estructura en la que su propia conclusión la vuelve a aprobar él mismo, así que más que una revisión es repetir la orden en voz alta.
Por eso saqué el juicio fuera. El código que arregla Claude se lo paso a Codex, y le encargo una sola cosa: "Asume que este arreglo está mal y refútalo."
Tres rondas seguidas, y cada vez salió un bug más profundo
La primera vez que sentí el valor de este bucle fue con un bug de logout. El síntoma era simple: a veces, al pulsar el botón de cerrar sesión, la sesión no se cerraba. Claude encontró la causa y la arregló, y leyendo solo el código parecía plausible. Estuve a punto de hacer el merge.
Le pedí a Codex la refutación y en la primera ronda llegó esta respuesta: el arreglo en sí es correcto, pero el redirect de la página de autenticación intercepta el flujo antes de que llegue a ese camino de código, así que el arreglo nunca tiene ocasión de ejecutarse. El código arreglado era código muerto.
Arreglé también el redirect y volví a pedir la refutación. En la segunda ronda salió otra cosa. Esta vez el problema era el flag de "en proceso" que se había puesto para evitar ejecuciones duplicadas. En ciertos timings, por culpa de esa guarda, la propia petición de logout se convierte en un no-op que no hace nada. Solo en la tercera ronda llegó un "no he encontrado nada que refutar", y solo entonces hice el merge.
Las tres rondas eran código que había pasado la autorevisión. Y en las tres rondas el refutador lo cazó porque no buscaba "si el código se ve bien", sino "si existe un escenario en el que esta afirmación se derrumba".
Después el patrón se siguió repitiendo. En un arreglo de servidor que había pasado dos autorevisiones, la verificación cruzada cazó un valor que faltaba en una restricción de la DB y hubo que añadir una migración. En un arreglo de la lógica de reintentos, el refutador devolvió un veredicto de bloqueo por un caso límite que clasificaba mal cierta respuesta de timeout como fallo permanente. Todos, sin excepción, eran de los que "estuvieron a punto de pasar por parecer plausibles".
No le pido una revisión, le pido una refutación
Lo más importante del diseño del prompt es el encuadre. Si le dices "revísame esto", el modelo te da una respuesta tibia: mitad elogios, mitad observaciones menores. Si le dices "asume que este arreglo está mal y refútalo; apruébalo solo si fracasas en refutarlo", la actitud cambia. Porque aprobar deja de ser el valor por defecto y pasa a ser la consecuencia de una refutación fallida.
A eso le añado tres condiciones más.
- Impongo un formato de evidencia. Toda observación tiene que llevar archivo y número de línea, más un escenario de reproducción en el que ese defecto estalla de verdad. Un "esta parte parece peligrosa" no se acepta. Si no consigue construir el escenario, esa sospecha no es una observación: se descarta. De hecho, es bastante frecuente que el refutador refute sus propias sospechas y las retire, y ese es el mecanismo clave que filtra los falsos positivos.
- El veredicto llega en grados. Aprobado / aprobado tras arreglos menores / bloqueado: con tres niveles basta. Con grados, el estado "hay observaciones pero se puede mergear" y el estado "no se debe mergear" no se mezclan.
- Se ejecuta en un contexto independiente. Al refutador solo le doy el diff y el código relacionado. No le doy la conversación del proceso de trabajo ni la narrativa de "por qué se arregló así". En el momento en que compartes la narrativa, el refutador se contagia de la misma confusión.
Un consejo operativo más: el refutador corre en modo de solo lectura. Si le das permiso para modificar código, en lugar de señalar intenta arreglarlo él mismo, y entonces reaparece el problema de quién verifica ese arreglo. El rol queda fijado en juzgar, y el arreglo se devuelve al trabajador original.
Cuándo ejecutar el bucle
No ejecuto este bucle en cada cambio. Convocar al refutador para corregir una errata es un desperdicio. Lo que sí pasa siempre por el bucle son los cambios difíciles de revertir: migraciones de DB, borrado de datos, rutas de pago o autenticación. También los cambios que llevan colgada la afirmación de "lo he arreglado". Un arreglo de bug necesita la pareja de pruebas de que el bug se reproducía antes y desapareció después, y el refutador es muy bueno encontrando los huecos de esa pareja. El último caso son los cambios cuyo código no voy a leer entero. Que una persona lea completo un diff grande generado por IA se derrumba en la práctica. Si no lo voy a leer, que al menos otro modelo lo lea con hostilidad.
No puedo dejar de hablar del coste: una ronda de refutación vale unas pocas llamadas al modelo. Comparado con lo que cuesta que una persona rastree un solo bug que llegó a producción, no había nada que pensar. Si el bug de logout de arriba hubiera llegado al despliegue, yo habría tenido que excavar por mi cuenta las dos capas, el redirect y la guarda del flag, con un solo reporte en la mano: "a veces no se cierra la sesión".
Por qué tiene que ser otro modelo
Con una sesión nueva del mismo modelo también hay cierto efecto. Solo con separar el contexto desaparece el problema de contagiarse de la narrativa. Pero la conclusión tras varios meses de rodarlo es que, cuando el modelo en sí es distinto, se caza claramente más.
Cada modelo presta atención a puntos distintos. Si uno es sensible a los problemas de timing en la gestión de estado, el otro lo es a las violaciones de contrato y a los valores límite. Dos sesiones del mismo modelo comparten los mismos puntos ciegos, pero en modelos distintos los puntos ciegos no coinciden. El valor de la verificación cruzada sale exactamente de ese desajuste.
Esta estructura no debería resultar extraña. Es la misma razón por la que los equipos humanos encargan la revisión de código a alguien que no es el autor. Trabajar con IA no hace desaparecer ese principio; solo lo ha vuelto barato y aplicable de forma permanente. Un juez que no es el autor, unos ojos sin contagiar por la narrativa. Y una aprobación que solo llega cuando la refutación fracasa. Lo que una organización humana hacía solo de vez en cuando por caro, ahora se puede dejar plantado siempre delante del merge.
Artículos relacionados
- 💻 Desarrollo
Lo que pasó cuando solté varios agentes de IA en un mismo repo
Pensé que lanzar sesiones de agente en paralelo multiplicaría la productividad. En la realidad, en una sola semana estallaron accidentes en cadena: secuestro de ramas, commits montados sobre la rama de otro, commits huérfanos rescatados con el reflog y hasta la doble implementación de la misma funcionalidad. Una tipología de los accidentes que crean los agentes paralelos que comparten un checkout, y las reglas que levanté tras sufrirlos.
- 💻 Desarrollo
Cuando la IA dice "ya está", el dispositivo que no se lo cree
Los agentes de codificación con IA dicen que han terminado sin haber terminado. Aseguran que los tests pasan sin haberlos ejecutado, y que han arreglado el bug sin haberlo reproducido. La historia de un desarrollador en solitario, que escribe casi todo su código con IA, sobre la puerta de verificación que la obligó a aceptar el "terminado" solo con pruebas, no con palabras.
- 💻 Desarrollo
Decidí dejar de creerme el "listo" de mi agente - prove-it, un gate con verify.sh
Los agentes de codificación con IA reportan "tests en verde" sin haberlos ejecutado. No es una mentira, es un problema estructural. Lo que aprendí construyendo prove-it, un gate open source que convierte el reporte en una comprobación - y las cuatro formas en que un hook casero de cinco líneas falla en silencio.