Cuando se ejecutan agentes de IA en producción, el primer desafío no es el modelo, sino la infraestructura. ¿Cómo se activa un agente aislado por usuario, sobre la marcha, sin filtrar credenciales o estados entre inquilinos?
Esta es la historia de cómo resolvimos ese problema a escala en GCP y lo que aprendimos a lo largo del camino.
El problema
Necesitábamos proporcionar un nuevo agente de IA para cada usuario que activara un flujo de trabajo específico. Cada agente requería:
- Sus propias credenciales: claves API para modelos de lenguaje, con alcance por inquilino.
- Su propio estado: historial de conversaciones, memoria y contexto que nunca deben filtrarse entre usuarios.
- Aprovisionamiento dinámico: los agentes tenían que subir y bajar según la demanda, no estar preasignados.
Esto inmediatamente planteó la pregunta fundamental sobre el arrendamiento múltiple: ¿Cómo se aíslan los datos de los inquilinos?
Opción A: base de datos compartida con seguridad a nivel de fila
El enfoque clásico. Una instancia de Postgres, un esquema y políticas RLS que filtran filas por tenant_id. Es rentable y operativamente simple: una base de datos para respaldar, monitorear y escalar.
Pero para los agentes de IA que manejan flujos de trabajo sensibles, el alcance de una política RLS mal configurada es enorme. Una consulta incorrecta y el inquilino A ve el historial de conversaciones del inquilino B.
Opción B: Base de datos Sidecar por inquilino (El MVP)
Nuestra arquitectura inicial utilizó el patrón sidecar de Kubernetes: cada Pod ejecutaba dos contenedores uno al lado del otro.
- Contenedor principal: el bucle agente: recibir mensajes, llamar al LLM, ejecutar herramientas y devolver respuestas.
- Contenedor Sidecar: una base de datos liviana (SQLite) que proporciona almacenamiento completamente aislado para el estado de ese inquilino.
apiVersion: v1
kind: Pod
metadata:
name: agent-tenant-42
spec:
containers:
- name: agent-loop
image: our-agent:v1
envFrom:
- secretRef:
name: tenant-42-credentials
- name: tenant-db
image: sqlite-sidecar:latest
volumeMounts:
- name: tenant-data
mountPath: /data
volumes:
- name: tenant-data
persistentVolumeClaim:
claimName: pvc-tenant-42El atractivo era obvio: aislamiento de confianza cero por defecto. Los inquilinos no podían ver los datos de los demás porque literalmente no había una base de datos compartida. Cada agente vivía en su propio apartamento con su propio archivador.
La lección del volumen persistente
Esto es lo que aprende rápidamente en producción: Los Kubernetes Pods son efímeros. Si el Pod falla, se reinicia o se reprograma en otro nodo, todos los datos escritos en el disco local (emptyDir) desaparecen.
Para un servidor web sin estado, está bien. ¿Para una base de datos que contiene el historial de conversaciones de un usuario? Eso es un desastre.
La solución es PersistentVolumeClaims (PVC): almacenamiento que sobrevive a los reinicios del Pod. Pero esto introduce su propia complejidad:
- Cada inquilino ahora necesita un PVC aprovisionado (usamos discos persistentes de GCP).
- Los PVC están bloqueados por zona en GCP, lo que significa que su Pod solo se puede programar en la zona donde reside su disco, lo que reduce la flexibilidad de programación de Kubernetes.
- La limpieza se vuelve crítica: los PVC huérfanos de inquilinos eliminados acumulan costos de manera silenciosa.
El pivote: servicio centralizado
Rápidamente nos dimos cuenta de que el modelo de sidecar por inquilino, si bien era elegante para el aislamiento, no escalaba económicamente. Las matemáticas eran simples:
- 1000 usuarios = 1000 PVC = 1000 discos persistentes.
- La mayoría de los usuarios estaban en niveles gratuitos o básicos: no necesitaban (ni querían pagar) una infraestructura dedicada.
- La sobrecarga operativa de gestionar miles de instancias de bases de datos individuales era significativa.El enfoque sidecar tenía sentido como función premium para los inquilinos empresariales que exigían un estricto aislamiento de datos. Pero para la mayoría de los usuarios, una base de datos centralizada con RLS correctamente implementado era la compensación correcta: menor costo, operaciones más simples y un aislamiento “suficientemente bueno” para cargas de trabajo no sensibles.
La arquitectura híbrida se convirtió en:
- Nivel estándar: Postgres compartido con RLS, administrado a través de Cloud SQL.
- Nivel Premium: Sidecar DB exclusivo con PVC, para inquilinos con requisitos de cumplimiento.
Infraestructura como código: Helm + Secret Manager
Los agentes de aprovisionamiento requerían dinámicamente dos piezas trabajando juntas:
Gráficos de timón para implementaciones con plantillas
Cada nuevo agente se implementó a través de un gráfico de Helm con valores específicos del inquilino inyectados en el momento de la instalación:
helm install agent-tenant-42 ./charts/agent \
--set tenant.id=42 \
--set tenant.tier=standard \
--set tenant.model=claude-opus-4El gráfico incluía todo: la especificación del Pod, la cuenta de servicio, las políticas de red y los límites de recursos. La actualización de todos los agentes a una nueva versión fue un único helm upgrade en toda la flota.
Administrador secreto de GCP para credenciales
Las claves API y la configuración confidencial nunca tocaron directamente los valores de Helm o las variables de entorno. En lugar de eso:
- Los secretos se almacenaron en GCP Secret Manager, con alcance por inquilino.
- Un
ExternalSecretde Kubernetes (a través del operador de secretos externos) los sincronizó en el clúster como secretos de Kubernetes nativos. - El
envFromdel Pod hacía referencia al secreto sincronizado, manteniendo las credenciales fuera del control de versiones y del historial de Helm.
Esto significaba que rotar una clave API era una actualización de Secret Manager y no era necesario volver a implementarla.
Lo que haría diferente hoy
- Comience con el modelo centralizado. El MVP con sidecar nos enseñó mucho, pero en retrospectiva, podríamos haber lanzado más rápido con RLS desde el primer día y agregar el nivel de sidecar más adelante para los usuarios premium.
- Utilice SQLite solo si acepta sus limitaciones. SQLite es brillante para cargas de trabajo locales de un solo escritor. Pero en el contexto de Kubernetes, la dependencia de PVC anula gran parte de su simplicidad. Para la ruta centralizada, Postgres (administrado a través de Cloud SQL) es la opción obvia.
- Invierta en observabilidad desde el principio. Cuando tiene cientos de agentes ejecutándose de forma independiente, necesita registro y seguimiento centralizados desde el primer día, no después del primer “¿por qué el agente del inquilino 42 dejó de responder?” incidente.
comida para llevar
Los agentes de IA multiinquilino son fundamentalmente un problema de infraestructura, no un problema de IA. Al modelo no le importa si circula en un sidecar o en un monolito. Pero sus usuarios se preocupan por la privacidad, su equipo de finanzas se preocupa por los costos y su equipo de operaciones se preocupa por no recibir llamadas a las 3 a. m. porque se llenó un volumen persistente.
Empiece de forma sencilla. Aislar donde importa. Escale lo que se amortiza por sí solo.
Diego Jiménez Vergara — Ingeniero de Infraestructura de IA y DevOps. Construyendo en la intersección de los sistemas FinTech, AI y Cloud Native.