Identificación de inquilinos: El fundamento del aislamiento
El primer paso hacia el aislamiento efectivo es establecer un mecanismo de identificación de inquilinos. En lugar de depender de parámetros de cliente que pueden ser falsificados, se recomienda implementar extracción segura del identificador en límites confiables.
from fastapi import Depends, Header, HTTPException
from typing import Annotated
async def extraer_identificador_inquilino(clave_api: Annotated[str, Header(alias="X-API-Key")]) -> str:
segmentos = clave_api.split(":")
if len(segmentos) < 2 or not segmentos[0].startswith("tenant_"):
raise HTTPException(401, "Formato de clave API no válido")
return segmentos[0]
Estrategias de aislamiento de datos
La separación física en directorios es insuficiente. Se recomienda una estrategia dual de aislamiento tanto a nivel de directorio como de colección en bases de datos vectoriales.
from langchain_community.vectorstores import Chroma
from langchain_community.embeddings import HuggingFaceEmbeddings
def crear_base_conocimiento(identificador: str, documentos: list):
modelo_embedding = HuggingFaceEmbeddings(
model_name="sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2"
)
ruta_almacenamiento = f"/almacenamiento/{identificador}/vectores"
nombre_coleccion = f"conocimiento_{identificador}"
return Chroma.from_documents(
documents=documentos,
embedding=modelo_embedding,
persist_directory=ruta_almacenamiento,
collection_name=nombre_coleccion
)
Configuración dinámica por inquilino
Para evitar múltiples instancias de servicio, se implementa carga dinámica de configuración según el identificador del inquilino.
CONFIGURACIONES_INQUILINO = {
"cliente_alpha": {
"plantilla_prompt": "Eres asistente técnico de Alpha Corp. Responde basándote en: {contexto}",
"ruta_vectores": "/datos/alpha/vectores",
"temperatura": 0.2,
"limite_tasa": 100
},
"cliente_beta": {
"plantilla_prompt": "Consultoría Beta SA: responde conforme a nuestras directrices internas.",
"ruta_vectores": "/datos/beta/vectores",
"temperatura": 0.5,
"limite_tasa": 75
}
}
def obtener_configuracion(identificador: str) -> dict:
if identificador not in CONFIGURACIONES_INQUILINO:
raise ValueError(f"Inquilino {identificador} no registrado")
return CONFIGURACIONES_INQUILINO[identificador]
Gestión de sesiones de conversación
El historial de conversaciones debe incluir identificación de inquilino para evitar mezclas de contexto entre diferentes clientes.
from langchain_community.chat_message_histories import RedisChatMessageHistory
def obtener_historial_sesion(identificador: str, usuario: str):
clave_sesion = f"historial:{identificador}:{usuario}"
return RedisChatMessageHistory(
session_id=clave_sesion,
url="redis://redis-cluster:6379",
ttl=7200
)
Arquitectura SaaS completa
Una implementación robusta requiere componentes adicionales para gestión centralizada y escalabilidad.
# Pseudocódigo para orquestación
class OrquestadorMultiInquilino:
def __init__(self):
self.gateway_api = APIGateway()
self.middleware_tenant = TenantMiddleware()
self.pool_vectores = VectorStorePool()
async def procesar_solicitud(self, solicitud):
identificador = await self.extraer_inquilino(solicitud)
configuracion = self.cargar_configuracion(identificador)
almacen = self.pool_vectores.obtener(identificador)
pipeline = self.construir_pipeline(configuracion, almacen)
return await pipeline.ejecutar(solicitud.contenido)
Consideraciones operativas
La implementación multitenant introduce desafíos específicos que requieren atención:
- Latencia de inicialización: Implementar pre-carga asíncrona para inquilinos frecuentes
- Consistencia de embeddings: Unificar modelos vectoriales entre inquilinos
- Control de recursos: Monitorear uso de memoria y espacio en disco por inquilino
- Auditoría: Registrar todas las operaciones con identificador de inquilino
La transformación hacia un modelo SaaS requiere no solo ajustes técnicos sino también reconsiderar la infraestructura de despliegue. Un enfoque multitenant permite optimizar recursos, simplificar operaciones y habilitar nuevos modelos de negocio como niveles de servicio diferenciados.