La operabilidad de sistemas basados en Large Language Models (LLM) en producción presenta desafíos únicos que van más allá de la monitorización tradicional de servicios. A diferencia de los servicios convencionales que fallan con un error 500, un sistema LLM defectuoso puede tomar acciones incorrectas a gran escala y rápidamente. Esto requiere un enfoque de observabilidad que no solo monitoree la salud del servicio, sino también la calidad y el comportamiento de las decisiones del agente LLM. La tesis central es que un sistema LLM operable se construye alrededor de un 'chokepoint' arquitectónico unificado (un gateway) que centraliza la medición, el control de costos, el enrutamiento, la conmutación por error y el 'kill switch', permitiendo una gestión proactiva y una respuesta rápida ante incidentes.
La necesidad de esta capa de operabilidad es crítica ahora debido a la naturaleza no determinista y el costo variable de los LLM, así como la rápida evolución del panorama de proveedores. Sin una infraestructura robusta para observar, controlar y reaccionar a las decisiones del modelo, un sistema LLM es inherentemente frágil y costoso de operar. Este enfoque se alinea con principios de ingeniería de confiabilidad (SRE) aplicados a un dominio emergente, donde la 'decisión' del agente se convierte en la unidad fundamental de monitoreo y control.
Arquitectura del Sistema
La arquitectura propuesta se basa en un 'chokepoint' centralizado, un LLMGateway, que actúa como el único punto de entrada para todo el tráfico de modelos. Este gateway es responsable de varias funciones críticas: medición de costos y tokens (llm_tokens_total, llm_cost_usd_total), aplicación de límites de tasa y presupuestos (redis.incr_window), enrutamiento de solicitudes a diferentes modelos (route_chain) basados en criterios como el riesgo o el tipo de tarea, y gestión de la conmutación por error (circuit breaker) entre proveedores de modelos. También implementa un 'kill switch' granular (Mode.LIVE, Mode.HUMAN_ONLY, Mode.HALTED) con propagación rápida a través de un almacén de estado compartido o pub-sub.
La observabilidad se estructura en tres capas de logging (Tier 1: operacional, Tier 2: metadatos de decisión, Tier 3: datos sensibles) y un sistema de tracing que abarca el grafo de decisiones interno del agente, utilizando un decision_id unificado para correlacionar métricas, logs y trazas. Las métricas clave incluyen agent_decisions_total, guardrail_blocks_total, llm_cost_usd_total y human_override_rate. La arquitectura también promueve un modelo de identidad robusto, propagando credenciales de usuario o mintiendo grants delegados de corta duración, con validación de tenant_id en cada salto para prevenir el problema del 'confused deputy'. Se enfatiza una arquitectura hexagonal (ports and adapters) para desacoplar la lógica de negocio de las implementaciones específicas de los proveedores de LLM, utilizando interfaces (Protocol) para la abstracción. Los componentes de infraestructura incluyen servicios de agente sin estado, una base de datos relacional para el ledger de auditoría, Redis para el estado de caché/rate-limit/kill-switch, un almacén de secretos y almacenamiento de objetos.
Flujo de Decisión del Agente LLM con Observabilidad y Control
- 1 Solicitud de Usuario Entrada de la solicitud al sistema LLM.
- 2 LLMGateway Punto de entrada único. Autenticación, autorización, rate limiting, estimació...
- 3 Routing Decision Selección del modelo LLM (cheap, capable, judge) o ruta a humano, basado en r...
- 4 Circuit Breaker Check Verifica la disponibilidad del modelo/proveedor antes de la llamada.
- 5 LLM Call Invocación del modelo LLM seleccionado con timeout y clave de idempotencia.
- 6 Guardrail/Judge Evaluación de seguridad y calidad de la salida del LLM.
- 7 Métricas/Logs/Trazas Registro de tokens, costos, latencia, outcome, decision_id en todas las capas.
- 8 Respuesta al Usuario Retorno de la decisión o enrutamiento a intervención humana.
| Capa | Tecnología | Justificación |
|---|---|---|
| orchestration | LLMGateway (custom) | Punto de control centralizado para enrutamiento, observabilidad, seguridad, control de costos y failover de todas las interacciones con LLM. vs Lógica distribuida en cada servicio (mayor complejidad, menor control), Service Mesh (menos control granular sobre la lógica de negocio LLM) Reglas de enrutamiento basadas en 'stakes', 'is_judge', 'needs_long_context'. Circuit breakers configurables por modelo/proveedor. |
| observability | Prometheus (o similar) | Recolección de métricas de decisión (agent_decisions_total, llm_cost_usd_total, guardrail_blocks_total) y métricas RED tradicionales. vs Datadog, Grafana Cloud Etiquetas 'tenant_id' y 'capability' en todas las series. Umbrales de alerta basados en síntomas de negocio (AutoExecutionSwing, GuardrailBlockSpike). |
| observability | Structured Logging (custom tiers) | Registro de eventos de decisión en tres niveles de sensibilidad (operacional, metadatos, regulado/raw) para auditoría y depuración. vs Un solo stream de logs (riesgo de fuga de datos sensibles) decision_id para correlación. Tier 3 encriptado y con acceso restringido. |
| observability | Distributed Tracing (OpenTelemetry o similar) | Visibilidad end-to-end del flujo de decisión, incluyendo spans internos del grafo del agente y llamadas a LLM. vs Jaeger, Zipkin Spans para 'agent.decision', 'llm_decision', 'output_guardrail', 'judge', 'routing'. |
| cache | Redis | Almacenamiento de estado compartido para rate limiting (incr_window), presupuestos de costo, y el estado del kill switch. vs Memcached (no persistente, menos características), Base de datos relacional (mayor latencia para operaciones de contador) TTL para ventanas de rate limit. Almacenamiento de estado del kill switch con propagación rápida. |
| storage | Base de Datos Relacional | Almacén de datos de auditoría (ledger de decisiones) y registros canónicos. vs NoSQL (si no se requiere consistencia fuerte o relaciones complejas), Object Storage (para logs brutos, no para ledger estructurado) Esquema append-only para el ledger. IAM de menor privilegio para el usuario de la DB. |
| security | Managed Secrets Manager | Almacenamiento seguro de claves de API de modelos, configuración de guardrails y claves de firma. vs Variables de entorno (menos seguro), HashiCorp Vault (si se requiere mayor complejidad y control) Rotación de claves. Acceso basado en roles (IAM) de menor privilegio. |
def complete(self, req: ChatRequest) -> ChatResponse:
rate_limiter.check(req.tenant_id, est_tokens(req))
resp = self._adapter.complete(req)
metrics.incr("llm_tokens_total", resp.tokens_in, direction="in", model=req.model, tenant_id=req.tenant_id)
metrics.incr("llm_tokens_total", resp.tokens_out, direction="out", model=req.model, tenant_id=req.tenant_id)
metrics.incr("llm_cost_usd_total", cost(req.model, resp), model=req.model, tenant_id=req.tenant_id, capability=req.capability)
return respRATE = { "default": { "tokens_per_min": 200_000, "usd_per_day": 50 } }
def check(tenant_id, est_tokens):
limits = RATE_FOR(tenant_id)
if redis.incr_window(f"tok:{tenant_id}", by=est_tokens, ttl=60) > limits["tokens_per_min"]:
raise RateLimited(tenant_id)
spent = redis.get_float(f"usd:{tenant_id}:{today()}")
if spent > limits["usd_per_day"]: raise BudgetExceeded(tenant_id)
if spent > 0.8 * limits["usd_per_day"]: alert(tenant_id, "80% of daily budget")class Mode(Enum):
LIVE = "live"
HUMAN_ONLY = "human_only"
HALTED = "halted"
def get_mode(switch, tenant_id, capability) -> Mode:
try:
raw = (switch.read(f"killswitch:{tenant_id}:{capability}")
or switch.read(f"killswitch:{tenant_id}")
or switch.read("killswitch:global"))
return Mode(raw) if raw else Mode.LIVE
except SwitchUnavailable:
return Mode.HUMAN_ONLYFundamentos Teóricos
La filosofía de este enfoque resuena con principios fundamentales de sistemas distribuidos y seguridad. La idea del 'chokepoint' o gateway centralizado para el control y la observabilidad es un patrón arquitectónico bien establecido, similar a los API Gateways o Service Meshes, que buscan centralizar preocupaciones transversales como la seguridad, el enrutamiento y la monitorización. La gestión de la identidad y la prevención del 'confused deputy' se basa en principios de seguridad de sistemas, donde la autoridad y los permisos deben ser explícitamente validados en cada interacción, un concepto explorado en trabajos clásicos sobre seguridad de sistemas operativos y distribuidos.
La implementación de Circuit Breakers para la resiliencia ante fallos de dependencias es una aplicación directa del patrón de Michael Nygard en 'Release It!' (2007), que busca prevenir cascadas de fallos en sistemas distribuidos. La insistencia en la inmutabilidad del ledger de decisiones y la atribución de cada acción se alinea con los principios de los Write-Ahead Logs (WAL) y los sistemas de auditoría, donde cada evento es registrado de forma persistente y verificable. Finalmente, la arquitectura hexagonal (Ports and Adapters) fue introducida por Alistair Cockburn en 2005, promoviendo la separación de la lógica de negocio del mundo exterior para mejorar la testabilidad y la adaptabilidad a cambios tecnológicos, un principio crucial dada la rápida evolución de los proveedores de LLM.