Saltar al contenido
Volver a todas las notas runtime · 8 min

La ejecución murió a las 2 de la mañana: la recuperación como flujo de primera

El agente topó con un rate limit, o perdió el hilo, o se llenó el contexto y olvidó en silencio el episodio uno. Te despiertas con una rama a medias. Ahora mismo recuperar es heroísmo. Debería ser un flujo.

nota de campo 8 min

Las ejecuciones largas nunca mueren a una hora cómoda. Mueren a las 2 de la mañana, tres horas dentro de una tarea, justo después de que el agente hiciera la parte interesante y justo antes de escribir nada. Vuelves a una rama a medias, un transcript que ya pasó de largo la parte útil, y una decisión con malas opciones: leer tres horas de logs para averiguar dónde llegó, o tirarlo y empezar de nuevo.

Esa decisión es el impuesto que nadie presupuesta. Y la razón por la que duele cada vez es que la recuperación se trata como un accidente. Algo salió mal, así que ahora un humano hace de detective. Pero el trabajo largo con agentes no falla de vez en cuando. Falla de rutina, porque la superficie de fallo es enorme: rate limits, un test flaky, una ventana de contexto que se llenó y compactó el plan, una herramienta que devolvió algo inesperado, un paso que se aplicó a medias antes de parar. Si el fallo es rutina, la recuperación no puede ser heroísmo. Tiene que ser un flujo que diseñaste a propósito.

Por qué los runs largos fallan en medio, no al principio

Una demo corre dos minutos y funciona o no. Un cambio real corre horas a través de lo que pienso como episodios: establece el primer comportamiento, mantenlo vivo mientras añades el segundo, sobrevive a un checkpoint y resume, gestiona el evento tardío, cubre el camino negativo, integra al final sin romper lo que dejó el primer episodio.

El fallo se agolpa en medio de esa cadena, y normalmente no es dramático. El agente no crashea. Deriva. El contexto se llena, la compactación tira la restricción del episodio uno, y el agente sigue, con seguridad, habiendo olvidado en silencio lo que hacía correcto al episodio uno. Escribí sobre ese mecanismo concreto en la compactación se come tus decisiones, y es la forma más común en que un run largo acaba mal en vez de parado.

Esa distinción importa. Un run que para es fácil: sabes dónde estás. Un run que sigue después de perder el hilo es el caro, porque ahora tienes un output que parece terminado y está sutilmente roto, y tienes que reconstruir lo que olvidó para siquiera ver el problema.

Recuperar no es reintentar

El arreglo ingenuo es un bucle de retry: falló, córrelo otra vez. A veces funciona, para un rate limit o un test flaky. Pero volver a correr desde arriba no es recuperación, es amnesia con tokens de más. Tira las tres horas de trabajo bueno para escapar de los últimos cinco minutos malos, y si el fallo fue una deriva y no un crash, el retry suele derivar igual.

reintentar

  • reinicia desde arriba
  • tira el trabajo bueno
  • deriva igual

recuperar

  • resume el último estado bueno
  • conserva el trabajo bueno
  • re-puntúa contra el contrato
Reintentar es amnesia con tokens de más; recuperar resume desde el último estado bueno y vuelve a demostrar el trabajo contra el contrato original.

La recuperación de verdad necesita tres cosas que un retry no tiene.

Un último estado bueno al que volver. No el principio, el último punto donde el trabajo se sabía correcto. Si tu único checkpoint es el commit inicial, cada fallo te cuesta el run entero.

Un registro de lo que ya era verdad. Las restricciones, las decisiones, los gates que estaban verdes antes del fallo. Sin eso, un run resumido no puede distinguir si continúa o si regresa.

Una forma de puntuar el trabajo resumido contra el contrato original. Esta es la parte que separa la recuperación que funciona de la que parece progreso y rompe otra vez el episodio uno en silencio. Un run reanudado tiene que demostrar que terminó igual que cualquier run, que es la disciplina entera de hacer que hecho signifique hecho: completitud demostrada contra los criterios, no auto-declarada.

Puse números reales sobre ese último punto en el experimento de recuperación: un agente de código guiado por un prompt fuerte, y el mismo agente con una skill de recuperación hecha a mano, los dos se quedaron clavados en la misma tasa de aprobado de dos tercios a través de cuatro episodios acumulativos, por la misma razón. Arreglaban el requisito nuevo y rompían un invariante viejo, y nada les decía que habían regresado. Lo que llegó a la meta no fue un modelo más listo. Fue algo fuera del agente que sostuvo el contrato y volvió a puntuar el trabajo tras cada intento. La recuperación es esa cosa de fuera, hecha rutina.

Diseñar para recuperar en vez de rezar contra el fallo

Si aceptas que los runs fallan en medio, diseñas el run para que el medio sea recuperable. Unos movimientos concretos, todos posibles hoy sin herramientas especiales:

  • Haz checkpoint en las fronteras de episodio, no solo al final. Después de que cada trozo coherente de trabajo pase su comprobación, commitéalo con el estado que lo hizo pasar. Una rama con seis checkpoints etiquetados es recuperable. Una rama con un diff gigante sin commitear es una moneda al aire.
  • Escribe el plan en un sitio que el run no posea. Si la única copia de “qué construimos y por qué” vive en la ventana de contexto del agente, muere cuando el contexto se compacta. Guárdalo en un fichero, una tarea, un artefacto, algo que dure más que la sesión. Es la misma razón por la que perder contexto entre sesiones mata la productividad, y el mismo arreglo.
  • Captura qué estaba verde antes de continuar. Antes de resumir, el run resumido necesita saber qué gates ya pasaban, para detectar una regresión en vez de celebrar un medio arreglo.
  • Haz que el fallo sea ruidoso. Un run que para con un claro “fallé aquí, este era el último estado bueno” vale por diez runs que derivan en silencio a una respuesta segura y equivocada. La mitad de la recuperación es solo darse cuenta pronto.

El hilo conductor: el estado que te deja recuperar tiene que vivir fuera del run. En cuanto la recuperación depende de leer el transcript, vuelves al trabajo de detective a las 2 de la mañana.

El caso del handoff

Recuperación y handoff son el mismo problema con distinta ropa. Cuando un run muere y lo resumes mañana, se lo estás pasando a una versión futura del run, y todo lo que hace funcionar un handoff limpio entre agentes es lo que hace funcionar la recuperación: el contrato, el último estado bueno, las decisiones hasta ahora y lo que aún falta demostrar tienen que viajar. Si nada viajó, resumir un run muerto y arrancar un agente nuevo duelen lo mismo, porque los dos empiezan desde un transcript que nadie quiere leer.

Dónde encaja PaellaDoc

Recuperar deja de ser heroísmo cuando el estado que un run necesita para resumir vive fuera del run y algo vuelve a comprobar el trabajo resumido contra el contrato original. Ese es el mecanismo, no una promesa de cifras. PaellaDoc trata un run como algo con checkpoints, un contrato contra el que se le sostiene, y un retry gobernado que resume desde el último estado bueno y vuelve a puntuar contra los gates que estaban verdes antes, así que un fallo a las 2am es un evento resumible y no una mañana leyendo logs. La persona que hace esa reconciliación a mano hoy es el runtime, y un build verde al final sigue contando solo cuando pasa los criterios que existían antes de empezar el run, que es el sentido entero de un build verde no es una feature correcta.

FAQ

¿Cómo recupero una ejecución fallida de agente sin empezar de cero?

Resume desde el último checkpoint donde el trabajo se sabía correcto, no desde el principio, y dale al run resumido el registro de qué ya estaba verde para que distinga continuación de regresión. Esto solo funciona si hiciste checkpoint en las fronteras de episodio y guardaste el plan y las restricciones fuera del contexto del agente. Si el único artefacto es un transcript largo, estás reconstruyendo, no recuperando.

¿Por qué volver a correr el agente no lo arregla sin más?

Volver a correr funciona para fallos transitorios como un rate limit, pero muchos fallos de run largo son deriva, no crashes: el agente perdió una restricción y siguió. Volver a correr desde arriba suele perder la misma restricción igual, y tira todo el trabajo correcto para escapar de la cola incorrecta. Recuperar necesita un último estado bueno y una forma de puntuar el trabajo resumido contra el contrato original.

¿Qué estado necesito capturar para que los runs sean recuperables?

Tres cosas: un checkpoint de último estado bueno en cada frontera de episodio, el plan y las restricciones guardados fuera del run para que la compactación no los borre, y un registro de qué gates pasaban antes del fallo. Con eso, un run resumido puede continuar desde un punto conocido y detectar si acaba de romper algo que antes funcionaba.