Implementación de Escalado Elástico para Sistemas de Preguntas-Respuestas con Langchain-Chatchat

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.

Etiquetas: Langchain-Chatchat RAG FastAPI Kubernetes hpa

Publicado el 6-27 06:07