Rate limiting para agentes de IA: cómo contener picos de solicitudes y tormentas de retries

Rate limiting práctico en producción: límites per-user/per-tenant/global, control de burst, retry_after, backoff, audit logs y alerting.
En esta página
  1. Idea en 30 segundos
  2. Problema
  3. Solución
  4. Rate limiting ≠ step limits
  5. Componentes del control rate limiting
  6. Cómo se ve en la arquitectura
  7. Ejemplo
  8. En código se ve así
  9. Cómo se ve durante la ejecución
  10. Escenario 1: parada por límite de tenant
  11. Escenario 2: pico burst
  12. Escenario 3: ejecución normal
  13. Errores típicos
  14. Autoevaluación
  15. FAQ
  16. Dónde encaja Rate Limiting en el sistema
  17. Páginas relacionadas

Idea en 30 segundos

Rate limiting es un control de runtime que limita la frecuencia de llamadas externas del agente para evitar picos y tormentas de retries en producción.

Cuándo se necesita: cuando un agente llama con frecuencia a model/tool API, tiene lógica de retries y opera bajo carga pico.

Problema

Sin rate limiting, un servicio inestable acelera rápido una cadena: retry → llamada → otro retry. En demo casi no se nota. En producción, este comportamiento crea olas de 429/5xx, colas y latencia.

Lo peor es que el incidente se amplifica solo:

  • un usuario demasiado activo consume cuotas
  • un tenant "asfixia" a otros
  • un pico global rompe dependencias para todos a la vez

Y cada minuto sin control de frecuencia agrega más retries, colas y retrasos, hasta que el sistema termina haciéndose DDoS a sí mismo.

Analogía: es como una rampa de acceso regulada a la autopista. Si no dosificas el flujo, incluso una buena vía se convierte en atasco en minutos.

Solución

La solución es añadir una capa policy de rate limit centralizada en runtime y en tool gateway. Cada llamada externa del agente se verifica contra límites per_user, per_tenant, global y burst_tokens.

La policy devuelve una decisión técnica: allow o stop con razón explícita:

  • rate_limited_user
  • rate_limited_tenant
  • rate_limited_global
  • burst_limited

Cuando devuelve stop, runtime responde al cliente con retry_after_ms y no ejecuta la llamada. Es una capa separada del sistema, no parte del prompt ni de la lógica del modelo.

Rate limiting ≠ step limits

Son niveles de control distintos:

  • Rate limiting limita la frecuencia de llamadas externas.
  • Step limits limita la longitud y el comportamiento del runtime loop.

Uno sin el otro no alcanza:

  • sin rate limiting, las API externas caen por picos y tormentas de retries
  • sin step limits, un run puede seguir girando demasiado incluso con frecuencia moderada

Ejemplo:

  • rate limiting: no más de per_user=6 llamadas por 10 segundos
  • step limits: max_steps=18, max_repeat_action=3

Componentes del control rate limiting

Estos componentes trabajan juntos en cada llamada externa del agente.

ComponenteQué controlaMecánicas clavePara qué
Per-user limitComportamiento de un usuarioper_user quota
sliding window
Evita que un usuario consuma toda la capacidad
Per-tenant limitCarga de un tenantper_tenant quota
tenant-scoped keys
Aísla picos entre clientes
Global limitCarga total del sistemaglobal cap
shared limiter
Protege dependencias externas frente a picos masivos
Burst controlPicos cortos de tráficotoken bucket
refill rate
Amortigua saltos inmediatos sin detener todo el sistema
Rate-limit observabilityVisibilidad de decisiones policyaudit logs
alerts sobre stop spikes
No limita llamadas de forma directa, pero ayuda a ubicar rápido la fuente del pico

Ejemplo de alerta:

Slack: 🛑 Support-Agent hit rate_limited_tenant. retry_after=1200ms, tenant=t_42.

Cómo se ve en la arquitectura

La capa policy de rate limit se ubica entre runtime y model/tool API externos. Cada decisión (allow o stop) se registra en audit log.

Cada llamada externa del agente pasa por este flow antes de ejecutarse: runtime no ejecuta llamadas directamente, primero pide una decisión al policy layer.

Resumen del flow:

  • Runtime forma una llamada externa del agente
  • Policy verifica per_user, per_tenant, global y burst_tokens
  • allow → se ejecuta la llamada
  • stop → se devuelve retry_after_ms y respuesta parcial
  • ambas decisiones se escriben en audit log

Ejemplo

Un agente de soporte procesa muchas solicitudes en paralelo y reintenta crm.search varias veces.

Con rate limiting:

  • per_user = 6 / 10s
  • per_tenant = 120 / min
  • global = 50 / s
  • burst_tokens = 5

→ el pico se detiene a nivel policy antes de que fallen dependencias y colas.

Rate limiting detiene el incidente justo antes de la llamada externa, no después de una ola de 429.

En código se ve así

El esquema simplificado de arriba muestra el flujo principal. Punto crítico: el rate-limit check debe ser O(1) y atómico (normalmente via Redis/Lua o equivalente), si no, se convierte en cuello de botella bajo pico. Después de stop(...), runtime suele devolver respuesta parcial al cliente con razón explícita y retry_after_ms.

Ejemplo de configuración de rate limit:

YAML
rate_limits:
  per_user_10s: 6
  per_tenant_min: 120
  global_rps: 50
  burst_tokens: 5
  refill_per_second: 2
PYTHON
action = planner.next(state)
action_key = make_action_key(action.name, action.args)

if not action.is_external_call():
    # execute_local — helper condicional para acciones locales sin API externa.
    # Decision.allow — helper condicional para mantener un modelo único outcome/reason.
    local_result = execute_local(action)
    local_decision = Decision.allow(reason=None)
    audit.log(
        run_id,
        decision=local_decision.outcome,
        reason=local_decision.reason,
        scope="local",
        action=action.name,
        action_key=action_key,
        result=local_result.status,
    )
    return local_result

decision = rate_limit.check(
    user_id=state.user_id,
    tenant_id=state.tenant_id,
    action=action.name,
    now_ms=clock.now_ms(),
)

if decision.outcome == "stop":
    audit.log(
        run_id,
        decision=decision.outcome,
        reason=decision.reason,
        scope=decision.scope,
        retry_after_ms=decision.retry_after_ms,
        action=action.name,
        action_key=action_key,
    )
    alerts.notify_if_needed(run_id, decision.reason, scope=decision.scope)
    return stop(decision.reason, retry_after_ms=decision.retry_after_ms)

result = executor.execute(action)

audit.log(
    run_id,
    decision=decision.outcome,
    reason=decision.reason,
    scope=decision.scope,
    action=action.name,
    action_key=action_key,
    result=result.status,
)

return result

Cómo se ve durante la ejecución

Escenario 1: parada por límite de tenant

  1. Runtime forma llamada externa crm.search.
  2. Policy detecta cuota per_tenant excedida.
  3. Decisión: stop (reason=rate_limited_tenant).
  4. Runtime devuelve retry_after_ms.
  5. La llamada no se ejecuta y el evento se guarda en audit log.

Escenario 2: pico burst

  1. Varios runs crean un pico corto de llamadas al mismo tiempo.
  2. Policy agota burst_tokens.
  3. Decisión: stop (reason=burst_limited).
  4. Parte de llamadas se rechaza con retry_after_ms.
  5. El sistema se mantiene estable sin cascade-failure.

Escenario 3: ejecución normal

  1. Runtime forma llamada externa.
  2. Policy verifica límites: todo dentro de rango.
  3. Decisión: allow.
  4. La llamada se ejecuta.
  5. Decisión y resultado se registran en audit log.

Errores típicos

  • poner solo límite global sin aislamiento per_user/per_tenant
  • no devolver retry_after al hacer stop
  • hacer retry sin backoff ni jitter
  • verificar rate limit en una sola capa (solo runtime o solo gateway)
  • no loguear decisiones stop (reason, scope, retry_after_ms)
  • no tener alerting en picos rate_limited_*

Resultado: el sistema parece controlado, pero se degrada rápido ante picos reales.

Autoevaluación

Chequeo rápido de rate limiting antes de lanzar a producción:

Progreso: 0/8

⚠ Faltan controles base de governance

Antes de production necesitas como mínimo control de acceso, límites, audit logs y parada de emergencia.

FAQ

P: ¿Con qué límites conviene empezar?
R: Como mínimo: per_user, per_tenant, global + un control pequeño de burst. Después ajusta según eventos stop reales.

P: Si la API externa ya devuelve 429, ¿igual necesitamos rate limiting propio?
R: Sí. El rate limiting interno protege runtime antes del 429 externo y da stop reasons, retry_after y audit controlados.

P: Ante límite alcanzado, ¿mejor stop o cola?
R: Para runs sync suele ser mejor stop + retry_after. En pipelines async puedes agregar cola, pero siempre con límites explícitos y timeout.

P: ¿Rate limiting reemplaza budget controls?
R: No. Rate limiting controla frecuencia de llamadas, budget controls controla el gasto total del run.

P: ¿Dónde guardar contadores?
R: En almacenamiento compartido de baja latencia con operaciones atómicas (a menudo Redis). Sin esto, los límites serán inconsistentes entre instancias.

Dónde encaja Rate Limiting en el sistema

Rate limiting es una de las capas de Agent Governance. Junto con RBAC, budgets, step limits, approval y audit, forma un sistema unificado de control de ejecución.

Páginas relacionadas

Siguiente sobre este tema:

⏱️ 7 min de lecturaActualizado 27 de marzo de 2026Dificultad: ★★★
Implementar en OnceOnly
Budgets + permissions you can enforce at the boundary.
Usar en OnceOnly
# onceonly guardrails (concept)
version: 1
budgets:
  max_steps: 25
  max_tool_calls: 12
  max_seconds: 60
  max_usd: 1.00
policy:
  tool_allowlist:
    - search.read
    - http.get
writes:
  require_approval: true
  idempotency: true
controls:
  kill_switch: { enabled: true }
Integrado: control en producciónOnceOnly
Guardrails para agentes con tool-calling
Lleva este patrón a producción con gobernanza:
  • Presupuestos (pasos / topes de gasto)
  • Permisos de herramientas (allowlist / blocklist)
  • Kill switch y parada por incidente
  • Idempotencia y dedupe
  • Audit logs y trazabilidad
Mención integrada: OnceOnly es una capa de control para sistemas de agentes en producción.

Autor

Nick — ingeniero que construye infraestructura para agentes de IA en producción.

Enfoque: patrones de agentes, modos de fallo, control del runtime y fiabilidad del sistema.

🔗 GitHub: https://github.com/mykolademyanov


Nota editorial

Esta documentación está asistida por IA, con responsabilidad editorial humana sobre la exactitud, la claridad y la relevancia en producción.

El contenido se basa en fallos reales, post-mortems e incidentes operativos en sistemas de agentes de IA desplegados.