En el contexto de la transformación digital empresarial, los sistemas de gestión del conocimiento están evolucionando hacia centros de inteligencia dinámicos. Frameworks como Langchain-Chatchat, basados en arquitecturas RAG (Generación Aumentada por Recuperación), son cruciales para construir asistentes AI privados y precisos. Sin embargo, su desafío radica en mantener la estabilidad bajo picos de tráfico, como sesiones de capacitación masivas o oleadas de consultas de atención al cliente.
El problema fundamental no reside en la capacidad del modelo, sino en la arquitectura subyacente. Un despliegue monolítico, donde todos los componentes (procesamiento de consultas, búsqueda vectorial, inferencia LLM) se ejecutan en un solo proceso, consume recursos de forma acelerada y falla de manera catastrófica bajo carga. El objetivo es diseñar un sistema que escale horizontalmente de forma automática, manteniendo la seguridad de los datos.
Principios de Diseño para un Sistema Escalable
Lograr la elasticidad requiere desacpolar los componentes. La cadena de trabajo de Langchain-Chatchat (análisis de documentos, vectorización, búsqueda, generación) es inherentemente modular, lo que permite escalar cada etapa de manera independiente.
1. Stateless API Layer
La capa de la API que recibe las solicitudes debe ser stateless. Si no guarda estado de sesión y accede a recursos compartidos, se puede replicar fácilmente. El siguiente endpoint de ejemplo, aunque simple, demuestra este principio: la lógica de negocio está contenida en una función pura.
@app.post("/consulta")
async def handle_request(payload: dict):
user_query = payload.get("texto", "")
chain_response = retrieval_qa_chain.invoke({"input": user_query})
return {
"respuesta": chain_response["output"],
"fuentes": [ref.metadata for ref in chain_response["context"]]
}
Las múltiples instancias de esta API deben conectarse a los mismos recursos de almacenamiento y modelo.
2. Almacenamiento Vectorial Compartido
Para evitar la inconsistencia de datos, el índice vectorial debe ser único y accesible por todas las instancias. Las soluciones varían desde sistemas de archivos compartidos hasta bases de datos vectoriales dedicadas.
Usar un servicio externo como Milvus desacopla el ciclo de vida de la base de conocimientos del despliegue de la aplicación. La inicialización del vectorstore se modifica para conectarse a un servicio remoto:
from langchain_community.vectorstores import Milvus
kb_store = Milvus(
embedding_function=embedding_model,
collection_name="docs_empresa",
connection_args={"host": "milvus-svc.namespace", "port": "19530"}
)
3. Pooling de Recursos GPU para Inferencia
La inferencia de LLM es el cuello de botella más probable. En lugar de cargar el modelo en cada instancia de la API, se debe desplegar como un servicio independiente (p.ej., usando vLLM) y acceder a él vía API.
llm_client = HuggingFaceEndpoint(
endpoint_url="http://llm-inferencia:8080/generate",
max_new_tokens=512,
temperature=0.6
)
Este servicio de inferencia puede alojarse en nodos con GPU y gestionar su propio pool de conexiones para controlar la concurrencia.
Automatización del Escalado con Kubernetes
Con los componentes desacoplados y contenedorizados, Kubernetes (K8s) orquesta el escalado automático basado en métricas. Un Deployment define la aplicación y un Horizontal Pod Autoscaler (HPA) gestiona la réplica de Pods.
Configuración del Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: chatchat-api-svc
spec:
replicas: 2
selector:
matchLabels:
app: chatchat-api
template:
metadata:
labels:
app: chatchat-api
spec:
containers:
- name: api-container
image: registry/chatchat-api:stable
ports:
- containerPort: 8000
resources:
requests:
cpu: "500m"
memory: "2Gi"
limits:
cpu: "1"
memory: "4Gi"
env:
- name: VECTOR_STORE_HOST
value: "milvus-svc"
livenessProbe:
httpGet:
path: /liveness
port: 8000
initialDelaySeconds: 40
periodSeconds: 15
readinessProbe:
httpGet:
path: /readiness
port: 8000
initialDelaySeconds: 70
periodSeconds: 20
El HPA escala la cantidad de Pods del Deployment anterior:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: chatchat-api-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: chatchat-api-svc
minReplicas: 1
maxReplicas: 15
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 75
behavior:
scaleDown:
stabilizationWindowSeconds: 240
Este HPA creará nuevos Pods cuando el uso promedio de CPU supere el 75% y eliminará pods (con una ventana de estabilización de 4 minutos) cuando la carga baje. Un Ingress o LoadBalancer se encarga de distribuir el tráfico entrante a los Pods activos.
Resultados y Buenas Prácticas
En una prueba de estrés, un sistema migrado a esta arqutiectura manejó 400 usuarios concurrentes con una latencia promedio de 850ms y una tasa de error inferior al 1%, frente a un colapso en el despliegue monolítico original con 40 usuarios.
Lecciones clave aprendidas:
- Las readiness probes deben tener tiempos de inicialización largos para permitir la carga de modelos e índices grandes antes de recibir tráfico.
- Definir límites de recursos (requests/limits) es crítico para que el scheduler de K8s pueda planificar eficientemente.
- Complementar las métricas de CPU con métricas personalizadas de la aplicación (como QPS o latencia P99) para un escalado más reactivo.
- Monitorear el uso de disco y la salud de los nodos para evitar fallos en el despliegue de nuevos Pods.
La elasticidad transforma un sistema de demostración en una pieza de infraestructura de producción fiable, optimizando costos al asignar recursos solo cuando son necesarios y garantizando la continuidad del servicio ante picos imprevistos.