Inicio / Artículos

Sobrevivir a picos de tráfico en sistemas multiagente con un contrato de tres capas

Mantener funcionales los sistemas multiagente cuando el tráfico aumenta diez veces: un controlador de colas, limitación del tráfico por herramienta y verificaciones sincrónicas de políticas, además de métricas y una demostración de picos de tráfico.

Editor: REACTAPP.TOP8 min de lectura1869 palabras

Fuentes: Multi-Agent Orchestration in Production: Handling Traffic Spikes at Scale

Un grafo de agentes puede funcionar sin problemas con 10 solicitudes por segundo, pero colapsa al llegar a 100. Las solicitudes se acumulan, se omiten las verificaciones de políticas, las llamadas a las herramientas entran en conflicto entre sí, y un sistema que parecía fiable se convierte en una carga. Rara vez son los agentes los culpables. Lo que falta es un acuerdo explícito entre la cola, la capa de orquestación y las restricciones establecidas. La mayoría de los equipos solo descubren esa brecha cuando el tráfico real ya está afectándola.

Por qué los picos de tráfico llegan antes de lo esperado

Los sistemas de agentes están saliendo de la fase de demostración y entrando en producción real, por lo que los picos de carga son inevitables: una venta flash, una función que se vuelve viral, un trabajo por lotes programado, o simplemente todos los usuarios que acceden a la API el martes por la mañana. A medida que aumenta la carga, ocurren dos cosas al mismo tiempo.

Primero, el enrutamiento se realiza en tiempo real. Elegir qué agente manejará una solicitud o decidir si colocarla en cola de inmediato es un proceso medible.

En segundo lugar, los sistemas tienden a fallar cuando están bajo presión. Por lo general, no se trata de ingenieros en pánico que eluden las verificaciones, sino de un manejo ingenuo de errores que establece como valor predeterminado “permitir” en caso de tiempo de espera excesivo, o de rutas de respaldo que omiten silenciosamente el middleware de validación. Cuando la aplicación de políticas desaparece bajo carga, normalmente ese antipatrón arquitectónico es la causa, y no la intervención humana.

La solución es un acuerdo claro y aplicable entre tres componentes: la cola (lo que ingresa), el grafo de orquestación (a dónde va) y el motor de políticas (qué puede hacer). Acuerden esto desde el principio, y un aumento repentino de carga se convertirá en un problema resuelto en lugar de una emergencia.

Un contrato con tres capas

Capa 1: la cola como guardián

La cola es el primer lugar donde se aplican las reglas. Antes de que cualquier solicitud llegue a un agente, pasa por una verificación del contrato que abarca tres aspectos:

  • Prioridad. Las solicitudes no son iguales. Las consultas interactivas de los usuarios pueden tener mayor prioridad que los trabajos en lotes, y un cliente de pago puede superar en orden a uno de nivel gratuito. La cola debe gestionar este ordenamiento para que los agentes nunca necesiten estar al tanto de él.
  • Límites de tasa y cuotas. Decir que no es la opción más económica antes de que se gaste cualquier recurso computacional. Un usuario que haya alcanzado el límite diario de la API debe recibir una rechazo claro y no ocupar un lugar en la cola, lo cual cuesta mucho menos que recurrir a un agente.
  • Adherencia al esquema. Los datos de entrada que el sistema no puede procesar deben ser rechazados aquí y no descubiertos dentro de un agente.

Lo que promete esta capa es que cada solicitud en la cola haya sido validada, clasificada y aprobada para consumir recursos.

Capa 2: la orquestación decide el enrutamiento y el ritmo

Después de que una solicitud se coloca en cola, la orquestación elige su destino y qué acciones puede realizar. Aquí se controla la concurrencia y el acceso a las herramientas.

El contrato de enrutamiento responde a algunas preguntas: ¿qué grupo de agentes debería encargarse de la solicitud, por ejemplo, agentes rápidos para consultas simples y otros más lentos para razonamientos complejos? ¿Cuántas solicitudes puede manejar cada agente al mismo tiempo? ¿Qué ocurre cuando todos los agentes están ocupados: se colocan en cola, se rechazan o se recurre a una solución alternativa?

El contrato de limitación de herramientas reconoce que las herramientas difieren entre sí. Llamar a una API externa cien veces por segundo no es lo mismo que ejecutar un proceso de incrustación local. Cada herramienta tiene su propio límite de concurrencia. Una herramienta basada en una base de datos podría permitir diez llamadas simultáneas, mientras que una que accede a una API de terceros podría permitir cinco. Cuando una herramienta alcanza su límite, la solicitud espera en la cola propia de esa herramienta y no en la principal.

Para mantener sanas esas colas de herramientas y evitar que los fallos se propaguen, las primitivas de nivel de producción ayudan de la siguiente manera:

  • Los interruptores de circuito y los contenedores de tokens detectan fallos rápidamente en la capa de herramientas, evitando tormentas de intentos de reenvío cuando una dependencia está saturada.
  • Las colas de correos no deseados almacenan cargas de herramientas dañadas o corruptas, de modo que una sola solicitud defectuosa nunca detiene todo el ciclo del agente.
  • Los contenedores con fugas, mantenidos por cada herramienta, aplican su propio límite de velocidad, lo que protege las ejecuciones paralelas de los agentes de restricciones externas.

Con estos elementos en su lugar, los cuellos de botella a nivel de herramientas permanecen contenidos y visibles, sin extenderse a una inestabilidad en todo el sistema. La promesa de esta capa es que una solicitud llegue a un agente con capacidad disponible, que cada herramienta respete su límite de concurrencia y que, si una herramienta está saturada, la solicitud espere a esa herramienta y no al agente.

Capa 3: políticas que nunca ceden

Lo difícil es que las políticas deben ejecutarse en cada ocasión, incluso bajo carga. No existe la opción de “verificarlo más tarde” ni de pensar que “esta vez parece segura”.

El contrato de la política consta de cuatro cláusulas:

  • Se realiza una verificación de la política antes de cualquier llamada a una herramienta, de forma síncrona y de forma bloqueante.
  • La verificación es rápida, en unos pocos milisegundos como máximo. Si no lo es, el diseño está mal.
  • Cuando la verificación falla, la solicitud también falla. El agente no tiene oportunidad de intentarlo de nuevo ni de negociar.
  • Las fallas se registran y se monitorean, y un aumento en las fallas de las políticas en sí mismo es una señal.

Las políticas típicas se ven así: el agente solo puede usar herramientas marcadas como de solo lectura para este usuario; una herramienta solo puede ejecutarse para un llamante autenticado; el agente puede realizar como máximo cinco llamadas a herramientas por solicitud. La capa garantiza que se realice una verificación antes de cada llamada a herramienta, y que las políticas sean rápidas, deterministas y absolutas.

La estructura de la tubería

En un pseudo-gráfico mínimo, una solicitud entra a través del guardián de la cola, pasa al enrutador, llega a un agente, y cada llamada a herramienta pasa por la aplicación de políticas y un control de velocidad por herramienta antes de ejecutarse.

[ Incoming Request ]
         │
         ▼
[ Queue Gatekeeper ]
  • Validate Schema
  • Enforce Rate Limits
  • Assign Priority
         │ (Pass)
         ▼
[ Orchestration Router ]
  • Check Agent Capacity
  • Route to Target Node
         │
         ▼
[ Tool-Level Gate ]
  • Sync Policy Check
  • Per-Tool Concurrency Limits
  • Circuit Breaker / DLQ
         │ (Pass)
         ▼
[ Tool Execution ]

El detalle importante es dónde se aplican las políticas. Estas se aplican a nivel de herramienta y no a nivel de agente, lo que impide que un agente las evite accidentalmente al usar una herramienta de manera inesperada.

Un escenario de aumento repentino de tráfico, con y sin contrato

Imagínese un agente de soporte al cliente que puede buscar pedidos, procesar reembolsos y enviar correos electrónicos, y que de repente el tráfico aumenta diez veces.

Sin contrato, el fallo se desarrolla en varias etapas:

  • Las solicitudes entrantes agotan el grupo de agentes disponibles.
  • Los agentes comienzan a hacer cola para usar las herramientas, y la herramienta send_email acumula 500 llamadas pendientes.
  • El servicio de correo electrónico comienza a experimentar tiempos de espera excesivos.
  • Los agentes intentan nuevamente, por lo que ahora hay 1,000 llamadas en espera.
  • Se activan las rutas de recuperación ante fallos. Los tiempos de espera excesivos o las rutas de respaldo sobrecargadas evitan la aplicación de las políticas para mantener el sistema operativo.
  • Un agente envía un correo de reembolso al cliente incorrecto.

Con un contrato, el mismo escenario se desarrolla de manera diferente:

  • El controlador de colas regula la admisión según la prioridad y la cuota establecidas.
  • El enrutador detecta que la herramienta para enviar correos ha alcanzado su límite de concurrencia de diez, por lo que coloca en cola las solicitudes adicionales en dicha herramienta y no en el agente.
  • Las verificaciones de política siguen ejecutándose con cada llamada a la herramienta. Si un agente intenta enviar un reembolso al cliente equivocado, la verificación falla y la solicitud es rechazada.
  • El sistema permanece estable. La latencia aumenta, pero la fiabilidad no disminuye.

Resumiendo

Comenzar con LangGraph

Un equipo que ya trabaja en LangGraph posee la mayoría de las primitivas. Los agentes y las herramientas son nodos, las aristas representan las opciones de enrutamiento, y las aristas condicionales determinan el camino según el estado. Lo que se necesita añadir es una cola en la parte frontal, ya sea Redis, RabbitMQ o una simple cola en memoria, además de un middleware de políticas que rodee las llamadas a las herramientas.

El propio wrapper de políticas puede ser un pequeño decorador que realice las verificaciones y genere un error de violación antes de que la función de la herramienta se ejecute.

from functools import wraps
from typing import Callable, Any

class PolicyViolation(Exception):
    """Raised when policy check fails."""
    pass

def enforce_tool_policy(
    policy_fn: Callable[[str, dict], bool]
):
    """Sync tool execution guardrail."""
    def decorator(func: Callable):
        @wraps(func)
        def wrapper(*args, **kwargs):
            tool_name = func.__name__
            context = kwargs.get(
                "request_context", {}
            )

            # Must fail closed
            if not policy_fn(tool_name, context):
                raise PolicyViolation(
                    f"Denied: {tool_name}"
                )

            return func(*args, **kwargs)
        return wrapper
    return decorator

# Example Usage
def strict_policy_check(tool_name: str, context: dict) -> bool:
    if not context.get("is_authenticated"):
        return False

    if (context.get("role") != "admin" and tool_name.startswith("execute_")):
        return False

    return True

@enforce_tool_policy(strict_policy_check)
def execute_refund(
    order_id: str,
    amount: float,
    request_context: dict = None
):
    # Downstream API call
    return f"Refund ${amount} sent: {order_id}"

Mídalo

Implementarlo y esperar no es suficiente. Instrumentéelo y observe:

  • La profundidad de la cola en cada capa.
  • Con qué frecuencia y qué herramientas alcanzan sus límites.
  • Si las políticas están rechazando solicitudes y por qué.
  • Las latencias p50, p95 y p99, registradas por separado para diferentes tipos de solicitudes.
  • El costo por solicitud bajo carga. Si aumenta, hay alguna ineficiencia oculta.

Una visión escéptica

Es razonable decir que esto parece mucha infraestructura para un problema que aún no existe. Un sistema que maneja diez solicitudes por segundo y se mantendrá así no la necesita. Una función que podría volverse viral, o clientes empresariales con tráfico impredecible, son otro caso; el diseño debe estar listo antes del lanzamiento.

Building it in advance costs little next to retrofitting it after a spike has broken the system. It also shouldn't be overbuilt. A simple queue, a policy check function and per-tool concurrency limits resolve most spike problems. Neither a distributed consensus algorithm nor a machine-learning load predictor is needed. What matters is clarity and constraints.

Main points

  1. Spikes expose design problems. When an agent system breaks under load, the agents are not too slow. Nobody set clear rules for how the queue, the routing and the policies fit together.
  2. The queue doubles as an enforcement point. It can validate, prioritize and reject requests so compute is not wasted.
  3. Policies are absolute and non-negotiable. They run at the tool level, synchronously, every time, with no exceptions.
  4. Controlar las herramientas antes que a los agentes. Los cuellos de botella suelen encontrarse aguas abajo, en APIs externas o bases de datos que suministran el contexto al agente, y no en el proveedor de LLM.
  5. Medir todo: profundidad de la cola, concurrencia de herramientas, fallos en las políticas, percentiles de latencia y costo por solicitud. Lo que no se mide no puede mejorarse.

Por dónde empezar

  1. Escribir el contrato de la cola: qué se acepta y qué se rechaza.
  2. Escribir el contrato de enrutamiento: qué agentes se encargan de qué tareas y cuáles son los límites de concurrencia.
  3. Escribir el contrato de las políticas: qué verificaciones se realizan antes de cada llamada a una herramienta.
  4. Construirlo con algo como LangGraph, agregar una capa de cola y envolver las herramientas en verificaciones de políticas.
  5. Realizar pruebas de carga. Simular un aumento diez veces mayor y observar qué falla.

Todo esto es mejor hacerlo antes de la producción, mientras aún haya tiempo para abordarlo con calma.

Leer más

  • Por qué los agentes de IA en producción necesitan algo más que un buen prompt — Contexto, herramientas, memoria, manejo de fallos, rastreo, pruebas y autonomía basada en riesgos: la arquitectura alrededor de un LLM que hace que un agente sea fiable.