¿Mejora la IA si le pides que revise su propio trabajo? Lo probé con Claude Code

En el post anterior hice la misma pregunta a Claude Code 21 veces, cambiando modelo, esfuerzo y herramientas. Nadie superó el 57% y la conclusión fue: ninguna combinación da una respuesta completa en un solo intento.

Pero me quedé con una pregunta: ¿y si le pido que revise su propio trabajo?

Claude Logo

La idea: autocorrección iterativa

El experimento anterior demostró que cada modelo tiene puntos ciegos. Opus no encuentra la caché de 1 hora. Sonnet no detecta problemas de concurrencia. Ninguno lo pilla todo.

Así que diseñé dos experimentos nuevos:

  • Experimento A: Self-review iterativo: Dejar que el modelo dé su respuesta inicial, y luego pedirle dos veces seguidas que revise si lo que dijo está bien. El prompt exacto: «Vale, ahora quiero que revises si lo que hiciste está bien o hay fallos, o cosas que no detectaste», seguido de «Vale, quiero que vuelvas a mirar, porque si antes detectaste cosas críticas, ¿quién nos dice que no las cometiste otra vez?»
  • Experimento B: Prompt riguroso desde el inicio: Reformular la pregunta original para que el modelo sea riguroso antes de contestar: «Revisa el flujo completo de GET /users/:user/stats y dime qué política de cache tiene actualmente. Por favor, quiero hacerlo de una sola pasada, así que quiero que lo revises todo que esté bien antes de dar la respuesta, quiero evitar errores o flujos erróneos.»

Mismo proyecto, mismo código, mismos 7 criterios de evaluación que en el post anterior.

Recordatorio: los 7 criterios y el scoring

Para quien no haya leído el post anterior, estos son los 7 checks que definen una respuesta completa:

CriterioQué se verifica
L1Encontró la caché de UserAlias (TTL 1h) y explicó su TTL
L2Encontró la caché de Stats View (TTL 24h)
FlujoDiagrama/descripción fiel al código real
Thundering herdDetectó el problema de concurrencia
updatedAt/OnDistinguió los dos campos temporales de la entidad
InconsistenciaDetectó que el TTL de 1h anula el de 24h
PoliciesMencionó las constantes de dominio (60min, 300, 100)

Cada check = 1 punto. Máximo = 7 = 100%. En el post anterior, nadie superó el 57%.

Experimento A: «Revisa si lo que hiciste está bien»

Tomé la respuesta inicial de cada modelo (la misma de la Ronda 1 del post anterior) y le pedí que la revisara. Luego le pedí que revisara la revisión. Tres pasadas en total.

Sonnet Low: el que inventa bugs que no existen

Respuesta inicial (43%): Encontró L1+L2+Flujo. Mismo resultado que en la Ronda 1.

Review 1: Leyó 5 archivos más y anunció haber encontrado un «bug real»:

«Este es un bug real: un usuario registrado como gaben que lleva >24h sin actualizar, tras el refresh perderá su alias en user_aliases.»

Afirmó que refreshUser(steamId) borraría su alias porque CreateUserWithGamesCommand se llamaría con el steamId en vez del alias, sobrescribiendo el alias con undefined.

Review 2: Leyó 10 archivos más y se autocorrigió:

«Dije que refreshUser(steamId) podía borrar el alias. Esto era incorrecto. MikroOrmUserAliasRepository.save() tiene lógica explícita: si NO tiene alias, solo actualiza updatedOn, preservando el alias existente en BD. El bug que reporté no existe.»

También encontró la confusión de naming updatedAt/On y un detalle de tipos (strictNullChecks: false silenciando un mismatch).

Scoring final tras 3 pasadas: 50% (L1+L2+Flujo+updatedAt/On parcial). Subió del 43% inicial, pero la Review 1 introdujo un falso positivo que la Review 2 tuvo que corregir.

Sonnet High: el que se autocorrige tres veces

La respuesta inicial era de una conversación separada donde Sonnet High solo encontró L2 (el resultado de la Ronda 1 fue 29%).

Review 1: Encontró los dos TTLs en conflicto por primera vez:

«Hay dos TTLs, no uno — y están en conflicto. El TTL de alias (1h) es más corto que el de stats (24h), lo que hace que el check de 24h sea prácticamente inútil en uso normal.»

También señaló updatedAt: new Date() como un «bug» y que UserHasPrivateProfileError es código muerto.

Review 2: Se autocorrigió en dos puntos:

«Dije ‘no hay error handling’ — INCORRECTO. BaseErrorFilter está registrado globalmente. Llamé ‘bug’ a updatedAt: new Date() — IMPRECISO. Registra el momento del sync intencionalmente.»

Encontró además que hay dos interfaces UserStatsFinder distintas (query vs command) y trazó la cadena completa de updatedAt de principio a fin.

Review 3: Confirmó lo anterior y añadió un detalle de tipos silenciado por strictNullChecks: false.

Scoring final tras 4 pasadas: 64% (L1+L2+Flujo+updatedAt/On+Inconsistencia parcial). Subió desde 29%. La mejora más dramática del experimento.

Opus Low: el que se fue por las ramas

La respuesta inicial incluía L2+thundering herd+updatedAt/On (50% en la Ronda 1). Pero cuando le pedí que revisara su trabajo, interpretó la petición de forma completamente diferente.

Review 1: En vez de revisar su análisis de caché, ejecutó git diff y empezó a revisar los cambios pendientes del repositorio. Encontró problemas reales del código (migración sin DEFAULT, código muerto, archivos duplicados), pero no revisó nada de lo que le había pedido.

Review 2: Siguió por el mismo camino. Encontró que falta una migración para users.is_visible y problemas de error handling con perfiles privados. Análisis útil, pero de una tarea completamente distinta.

Scoring de la tarea original: 50% (sin cambio). No revisó su respuesta, se inventó otra tarea. Para la tarea que se inventó, el análisis fue bastante bueno, pero no era lo que le pedí.

Opus Max: el que descubre las transacciones

La respuesta inicial incluía L2+thundering herd+updatedAt/On (50% en la Ronda 1).

Review 1: Corrigió su afirmación errónea sobre updatedAt («dije que venía de Steam, falso») y encontró la caché de dos capas que había pasado por alto. Pero fue demasiado absoluto:

«El TTL de 24h es prácticamente código muerto. Dentro de la ventana de 1h donde el alias está cached, las stats siempre tendrán < 1h de antigüedad, nunca llegan a 24h.»

También encontró el type mismatch en saveUserAlias y las llamadas secuenciales a Steam paralelizables.

Review 2: Se autocorrigió de forma brillante. Fue al config de MikroORM, vio allowGlobalContext: true, comprobó que no hay RequestContext ni @Transaction, y dedujo:

«No hay transacción compartida entre los dos commands. Cada em.upsert() se commitea de forma independiente. Si CreateUserWithGamesCommand tiene éxito pero SyncUserStatsCommand falla, el alias queda fresco pero las stats no se actualizan. La siguiente petición dentro de 1h vería el alias cached + stats >24h. La comprobación de 24h es un mecanismo de recuperación ante fallos parciales, no código muerto.»

Este es probablemente el análisis más profundo de todo el experimento, incluyendo las 21 pruebas del post anterior. Nadie más llegó al nivel de analizar las garantías transaccionales.

También identificó que el hook onUpdate del entity es configuración muerta (el repositorio setea el valor explícitamente, el hook nunca se ejecuta) y un posible null sin check en createAndSyncUser.

Scoring final tras 3 pasadas: 79% (L1+L2+Flujo parcial+TH+updatedAt/On+Inconsistencia parcial). Subió desde 50%. El más alto de todo el estudio.

Opus High: el gemelo de Opus Max

La respuesta inicial incluía L2 con mención parcial de isCached (36% en la Ronda 1).

Review 1: Patrón casi idéntico a Opus Max. Encontró L1, la confusión updatedAt/On, y concluyó que «la comprobación de 24h es prácticamente código muerto».

Review 2: Misma autocorrección que Opus Max: descubrió allowGlobalContext: true, la falta de transacciones compartidas, y corrigió su conclusión sobre el «código muerto». Llegó al mismo resumen de «safety net para fallos parciales».

Scoring final tras 3 pasadas: 71% (L1+L2+Flujo parcial+updatedAt/On+Inconsistencia). Subió desde 36%.

Steam Playtime logo

Experimento B: «Hazlo bien desde el principio»

En vez de dejar que el modelo se equivoque y luego pedirle que se corrija, probé a reformular la pregunta original para forzar rigor desde el inicio:

«Revisa el flujo completo de GET /users/:user/stats y dime qué política de cache tiene actualmente. Por favor, quiero hacerlo de una sola pasada, así que quiero que lo revises todo que esté bien antes de dar la respuesta, quiero evitar errores o flujos erróneos.»

Solo probé dos configuraciones: Sonnet High y Opus High.

Sonnet High con prompt riguroso

Encontró ambas cachés (L1 1h + L2 24h), las policies de dominio (60min, 300, 100), el flujo correcto con líneas de código referenciadas, y la ausencia de HTTP cache. Todo de una sola pasada.

Scoring: 57% (L1+L2+Flujo+Policies). Sin thundering herd ni updatedAt/On ni inconsistencia, pero sólido.

Opus High con prompt riguroso

Encontró ambas cachés con TTLs correctos, las policies de dominio, el flujo completo con tabla resumen, y un detalle que nadie más mencionó: «Stats se calculan sobre todos los juegos (no solo los filtrados) — los juegos filtrados (>60min) son solo para la lista userGames, no para los aggregates.»

Scoring: 57% (L1+L2+Flujo+Policies). Mismo resultado que Sonnet High. Sin thundering herd ni updatedAt/On, pero completo en lo fundamental.

La tabla comparativa completa

Modelo + EffortRespuesta inicialTras Review 1Tras Review 2Prompt riguroso
Sonnet Low43%43% (+ falso positivo)50% (corrigió falso positivo)
Sonnet High29%50% (+ 2 errores nuevos)64% (corrigió errores)57% (una sola pasada)
Opus Low50%50% (revisó git diff, no su análisis)50% (siguió por las ramas)
Opus High36%57% (encontró L1+inconsistencia)71% (descubrió transacciones)57% (una sola pasada)
Opus Max50%64% (encontró L1, dijo «código muerto»)79% (corrigió, análisis transaccional)

Lo que cambió respecto al post anterior

En el post anterior, el techo era 57%. Con self-review iterativo, Opus Max llegó al 79%. Es una mejora del 58% sobre su respuesta inicial (de 50% a 79%).

Pero la mejora no es lineal ni gratis:

MétricaRespuesta únicaSelf-review x2Prompt riguroso
Mejor score57%79%57%
Errores nuevos introducidos0Sí (falsos positivos)0
Pasadas necesarias131
Requiere validación humanaMás aún

¿Cuánto cuesta todo esto?

En el post anterior medimos que cada test costaba ~$0.13 de media sin MCP. El self-review no es gratis: cada «revisa tu trabajo» es otra conversación con el modelo que consume tokens adicionales (y más que la primera, porque incluye el contexto previo).

Estimación basada en los costes del post anterior:

EstrategiaPasadasCoste estimadoScore máximoCoste por punto de %
Pregunta directa1~$0.1357%$0.002/punto
Prompt riguroso1~$0.1557%$0.003/punto
Self-review x12~$0.3064% (con errores)$0.005/punto
Self-review x23~$0.5079%$0.006/punto

Llegar del 57% al 79% cuesta unos 37 céntimos extra (3 pasadas en vez de 1). Sigue siendo ridículamente barato. El cuello de botella no es el dinero sino tu tiempo validando si las autocorrecciones son reales o alucinaciones.

Las cinco lecciones del self-review

1. Cada revisión corrige errores viejos pero introduce nuevos

Es el patrón más consistente del experimento. Sonnet Low encontró un «bug real» en la Review 1 que resultó no existir. Sonnet High llamó «bug» a algo intencionado. Opus Max dijo «código muerto» sobre algo que no lo era. En todos los casos, la siguiente review corrigió el error, pero el proceso requiere que alguien sepa distinguir qué correcciones son válidas y cuáles son nuevas alucinaciones.

2. Dos reviews son el punto dulce

Los números lo dejan claro:

ModeloInicial+1 review+2 reviewsGanancia R1Ganancia R2
Sonnet Low43%43% (+falso positivo)50%+0 (peor calidad)+7 (limpió errores)
Sonnet High29%50% (+2 errores)64%+21 (con ruido)+14 (corrigió ruido)
Opus High36%57%71%+21+14
Opus Max50%64% («código muerto»)79%+14 (con error)+15 (corrigió + transacciones)

La primera review da el salto más grande en puntuación bruta (+14-21 puntos), pero introduce errores nuevos en 3 de 4 casos. La segunda review corrige esos errores y añade profundidad (+7-15 puntos). Una tercera review (cuando la probé con Sonnet High) apenas aportó nada nuevo.

Dicho de otra forma: con 1 review tienes más hallazgos pero no puedes fiarte de todos. Con 2 reviews los resultados se estabilizan. La tercera ya no compensa.

3. El prompt «hazlo bien desde el principio» es sorprendentemente efectivo

Sonnet High y Opus High con el prompt riguroso alcanzaron 57% de una sola pasada, sin self-review. Es el mismo techo que el post anterior pero conseguido de forma más fiable y sin el riesgo de falsos positivos. No llegaron al 79% de Opus Max con self-review, pero el ratio esfuerzo/resultado es mucho mejor.

4. El self-review profundo es exclusivo de Opus

Sonnet mejoró con self-review (de 29% a 64%), pero sus correcciones fueron sobre hechos: «esto era un bug / no lo era». Opus Max fue el único que en la segunda review hizo un análisis transaccional nuevo: fue al config de MikroORM, comprobó que no había transacciones compartidas, y dedujo un escenario de fallo parcial que nadie más vio en todo el estudio. Ese nivel de razonamiento en la autocorrección parece requerir la capacidad del modelo más potente.

5. Opus Low no entiende «revisa tu trabajo»

Cuando le pedí que revisara lo que había hecho, Opus Low ejecutó git diff e hizo una code review de los cambios pendientes del repositorio. No revisó su análisis de caché en absoluto. El análisis que hizo era bueno (encontró una migración faltante y problemas de error handling), pero era una tarea completamente distinta a la que le pedí. Con low effort, el modelo no tiene suficiente contexto para entender qué se supone que debe revisar.

El dato que más me impactó

Opus Max en su tercera pasada descubrió algo que ningún modelo, en ninguna configuración, en ninguna de las 21+ pruebas anteriores había detectado: que la caché de 24 horas funciona como safety net porque no hay transacciones compartidas entre los commands.

El razonamiento fue:

  1. Leyó mikro-orm.config.ts y vio allowGlobalContext: true
  2. Verificó que no hay RequestContext ni decoradores @Transaction
  3. Dedujo que cada em.upsert() se commitea independientemente
  4. Construyó un escenario de fallo parcial: el alias se actualiza pero el sync de stats falla
  5. Concluyó que isStale() de 24h es el mecanismo de recuperación para ese caso

Esto no es algo que puedas encontrar leyendo más archivos. Es razonamiento sobre las garantías de consistencia del sistema. Y solo apareció después de que el modelo cometiera un error (decir «código muerto»), fuera cuestionado, y tuviera que justificar o corregir su posición.

La presión de tener que defender tu análisis produce mejor análisis. Igual que en una code review entre humanos.

¿Qué funciona mejor entonces?

Depende de lo que necesites:

EstrategiaScore máximoRiesgoCuándo usarla
Pregunta directa57%Puntos ciegos consistentesExploración rápida, visión general
Prompt riguroso57%Bajo (sin falsos positivos)Cuando necesitas fiabilidad en una pasada
Self-review x279%Falsos positivos, requiere criterio humanoAuditoría profunda, cuando puedes validar

Mi recomendación práctica: empieza con el prompt riguroso (una pasada, sin sorpresas) y si necesitas profundizar, pide una sola ronda de self-review con Opus Max. Dos reviews son suficientes. La tercera apenas aporta.

La conclusión: la autocorrección funciona, pero no es magia

El self-review mejoró los resultados de forma dramática. Opus Max pasó de 50% a 79%. Opus High de 36% a 71%. Sonnet High de 29% a 64%. Son mejoras enormes.

Pero cada review introduce nuevos errores que la siguiente tiene que corregir. Es un proceso que converge hacia la verdad, no un salto directo a ella. Y requiere un humano que sepa distinguir las correcciones válidas de las alucinaciones nuevas.

El hallazgo más práctico es que un buen prompt inicial («hazlo bien desde el principio, quiero evitar errores») consigue de una sola pasada lo que antes requería múltiples intentos. No llega al 79% del self-review iterativo, pero el 57% es fiable y sin falsos positivos.

Al final, la conclusión del post anterior sigue siendo válida: la IA es una herramienta, no un oráculo. Pero ahora sabemos que es una herramienta que mejora significativamente cuando le pides que revise su propio trabajo. Como un compañero junior al que le dices «¿estás seguro?» y de repente encuentra tres cosas más. Eso sí: a veces lo que encuentra no existe, así que conviene tener criterio propio.

Y eso, de momento, sigue requiriendo un humano detrás.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *