Зачем маршрутизировать LLM-запросы
Обучающий материал: от проблемы к работающему решению. Каждая глава строится на предыдущей. В конце: сквозной пример и чек-лист, с которым можно начинать строить.
Проблема: одна модель на все запросы
Представь: ты запустил AI-продукт. Чат-бот для customer support, или coding assistant, или аналитический инструмент. У тебя 10,000 запросов в день.
Перед тобой выбор: какую модель использовать?
Качество отличное на любом запросе. Но ты платишь $25/1M output-токенов за каждый ответ, включая "Какой у вас график работы?" и "Спасибо, все понятно".
Стоимость: ~$12,500/мес
Дешево. Но на сложных вопросах ("Сравните три тарифных плана с учетом моей ситуации") ответ поверхностный и пользователь уходит недовольным.
Стоимость: ~$2,500/мес, но качество падает
Это ложная дилемма. На практике ~70% запросов в большинстве продуктов простые: фактические вопросы, повторяющиеся сценарии, короткие уточнения. И только ~30% действительно требуют мощной модели.
Идея routing: не человек выбирает модель, а система автоматически направляет каждый запрос к оптимальной модели, исходя из его сложности и типа.
Как работает routing (30 секунд)
Весь механизм сводится к четырем шагам:
- Классифицировать входящий запрос: какая сложность (simple / medium / complex)? какой тип (factual / reasoning / creative / code)?
- Маршрутизировать: по конфигу (YAML-файл) определить, какая модель обрабатывает эту комбинацию сложности и типа
- Подстраховать: если выбранная модель недоступна (timeout, rate limit, outage), автоматически переключить на fallback
- Измерить: логировать каждый запрос: какая модель использована, сколько токенов, стоимость, латентность, качество
Дальше мы разберем каждый шаг. Начнем с экономики: нужно понимать, между чем мы выбираем.
Глава 1: Экономика моделей
Прежде чем строить router, нужно понимать: какие модели существуют, сколько стоят, и где разница в качестве оправдывает разницу в цене.
Ландшафт моделей (начало 2026)
У каждого провайдера есть линейка от дорогих и мощных до дешевых и быстрых. Это не случайность: именно этот спектр делает routing возможным.
| Tier | Anthropic | OpenAI | Характер | |
|---|---|---|---|---|
| Flagship | Opus 4.6 $5 / $25 |
GPT-5.4 $2.50 / $15 |
Gemini 3.1 Pro $2 / $12 |
Complex reasoning, agentic coding, hard prompts. Лучшее качество. |
| Mid-tier | Sonnet 4.6 $3 / $15 |
GPT-4.1 / GPT-5 $2 / $8 |
Gemini 3 Flash $0.50 / $3 |
Баланс. Основная production-модель для большинства задач. |
| Budget | Haiku 4.5 $1 / $5 |
GPT-4.1 Nano $0.05 / $0.20 |
Gemini 2.5 Flash-Lite $0.10 / $0.40 |
Classification, extraction, простые Q&A, высокий volume. |
Цены: input / output за 1M токенов.
Где разница в качестве критична, а где нет
Это центральный вопрос routing. Не "какая модель лучше?", а "на каких задачах дешевая модель выдает результат, неотличимый от дорогой?"
- Мультишаговый reasoning с неоднозначностью
- Архитектурные решения в коде, большие рефакторинги
- Синтез информации из нескольких доменов
- Reasoning по 50K+ токенов контекста
- Creative writing для публикации
- Sentiment, intent detection, classification
- Data extraction (имена, даты, суммы)
- FAQ и фактические вопросы
- Конвертация форматов (text → JSON)
- Суммаризация коротких документов
Данные LMSYS Chatbot Arena (крупнейший crowdsourced бенчмарк, где реальные пользователи вслепую сравнивают ответы двух моделей) подтверждают: на factual Q&A и extraction mid-tier модели набирают Elo-рейтинг, статистически неотличимый от flagship. Разрыв проявляется на hard prompts, coding и reasoning.
Как считать стоимость запроса
LLM API тарифицируют по токенам. Формула простая:
Cost = (input_tokens x input_price / 1M) + (output_tokens x output_price / 1M)
Но есть нюанс: output-токены в 3-5x дороже input. Причина техническая: генерация output требует последовательных вычислений, а input обрабатывается параллельно.
Типичные размеры запросов
| Тип запроса | Input | Output | Haiku 4.5 | Sonnet 4.6 | Opus 4.6 |
|---|---|---|---|---|---|
| Classification / extraction | 300 | 50 | $0.00055 | $0.0017 | $0.0028 |
| Customer support ответ | 2,500 | 1,200 | $0.0085 | $0.026 | $0.043 |
| Complex reasoning | 5,000 | 2,000 | $0.015 | $0.045 | $0.075 |
| Code generation (функция) | 1,000 | 800 | $0.005 | $0.015 | $0.025 |
| RAG с контекстом | 15,000 | 1,000 | $0.02 | $0.06 | $0.1 |
10,000 запросов/день, средний запрос = customer support:
- Все через Opus: $0.043 x 10K x 30 = $12,900/мес
- Все через Sonnet: $0.026 x 10K x 30 = $7,800/мес
- Routing (70% Haiku, 30% Opus): (7K x $0.0085 + 3K x $0.043) x 30 = $5,655/мес
Экономия routing vs all-Opus: $7,245/мес (56%). И это консервативный split. С оптимизированной классификацией можно довести до 70%+.
Prompt caching и batch API: дополнительная экономия
Помимо routing, есть два механизма снижения стоимости, которые стакаются с ним:
Prompt caching
Если ваш system prompt или контекст повторяется между запросами (а он почти всегда повторяется), провайдер может кэшировать его и брать за повторное чтение в 10x меньше.
| Провайдер | Скидка на cache hit | Наценка на write | Настройка |
|---|---|---|---|
| Anthropic | 90% | 25% (5-мин) или 100% (1-час) | Явная: cache_control в API |
| OpenAI | 75-90% (зависит от модели) | Нет | Автоматическая |
| 90% | Storage: $1-4.50/1M токенов/час | Явная |
Batch API
Если запросы не нужно обрабатывать real-time (eval, аналитика, bulk processing), batch API дает 50% скидку у всех трех провайдеров. Комбинация batch + caching может дать до 95% экономии на input-токенах.
- Разброс цен между моделями: до 125x. Даже грубый routing дает десятки процентов экономии.
- 70-80% запросов в типичном продукте не требуют flagship-модели.
- Output-токены в 3-5x дороже input. Оптимизация длины ответа дает непропорциональную экономию.
- Prompt caching (90% скидка) и batch API (50%) стакаются с routing.
- При 10K req/day разница между "все через Opus" и routing: тысячи долларов в месяц.
Глава 2: Как классифицировать запросы
Routing работает, только если мы можем понять, какой запрос перед нами: простой или сложный, фактический или творческий. Это задача классификации.
Есть три подхода, от простого к сложному. Каждый следующий точнее, но дороже и медленнее. Смысл в том, чтобы выбрать подход, соответствующий стадии продукта.
Подход 1: Rule-based (правила)
Самый простой: набор if/else правил на основе наблюдаемых свойств запроса.
def classify(prompt: str) -> str:
tokens = prompt.split()
# Длина как прокси сложности
if len(tokens) < 15:
complexity = "simple"
elif len(tokens) < 80:
complexity = "medium"
else:
complexity = "complex"
# Ключевые слова для категории
code_signals = ["code", "function", "debug", "error", "```"]
if any(s in prompt.lower() for s in code_signals):
category = "code"
elif any(s in prompt.lower() for s in ["explain", "compare", "analyze", "why"]):
category = "reasoning"
else:
category = "factual"
return complexity, category
- Latency: <1ms
- Cost: $0
- Прозрачность: видно почему именно это решение
- Нет зависимостей
- Accuracy: 60-75%
- Хрупкость: "What's 2+2?" = simple, но "What's the integral of e^(x^2)?" тоже выглядит коротким
- Не генерализует на новые формулировки
Когда хватит: MVP, первая неделя разработки, узкий домен с предсказуемыми запросами, или как первый слой в гибридном подходе.
Подход 2: Embedding-based (по похожести)
Идея: если у тебя есть примеры запросов каждой категории, можно находить ближайшую категорию для нового запроса по семантической близости.
Как это работает
- Готовишь примеры: 20-50 запросов на категорию с правильными метками. "Какой курс доллара?" → simple/factual. "Проанализируй динамику за 3 года" → complex/reasoning.
- Эмбеддишь: каждый пример превращается в числовой вектор через sentence-transformer модель (например,
all-MiniLM-L6-v2: бесплатная, работает локально). - При запросе: входящий промпт эмбеддится, считается cosine similarity со всеми примерами, запрос назначается категории ближайших (kNN).
Готовая реализация: Semantic Router (MIT, open-source). Снижает latency классификации с ~5000ms (LLM) до ~100ms.
- Accuracy: 75-90%
- Latency: 50-200ms
- Cost: ~$0.0001 (локально) или ~$0.0002 (через API)
- Детерминистично: один запрос → всегда один ответ
- Cold start: нужны labeled примеры
- Плохо с запросами, непохожими ни на один пример
- Нужно обновлять примеры по мере изменения паттернов
Когда использовать: production-система с известными категориями запросов. Когда уже собрал достаточно данных, чтобы набрать по 20+ примеров на категорию.
Подход 3: LLM-as-classifier
Используем саму LLM (дешевую и быструю) для классификации запроса. По сути, задаем LLM вопрос: "какой это тип запроса?"
Zero-shot (без примеров)
System: Classify the user query.
Complexity: simple | medium | complex
Category: factual | reasoning | creative | code
Return only JSON: {"complexity": "...", "category": "..."}
User: {query}
Промпт короткий, latency минимальная (200-500ms на Haiku). Хорошо работает, когда категории интуитивно понятные. Accuracy: 70-85%.
Few-shot (с примерами)
Добавляем 3-10 примеров на категорию прямо в промпт. Accuracy растет до 80-92%, но промпт становится длиннее, а значит каждая классификация дороже.
Как выбрать подход: decision framework
Начни с вопроса: что у тебя уже есть?
- Нет данных, нужно запуститься сегодня → Rule-based. Accuracy 60-75% лучше, чем 0%. Собирай логи для следующего шага.
- Есть 100+ примеров запросов с метками → Embedding-based. Лучший баланс accuracy/cost/latency.
- Категории сложные, формулировки разнообразные, accuracy критична → LLM-as-classifier (few-shot).
- Не уверен → начни с rule-based, через неделю переключи на embeddings. Можно запускать оба параллельно и сравнивать (shadow mode).
Гибридный подход (часто лучший): rule-based как первый фильтр для очевидных случаев, embedding-based для остального. LLM-classifier только для запросов с низким confidence.
- Три подхода: rules (быстро, неточно) → embeddings (баланс) → LLM (точно, дорого).
- Идеальная стратегия зависит от стадии: MVP = rules, production = embeddings, high-stakes = LLM.
- Классификация сама стоит денег. Считай ROI: сколько тратишь на классификацию vs сколько экономишь на routing.
- Гибрид (rules + embeddings) часто лучше, чем один подход.
Глава 3: Routing-паттерны
Классификация определила, что запрос: medium/reasoning. Теперь нужно решить, какой модели его отправить. Это решение определяется routing-паттерном.
Базовый routing: конфиг-маппинг
Простейший вариант: YAML-файл, который маппит комбинации сложности и типа на конкретные модели.
routing:
simple:
factual: haiku-4.5
reasoning: haiku-4.5
creative: sonnet-4.6
code: haiku-4.5
medium:
factual: sonnet-4.6
reasoning: sonnet-4.6
creative: sonnet-4.6
code: sonnet-4.6
complex:
factual: sonnet-4.6
reasoning: opus-4.6
creative: opus-4.6
code: opus-4.6
Это работает и покрывает 80% потребностей. PM может менять конфиг без деплоя. Но есть паттерны, которые дают больше.
Cascade routing: попробуй дешевую, эскалируй если нужно
Идея: не выбирать модель заранее, а сначала отправить запрос дешевой модели. Если ее ответ "достаточно хорош", возвращаем его. Если нет, эскалируем на дорогую.
- Запрос уходит на Haiku. Haiku отвечает.
- Система оценивает ответ: confidence score, полнота, есть ли "не знаю".
- Если score > threshold → возвращаем ответ Haiku. Стоимость: $0.001.
- Если score < threshold → отправляем тот же запрос на Opus. Стоимость: $0.001 + $0.05.
Как оценивать "достаточно хорош"
- Logprobs: если API возвращает вероятности токенов, низкая средняя вероятность = модель не уверена. Хорошо для factual, хуже для creative.
- LLM-as-judge: маленькая модель оценивает ответ большой. Надежнее, но добавляет вызов.
- Self-reported confidence: попросить модель оценить себя (1-10). Ненадежно для слабых моделей.
- Heuristics: длина ответа, наличие "I'm not sure", структура.
ETH Zurich paper (2024) доказывает: комбинация routing + cascading в единый фреймворк теоретически оптимальна по сравнению с каждой стратегией по отдельности.
Confidence-based routing: страховка от ошибок классификатора
Улучшение базового routing: классификатор выдает не только метку, но и confidence score. Routing учитывает оба:
- Confidence высокий (>0.85) → отправляем модели по конфигу
- Confidence низкий (<0.5) → safety net: отправляем на более мощную модель, чем по конфигу
- Confidence средний → зависит от cost tolerance
Это простое улучшение, которое значительно снижает under-routing (сложные запросы на дешевую модель). Имплементируется за час поверх базового routing.
RouteLLM и другие решения
RouteLLM (open-source, LMSYS)
Фреймворк от команды Chatbot Arena. GitHub, paper.
Отличие от нашего подхода: RouteLLM обучен на 80K+ реальных human preference comparisons из Arena. Не классифицирует запрос, а напрямую предсказывает, какой модели пользователь предпочтет ответ.
Лучший результат: Matrix Factorization роутер достигает 95% качества GPT-4, отправляя на GPT-4 только 14% запросов. Остальные 86% идут на дешевую модель. Это 85% снижение cost на MT-Bench.
Ограничение: роутит между ровно двумя моделями (strong vs weak). Для multi-model routing (3+ модели) нужна кастомизация.
Коммерческие решения
| Решение | Подход | Особенность |
|---|---|---|
| Martian | Mechanistic interpretability: "маппит" трансформеры в программные представления | Предсказывает лучшую модель без inference. Claims: 98% cost savings. |
| Unify.ai | Neural network router + dynamic benchmarks (обновляются каждые 10 мин) | Единый API + custom router training на своих данных. |
| Not Diamond | Model router + prompt adaptation | Переписывает промпт под target-модель. Powered OpenRouter Auto. |
Как выбрать паттерн: decision framework
- Первая версия, нужно быстро → Базовый конфиг-маппинг (YAML). Просто, прозрачно, PM-editable.
- Хочу выжать больше экономии, большинство запросов простые → Cascade. Но только если >60% запросов разрешаются cheap-моделью.
- Классификатор иногда ошибается, хочу safety net → Confidence-based поверх базового routing. Малой кровью, большой эффект.
- Есть human feedback data или готов собирать → RouteLLM. Обучается на реальных предпочтениях, не на правилах.
- Не хочу строить сам, нужно в production быстро → Unify.ai или Martian как managed solution.
Рекомендация для учебного проекта: начни с конфиг-маппинга, добавь confidence-based routing, сравни с RouteLLM. Каскад имплементируй последним как V2.
- Конфиг-маппинг покрывает 80% потребностей и ставится за час.
- Каскад экономит больше, но добавляет latency и сложность. Выгоден при >60% simple-запросов.
- Confidence-based routing: лучшее улучшение за минимальные усилия.
- RouteLLM (LMSYS): state-of-the-art open-source, 85% cost reduction при 95% качества.
- Cascade + routing гибрид теоретически оптимален (ETH Zurich, 2024).
Глава 4: Надежность
Routing добавляет зависимость от нескольких провайдеров. Каждый из них может упасть, затормозить, или вернуть ошибку. Если не подстраховаться, routing принесет больше проблем, чем решит.
Три слоя защиты, от мелкого к крупному:
Слой 1: Fallback chains
Для каждой модели определяешь, кто подхватывает при отказе:
# routing config
complex:
primary: claude-opus-4.6
fallback:
- gpt-5.4 # same-tier: другой провайдер
- claude-sonnet-4.6 # downgrade: слабее, но работает
timeout_ms: 30000
simple:
primary: claude-haiku-4.5
fallback:
- gpt-4.1-nano
timeout_ms: 5000
Четыре уровня деградации (в порядке приоритета):
- Same-tier: Opus → GPT-5.4. Другой провайдер, аналогичное качество.
- Downgrade: Opus → Sonnet. Слабее, но ответ будет.
- Cached: если был похожий запрос недавно, вернуть кэшированный ответ.
- Graceful failure: понятное сообщение пользователю, не stack trace.
Слой 2: Circuit breaker
Проблема: если провайдер лёг на 10 минут, fallback chain будет каждый раз ждать timeout primary-модели (30 секунд) прежде чем переключиться. 10,000 запросов x 30 сек ожидания = пользователи уходят.
Circuit breaker решает это. Паттерн из электротехники: когда downstream-сервис нездоров, "выбиваем автомат" и перестаем к нему обращаться.
Три состояния
| Состояние | Что происходит | Когда переключается |
|---|---|---|
| Closed (норма) | Запросы идут к провайдеру. Считаем failures. | → Open, когда 5+ failures за 60 сек |
| Open (отключен) | Все запросы сразу на fallback. Провайдер не трогаем. | → Half-Open через 30 сек |
| Half-Open (тест) | Пропускаем 1 тестовый запрос к провайдеру. | → Closed если OK, → Open если fail |
class CircuitBreaker:
def __init__(self, provider, threshold=5, timeout=30):
self.provider = provider
self.state = "closed"
self.failures = 0
self.threshold = threshold
self.timeout = timeout
self.opened_at = None
def call(self, prompt):
if self.state == "open":
if time.time() - self.opened_at > self.timeout:
self.state = "half_open"
else:
raise CircuitOpen(self.provider)
try:
result = self.provider.complete(prompt)
if self.state == "half_open":
self.state = "closed"
self.failures = 0
return result
except (Timeout, RateLimit, ServerError) as e:
self.failures += 1
self.opened_at = time.time()
if self.failures >= self.threshold:
self.state = "open"
raise
Готовые библиотеки: PyBreaker, circuitbreaker.
Слой 3: Retry с backoff и timeout management
Порядок действий при ошибке:
- Retry с exponential backoff (1s, 2s, 4s). Тот же провайдер, max 2-3 попытки. Только для transient errors.
- Fallback на другой провайдер: та же модель через другой endpoint (Anthropic → AWS Bedrock).
- Fallback на другую модель: Opus → Sonnet → Haiku.
- Circuit breaker: если провайдер стабильно падает, отключаем его на cooldown.
Timeout по классам моделей
| Класс | Timeout | Почему |
|---|---|---|
| Budget (Haiku, Nano) | 5-10 сек | Если за 10 сек не ответила, что-то не так |
| Mid-tier (Sonnet, GPT-4.1) | 15-30 сек | Более сложные запросы, длиннее генерация |
| Flagship (Opus, o3) | 30-120 сек | Reasoning-модели объективно думают дольше |
Streaming + heartbeat: при streaming, если нет новых токенов 5+ секунд, считай ответ "зависшим" и переключайся. Это ловит partial failures, которые обычный timeout пропускает.
# Полная цепочка обработки ошибок
Request -> Primary (timeout T)
-> Retry x2 с backoff (если transient error)
-> Same model, другой провайдер (timeout T/2)
-> Fallback model (timeout T)
-> Graceful error + cached response if available
- Три слоя работают вместе: retry (blips) → fallback (Plan B) → circuit breaker (outage).
- Circuit breaker критичен: без него каждый запрос при outage ждет полный timeout.
- Breaker per provider, не глобально. Только transient ошибки считаются.
- Timeout разный для разных моделей: flagship объективно нужно больше времени.
- При streaming используй heartbeat detection (5 сек без токенов = stall).
Глава 5: Измерение и observability
Routing внедрен, fallback работает. Но без observability ты не знаешь: экономит ли routing? Где классификатор ошибается? Деградирует ли качество?
Без данных невозможно итерировать. А итерация: это то, что отличает "поставил routing" от "routing приносит результат".
Какие метрики трекать и зачем
Не все метрики одинаково важны. Вот приоритеты:
Tier 1: бизнес-метрики (показываешь на review)
| Метрика | Что показывает | Как считать |
|---|---|---|
| Cost savings vs baseline | Сколько сэкономили | Actual cost vs "все запросы через Sonnet" cost. Процент. |
| Quality delta | Не деградировало ли качество | LLM-as-judge score (routed) vs score (single-model). Дельта <5% = OK. |
| Monthly cost trend | Куда идем | Burn rate: total_cost за 30 дней, тренд. |
Tier 2: операционные (смотришь каждый день)
| Метрика | Зачем | Alert threshold |
|---|---|---|
| Error rate per provider | Здоровье провайдеров | >2% за 5 мин |
| Fallback trigger rate | Растет = primary деградирует | >5% за час |
| Latency p50 / p95 | User experience | p95 > 10s |
| Model distribution | Drift от ожидаемого = classifier regression | Отклонение >15% от baseline |
Tier 3: аналитические (смотришь раз в неделю)
| Метрика | Зачем | Как получить |
|---|---|---|
| Over-routing rate | Simple на дорогую модель = wasted $ | Eval set с ground truth labels |
| Under-routing rate | Complex на дешевую = quality loss | Тот же eval set |
| Classifier confidence distribution | Pile-up на low confidence = classifier не справляется | Histogram confidence scores |
| Cost per quality-adjusted request | Реальная эффективность | cost / quality_score |
Как оценивать качество в production
Три подхода (используй все три):
- Heuristic checks (100% трафика): ответ не пустой? JSON парсится? Код компилируется? Нет отказа? Дешево, ловит грубые проблемы.
- LLM-as-judge (5-10% sample): дешевая модель оценивает ответы по rubric (helpfulness, relevance, accuracy). Score 0-1. Alignment с human judgment: до 85%.
- User feedback: thumbs up/down. Implicit: retry = плохо, copy = хорошо. Самые ценные данные, но мало и biased.
Готовые инструменты
Прежде чем строить свое, стоит знать, что уже существует:
| LangSmith | Langfuse | Helicone | |
|---|---|---|---|
| Что | Observability от LangChain | Open-source LLM platform | LLM proxy с логированием |
| Лицензия | Proprietary | MIT | Open source |
| Self-hosting | Enterprise only | Full parity, бесплатно | Docker, одна команда |
| Как работает | SDK instrumentation | SDK / OpenTelemetry | Proxy: меняешь base URL |
| LiteLLM | Через wrapper | Native интеграция | Native (proxy) |
| Quality eval | Best: judges, rubrics, annotation queues | LLM-as-judge, Ragas | Minimal |
| Free tier | 5K traces/мес | 50K units/мес | 10K req/мес |
| Best for | LangChain stack, нужна полная платформа | Self-hosted, API-first | Zero-code, быстрый старт |
- Для учебного проекта: строим свою observability (SQLite + React). Главная ценность: понимаешь что и зачем логируешь.
- Если потом нужно в production: Langfuse. Open source, self-hosted, native LiteLLM.
- Если нужно за 5 минут: Helicone. Меняешь URL, всё работает.
Своя observability: что логировать
SQLite-схема, адаптированная для multi-model router:
CREATE TABLE requests (
id TEXT PRIMARY KEY,
timestamp_utc TEXT NOT NULL,
-- Что пришло
prompt TEXT NOT NULL,
prompt_tokens INTEGER,
-- Как классифицировали
complexity TEXT, -- simple/medium/complex
category TEXT, -- factual/reasoning/creative/code
classifier_strategy TEXT, -- rules/embeddings/llm_classifier
classifier_confidence REAL, -- 0.0-1.0
-- Куда отправили
target_model TEXT NOT NULL,
fallback_used BOOLEAN DEFAULT 0,
fallback_model TEXT,
-- Что получили
response TEXT,
output_tokens INTEGER,
status TEXT DEFAULT 'success', -- success/error/timeout/fallback
error_type TEXT,
-- Перформанс
classification_latency_ms INTEGER,
llm_latency_ms INTEGER,
total_latency_ms INTEGER,
-- Деньги
input_cost REAL,
output_cost REAL,
total_cost REAL,
-- Качество (заполняется асинхронно)
quality_score REAL,
user_feedback INTEGER -- -1/0/1
);
Python tracker (writes) → SQLite (WAL mode) → FastAPI (reads) → React + Recharts (dashboard)
Tracker пишет в SQLite на каждом запросе (sync, <1ms). FastAPI отдает JSON для дашборда. Для тяжелых агрегаций: materialized таблица
daily_stats (обновляется cron-ом раз в час).
SQLite с WAL mode нормально работает до ~10M строк. Для учебного проекта: более чем достаточно.
Ключевые views дашборда
- KPI cards (top): total requests, total cost, cost savings %, avg latency, error rate
- Cost breakdown by model (stacked bar, daily): видишь, куда уходят деньги
- Model distribution (pie/donut): % запросов на каждую модель. Drift = проблема.
- Latency p50/p95 (line, per model): деградация видна сразу
- Routing efficiency (comparison): actual cost vs single-model baseline
- Request log (table): searchable, filterable, click to expand
- Без observability routing: черный ящик. Ты не знаешь, экономишь ли и не деградирует ли качество.
- Tier 1 метрика: cost savings vs baseline. Это то, что показываешь на review.
- Качество оценивай на трех уровнях: heuristics (100%), LLM-judge (5-10%), user feedback.
- Для учебного проекта: своя SQLite + FastAPI + React. Для production: Langfuse.
Сквозной пример: от проблемы до результата
Соберем все вместе. Один сценарий, который проходит через каждую главу.
Продукт: AI-ассистент для customer support в e-commerce. 8,000 запросов/день.
Текущее состояние: все через Claude Sonnet 4.6. Работает хорошо, но стоит $6,240/мес и растет.
Задача: снизить расходы на 40%+, не потеряв качество.
Шаг 1: Анализ запросов
Берем выборку из 500 запросов за последнюю неделю. Размечаем вручную (или с помощью LLM + spot-check):
| Категория | % запросов | Примеры |
|---|---|---|
| Simple / factual | 45% | "Где мой заказ?", "Какие способы оплаты?", "Есть ли доставка в Ташкент?" |
| Medium / reasoning | 35% | "Мне не подошел размер, что делать?", "Сравните два тарифа доставки" |
| Complex / reasoning | 15% | "У меня 3 заказа, один вернули, второй потеряли, третий пришел не тот. Разберитесь." |
| Creative | 5% | Персонализированные рекомендации, нестандартные запросы |
Вывод: 45% запросов (простые, фактические) не требуют Sonnet. Еще 35% (medium) можно попробовать на дешевой модели с confidence-based escalation.
Шаг 2: Routing config
routing:
simple:
factual: claude-haiku-4.5 # $1/$5
reasoning: claude-haiku-4.5
creative: claude-sonnet-4.6
code: claude-haiku-4.5
medium:
factual: claude-sonnet-4.6 # $3/$15
reasoning: claude-sonnet-4.6
creative: claude-sonnet-4.6
code: claude-sonnet-4.6
complex:
factual: claude-sonnet-4.6
reasoning: claude-opus-4.6 # $5/$25
creative: claude-opus-4.6
code: claude-opus-4.6
fallback:
claude-opus-4.6: [gpt-5.4, claude-sonnet-4.6]
claude-sonnet-4.6: [gpt-4.1, claude-haiku-4.5]
claude-haiku-4.5: [gpt-4.1-nano]
Шаг 3: Классификация
MVP: rule-based (длина + keywords). Accuracy ~70%. Через неделю, когда наберется 200+ размеченных запросов: embedding-based через Semantic Router. Accuracy ~85%.
Шаг 4: Считаем экономику
Средний запрос: 2,000 input + 800 output tokens.
| Модель | % запросов | Req/день | Cost/req | Cost/день | |
|---|---|---|---|---|---|
| Simple | Haiku 4.5 | 45% | 3,600 | $0.006 | $21.60 |
| Medium | Sonnet 4.6 | 35% | 2,800 | $0.018 | $50.40 |
| Complex | Opus 4.6 | 15% | 1,200 | $0.030 | $36.00 |
| Creative | Sonnet 4.6 | 5% | 400 | $0.018 | $7.20 |
| Total/день | $115.20 | ||||
| Total/мес | $3,456 | ||||
И это без prompt caching (ещё -20-30% на input) и без batch API. С ними реалистично дойти до 60%+ экономии.
Шаг 5: Что на дашборде
Через неделю работы router показывает на дашборде:
- Model distribution: Haiku 45%, Sonnet 40%, Opus 15%. Совпадает с ожиданием.
- Cost savings: 44% vs all-Sonnet baseline.
- Quality (LLM-judge, 10% sample): 0.87 routed vs 0.89 all-Sonnet. Delta 2.2%. Приемлемо.
- Fallback rate: 0.3%. Здоровая система.
- Classifier confidence: 82% запросов с confidence >0.8. 6% с confidence <0.5 (зона для улучшения).
Action item: 6% запросов с low confidence. Посмотреть, что это за запросы, добавить примеры в embedding set, пересчитать accuracy.
Итог: что ты теперь знаешь и что делать дальше
Карта знаний
| Тема | Ключевой insight |
|---|---|
| Экономика | Разброс цен между моделями до 125x. 70-80% запросов не требуют flagship. Routing дает 40-70% экономии. |
| Классификация | Три подхода (rules → embeddings → LLM), выбор зависит от стадии. Классификация сама стоит денег: считай ROI. |
| Routing | Конфиг-маппинг = 80% результата. Confidence-based = лучшее улучшение за минимум усилий. Cascade + routing = теоретический оптимум. |
| Надежность | Три слоя: retry → fallback → circuit breaker. Breaker per provider. Tiered timeouts. |
| Observability | Главная метрика: cost savings vs baseline. Качество: heuristics (100%) + LLM-judge (5-10%) + feedback. |
Чек-лист: порядок действий
- Собери eval dataset (100+ запросов с ground truth метками). Без этого нельзя ни измерить accuracy классификатора, ни сравнить качество routing.
- Имплементируй rule-based classifier как baseline. Замерь accuracy на eval set.
- Сделай конфиг-маппинг routing (YAML). Подключи LiteLLM.
- Добавь tracker (SQLite). Логируй каждый запрос: модель, токены, стоимость, latency.
- Добавь fallback chains + circuit breaker.
- Замени rule-based на embedding-based classifier. Сравни accuracy.
- Добавь confidence-based routing.
- Построй dashboard. Покажи cost savings vs baseline.
- Добавь LLM-as-classifier как третью стратегию. Сравни все три.
- Оптимизируй: prompt caching, batch eval, cascade routing.
Ресурсы для углубления
Papers
- RouteLLM: Learning to Route LLMs (LMSYS, 2024). Главный paper по теме. Четыре реализации роутера, 85% cost reduction.
- Unified Cascade-Routing (ETH Zurich, 2024). Доказательство оптимальности гибрида cascade + routing.
- "Doing More with Less" (Feb 2025). Наиболее полный survey по LLM routing.
- ZeroRouter (Jan 2025). Model-agnostic routing без model-specific данных.
Open-source инструменты
- RouteLLM: framework для learned routing (LMSYS)
- Semantic Router: embedding-based routing (MIT)
- BEST-Route: adaptive model + sample selection (Microsoft)
- PyBreaker: circuit breaker для Python
- Langfuse: open-source LLM observability
- Helicone: LLM proxy с observability
Коммерческие платформы
- Martian: routing через mechanistic interpretability
- Unify.ai: LLM optimization + dynamic benchmarks
- Not Diamond: routing + prompt adaptation
Бенчмарки и цены
- LMSYS Chatbot Arena: crowdsourced model ranking
- Artificial Analysis: LLM Leaderboard
- Pricing: Anthropic | OpenAI | Google