LLM Fallback: automatizaciones que sobreviven caídas de API
TL;DR
El 2 de marzo de 2026, Claude sufrió un outage global que afectó a miles de usuarios durante casi tres horas. Si tus pipelines de automatización dependen de un único proveedor de LLM, ese tipo de incidente los para en seco. Este artículo muestra tres formas de implementar fallback de LLM en Python, cuándo usar LiteLLM o OpenRouter como proxy, y qué cambios necesitan estos patrones para funcionar de verdad en producción.
El día que los pipelines pararon sin avisar
El lunes 2 de marzo, a las 11:30 UTC, el status de Anthropic empezó a recibir reportes masivos. Claude.ai no cargaba, los intentos de autenticación devolvían HTTP 500, y Claude Code mostraba errores intermitentes. En pocas horas, casi 2.000 informes activos. El outage duró aproximadamente dos horas y 45 minutos para la mayoría de usuarios, aunque al día siguiente Claude Haiku 4.5 seguía presentando errores elevados y el equipo de Anthropic continuaba trabajando en una corrección definitiva.
Un detalle importante sobre el impacto real en pipelines: el API de Anthropic (api.anthropic.com) funcionó de forma mayormente normal durante el incidente. El problema estaba en la infraestructura de autenticación, no en los modelos. Claude Haiku 4.5 sí tuvo fallos intermitentes, y ese modelo es el que muchos equipos usan en tareas de clasificación y extracción por su coste reducido. Suficiente para romper pipelines que no tienen plan B.
Este tipo de incidente no es exclusivo de Anthropic. OpenAI ha tenido outages similares, igual que Gemini API. La pregunta no es si va a ocurrir, sino qué pasa con tus automatizaciones cuando ocurra. Si construyes workflows con n8n y LLMs como back-end de decisión, la respuesta puede ser: todo el flujo se detiene hasta que el proveedor vuelve.
¿Qué es un fallback de LLM?
Un fallback de LLM es un mecanismo por el que un pipeline cambia automáticamente de proveedor o modelo cuando el principal no está disponible o devuelve errores, sin intervención manual y sin interrumpir el flujo de trabajo.
El concepto viene del patrón circuit breaker en arquitecturas de microservicios: en lugar de reintentar indefinidamente una operación que va a fallar, el sistema detecta el fallo, "abre el circuito" y redirige el tráfico a una alternativa funcional. Cuando el proveedor principal se recupera, el circuito se cierra de nuevo de forma automática.
En el contexto de LLMs, esto tiene una complejidad adicional: los modelos de diferentes proveedores no son intercambiables en todos los casos. Para tareas de extracción estructurada o clasificación, la diferencia entre Claude y GPT-4o es pequeña en la mayoría de prompts. Para generación de texto con estilo muy específico o prompts muy ajustados a un modelo concreto, el fallback puede producir outputs con formato o tono diferente. Hay que tenerlo en cuenta antes de asumir que es transparente.
Tres estrategias: cuándo usar cada una
| Estrategia | Herramienta | Control | Complejidad | Mejor para |
|---|---|---|---|---|
| Fallback declarativo | LiteLLM SDK | Alto | Baja | Pipelines Python con lógica de retry propia |
| Proxy multi-proveedor | OpenRouter | Medio | Muy baja | Prototipos rápidos y equipos pequeños |
| Circuit breaker manual | Código propio | Total | Alta | Requisitos de compliance o auditoría estricta |
Opción 1: LiteLLM con fallback declarativo
LiteLLM proporciona una interfaz unificada para más de 100 modelos de diferentes proveedores, con soporte nativo para listas de fallback, retry y context window fallbacks. Es la forma más directa de añadir resiliencia a un pipeline Python existente sin reescribir la lógica de llamada.
import litellm
import os
# Variables de entorno necesarias:
# ANTHROPIC_API_KEY, OPENAI_API_KEY, GOOGLE_API_KEY
def completar_con_fallback(prompt: str) -> str:
"""
Llama al LLM principal con fallback automático.
Orden: Claude Sonnet -> GPT-4o -> Gemini Flash
"""
response = litellm.completion(
model="claude-sonnet-4-20250514",
messages=[{"role": "user", "content": prompt}],
fallbacks=["gpt-4o", "gemini/gemini-2.0-flash"],
num_retries=2, # reintentos antes de pasar al fallback
timeout=20, # segundos por intento
)
return response.choices[0].message.content
Si Claude devuelve un error 529 (overloaded) o un 5xx, LiteLLM pasa automáticamente a GPT-4o. Si este también falla, intenta con Gemini Flash. El orden de fallback es exactamente el que defines en la lista.
Para pipelines más críticos, LiteLLM permite separar los fallbacks por tipo de error. Esto es útil cuando el fallo no es una caída del proveedor, sino que el contexto excede el límite del modelo:
from litellm import Router
router = Router(
model_list=[
{
"model_name": "principal",
"litellm_params": {
"model": "claude-sonnet-4-20250514",
"api_key": os.environ["ANTHROPIC_API_KEY"],
},
},
{
"model_name": "backup",
"litellm_params": {
"model": "gpt-4o",
"api_key": os.environ["OPENAI_API_KEY"],
},
},
],
# Fallback general: errores de conexión, rate limits, 5xx
fallbacks=[{"principal": ["backup"]}],
# Fallback específico: contexto demasiado largo
context_window_fallbacks=[{"principal": ["backup"]}],
)
response = router.completion(
model="principal",
messages=[{"role": "user", "content": "Texto muy largo aquí..."}],
)
Una nota sobre la configuración del Router: el modelo de fallback debe estar presente en el model_list para que el routing funcione. Si no está, LiteLLM lanza un BadRequestError en lugar de ejecutar el fallback. Es un error de configuración común que solo aparece cuando el fallback se activa por primera vez en producción.
Opción 2: OpenRouter como proxy transparente
OpenRouter es un proxy que enruta peticiones a más de 100 modelos usando una API compatible con la de OpenAI. La ventaja principal: no necesitas cambiar tu código Python existente, solo la URL base y la API key.
from openai import OpenAI
import os
# OpenRouter usa la misma interfaz que el SDK de OpenAI
client = OpenAI(
base_url="https://openrouter.ai/api/v1",
api_key=os.environ["OPENROUTER_API_KEY"],
)
def completar_openrouter(prompt: str) -> str:
response = client.chat.completions.create(
model="anthropic/claude-sonnet-4",
messages=[{"role": "user", "content": prompt}],
extra_body={
"fallbacks": ["openai/gpt-4o", "google/gemini-2.0-flash"],
},
)
# El header x-litellm-model-used indica qué proveedor respondió
return response.choices[0].message.content
OpenRouter no añade markup de precio en la mayoría de modelos: pagas lo mismo que con el proveedor directamente, más un pequeño coste de routing que en la práctica es imperceptible para volúmenes pequeños. La latencia añadida suele estar entre 10 y 30 ms por petición.
La limitación principal: pierdes determinismo de proveedor. OpenRouter puede cambiar qué modelo sirve una petición según disponibilidad y pricing, lo que complica los tests de regresión. Si tus pipelines necesitan reproducibilidad (por ejemplo, para auditoría o para comparar outputs entre sesiones), es mejor usar LiteLLM con control explícito del orden de fallback.
Opción 3: circuit breaker básico en Python
Para casos con requisitos de compliance donde los datos no pueden pasar por servicios intermedios, o cuando necesitas registro detallado de qué proveedor sirvió cada petición, un circuit breaker propio tiene sentido:
from datetime import datetime, timedelta
from enum import Enum
import logging
logger = logging.getLogger(__name__)
class Estado(Enum):
CERRADO = "cerrado" # Normal
ABIERTO = "abierto" # Proveedor caído, redirigiendo
SEMI_ABIERTO = "semi_abierto" # Probando recuperación
class CircuitBreaker:
def __init__(self, umbral_fallos: int = 3, timeout_seg: int = 120):
self.umbral = umbral_fallos
self.timeout = timeout_seg
self.fallos = 0
self.ultimo_fallo: datetime | None = None
self.estado = Estado.CERRADO
def puede_llamar(self) -> bool:
if self.estado == Estado.CERRADO:
return True
if self.estado == Estado.ABIERTO:
if self.ultimo_fallo and datetime.now() > (
self.ultimo_fallo + timedelta(seconds=self.timeout)
):
self.estado = Estado.SEMI_ABIERTO
return True
return False
return True # SEMI_ABIERTO: permite una petición de prueba
def registrar_exito(self) -> None:
self.fallos = 0
self.estado = Estado.CERRADO
def registrar_fallo(self) -> None:
self.fallos += 1
self.ultimo_fallo = datetime.now()
if self.fallos >= self.umbral:
self.estado = Estado.ABIERTO
logger.warning("Circuit breaker ABIERTO tras %d fallos", self.fallos)
import anthropic
import openai
circuit_claude = CircuitBreaker()
circuit_openai = CircuitBreaker()
def llamar_con_circuit_breaker(prompt: str) -> str:
if circuit_claude.puede_llamar():
try:
client = anthropic.Anthropic()
msg = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1024,
messages=[{"role": "user", "content": prompt}],
)
circuit_claude.registrar_exito()
return msg.content[0].text
except Exception as e:
logger.error("Claude falló: %s", e)
circuit_claude.registrar_fallo()
if circuit_openai.puede_llamar():
try:
client = openai.OpenAI()
resp = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": prompt}],
)
circuit_openai.registrar_exito()
return resp.choices[0].message.content
except Exception as e:
logger.error("OpenAI también falló: %s", e)
circuit_openai.registrar_fallo()
raise RuntimeError("Todos los proveedores no disponibles")
La ventaja de este enfoque es el control total: sabes en todo momento en qué estado está cada proveedor y puedes exponer esa información a un dashboard de monitorización o a un endpoint de health check.
En producción
Hay varios aspectos que cambian significativamente cuando este código sale del entorno de desarrollo.
Coste de activar el fallback. Si tu proveedor principal es Claude Haiku (el más económico de Anthropic) y el fallback es GPT-4o, una caída prolongada puede multiplicar tu factura. A marzo de 2026, Claude Haiku 4.5 cuesta aproximadamente 0,001 € por cada 1.000 tokens de entrada, mientras que GPT-4o está en torno a 0,003 €. Para un pipeline que procesa 10 millones de tokens al mes, la diferencia entre ambos es de unos 20 €. No es catastrófico, pero hay que contabilizarlo. Configura alertas cuando el fallback se activa para detectar si el gasto anómalo persiste.
Consistencia de outputs. Modelos diferentes responden de forma diferente aunque el prompt sea idéntico. Si tus pipelines extraen JSON estructurado, valida el formato de salida antes de procesarlo, independientemente del proveedor que respondió. Un esquema Pydantic para parsear el output hace esto menos frágil. Si estás construyendo flujos multi-agente con revisión cruzada, esta validación es especialmente importante porque un output mal formateado del agente de extracción puede romper todos los pasos posteriores.
Timeouts agresivos. El error más ignorado: si Claude tarda 45 segundos en responder porque está sobrecargado y tu timeout está en 60 segundos, no llegas al fallback. El worker queda bloqueado esperando. Configura timeouts de 15 a 20 segundos para la mayoría de tareas. El fallback no solo sirve para errores 5xx, también para latencia extrema.
Estado compartido del circuit breaker. En entornos con múltiples workers (Celery, FastAPI con varios procesos de uvicorn), cada proceso mantiene su propio circuit breaker en memoria. Si el proveedor se cae, cada proceso tiene que aprender esto por separado. Para sincronizar el estado entre procesos, necesitas Redis u otra capa compartida. LiteLLM Proxy resuelve esto centralizando la lógica de routing en un único proceso.
Logging del proveedor activo. En producción necesitas saber qué modelo sirvió cada petición. Guarda esta información junto con el output. Si un pipeline produce resultados erróneos y resulta que "durante el outage del 2 de marzo estábamos usando GPT-4o", ese dato puede ser la diferencia entre diagnosticar el problema en una hora o en un día. LiteLLM devuelve el modelo usado en response.model; OpenRouter lo incluye en el header x-litellm-model-used.
Errores comunes al implementar fallback
Error: configurar fallback solo para errores de conexión. Causa: los rate limits (HTTP 429) y los errores de modelo sobrecargado (HTTP 529) también rompen el pipeline, pero no siempre se tratan como errores de "caída". Solución: incluye estos códigos de estado en la lógica de fallback, no solo los 5xx de red.
Error: usar el mismo prompt en todos los modelos sin validar el output. Causa: Claude y GPT-4o siguen instrucciones de system prompt de forma diferente. Si tienes prompts muy ajustados a un modelo, el fallback puede producir outputs con formato inconsistente. Solución: testea el fallback activamente con datos reales de producción, no solo con prompts genéricos de verificación.
Error: no alertar cuando el fallback se activa. Causa: el pipeline "funciona" porque el fallback responde, pero nadie sabe que el proveedor principal lleva horas caído. Solución: loguea cada activación de fallback y envía una notificación (Telegram, Slack) cuando la tasa de uso del proveedor secundario supera el 10% durante un período de tiempo. Si tienes sistemas multi-agente con varios LLMs encadenados, este monitoring es especialmente crítico porque un fallback silencioso en el primer agente puede propagar outputs de calidad diferente a todos los pasos siguientes.
Preguntas frecuentes
¿El fallback afecta a la calidad de los outputs en tareas de producción reales?
Depende de la tarea. Para clasificación binaria, extracción de entidades o resúmenes de texto cortos, la diferencia entre Claude Sonnet y GPT-4o es pequeña en la mayoría de casos. Para tareas con prompts muy ajustados a las particularidades de un modelo concreto, sí puede haber diferencia de calidad o formato. La recomendación es probar el fallback con datos reales de producción antes de confiar en él para tareas críticas.
¿OpenRouter es adecuado para pipelines que manejan datos sensibles?
OpenRouter actúa como intermediario: las peticiones pasan por sus servidores antes de llegar al proveedor original. Si tienes requisitos estrictos de GDPR o procesas información confidencial, revisa su política de privacidad antes de usarlo. Para esos casos, LiteLLM con acceso directo a cada proveedor (o LiteLLM Proxy auto-hospedado) es la alternativa que elimina el intermediario y mantiene el control sobre dónde viajan los datos.
¿Cuándo no tiene sentido implementar fallback de LLM?
Cuando el coste de mantener múltiples API keys y la lógica de routing supera el coste de una parada ocasional. Para proyectos pequeños o tareas no críticas donde un retraso de dos horas no tiene impacto real, la complejidad añadida no compensa. Si el pipeline puede reintentar más tarde (procesamiento batch sin tiempo real), una estrategia de retry con backoff exponencial sobre el mismo proveedor puede ser suficiente sin necesidad de un segundo proveedor.
Para terminar
El outage de Claude del 2 de marzo fue un recordatorio de que cualquier proveedor puede fallar, independientemente de su fiabilidad histórica. Para flujos de automatización donde la disponibilidad importa, el fallback de LLM pasa de ser un detalle de arquitectura a una necesidad operativa. LiteLLM lo hace accesible en pocas líneas con control granular por tipo de error; OpenRouter lo simplifica aún más si no necesitas control sobre qué proveedor sirve cada petición; el circuit breaker manual tiene sentido solo cuando tienes requisitos específicos de auditoría o compliance que desaconsejan el uso de intermediarios.
La clave está en definir antes de implementar: qué tareas son críticas y no pueden esperar, qué tareas son tolerantes a latencia y pueden reintentar más tarde, y qué tareas no tienen suficiente impacto como para justificar esta complejidad. No todo pipeline necesita fallback multi-proveedor. Los que sí lo necesitan, y no lo tienen, suelen descubrirlo en el peor momento posible.
¿Tienes fallback configurado en tus pipelines de automatización? ¿O confías en la disponibilidad del proveedor? Cuéntamelo en los comentarios o en Twitter @sergiomarquezp_. El próximo artículo explorará estrategias de caché de completions para reducir tanto costes como dependencia de disponibilidad en tiempo real.