Роутер моделей вместо большего бюджета на LLM
Большинство AI-продуктов платят за frontier-модель там, где хватило бы регулярки. Разбираю роутер, который я ставлю перед каждым LLM-вызовом: три тира, дешёвый классификатор, кэш, эскалация вместо ретраев и учёт стоимости по фичам.
Pavel Duglas
AI Automation & MVP Architect
Почти в каждом AI-продукте, который я разбираю, одна и та же болезнь: в коде захардкожено одно имя модели, и оно используется на всё - от «определи, спам это или нет» до «сделай трёхстраничную юридическую выжимку». Счёт растёт линейно с пользователями, маржа сжимается, а основатель идёт не в архитектуру, а в переписку с вендором за скидку. Лечится это скучно и надёжно: перед LLM-вызовами ставится роутер.
Я делал это для support-автоматизации в SaaS, для парсера документов и для Telegram-бота, который квалифицирует лиды. Во всех трёх случаях счёт упал на 60–80%, а качество не просело или даже выросло - потому что роутинг заставляет наконец сформулировать, что значит «достаточно хорошо» для каждой задачи.
Почему счёт на самом деле такой большой
Дело почти никогда не в объёме токенов. Дело в трёх вещах:
- Одна модель на всё. Вы выбрали самую умную на этапе прототипа, когда важна была правильность, а не копейки, и с тех пор не пересматривали.
- Один и тот же контекст на всё. В вызов, которому нужно 200 токенов, чтобы ответить «да/нет», уезжает system prompt на 6k токенов плюс вся история диалога.
- Слепые ретраи. Что-то упало - повторяем тем же дорогим вызовом. Иногда трижды. Иногда в цикле, за которым никто не смотрит.
Роутер бьёт по всем трём сразу, потому что решение «какая модель» всегда тянет за собой решение «какой контекст».
Шаг 1: инвентаризация вызовов, а не моделей
До того как писать код роутера, выпишите все места в продукте, где вызывается LLM. Для каждого - четыре поля:
- Тип задачи - классификация, извлечение, переписывание, генерация, ризонинг, выбор инструмента.
- Цена ошибки - что будет, если ответ неверный? Неправильный тег - дёшево. Неправильная сумма в счёте - дорого.
- Бюджет по latency - ждёт ли этого человек в реальном времени?
- Объём - вызовов в сутки.
Я делаю это в гугл-таблице примерно за час. Картина всегда одинаковая: 70–85% вызовов - это высокочастотные задачи с низкой ценой ошибки, и они же съедают 70–85% бюджета без всякой на то причины.
Эта таблица и есть ваша политика роутинга. Дальше - только реализация.
Шаг 2: три тира, а не двенадцать
Я использую ровно три уровня. Больше - и в системе уже никто не может держать логику в голове.
Tier 0 - без модели вообще
Самый дешёвый LLM-вызов - тот, которого не было. Регулярка, сравнение строк, справочная таблица, маленькая локальная embedding-модель, детерминированный парсер. В проекте с парсингом документов 40% «AI-извлечений» заменились регуляркой плюс нормализатором валют - потому что документы приходили из трёх известных шаблонов. Стоимость стала нулевой, latency - 2 мс, а точность выросла.
Всегда спрашивайте себя: это правда языковая задача - или я просто держал в руке LLM и всё стало похоже на текст?
Tier 1 - маленькая / open-weight рабочая лошадка
Классификация, тегирование, маршрутизация, короткое извлечение полей, определение интента, да/нет-гейты, саммари одного абзаца, переформатирование. Небольшие хостовые или self-hosted open-weight модели закрывают это без заметной разницы в качестве - но только на ограниченных задачах. Ключевое слово «ограниченных»: дайте жёсткую схему, enum допустимых значений и 3–5 few-shot примеров. Маленькая модель с хорошим промптом на таких задачах уверенно обгоняет большую с ленивым промптом.
Tier 2 - frontier-модель
Многошаговый ризонинг, неоднозначные входные данные, длинные документы, генерация кода и всё, что видит клиент и где ошибка превращается в тикет в поддержку или юридический риск. Этот тир должен быть меньшинством трафика, и вы должны уметь по памяти назвать, какие именно фичи в него ходят.
Шаг 3: пишем роутер
Роутер - это функция, а не фреймворк. Примерно такой формы:
def route(task: str, payload: dict) -> ModelChoice:
policy = POLICIES[task] # из вашей таблицы
if policy.deterministic_handler and policy.deterministic_handler.can_handle(payload):
return ModelChoice(tier=0)
tokens = estimate_tokens(payload)
complexity = policy.complexity_signal(payload) # дешёвая эвристика
if complexity == "low" and tokens < policy.small_model_limit:
return ModelChoice(tier=1, model=policy.small_model)
return ModelChoice(tier=2, model=policy.big_model)
Обратите внимание, чего здесь нет: LLM, которая решает, какую LLM позвать. Это ловушка, в которую регулярно попадают - «роутер-агент», который тратит frontier-вызов на решение, делать ли frontier-вызов. Используйте модель как классификатор только тогда, когда эвристики реально не справляются, и тогда - Tier 1, жёсткий лимит токенов и enum на выходе.
Бесплатные и хорошие сигналы сложности:
- длина и структура входа (парсится ли как известный шаблон);
- количество сущностей/вопросов в запросе;
- нужны ли инструменты (tool use);
- тариф пользователя (платным можно отдавать Tier 2 в спорных случаях);
- номер попытки (см. ниже).
Шаг 4: эскалация вместо ретраев
Слепые ретраи - это способ утроить счёт во время инцидента. Замените их эскалацией:
- Идёт вызов Tier 1.
- Ответ проходит валидатор - проверка JSON-схемы, проверка enum, бизнес-правило (сумма равна сумме позиций?) или поле confidence, которое модель обязана вернуть.
- Если валидация не прошла - один раз эскалируем в Tier 2, добавив в промпт причину провала.
- Если и Tier 2 не прошёл - громко падаем в очередь на человека. Никаких циклов.
Это самый сильный по отдаче паттерн во всей статье. Он означает, что дешёвому тиру достаточно быть правым в большинстве случаев, потому что ошибки перехватываются и апгрейдятся, а не уезжают клиенту. На практике «маленькая модель с точностью 92% + валидатор + одна эскалация» выигрывает у «дорогой модели с 97%» и по деньгам, и по итоговой точности - потому что 3% ошибок дорогой модели уходят наружу непроверенными.
Жёстко ограничивайте суммарный расход на запрос. Я привязываю бюджет по токенам/деньгам к каждой входящей задаче, и роутер просто отказывается эскалировать за его пределы. Один сорвавшийся agent-loop стоит дороже, чем неделя инженерного времени на такую защиту.
Шаг 5: кэш всерьёз
Три слоя, в порядке пользы:
- Exact-match кэш. Хэш от (task, model, prompt, params) → ответ. Тривиально корректно и неожиданно эффективно. У support-ботов и генераторов описаний товаров hit rate бывает огромным.
- Prefix / prompt caching. Большинство провайдеров берут меньше за повторяющийся префикс. Перестройте промпт так, чтобы стабильная часть (инструкции, схема, примеры) шла первой, а переменная - в конце. Рефакторинг на 30 минут с реальной экономией.
- Семантический кэш. Эмбеддим запрос, ищем близкий дубликат выше порога похожести, переиспользуем ответ. Мощно и опасно: только там, где слегка неточный ответ допустим, и никогда - где в ответе есть персональные данные конкретного пользователя. Порог ставьте высокий, каждый хит логируйте, чтобы потом можно было провести аудит.
Шаг 6: eval-гейт, иначе вы гадаете
Нельзя ответственно понизить модель без тестового набора. Он не обязан быть красивым. Для каждой задачи собираем 50–200 реальных входов с ожидаемыми выходами. Для классификации - точное совпадение. Для извлечения - совпадение по полям. Для генерации - рубрика, которую оценивает ваша Tier 2 модель, плюс 20 примеров, проверенных руками, чтобы проверить самого оценщика.
Запускаем это как CI-джобу. Правила, которые я ввожу:
- изменение политики роутинга - это изменение кода, и в PR должен быть diff по eval;
- у каждой задачи есть минимальный порог точности, ниже - билд красный;
- отчёт по eval печатает стоимость прогона, чтобы точность и деньги были в одной таблице.
Это превращает «мне кажется, дешёвая модель хуже» в число. В половине случаев число говорит, что дешёвая модель нормальна, и спор на этом заканчивается.
Шаг 7: считать деньги по фичам, а не по месяцам
Логируйте каждый вызов: имя задачи, тир, модель, input-токены, output-токены, посчитанная стоимость, флаг cache hit, результат валидации, флаг эскалации, id пользователя/тенанта. Одна широкая таблица, один дашборд.
Что вы должны узнавать за пять секунд:
- стоимость фичи в день;
- стоимость на активного пользователя и на платящего пользователя;
- rate эскалаций по задачам (растёт - значит поменялся промпт или входные данные);
- топ-10 самых дорогих отдельных запросов за вчера.
Последний пункт вылавливает патологию: пользователя, который вставил PDF на 200 страниц; цикл, отработавший 47 раз; тенанта, чьё потребление в одиночку убивает вашу юнит-экономику. В том support-SaaS три аккаунта из 400 давали 31% всех AI-расходов. Это не проблема модели - это проблема тарифной сетки, и увидеть её можно только при логировании по тенантам.
Порядок миграции, при котором никто не паникует
Не надо переписывать всё сразу. Последовательность:
- Добавьте логирование и атрибуцию стоимости. Больше ничего не меняйте. Подождите неделю.
- Соберите eval-набор для двух самых дорогих задач.
- Переведите эти две задачи на Tier 1 с валидатором и одной эскалацией.
- Включите exact-match кэш и перестройте промпты под prefix caching.
- Замените очевидных кандидатов Tier 0 на детерминированный код.
- Только после этого думайте про self-hosting.
Self-hosting open-weight моделей стоит последним намеренно. У него лучший заголовок по экономии и худшие скрытые издержки: GPU, ops, свои евалы, дежурства. Идти туда стоит, когда объём стабилен и предсказуем, - а не потому что вас впечатлил чей-то бенчмарк.
Что вы на самом деле покупаете
Помимо счёта - независимость от вендора. Когда есть роутер, eval-харнесс и политики по задачам, замена модели превращается в правку конфига и прогон CI, а не в двухнедельную панику. Новые модели выходят каждые несколько недель. Выигрывают команды, которые могут протестировать и внедрить за один вечер; команды с захардкоженным именем модели в сорока файлах просто смотрят со стороны.
Соберите роутер до того, как он вам понадобится. Это день работы, и он делает дешевле каждое следующее решение по AI в вашем продукте.
Вопросы и ответы
С чего начать, если продукт уже в проде и трогать его страшно?
Начните с наблюдаемости, а не с оптимизации. Оберните все LLM-вызовы одной функцией, которая логирует имя задачи, модель, токены, стоимость и результат, и оставьте это работать неделю. После этого у вас будет отчёт, из которого видно две-три задачи, съедающие большую часть бюджета. Роутинг вводите только для них - так изменение остаётся маленьким и обратимым, а эффект уже заметный.
Не сломается ли качество, если перевести часть задач на маленькую модель?
Сломается, если делать это на глаз. Поэтому в схеме два предохранителя: eval-набор из 50–200 реальных примеров с порогом точности в CI и валидатор с одной эскалацией в Tier 2 на рантайме. Валидатор проверяет схему, enum и бизнес-правила; если ответ не проходит, запрос автоматически идёт в дорогую модель. Итоговая точность обычно оказывается выше, чем при работе только на frontier-модели без проверок.
Стоит ли использовать LLM в качестве самого роутера?
Как правило, нет. Дешёвых сигналов хватает почти всегда: длина и структура входа, число сущностей, нужны ли инструменты, тариф пользователя, номер попытки. Если эвристики действительно не различают случаи, ставьте классификатором Tier 1 модель с жёстким лимитом токенов и enum на выходе - но никогда не тратьте frontier-вызов на решение о том, делать ли frontier-вызов.
Похожие статьи
Сделаю под ключ
Соберу ИИ-агента под реальную задачу
С инструментами, памятью и логами, чтобы он работал в проде, а не только в демо.
от 1 500 $ · 1-2 недели