Cuando conectas tu primer agente a un LLM, eliges un proveedor y escribes su SDK directamente en el código. Funciona. Y sin darte cuenta acabas de firmar tres deudas a la vez.
La primera es de acoplamiento: el nombre del modelo, el formato de la petición y las claves están cosidos por todo tu código. La segunda es de disponibilidad: cuando ese proveedor tiene una caída —y la tendrá— tu agente se para en seco. La tercera es de coste y control: no puedes mandar la tarea barata a un modelo gratuito y la crítica a uno de pago sin reescribir la integración.
Y el escenario real es peor que el teórico, porque no eliges un proveedor: tienes Azure AI Foundry por contrato, los modelos gratuitos de NVIDIA (NIM API) para experimentar sin coste, y un Ollama local para lo que no puede salir de tu máquina. Tres proveedores, tres dialectos, tres formas de autenticar.
Eso es la entropía multiproveedor: cada proveedor que añades mete desorden en el sistema — otro dialecto de API, otro esquema de autenticación, otro catálogo de modelos que evoluciona por su cuenta. Y el desorden crece más rápido que el número de proveedores, porque cada integración nueva interactúa con todas las anteriores. Resolverlo a mano —un if por proveedor— no reduce la entropía: la reparte por tu código.
La solución es un patrón de infraestructura clásico: un gateway. Una pieza que absorbe el desorden por dentro —habla el dialecto de cada proveedor— y expone orden por fuera: un único contrato OpenAI-compatible. Ese gateway es LiteLLM: open source, ~40.000 estrellas en GitHub, y en producción en sitios como Netflix o Rocket Money — no es una apuesta exótica, es la pieza estándar de esta capa.
🏗️ La arquitectura: un endpoint, N proveedores
El agente deja de conocer a los proveedores. Solo conoce al gateway. Cambiar de proveedor pasa a ser un cambio de configuración, no de código.
graph LR A[Agente] -->|OpenAI-compatible| G[LiteLLM Gateway<br/>:4000] G --> AZ[Azure AI Foundry] G --> NV[NVIDIA NIM<br/>modelos gratuitos] G --> L[Ollama local] G -. fallback .-> NV
Componentes del stack
- Cliente: cualquier agente que hable OpenAI (SDK, Claude Code, Cursor, un
curl).- Gateway: LiteLLM Proxy en contenedor (Docker Compose, endpoint
:4000).- Proveedores: Azure AI Foundry, NVIDIA NIM (gratuito, build.nvidia.com), Ollama (local, privacidad).
La estrategia de routing sale sola de las restricciones: NVIDIA para experimentar (gratis), Azure para producción (contrato y compliance), Ollama para lo sensible (no sale de casa). El fallback conecta los tres: si Azure no responde, la petición cae a NVIDIA.
⚙️ Los tres ficheros
Todo el laboratorio son tres ficheros en un directorio vacío: docker-compose.yml (la infraestructura), config.yaml (el conocimiento de proveedores) y .env (las claves, que nunca tocan los otros dos).
1. docker-compose.yml
services:
litellm:
image: ghcr.io/berriai/litellm:main-stable
container_name: litellm_gateway
ports:
- "4000:4000"
volumes:
- ./config.yaml:/app/config.yaml:ro
command: ["--config", "/app/config.yaml", "--port", "4000"]
env_file:
- .env
restart: unless-stopped
extra_hosts:
- "host.docker.internal:host-gateway" # para alcanzar Ollama en el host2. config.yaml — donde la entropía se ordena
Cada entrada mapea un nombre lógico (el que usará el agente) a un modelo real de un proveedor concreto:
model_list:
# ── Azure AI Foundry: el contrato de empresa ─────────────────
- model_name: produccion
litellm_params:
model: azure/gpt-4o # nombre del *deployment* en Foundry
api_base: os.environ/AZURE_API_BASE # https://<recurso>.openai.azure.com
api_key: os.environ/AZURE_API_KEY
api_version: "2024-10-21"
# ── NVIDIA NIM: modelos gratuitos para experimentar ──────────
- model_name: experimentos
litellm_params:
model: nvidia_nim/meta/llama-3.3-70b-instruct
api_key: os.environ/NVIDIA_NIM_API_KEY # gratis en build.nvidia.com
# ── Ollama: 100% local, para datos que no salen de casa ──────
- model_name: local
litellm_params:
model: ollama/llama3
api_base: http://host.docker.internal:11434
# El corazón del patrón: si producción falla, cae a los gratuitos,
# y si tampoco, al modelo local.
litellm_settings:
fallbacks:
- produccion: ["experimentos", "local"]
- experimentos: ["local"] # cada nombre lógico declara los suyos
num_retries: 2 # reintenta el primario antes de degradar
request_timeout: 60 # y considéralo caído si tarda más de esto
general_settings:
master_key: os.environ/LITELLM_MASTER_KEY # clave que usarán los clientesFíjate en lo que acabas de comprar: el agente pedirá produccion sin saber que es Azure, y si Azure no responde, LiteLLM redirige la misma petición al modelo gratuito de NVIDIA y, en última instancia, al local. El fallback es la entropía multiproveedor resuelta en tres líneas.
La letra pequeña del fallback
Dos cosas que este lab enseña a base de errores. Primera: el fallback solo aplica al modelo pedido — declarar fallbacks para
produccionno cubre las llamadas directas aexperimentos; cada nombre lógico necesita su propia entrada (por eso el ejemplo declara las dos). Segunda: no rescata fallos a mitad de stream — si el proveedor corta cuando ya empezó a responder, la petición muere igualmente.
Y un ajuste de expectativas: lo que disparará el fallback casi nunca será una caída épica del proveedor, sino un 429 de cuota — el tier gratuito saturado, o el TPM de tu deployment agotado por un agente que mete 30.000 tokens por petición. El patrón es el mismo; el enemigo es más mundano.
3. .env — las claves, fuera de la configuración
AZURE_API_BASE=https://mi-recurso.openai.azure.com
AZURE_API_KEY=...
NVIDIA_NIM_API_KEY=nvapi-...
LITELLM_MASTER_KEY=sk-lab-1234 # genera una clave propia, no reutilices estaNombres de modelo
gpt-4oes el nombre de tu deployment en Azure AI Foundry (puede llamarse como quieras al desplegarlo);meta/llama-3.3-70b-instructes un modelo del catálogo gratuito de NVIDIA. Ambos catálogos evolucionan: ajusta cadamodel:al vigente. Para modelos de Foundry que no son de OpenAI (DeepSeek, Phi, Mistral serverless) el prefijo en LiteLLM esazure_ai/en lugar deazure/.
🚀 Arrancar y verificar
docker compose up -dEl contenedor tarda 10–15 segundos en estar listo: si el primer curl no responde, no es un fallo — espera y repite.
Comprueba que el gateway está vivo (sin clave):
curl -s http://localhost:4000/health/liveliness
# "I'm alive!"Y una petición real, ya en formato OpenAI, contra el nombre lógico:
curl -s http://localhost:4000/v1/chat/completions \
-H "Authorization: Bearer sk-lab-1234" \
-H "Content-Type: application/json" \
-d '{
"model": "experimentos",
"messages": [{"role": "user", "content": "Resume el patrón gateway en una frase."}]
}'🔌 Conectar tu agente al gateway
Aquí está el pago del patrón. El agente no cambia: solo lo apuntas al gateway. Como LiteLLM habla OpenAI, cualquier cliente OpenAI sirve con dos variables de entorno:
export OPENAI_API_BASE="http://localhost:4000/v1"
export OPENAI_API_KEY="sk-lab-1234"from openai import OpenAI
# El agente cree que habla con OpenAI. Habla con tu gateway.
client = OpenAI() # lee OPENAI_API_BASE y OPENAI_API_KEY del entorno
resp = client.chat.completions.create(
model="experimentos", # nombre lógico, no proveedor
messages=[{"role": "user", "content": "Dame un plan en 3 pasos."}],
)
print(resp.choices[0].message.content)Para saltar de NVIDIA a Azure no tocas ni una línea de Python: cambias model="experimentos" por model="produccion", o dejas que el fallback decida cuando el primario caiga. El agente ha quedado desacoplado del proveedor, que era todo el objetivo.
Agentes que hablan Anthropic (Claude Code, etc.)
LiteLLM también expone un endpoint compatible con la API de Anthropic. Puedes apuntar herramientas que usan
ANTHROPIC_BASE_URLal gateway y enrutar por debajo hacia cualquiera de los tres proveedores.
Y el cliente menos obvio, que fue el que más guerra dio:
¿Y GitHub Copilot Chat como cliente?
Se puede — este gateway acabó sirviendo modelos al chat de VS Code vía BYOK — pero tiene trampas propias: el proveedor correcto es Custom Endpoint (
customendpoint,apiType: chat-completions), y la API key debe entrar por el diálogo seguro de la UI, no escrita en el JSON (si la pones en texto plano, VS Code manda la cabeceraAuthorization: Bearervacía). El debugging completo da para su propio post; queda apuntado para el siguiente de la serie.
🚦 Acotar el gasto sin base de datos
Los presupuestos en euros por consumidor necesitan Postgres y daría para un lab aparte. Pero un techo de daño sí cabe en el config.yaml: límites estáticos de tasa por modelo, directamente en litellm_params.
- model_name: produccion
litellm_params:
model: azure/gpt-4o
api_base: os.environ/AZURE_API_BASE
api_key: os.environ/AZURE_API_KEY
api_version: "2024-10-21"
rpm: 20 # peticiones por minuto
tpm: 40000 # tokens por minutoNo mide gasto acumulado, pero acota el daño máximo por minuto. Para un lab o un uso personal, es el 80% del valor con el 5% de la complejidad: un bucle mal cerrado ya no te vacía la cuenta antes de que te des cuenta.
Notas para agentes
Si reproduces este lab con tu agente
Los pasos ya están en el post; los bloques de código de “Los tres ficheros” son la fuente canónica. Estas notas son lo que la narrativa no dice:
- Antes de empezar: verifica que
docker compose versionresponde y pregunta al usuario qué proveedores tiene (Azure, NVIDIA NIM, Ollama) y dónde quiere el directorio de trabajo. - Adapta la config: elimina de
model_listlos proveedores que el usuario no tenga y ajusta losfallbacksa los que queden. Genera unLITELLM_MASTER_KEYaleatorio en.env, nunca reutilices elsk-lab-1234del ejemplo. - Claves: pídeselas al usuario y ponlas solo en
.env. Nunca las inventes ni las escribas enconfig.yaml. - Hecho significa verificado: no des el lab por completado hasta que
/health/livelinessresponda y una petición a/v1/chat/completionsdevuelva contenido. Termina informando del endpoint, elmaster_keyy los nombres lógicos disponibles. - Si algo falla:
docker compose logs litellm. Lo típico: 401 del proveedor (clave), deployment de Azure que no coincide, Ollama inaccesible desde el contenedor (extra_hosts), 429 de cuota del proveedor (no es un fallo del gateway: mira el TPM del deployment). - Si el error no cuadra: desactiva temporalmente los
fallbacksy reproduce. El fallback enmascara al culpable — el log señala al último modelo de la cadena, no al que falló primero. Fue el truco que resolvió el diagnóstico más difícil de este lab.
Aplícalo
¿Trabajas con un agente de código (Claude Code, Cursor, ChatGPT con herramientas…)? Pégale esto tal cual — leerá el post y montará el lab contigo. Si no tiene acceso web, copia el post entero en su lugar. Y si prefieres hacerlo tú a mano, las secciones de arriba ya tienen todo lo necesario.
Lee https://blog.rcmon.dev/02-Laboratorios/litellm-multiproveedor y móntame
el gateway LiteLLM del lab. Sigue las "Notas para agentes" del final:
pregúntame qué proveedores tengo y dónde quiero el directorio de trabajo,
pídeme las claves (van solo en .env) y no lo des por hecho hasta verificar
/health/liveliness y una petición de chat real.✅ Resultado
Con un gateway y tres ficheros:
- Un contrato, N proveedores. El agente habla un solo dialecto.
- Fallback automático. Una caída de Azure deja de ser una caída de tu agente.
- Routing por coste y privacidad. Experimentos → NVIDIA gratis; producción → Azure; datos sensibles → Ollama local. Decidido por configuración.
- Claves centralizadas. Rotación y auditoría en un punto (
.env), no repartidas por el código.
La entropía multiproveedor no se resuelve eligiendo bien el proveedor. Se resuelve dejando de elegir en el código y concentrando el desorden en una capa que puedes cambiar sin desplegar.
Cuándo dar el siguiente paso
Esta versión cubre un consumidor: tú. En cuanto haya más de una aplicación o más de un agente detrás del gateway, aparece la pregunta de quién gasta qué — y ahí entra Postgres, con claves virtuales por consumidor, presupuesto máximo (max_budget) y dashboard de gasto. Ese es el terreno del siguiente escalón: aislar consumidores sobre el mismo gateway.