Nota

El bucle del agente bajo un orquestador durable: dónde está la frontera

El grafo de agentes y el motor de durable execution son ambos orquestadores. Un mes en el banco mostró dónde trazar la frontera y cuánto cuesta.

¿Dónde colocar el bucle del agente cuando el sistema ya tiene un motor de durable execution? La bifurcación parece una elección de herramienta — grafo de agentes contra motor —, pero ambos son orquestadores: cada uno con su bucle, su estado y sus reintentos. La pregunta no es cuál gana, sino dónde trazar la frontera entre ellos y cuánto cuesta esa frontera.

Lo que sigue es un mes de trabajo en un banco de pruebas donde esa frontera está trazada: Temporal sobre Python, una pasarela de modelos, trazado de llamadas y siete escenarios, entre ellos un agente desarrollador y una investigación SRE automatizada.

La tesis inicial

La regla se adoptó el primer día: una llamada al modelo equivale a una activity, y el grafo pertenece al motor de durable execution, no al framework de agentes.

El razonamiento sonaba sólido. Si envuelves todo el bucle del agente en una sola activity, el motor ve desde fuera un bloque opaco: un fallo en el paso ocho de quince lo reproduce desde el paso uno. La resistencia paso a paso, la razón por la que se eligió el motor, desaparece, y encima aparecen dos capas de reintentos que se ignoran mutuamente.

Una advertencia honesta: eso es razonamiento, no medición. La alternativa nunca se construyó en este banco, así que el mes puso a prueba el precio de la decisión, no la tesis. Ese precio son los datos.

Lo que no se puede afirmar

«Los frameworks de agentes no ofrecen durabilidad» es falso. Los grafos de agentes actuales persisten su estado, con elección de modo: solo al salir del grafo, de forma asíncrona o de forma síncrona antes de cada paso. La diferencia real corre por otra línea.

Primero, la granularidad: qué cuenta como paso que no se vuelve a ejecutar. Segundo, de quién es el presupuesto que limita los intentos. Tercero, qué hace una reanudación: los nodos posteriores al punto de recuperación se ejecutan otra vez, llamadas al modelo y peticiones externas incluidas. El punto de recuperación es justo la unidad que se paga dos veces.

La integración oficial entre el motor y el SDK de agentes resuelve exactamente eso: el bucle del agente, la selección de herramientas y los traspasos viven en el workflow, mientras que cada llamada individual al modelo se ejecuta como activity y por eso no se repite en el replay. Un framework de agentes encerrado en una sola activity es malo. Un bucle de agente bajo un orquestador con un turno por activity es un diseño corriente y soportado. La versión ingenua de la tesis borra esa distinción.

Tres capas de reintento que no se pueden apilar

En la práctica todo se redujo a una separación dura de perfiles de activity, y la diferencia entre ellos es de fondo.

PerfilTiempo límiteIntentosPor qué
código: validación, renderizado, escrituras20–30 s3el fallo es barato y suele ser transitorio
llamada al modelo180–240 s1el fallo no es transitorio y cuesta dinero
efecto en produccióndesde la política1un replay silencioso de una mutación es una decisión, no un valor por defecto

El reintento con retroalimentación — los errores de validación vuelven al prompt — vive dentro del paso. Un reintento de infraestructura por encima sería una segunda capa invisible: no conoce el contrato, no ve el presupuesto del episodio y paga cada intento. De eso advertía la tesis original, solo que las capas resultaron ser «reintento del paso contra reintento de la activity» y no un framework contra otro.

El efecto merece línea propia. Es idempotente por clave y aun así no recibe reintento de infraestructura: la idempotencia hace segura la repetición, no deseable. Una activity así toma su plazo del fichero de política y no de una constante en el código, para que el tiempo límite escrito en la política sea el real.

Dónde termina la autonomía

La fórmula a la que llegó el banco: libertad dentro del episodio, determinismo entre episodios.

El único lugar donde el modelo decide qué hacer a continuación es un episodio hijo. Elige qué ficheros abrir y en qué orden, devuelve una propuesta tipada y muere. Fuera de él no cambia nada: la instantánea, el presupuesto de intentos, si el resultado se aplica y si habrá otra pasada pertenecen al padre. El hijo no tiene herramienta de escritura ni shell, y las herramientas de lectura resuelven cada ruta que envía el modelo y rechazan todo lo que caiga fuera.

Los presupuestos viven como constantes en el código, no como deseos en el prompt: un límite de turnos dentro de una pasada, un límite de pasadas y un tope aparte para respuestas rechazadas, contado por episodio y no por ronda. Un tope por ronda se multiplica: tres rondas de tres intentos son nueve llamadas en el peor camino, cada una cargando ficheros enteros en el contexto.

El límite de turnos se gana su sitio por otra razón que conviene conocer de antemano. Un modelo que relee el mismo fichero una y otra vez agotará todo el presupuesto sin avanzar. Así se comporta un modelo de razonamiento cuando le reescriben su propio historial por debajo — por ejemplo, un saneador que tokeniza rutas en sus llamadas previas a herramientas.

El fallo que ninguna capa ve

El hallazgo principal del banco y la razón por la que «envolver y olvidar» no funciona.

Una generación que termina en error del proveedor llega como HTTP 200 sin contenido y sin llamada a herramienta. Quema decenas de miles de tokens de salida y no trae cabecera de precio: nadie la factura.

CapaReacciona antePor qué calla
cadena de fallback de la pasarelaerror HTTPla respuesta es 200
reintento de activity del orquestadoruna excepciónno hay excepción
SDK de agentesrespuesta mal formadala respuesta está bien formada, solo vacía

Donde un turno está obligado a llamar a una herramienta, un turno vacío se come uno de diez intentos, y el episodio puede gastar todo su presupuesto sin responder nada. Mientras tanto la factura parece barata: de ahí una métrica dedicada a las llamadas sin precio, que hace visible la pérdida en lugar de dejarla con aspecto de gratuita. Qué técnicas de ahorro de tokens funcionan de verdad y cuáles están sobrevendidas lo conté aparte.

El detector tuvo que leer la respuesta ya parseada y no el cuerpo HTTP: consumir el cuerpo en un hook de transporte rompe el propio parseo del SDK. Cuando el SDK devuelve el resultado, la razón de finalización ya se perdió, así que queda comprobar la consecuencia: el turno no produjo nada. Los límites de esa comprobación importan. «Sin llamada a herramienta» es incorrecto para agentes que responden en prosa; «consumo de tokens cero» declararía fallo una terminación legítimamente vacía.

Una ejecución medida en el nivel barato: 167 turnos, de los cuales 97 volvieron sin precio. Reenviar la petición al mismo modelo salvó 0 de 36, ni una sola vez. El nivel fuerte respondió los 36, ninguno vacío. La proporción de casos respondidos subió del 79 al 91 por ciento. El precio por turno entre niveles difiere en torno a siete veces.

Lo que importa más que las cifras: el arreglo rodea el fallo, no lo reduce. El nivel barato sigue produciendo un turno vacío en más de la mitad de sus llamadas. Un tercer camino — cambiar la petición en vez del nivel, con un esquema de herramientas más pequeño o una obligación relajada de invocarlas — nunca se midió aquí y sigue siendo una conjetura.

El precio: un paso no se puede borrar

Consecuencia directa de que cada paso sea una activity. El replay corre contra el historial grabado, así que quitar una llamada que un workflow vivo ya hizo es una ruptura de compatibilidad.

En el banco esto apareció durante una migración de métricas: dos activities perdieron su sentido cuando los datos pasaron a escribirse directamente en una tabla durable, y se quedaron en el código como cuerpos vacíos con el comentario «nombre y firma conservados a propósito». La métrica no vale una ruptura, y la llamada no se puede eliminar.

La otra cara de esa moneda es la razón de ser del diseño: el paso corre dentro de una activity, así que el replay nunca vuelve a llamar al modelo — el resultado ya está en el historial. Un reinicio del worker, una recuperación tras una caída o un historial reproducido no pagan tokens dos veces. Lo que se compra no es atomicidad sino repetibilidad sin segunda factura, la misma lógica que en un despliegue donde el registro de estado se escribe al final.

Cuándo elegir qué

SituaciónQué tomarQué se acepta
la tubería ya corre bajo un motor de durable executionturno del modelo = activity, bucle en el workflowun paso no se puede borrar; las firmas viven para siempre
agente autónomo, pasos baratos y reversiblesgrafo de agentes con su propia persistencialos nodos posteriores al punto de recuperación se repiten, llamadas al modelo incluidas
hay efectos en el mundo exteriormotor con perfiles de reintento explícitosmás código alrededor de cada paso, reintentos bajo control
prototipo, un error solo cuesta tiemponinguno de los dos; un bucle sencillo en códigosin recuperación; reescribir en el primer uso productivo

Lo que esta experiencia no demuestra

Un grafo de agentes como dueño del bucle nunca se levantó aquí: todo lo dicho sobre sus modos de persistencia viene de su documentación, no de una ejecución propia. En un mes no se produjo ni un solo error de no determinismo en el replay — los datos no distinguen entre «la disciplina aguantó» y «nunca se le exigió», y la conclusión «la descomposición protege del no determinismo» no se sigue de ahí. Las cifras son un banco y una o dos ejecuciones: la variación entre sesiones nunca se midió.

Que la parte cara de un sistema agéntico no sea el harness sino todo lo que lo rodea tiene su propio texto. Este banco añade una corrección: lo caro es la frontera. Se traza una vez, antes del primer uso productivo, y luego se convive con que cada paso queda en el historial para siempre.

© 2026 axyi.ru · CC BY 4.0