Enrutamiento de modelos: Deja de usar un solo modelo para todo
El modelo adecuado para la tarea correcta.
Ejecutar un modelo de 70 mil millones de parámetros para resumir un correo electrónico de 200 palabras es un desperdicio. Utilizar un modelo de 3 mil millones de parámetros para revisar código en producción es imprudente. La mayoría de los sistemas se encuentran en algún punto intermedio, y ahí es donde entra el enrutamiento de modelos.
Este enfoque adapta la complejidad de la tarea a la capacidad del modelo. Los compromisos (trade-offs) son reales, pero los ahorros también lo son.

El problema del enrutamiento
La gente suele comenzar con un solo modelo y ceñirse a él. Eso funciona hasta que te das cuenta del coste, de la latencia o de ambos. La alternativa es construir un enrutador, algo que decida qué modelo maneja cada solicitud.
En la práctica, funcionan cuatro estrategias:
- Basada en capacidades — enrutar según lo que el modelo puede hacer.
- Consciente del coste — enrutar según lo que estás dispuesto a gastar.
- Consciente de la latencia — enrutar según la velocidad requerida.
- Híbrida — combinarlas.
Cada una optimiza algo diferente. Elegir una suele ser una decisión sobre qué duele más.
Enrutamiento basado en capacidades
Es el enfoque más sencillo. Clasifica la tarea y envíala al modelo que la maneje.
| Tarea | Tamaño del modelo | Ejemplos |
|---|---|---|
| Clasificación, etiquetado | 1-3B | Qwen2.5-1.5B, Gemma-2-2B |
| Resumen, extracción | 3-7B | Qwen2.5-7B, Llama-3.1-8B |
| Generación de código | 7-14B | Qwen2.5-Coder-7B, DeepSeek-Coder-V2 |
| Razonamiento complejo | 14-32B | Qwen2.5-32B, Llama-3.1-70B |
| Escritura creativa, análisis | 32B+ | Qwen2.5-72B, Claude, GPT-4 |
Si la tarea no necesita el modelo más grande, no lo uses. Un modelo de 1.5B maneja bien la clasificación de sentimientos. Simplemente no escribirá un ensayo coherente.
La implementación es directa:
ROUTING_RULES = {
"classify": {"model": "qwen2.5-1.5b", "max_tokens": 100},
"summarize": {"model": "qwen2.5-7b", "max_tokens": 500},
"code_review": {"model": "qwen2.5-coder-7b", "max_tokens": 2000},
"reason": {"model": "qwen2.5-32b", "max_tokens": 4000},
"creative": {"model": "claude-sonnet-4", "max_tokens": 8000},
}
def route_request(task_type: str) -> dict:
return ROUTING_RULES.get(task_type, ROUTING_RULES["reason"])
El problema es la propia clasificación. Si te equivocas en el tipo de tarea, enrutarás al modelo incorrecto. He visto sistemas que clasificaban la revisión de código como “resumen” y perdían calidad silenciosamente.
Enrutamiento consciente del coste
La inferencia local brilla aquí. Los modelos locales son prácticamente gratuitos después de amortizar el hardware. Una RTX 5080 se paga sola en unos seis meses con un uso moderado de la API.
| Modelo | Entrada ($/M tokens) | Salida ($/M tokens) | Coste local/hora |
|---|---|---|---|
| GPT-4o | $2.50 | $10.00 | — |
| Claude Sonnet 4 | $3.00 | $15.00 | — |
| Qwen2.5-72B (API) | $0.50 | $2.00 | — |
| Qwen2.5-32B (local) | $0.00 | $0.00 | ~$0.10 |
| Qwen2.5-7B (local) | $0.00 | $0.00 | ~$0.05 |
Si procesas miles de solicitudes por sesión, incluso $0.05 en electricidad superan a los $15 por millón de tokens.
El enrutamiento basado en presupuesto retrocede a medida que gastas:
class CostAwareRouter:
def __init__(self, budget_per_session: float = 0.10):
self.budget = budget_per_session
self.spent = 0.0
self.models = {
"cheap": {"model": "qwen2.5-7b", "cost": 0.0},
"medium": {"model": "qwen2.5-32b", "cost": 0.0},
"expensive": {"model": "claude-sonnet-4", "cost": 0.000015},
}
def route(self, task: str) -> str:
ratio = self.spent / self.budget
if ratio < 0.5:
return self.models["expensive"]["model"]
elif ratio < 0.8:
return self.models["medium"]["model"]
return self.models["cheap"]["model"]
La calidad se degrada a medida que retrocedes. Comienzas con Claude, pasas a Qwen-32B y luego a Qwen-7B. Al final de una sesión larga, la salida es notablemente peor. Si eso importa depende de lo que estés construyendo.
Enrutamiento consciente de la latencia
Las herramientas interactivas necesitan primeros tokens rápidos. Las tareas por lotes pueden esperar. La diferencia suele ser un factor de cinco en el tamaño del modelo.
| Caso de uso | Primer token | Completado | Tamaño máximo del modelo |
|---|---|---|---|
| Chat en tiempo real | < 200ms | < 2s | < 7B |
| Herramientas interactivas | < 500ms | < 5s | < 14B |
| Procesamiento por lotes | < 1s | < 30s | Cualquiera |
| Investigación/Análisis | < 2s | < 60s | Cualquiera |
Cuando transmites tokens a un usuario, la latencia del primer token es lo que perciben. Un modelo de 32B que tarda medio segundo en empezar se siente lento en comparación con un modelo de 1.5B que responde al instante.
class LatencyAwareRouter:
def __init__(self):
self.model_latencies = {
"qwen2.5-1.5b": {"first_token": 0.05, "complete": 0.5},
"qwen2.5-7b": {"first_token": 0.15, "complete": 2.0},
"qwen2.5-32b": {"first_token": 0.5, "complete": 10.0},
"claude-sonnet-4": {"first_token": 0.3, "complete": 5.0},
}
def route(self, target_latency: float) -> str:
for model, latencies in sorted(
self.model_latencies.items(),
key=lambda x: x[1]["complete"]
):
if latencies["complete"] <= target_latency:
return model
return "qwen2.5-1.5b"
Los números de latencia son aproximados: dependen de tu hardware, cuantización y tamaño de lote. Mide en tu propia configuración.
Estrategias de respaldo
Los modelos fallan. Las APIs limitan las tasas. Ocurren tiempos de espera. El patrón que funciona es una cadena de respaldo, ordenada de la mejor a la más fiable:
class FallbackRouter:
def __init__(self):
self.fallback_chain = [
{"model": "claude-sonnet-4", "timeout": 30},
{"model": "qwen2.5-72b", "timeout": 60},
{"model": "qwen2.5-32b", "timeout": 120},
{"model": "qwen2.5-7b", "timeout": 300},
]
def route_with_fallback(self, prompt: str) -> str:
for config in self.fallback_chain:
try:
return self.call_model(
config["model"], prompt,
timeout=config["timeout"]
)
except (TimeoutError, APIError) as e:
log.warning(f"Model {config['model']} failed: {e}")
continue
raise RuntimeError("All fallback models failed")
El último modelo de la cadena debería ser local. Es más lento, pero no fallará debido a un problema de red o a una clave de API.
Cuándo ayuda el enrutamiento
El enrutamiento tiene sentido cuando tu carga de trabajo es mixta. Si estás haciendo clasificación, resumen y razonamiento en el mismo sistema, un enrutador ahorra dinero y latencia.
No tiene sentido cuando todo lo que haces tiene la misma complejidad. Simplemente usa el modelo que sea bueno para esa tarea. El enrutador añade complejidad que no necesitas.
El prototipado inicial es otra razón para omitirlo. Consigue que la tarea funcione con un modelo y luego añade enrutamiento cuando el coste o la latencia se conviertan realmente en un problema.
Compromisos (Trade-offs)
Cada estrategia de enrutamiento optimiza algo y sacrifica otra cosa:
- Modelo único — más sencillo, más caro, calidad consistente.
- Basado en capacidades — mejor coste, mayor calidad por tarea, complejidad moderada.
- Consciente del coste — más barato, la calidad varía, complejidad moderada.
- Consciente de la latencia — más rápido, puede sacrificar calidad, complejidad moderada.
- Híbrido — lo mejor de todo, más complejo de implementar.
Los sistemas en producción suelen converger hacia la opción híbrida. Comienza con el enrutamiento basado en capacidades, añade la conciencia del coste cuando llegue la factura y añade la conciencia de la latencia cuando los usuarios se quejen de la lentitud.
Relacionado
- Optimización de costes para sistemas LLM — presupuesto de tokens, caché, modelos de respaldo.
- Barreras de seguridad LLM en la práctica — validación de entrada, filtrado de salida, seguridad.
- Diseño de sistemas multimodelo — arquitectura para múltiples modelos.
- Arquitectura LLM — pilar de diseño de sistemas: enrutamiento, coste, barreras de seguridad y orquestación.