Маршрутизация моделей: перестаньте использовать одну модель для всего
«Подходящая модель для подходящей задачи.»
Запуск модели с 70 миллиардами параметров для обобщения электронного письма на 200 слов — расточительно. Использование модели с 3 миллиардами параметров для ревью продакшн-кода — безрассудно. Большинство систем находятся где-то посередине, и именно здесь на сцену выходит маршрутизация моделей.
Она сопоставляет сложность задачи с возможностями модели. Компромиссы реальны, но и экономия тоже.

Проблема маршрутизации
Обычно люди начинают с одной модели и придерживаются её. Это работает, пока вы не заметите проблему с затратами, задержкой или тем и другим одновременно. Альтернатива — создание маршрутизатора, который решает, какая модель обрабатывает тот или иной запрос.
На практике работают четыре стратегии:
- На основе возможностей — маршрутизация в зависимости от того, что умеет модель
- С учетом стоимости — маршрутизация в зависимости от того, сколько вы готовы потратить
- С учетом задержки — маршрутизация в зависимости от требуемой скорости
- Гибридная — их комбинация
Каждая оптимизирует что-то свое. Выбор одной из них обычно зависит от того, что болит сильнее всего.
Маршрутизация на основе возможностей
Самый простой подход. Классифицируйте задачу и отправьте её в модель, которая справляется с ней.
| Задача | Размер модели | Примеры |
|---|---|---|
| Классификация, тегирование | 1-3B | Qwen2.5-1.5B, Gemma-2-2B |
| Обобщение, извлечение данных | 3-7B | Qwen2.5-7B, Llama-3.1-8B |
| Генерация кода | 7-14B | Qwen2.5-Coder-7B, DeepSeek-Coder-V2 |
| Сложный вывод | 14-32B | Qwen2.5-32B, Llama-3.1-70B |
| Творческое письмо, анализ | 32B+ | Qwen2.5-72B, Claude, GPT-4 |
Если для задачи не нужна большая модель, не используйте её. Модель на 1.5B параметров отлично справляется с классификацией тональности. Просто она не напишет связное эссе.
Реализация проста:
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"])
Подводный камень — сама классификация. Если вы неверно определите тип задачи, вы маршрутизируете её к неправильной модели. Я видел системы, которые классифицировали ревью кода как «обобщение» и теряли качество незаметно.
Маршрутизация с учетом стоимости
Локальный инференс здесь раскрывается во всей красе. Локальные модели фактически бесплатны после амортизации оборудования. RTX 5080 окупается примерно за шесть месяцев при умеренном использовании API.
| Модель | Вход ($/M токенов) | Выход ($/M токенов) | Локальная стоимость/час |
|---|---|---|---|
| GPT-4o | $2.50 | $10.00 | — |
| Claude Sonnet 4 | $3.00 | $15.00 | — |
| Qwen2.5-72B (API) | $0.50 | $2.00 | — |
| Qwen2.5-32B (локально) | $0.00 | $0.00 | ~$0.10 |
| Qwen2.5-7B (локально) | $0.00 | $0.00 | ~$0.05 |
Если вы обрабатываете тысячи запросов за сессию, даже $0.05 на электричество выгоднее, чем $15 за миллион токенов.
Маршрутизация, основанная на бюджете, использует резервные варианты по мере расходования средств:
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"]
Качество ухудшается по мере перехода к резервным вариантам. Вы начинаете с Claude, переходите к Qwen-32B, затем к Qwen-7B. К концу длинной сессии результат заметно хуже. Имеет ли это значение, зависит от того, что вы строите.
Маршрутизация с учетом задержки
Интерактивным инструментам нужны быстрые первые токены. Пакетные задания могут подождать. Разница обычно составляет фактор пять в размере модели.
| Сценарий использования | Первый токен | Завершение | Максимальный размер модели |
|---|---|---|---|
| Чат в реальном времени | < 200 мс | < 2 с | < 7B |
| Интерактивные инструменты | < 500 мс | < 5 с | < 14B |
| Пакетная обработка | < 1 с | < 30 с | Любая |
| Исследования/анализ | < 2 с | < 60 с | Любая |
Когда вы транслируете токены пользователю, именно задержка первого токена ощущается ими. Модель на 32B, требующая полсекунды для старта, кажется медленной по сравнению с моделью на 1.5B, которая отвечает мгновенно.
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"
Цифры задержек приблизительны — они зависят от вашего оборудования, квантования и размера пакета. Измеряйте на своей собственной конфигурации.
Стратегии резервного переключения
Модели дают сбой. API ограничивает частоту запросов. Возникают таймауты. Рабочий паттерн — цепочка резервных переключений, отсортированная от лучшего к наиболее надежному:
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")
Последняя модель в цепочке должна быть локальной. Она медленнее, но не откажет из-за сетевых проблем или невалидного API-ключа.
Когда маршрутизация полезна
Маршрутизация имеет смысл, когда ваша нагрузка смешанная. Если вы выполняете классификацию, обобщение и логический вывод в одной системе, маршрутизатор экономит деньги и снижает задержки.
Она бессмысленна, если все, что вы делаете, имеет одинаковую сложность. Просто используйте модель, которая хорошо справляется с этой задачей. Маршрутизатор добавляет сложность, в которой нет необходимости.
Ранний прототипинг — еще одна причина пропустить этот этап. Сначала заставьте задачу работать с одной моделью, а затем добавьте маршрутизацию, когда стоимость или задержка действительно станут проблемой.
Компромиссы
Каждая стратегия маршрутизации оптимизирует что-то одно и жертвует чем-то другим:
- Одна модель — простейшая, самая дорогая, стабильное качество
- На основе возможностей — лучшая стоимость, более высокое качество для каждой задачи, средняя сложность
- С учетом стоимости — самая дешевая, варьирующееся качество, средняя сложность
- С учетом задержки — самая быстрая, может жертвовать качеством, средняя сложность
- Гибридная — лучшее из всего, самая сложная в реализации
Продакшн-системы обычно сходятся к гибридной модели. Начните с маршрутизации на основе возможностей, добавьте учет стоимости, когда придет счет, добавьте учет задержки, когда пользователи начнут жаловаться на медлительность.
См. также
- Оптимизация затрат для систем LLM — бюджетирование токенов, кэширование, резервные модели
- Защитные механизмы LLM на практике — валидация ввода, фильтрация вывода, безопасность
- Проектирование многомоделевых систем — архитектура для нескольких моделей
- Архитектура LLM — столп системы: маршрутизация, стоимость, защитные механизмы и оркестрация