Capítulo 9: Bucles de agentes, validación y reintentos

Pon a los agentes límites de tiempo y de gasto, y ajusta cada política de reintentos al tipo de fallo.

Publicada el

Leer hasta 4 min completo

Visión general#

La petición inicial de un agente no te dice cuánto va a costar. Puede inspeccionar un repositorio, llamar a varias herramientas, revisar hipótesis e intentar un arreglo. Algunas tareas necesitan ese trabajo. Otras se atascan repitiendo la misma búsqueda o reparando la misma respuesta no válida.

Un límite de cinco pasos para todos los agentes es demasiado tosco para controlar el coste. Diez llamadas cortas a un modelo pequeño cuestan mucho menos que diez llamadas largas a un modelo de razonamiento caro. Necesitas límites de tiempo y de gasto, y una forma de detectar el trabajo improductivo.

Los fallos también son distintos. Una sobrecarga temporal del proveedor, un JSON no válido y unos datos extraídos incorrectos son problemas diferentes, y reintentar los tres con el mismo prompt rara vez es la mejor política.

Práctica#

Presupuesta los agentes según la operación que realizan. Registra las llamadas al modelo, los cargos de herramientas externas, el tiempo real transcurrido y un total monetario estimado. Aplica los límites en el código de la aplicación, no pidiéndole al modelo que “gaste menos de 1 $”. Si el presupuesto tiene que ser estricto, reserva el coste estimado antes de lanzar trabajo concurrente y después concilia el uso real.

Define qué devuelve el agente cuando se detiene. Al llegar al límite del presupuesto, debería informar de los hallazgos confirmados, los cambios realizados, el trabajo pendiente y el motivo por el que se detuvo. Un resultado parcial que puedes rastrear vale más que un fallo genérico que tira el progreso a la basura.

Ajusta la política de recuperación al fallo:

FalloMejor primera respuesta
Sobrecarga transitoria o error de redReintentos acotados, backoff y jitter
Esquema no válidoForzar la salida estructurada soportada y validar de forma determinista
Falta de evidenciaRecuperar o aportar la fuente que falta
Interpretación semántica incorrectaReevaluar el modelo, la entrada y el método de verificación
Parámetro no soportado o crédito agotadoCorregir la configuración o la facturación, no reintentar a ciegas

Vigila la amplificación de reintentos. Si un SDK hace hasta tres intentos y tu aplicación lo envuelve en otros tres, el servicio de origen puede ver nueve peticiones. Fija un plazo global y conoce el comportamiento de reintentos de cada capa.

Tres intentos de la aplicación alrededor de un SDK que también reintenta tres veces pueden llegar al proveedor nueve veces por una sola tarea.

Ledgerline. El agente de investigación de Ledgerline promediaba 0,40 $ por informe aceptado, pero su ejecución P95 costaba varios dólares: una ejecución de cada veinte seguía buscando documentos regulatorios que no existían. Un tope de 1,00 $ por ejecución, una comprobación de búsquedas repetidas y un estado de parada que devuelve los hallazgos obtenidos hasta ese momento bajaron la línea de investigación de 800 $ a 640 $ al mes, con el mismo número de informes aceptados.

En profundidad#

La validación tiene su propio coste. Un parser de JSON es barato. Un segundo LLM que revise cada respuesta puede aumentar de forma considerable el gasto de inferencia y aun así pasar por alto errores semánticos. Usa comprobaciones deterministas siempre que puedas: restricciones de esquema, códigos de estado conocidos, aritmética, identificadores y datos de referencia fiables.

Para un modelo simple de reintentos acotados, en el que cada intento cuesta C, tiene éxito de forma independiente con probabilidad p > 0 y se permiten N intentos:

Con p = 0,8 y N = 3, eso son 1,24 intentos, así que la llamada cuesta de media alrededor de un 24 % más que C. Con p = 0,4, son 1,96 intentos, y el 22 % de las tareas sigue fallando después del tercer intento.

Los reintentos reales rara vez se comportan de forma tan ordenada. Los errores del modelo suelen estar correlacionados entre intentos, los intentos pueden costar cantidades distintas y algunos fallos necesitan recuperación mediante herramientas o personas. En los agentes, guarda un registro de los enfoques fallidos para que el agente no los repita después de una compactación del contexto.

Mide el progreso por los cambios de estado, como evidencia nueva, hipótesis descartadas, trabajo completado o un enfoque distinto. Que el propio agente afirme que está avanzando no cuenta.

Una ejecución con presupuesto, sin fingir que las estimaciones son facturas#

Mantén el presupuesto del agente fuera del modelo. El patrón mínimo de abajo hace una reserva antes de lanzar una llamada. Supone que un único worker actualiza el estado local; la concurrencia en producción necesita una reserva atómica en base de datos u otro mecanismo de sincronización.

python
from dataclasses import dataclass
from decimal import Decimal

@dataclass
class RunBudget:
    ceiling: Decimal
    reserved: Decimal = Decimal("0")
    actual: Decimal = Decimal("0")
    model_calls: int = 0
    tool_calls: int = 0

    def reserve(self, predicted: Decimal) -> None:
        if self.actual + self.reserved + predicted > self.ceiling:
            raise RuntimeError("Task budget exhausted")
        self.reserved += predicted

    def reconcile(self, predicted: Decimal, charged: Decimal) -> None:
        self.reserved -= predicted
        self.actual += charged

La reserva solo decide si una llamada puede empezar; no pone un tope a la factura. El cargo real puede superar la estimación, los proveedores pueden facturar una petición que agotó el timeout y varios workers podrían entrar en condición de carrera salvo que la reserva sea atómica. Para limitar la exposición, acota los tokens y las herramientas donde se pueda, fija plazos, impide la concurrencia no autorizada y define qué pasa al llegar al tope.

Las llamadas de pago a búsqueda, navegación y procesamiento de documentos entran en el mismo presupuesto que las llamadas al modelo.

Referencias: AWS: backoff y jitter, Rate limits de OpenAI, Salidas estructuradas de OpenAI, Errores de la API de Anthropic y reintentos del SDK.