BrunoP.Blog

Mi agente de IA entró en bucle y casi revienta el presupuesto: el 'balde de fichas' que frena la cuenta

Un agente autónomo entró en bucle llamando una herramienta cara y se volvió un DoS financiero contra mí mismo. Te muestro el disyuntor clásico que usa todo sistema serio — visto como un balde de fichas que gotea.

El otro día dejé un agente de IA corriendo solo de madrugada — de esos que reciben una tarea, piensan, llaman herramientas, leen el resultado y deciden el siguiente paso. La idea era noble: iba a organizar un montón de datos por mí mientras yo dormía. Me desperté, abrí el panel de costos de la API y casi tiro el café sobre el teclado. La cosa había entrado en bucle — llamando, sin parar, una herramienta cara que hace una consulta externa — y se pasó la noche entera metralleando la misma llamada miles de veces.

No fue un ataque de afuera. Fui yo mismo, sin querer, montando un DoS financiero contra mi propia cuenta. El agente no tenía noción de "freno": para él, si la tarea no terminó, basta con intentar de nuevo. Y de nuevo. Y de nuevo. Por suerte el daño fue pequeño porque tenía un tope de gasto en la propia plataforma — pero el susto me devolvió a un patrón de backend que ya conocía bien y que, sinceramente, debería haber estado ahí desde el primer día: el token bucket, el "balde de fichas".

El nuevo dolor de cabeza de 2026: el agente que se ataca solo

Siempre pensamos el rate limiting como defensa contra los demás: el bot que intenta entrar mil veces, el scraper que exprime tu API, el gracioso del F5. Pero con los agentes autónomos surgió una categoría nueva de problema — el sistema que se ataca a sí mismo. Un bucle mal cerrado, una condición de parada que nunca se cumple, una herramienta que devuelve error y el agente lo reinterpreta como "intenta de nuevo": listo, tienes una máquina perfecta de quemar dinero y reventar la cuota.

El detalle cruel es que el costo es real e inmediato. Cada llamada de herramienta cara — una búsqueda paga, una generación de imagen, una consulta a una base externa — tiene precio. Multiplica por miles de iteraciones por minuto y la pérdida se vuelve exponencial mientras duermes tranquilo creyendo que "está trabajando".

El balde de fichas, explicado de verdad

El token bucket es probablemente el algoritmo de rate limiting más usado del mundo — está en el borde de AWS, de Cloudflare, de nginx, de prácticamente toda API seria. Y su belleza es que cabe en una imagen mental simple: imagina un balde.

  • El balde tiene una capacidad máxima de fichas (digamos, 10).
  • Un grifo gotea fichas nuevas dentro a una tasa constante (por ejemplo, 2 fichas por segundo).
  • Cada vez que llega una petición, tiene que tomar una ficha para ser atendida.
  • Si hay ficha, la petición pasa y la ficha desaparece. Si el balde está vacío, la petición es rechazada al instante.
  • El balde nunca se desborda: cuando se llena, las fichas que gotearían de más simplemente se pierden.

Es solo eso. Y esa simplicidad es exactamente lo que le da su propiedad más valiosa: permite ráfagas cortas, pero limita el promedio. Si el balde está lleno y llegan 10 pedidos de golpe, todos pasan — perfecto para picos legítimos. Pero si los pedidos siguen llegando más rápido de lo que el grifo repone, el balde se vacía y el exceso empieza a chocar contra la pared. Es el disyuntor: deja fluir el uso normal y corta el abuso, sin que tengas que adivinar un límite rígido de "X por segundo".

El bucle de mi agente habría chocado contra esa pared en pocos segundos. En vez de miles de llamadas caras, habría conseguido solo el puñado que el balde permite por ventana de tiempo — y cada rechazo habría sido una señal clara de "oye, algo anda mal aquí".

¿Por qué no un contador simple?

La pregunta obvia es: ¿por qué no solo contar "máximo 100 por minuto" y poner el contador a cero cada minuto? Eso es la ventana fija, y tiene un agujero clásico: el problema del borde de la ventana. Si tu límite es 100 por minuto, puedes mandar 100 a las 12:00:59 y otras 100 a las 12:01:00 — 200 llamadas en dos segundos, dentro de las reglas. El token bucket no tiene ese agujero porque razona sobre una tasa continua de reposición, no sobre bloques de tiempo recortados.

Tiene un primo llamado leaky bucket, que piensa al revés: las peticiones entran en un balde y se filtran por un agujero a una tasa fija, suavizando el flujo de salida. El token bucket es más permisivo con las ráfagas; el leaky bucket es más rígido y constante. Para frenar a un agente en bucle, prefiero el token bucket: tolera el trabajo legítimo en lote y solo muerde cuando el ritmo pasa del límite sostenible.

Curiosidades que me gustan

  • Vive escondido frente a ti. ¿Ese encabezado Retry-After o X-RateLimit-Remaining que has visto en una respuesta HTTP 429? Casi siempre es un token bucket (o un primo) diciendo "tu balde está vacío, vuelve en tantos segundos".
  • Se implementa en ~15 líneas. Ni siquiera necesitas un hilo goteando fichas. El truco es guardar solo dos números — cuántas fichas tenías y cuándo fue la última vez — y calcular "cuántas gotearon desde entonces" en el momento en que llega el pedido. Recarga perezosa. Demasiado elegante.
  • Es la base del "burst" que adoras. Cuando un servicio te deja superar el límite por unos segundos antes de frenarte, es el balde lleno gastándose. La capacidad del balde es el tamaño de la ráfaga permitida.

Mi opinión honesta

Después de ese susto, mi regla se volvió simple: todo agente autónomo nace con correa. Antes de soltar cualquier bucle que llame una herramienta paga, pongo un token bucket frente a la llamada cara — y, de paso, un tope absoluto de gasto por ejecución. No es desconfianza del modelo; es higiene de ingeniería. La misma razón por la que ponemos un fusible en el enchufe no es creer que el aparato es malo — es que las fallas ocurren, y el costo de no tener el freno es demasiado alto.

Y aquí está el punto que más quiero transmitir: el rate limiting no es "cosa aburrida de infra". Es control de costo, es previsibilidad, es lo que separa un prototipo que se vuelve una factura sorpresa de un producto que puedes dejar corriendo sin miedo. Cuando construyo algo para un cliente, ese freno va incluido — porque el peor bug no es el que rompe, es el que funciona perfectamente haciendo lo equivocado miles de veces.

Basta de charla — siente el freno en la mano 👇

Armé un simulador del balde aquí abajo. Mueve el grifo (fichas por segundo) y la capacidad del balde, y dispara pedidos haciendo clic. Cuando quieras ver la pesadilla, activa el modo "agente en bucle": metrallará peticiones igual que el mío de la madrugada. Mira el balde vaciarse, los pedidos chocar contra la pared (rojo) y el contador de "costo ahorrado" subir — es exactamente ese dinero que el freno salva.

token-bucket.js

Simulación 100% local en JS, sin red. El "precio" por petición es ficticio, solo para que sientas el daño.

Si activaste el modo bucle y miraste al balde intentar (y fracasar en) seguirle el ritmo a la metralleta, sentiste en carne propia por qué este patroncito es tan querido. Cuatro o cinco líneas de lógica que separan a un agente bien portado de una factura que da miedo abrir. Así es más o menos como trabajo: tomo el aprieto real de una madrugada y lo convierto en una pieza de ingeniería que puedes tocar. Si tienes un producto, un agente o una API que necesita correr sin darte un susto a fin de mes — ¿charlamos?

Hablemos de tu proyecto