Introducción

En nuestra infraestructura de IA con K3s, LiteLLM sirve como la puerta de enlace central de modelos: un proxy que unifica múltiples proveedores de modelos tras una única API compatible con OpenAI. Combinado con el Model Context Protocol (MCP), se convierte en la columna vertebral de la comunicación entre agentes y modelos. Este post documenta nuestra configuración de producción y las lecciones aprendidas a lo largo del camino.

Resumen de la arquitectura

┌──────────────┐     ┌──────────────┐     ┌──────────────┐
│   Hermes     │────▶│   LiteLLM    │────▶│   NVIDIA     │
│   Agents     │     │   Gateway    │     │   Nemotron   │
└──────────────┘     │              │     └──────────────┘
┌──────────────┐     │  (puerto 4000)│     ┌──────────────┐
│   Cron Jobs  │────▶│              │────▶│   Mistral    │
└──────────────┘     └──────────────┘     └──────────────┘
┌──────────────┐           ▲               ┌──────────────┐
│   Webhooks   │───────────┘               │   OpenAI     │
└──────────────┘   Endpoint MCP           └──────────────┘
                    /mcp

Despliegue de LiteLLM en K3s

Configuración

Nuestro despliegue de LiteLLM usa una configuración de lista de modelos que enruta las peticiones a diferentes proveedores según la tarea:

# litellm-config.yaml
model_list:
  # Modelo principal para tareas de código
  - model_name: hermes-coder
    litellm_params:
      model: nvidia/nemotron-3-ultra-550b-a55b
      api_base: https://integrate.api.nvidia.com/v1
      api_key: ${NVIDIA_API_KEY}
      timeout: 7200

  # Modelos Mistral para investigación y análisis
  - model_name: mistral-reasoning
    litellm_params:
      model: mistral/devstral-latest
      api_base: https://api.mistral.ai/v1
      api_key: ${MISTRAL_API_KEY}
      timeout: 3600

  # Modelo ligero para tareas rápidas
  - model_name: hermes-lean
    litellm_params:
      model: nvidia/nemotron-3-nano-omni-30b-a3b-reasoning
      api_base: https://integrate.api.nvidia.com/v1
      api_key: ${NVIDIA_API_KEY}
      timeout: 7200

Manifiesto de Kubernetes

apiVersion: apps/v1
kind: Deployment
metadata:
  name: litellm-gateway
  namespace: ia-services-payed
spec:
  replicas: 2
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  selector:
    matchLabels:
      app: litellm
  template:
    metadata:
      labels:
        app: litellm
        version: main-stable
    spec:
      containers:
      - name: litellm
        image: ghcr.io/anthropics/litellm:main-stable
        ports:
        - containerPort: 4000
        env:
        - name: MODEL_LIST
          valueFrom:
            secretKeyRef:
              name: litellm-config
              key: model-list.yaml
        - name: PROXY_SERVER_KEY
          valueFrom:
            secretKeyRef:
              name: litellm-secrets
              key: api-key
        - name: HTTP_TIMEOUT
          value: "7200"
        - name: REQUEST_TIMEOUT
          value: "3600"
        - name: LOG_PII
          value: "false"
        resources:
          requests:
            memory: "2Gi"
            cpu: "500m"
          limits:
            memory: "4Gi"
            cpu: "1"
        livenessProbe:
          httpGet:
            path: /health
            port: 4000
          initialDelaySeconds: 30
          periodSeconds: 15
        readinessProbe:
          httpGet:
            path: /health
            port: 4000
          initialDelaySeconds: 10
          periodSeconds: 10
      restartPolicy: Always
---
apiVersion: v1
kind: Service
metadata:
  name: litellm
  namespace: ia-services-payed
spec:
  selector:
    app: litellm
  ports:
  - port: 4000
    targetPort: 4000
  type: ClusterIP

Integración de MCP

Configuración del endpoint MCP

LiteLLM expone el endpoint MCP en http://litellm.ia-services-payed.svc.cluster.local:4000/mcp. Los agentes y los trabajos programados (cron jobs) se conectan a este endpoint para acceder a herramientas y modelos:

# Ejemplo de conexión de cliente MCP
from mcp import ClientConnection

# Conexión a través del DNS del servicio en K3s
connection = await ClientConnection.connect(
    "litellm.ia-services-payed.svc.cluster.local",
    port=4000,
    path="/mcp",
)

# Listar las herramientas disponibles
tools = await connection.list_tools()
for tool in tools:
    print(f"{tool.name}: {tool.description[:80]}...")

Registro del servidor MCP

Cada servidor MCP se registra con la puerta de enlace de LiteLLM:

# Registrar un nuevo servidor MCP
curl -X POST http://litellm.ia-services-payed.svc.cluster.local:4000/mcp/register \
  -H "Authorization: Bearer ${LITE...KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "kubernetes-mcp",
    "url": "http://mcp-k8s.forgejo-runners.svc.cluster.local:8080/mcp",
    "timeout": 300
  }'

Estrategia de enrutamiento multi-modelo

Reglas de enrutamiento

Usamos un enfoque de enrutamiento por niveles:

Tipo de tarea Modelo principal Alternativa Justificación
Generación de código hermes-coder (Nemotron) devstral Nemotron destaca en código
Investigación/análisis devstral nemotron-35b El razonamiento de Mistral
Tareas rápidas nemotron-3-nano devstral Rápido y económico
Razonamiento complejo devstral hermes-coder El mejor razonamiento global
Redacción creativa hermes-coder devstral Calidad de estilo

Enrutamiento basado en el estado de salud

LiteLLM enruta automáticamente hacia los modelos sanos:

# Si el modelo principal no está sano, LiteLLM usa la alternativa
# Comprobar el estado del modelo
import httpx

async def check_model_health(model_name: str) -> bool:
    try:
        async with httpx.AsyncClient(timeout=5.0) as client:
            response = await client.post(
                f"{API_BASE}/v1/models",
                headers={"Authorization": f"Bearer {API_KEY}"},
            )
            return response.status_code == 200
    except httpx.TimeoutException:
        return False
    except httpx.RequestError:
        return False

Lecciones operativas

Lección 1: la configuración de los timeouts importa

La API de NVIDIA puede ser lenta con los modelos grandes. Configuramos:

HTTP_TIMEOUT=7200s  # 2 horas — para la carga del modelo
REQUEST_TIMEOUT=3600s  # 1 hora — para peticiones individuales

Sin estos valores, las peticiones superan el tiempo de espera durante el arranque en frío del modelo.

Lección 2: limitación de tasa en la capa de proxy

Configura los límites de tasa de LiteLLM para impedir que un solo consumidor desborde el backend:

model_aliases:
  "nvidia/nemotron-3-ultra-550b-a55b": "nemotron-35b"

litellm_settings:
  model_fallbacks:
    "nemotron-35b": ["nemotron-35b", "devstral-large"]
  num_retries: 3
  retry_after: 5

Lección 3: registro y observabilidad

Habilita el registro estructurado para la depuración:

litellm_settings:
  success_callback: ["langsmith", "opentelemetry"]
  failure_callback: ["langsmith", "opentelemetry"]
  cache: true
  cache_params:
    type: "redis"
    host: "redis-monitoring.svc.cluster.local"
    port: 6379

Consideraciones de seguridad

Gestión de claves de API

Nunca codifiques claves de API en texto plano. Usa secretos de Kubernetes:

# Crear el secreto a partir de variables de entorno existentes
kubectl create secret generic litellm-secrets \
  --from-literal=api-key="${PROXY_SERVER_KEY}" \
  --namespace=ia-services-payed

kubectl create secret generic litellm-config \
  --from-file=model-list.yaml \
  --namespace=ia-services-payed

Políticas de red

Restringe quién puede acceder a la puerta de enlace de LiteLLM:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: litellm-ingress
  namespace: ia-services-payed
spec:
  podSelector:
    matchLabels:
      app: litellm
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          kubernetes.io/metadata.name: forgejo-runners
    - namespaceSelector:
        matchLabels:
          kubernetes.io/metadata.name: cicd
    - namespaceSelector:
        matchLabels:
          kubernetes.io/metadata.name: flux-system
    ports:
    - port: 4000
  policyTypes:
  - Ingress

Monitorización de LiteLLM

Métricas clave a seguir:

  1. Latencia de la petición — P50, P95, P99 por modelo
  2. Uso de tokens — entrada/salida por modelo y consumidor
  3. Tasa de error — por código de estado y tipo de error
  4. Estado del modelo — tiempo activo y tiempo de respuesta por backend
  5. Tasa de aciertos de caché — eficacia de la caché de respuestas

Consultas del panel de Grafana (Prometheus):

# Tasa de peticiones por modelo
rate(litellm_proxy_request_count[5m])

# Latencia media por modelo
litellm_proxy_latency_seconds / 1000

# Tasa de error
sum(rate(litellm_proxy_request_error_count[5m])) / sum(rate(litellm_proxy_request_count[5m]))

Conclusión

LiteLLM + MCP proporciona una puerta de enlace de modelos robusta y escalable para infraestructuras de IA basadas en K3s. Las principales conclusiones:

  • Configura tiempos de espera generosos para la carga del modelo
  • Usa secretos de Kubernetes para la gestión de claves de API
  • Implementa alternativas de modelo basadas en el estado de salud
  • Monitoriza el uso de tokens y la latencia por modelo
  • Restringe el acceso con NetworkPolicies

Con estas prácticas en su lugar, tu puerta de enlace de modelos gestionará la complejidad del enrutamiento multi-modelo mientras tus agentes se centran en sus tareas reales.