ГлавнаяБлогКак срезать 71% расходов на LLM: дешёвая модель + шлюз проверки
AI / Нейросети

Как срезать 71% расходов на LLM: дешёвая модель + шлюз проверки

Узнайте, как роутер с дешёвой моделью и шлюзом проверки сократил расходы на 71%. Практический гайд с кодом и подводными камнями.

Al
Редакция Algolitalgolit.ru
9 мин чтения30 августа 2026 г.

Почему вы переплачиваете за LLM и как это исправить

30 июля OpenAI урезала цену на GPT-5.6 Luna до $0.20 за миллион входных токенов и $1.20 за миллион выходных — это на 80% дешевле прежних $1 и $6. Azure повторила это 1 августа. Большинство команд просто продолжили отправлять все запросы на сильную модель, потому что «просто использовать дешёвую» звучит как решение, которое обернётся тикетом в поддержке через три недели. Но когда вы видите счёт рядом с распределением трафика, становится очевидно: подавляющее большинство запросов — «озаглавь тред», «суммаризируй дифф», «извлеки поля из формы», «назови файл». Мы платили цены frontier-модели за генерацию заголовков из трёх слов.

В этой статье я покажу, как мы перестроили роутинг: около сорока строк кода на Go, которые переместили 81% запросов с дорогой модели и сократили счёт на 71%. А также разберу четыре вещи, которые сломались по пути — они и есть самое интересное.

Почему классификация промпта не работает

Очевидное решение, которое приходит в голову первым — классификатор перед роутером: посмотреть на входящий запрос, решить, лёгкий он или сложный, и отправить на соответствующую модель. Мы так и сделали, и это оказалось хуже, чем кажется, по трём причинам.

  • Нужна модель для классификатора. Каждый запрос платит дополнительный вызов до начала работы. Дешёвый классификатор — это ещё один вызов Luna и ещё 300 мс; точный классификатор — вызов сильной модели, то есть те самые затраты, которых вы пытались избежать.
  • Короткий не значит лёгкий. «Исправь баг с таймзоной» — одиннадцать символов, но требует всех ресурсов. «Суммаризируй RFC на 4000 слов» — длинно и тривиально. Длина, количество токенов, списки ключевых слов — каждая дешёвая эвристика коррелировала с формой запроса, а не со сложностью.
  • Вы предсказываете ответ до того, как его увидели. Это главная проблема. Сложность — свойство работы, и единственный честный способ узнать её — выполнить работу.

Правило: не классифицируйте промпт. Запустите дешёвую модель и оцените результат.

Схема: сначала дешёвая, с шлюзом эскалации

Работающая схема до смешного проста:

  1. Отправьте запрос на дешёвую модель.
  2. Посмотрите, что вернулось.
  3. Если ответ не проходит шлюз — запустите запрос на сильной модели и верните его результат.

Жизнеспособность этой схемы обеспечивает арифметика, которая нас удивила. При нашем среднем запросе — примерно 3000 входных и 700 выходных токенов — вызов Luna стоит около $0.0014, а вызов сильной модели — около $0.0145. Разница в десять раз.

Запрос, который эскалируется, обходится в 0.0014 + 0.0145 = $0.0159 вместо $0.0145. Это примерно на 10% дороже. Посчитаем точку безубыточности для частоты эскалации:

дешёвая + (p × сильная) < сильная
p < (сильная − дешёвая) / сильная
p < (0.0145 − 0.0014) / 0.0145
p < 0.90

Вам пришлось бы эскалировать девять раз из десяти, чтобы дешёвый подход начал стоить дороже. Мы эскалируем в 15% случаев. Стоимость — не ограничение. Если вы спорите о двойной оплате, вы спорите не о том ресурсе. Ограничение — задержка. К этому вернёмся.

Код роутера

type Result struct {
    Text   string
    Model  string
    Tokens Usage
}

// Gate возвращает причину, почему ответу нельзя доверять, или "" — если можно.
type Gate func(req Request, out string, fin FinishReason) string

func (r *Router) Do(ctx context.Context, req Request) (Result, error) {
    // Принудительно сильная модель для определённых типов запросов
    if req.ForceStrong || r.alwaysStrong[req.Kind] {
        return r.call(ctx, r.strong, req)
    }

    // Пробуем дешёвую модель
    cheap, err := r.call(ctx, r.cheap, req)
    if err != nil {
        // Ошибка доступности, не качества. Продолжаем, не падаем.
        return r.call(ctx, r.strong, req)
    }

    // Прогоняем через все шлюзы
    for _, gate := range r.gates {
        reason := gate(req, cheap.Text, cheap.Finish)
        if reason == "" {
            continue
        }
        r.metrics.Escalate(req.Kind, reason)
        // Эскалация: вызываем сильную модель с исходным запросом
        strong, err := r.call(ctx, r.strong, req)
        if err != nil {
            // У нас уже есть ответ — отдаём дешёвый, деградируем, но не падаем
            return cheap, nil
        }
        return strong, nil
    }

    r.metrics.Accept(req.Kind)
    return cheap, nil
}

Две строки здесь критически важны, и обе неочевидны. Первая — return r.call(ctx, r.strong, req) при ошибке дешёвого вызова: 429 или таймаут — это проблема доступности, и она не должна превращаться в ошибку для пользователя, когда у вас есть второй провайдер. Вторая — return cheap, nil при ошибке сильного вызова: у вас уже есть ответ, пусть и худший, но он лучше, чем бесконечный спиннер.

Комментарий про эскалацию — тот, который все понимают неправильно, и это четвёртая поломка ниже.

Четыре шлюза

Шлюзы — это и есть продукт. Роутер — просто сантехника.

var defaultGates = []Gate{
    SchemaInvalid,    // должен быть JSON по схеме, но не является
    ToolArgsMissing,  // вызвал инструмент, но не передал обязательный аргумент
    Truncated,        // finish_reason != stop
    EmptyOrHedged,    // короче 24 символов или совпадает с набором отговорок
}

Они упорядочены по стоимости проверки, и каждый из них — структурный. Ни один не заставляет модель оценивать другую модель.

SchemaInvalid делает больше всего работы. Всё, что имеет заданную форму вывода — извлечение полей, классификация, структурированные сводки — проверяется по схеме, которая у вас уже есть. Если не парсится или не соответствует — эскалация. Этот шлюз ловит около 60% наших эскалаций.

ToolArgsMissing — та же идея для вызова функций. Дешёвая модель выбирает правильный инструмент гораздо надёжнее, чем заполняет аргументы, а отсутствие обязательного аргумента — бесплатный точный сигнал.

Truncated — сравнение одного поля, и его постоянно пропускают. finish_reason со значением length означает, что предложение обрывается на полуслове.

EmptyOrHedged — самый слабый, и я хочу подчеркнуть, насколько слабый, потому что его все хотят сделать первым. Мы начали с интуитивной версии: просили дешёвую модель сообщать, когда она не уверена, и эскалировали на этом. Срабатывало в 0.4% ответов. Наша измеренная ошибка на том же трафике была около 15%. Самооценка модели — не сигнал, а украшение.

Что реально работает в этой роли — маленький скучный список: пусто, короче 24 символов, или точное совпадение с набором отговорок, который вы собрали, прочитав двести реальных отказов («недостаточно информации», «как ИИ», «уточните вопрос»). Не уверенность, а отказ.

Правило: проверяйте структуру, которую можно проверить, а не мнение модели о себе.

Четыре вещи, которые сломались

1. p50 улучшилось, p95 стало хуже

Это было немедленно, и это настоящая цена схемы.

                Все-сильные     Сначала-дешёвая + шлюз
p50             2.9s            1.4s
p95             7.8s            9.6s
p99             11.2s           16.4s

Luna быстрая, поэтому принятые 85% стали намного быстрее. Эскалированные 15% платят за оба вызова последовательно и попадают в хвост. Если у вас есть SLO по задержке, этот хвост — где схема выживает или умирает. Помогли две вещи: запускать шлюзы на потоковой голове ответа, где это возможно, и установить жёсткий бюджет эскалации по времени — если дешёвый вызов уже сжёг 4 секунды, верните его и залогируйте промах, а не запускайте второй вызов, который не можете себе позволить.

2. Нельзя стримить ответ, который вы можете выбросить

Вся архитектура предполагает, что вы смотрите на вывод перед решением. Стриминг предполагает, что вы уже приняли решение. Умного исправления нет, только выбор:

  • Буферизовать, потом решать. Правильно, но вы теряете время до первого токена — а для чата это то, что пользователи реально чувствуют.
  • Стримить и никогда не эскалировать. Подходит для разговорных сценариев.
  • Разделить трафик по типу. Мы делаем так. Структурированная не-стримовая работа — извлечение, заголовки, классификация, суммаризация, выбор инструмента — идёт через роутер. Свободный чат стримится напрямую с той модели, к которой привязан интерфейс.

Большая часть денег всё равно была в первом бакете: структурированная фоновая работа — это большой объём, и никто не смотрит на мигающий курсор.

3. Дешёвая модель проваливает тест на правильность задолго до теста на внешний вид

Вывод беглый, хорошо отформатирован, использует ваши заголовки, попадает в тон, правильной длины. Просто неверный. Поэтому шлюзы вида «выглядит ли это хорошим ответом», включая LLM-как-судья на горячем пути, у нас работали плохо. Беглость — именно та ось, где разрыв в цене сократился больше всего. Суждение, многошаговые рассуждения и знание того, чего модель не знает, — там разрыв не сократился вовсе. Структурные шлюзы работают, потому что у них нет мнения. Валидный JSON — это валидный JSON.

4. Не показывайте сильной модели ответ дешёвой

Наша первая версия передавала неудачную попытку как контекст — «вот черновик, улучши его». Казалось, это очевидно эффективнее. Это сильно якорит: сильная модель наследует рамку дешёвой, сохраняет её структуру и исправляет формулировки, а не рассуждения. На эскалациях, которые мы проверяли вручную, путь «улучшить черновик» был хуже чистого запуска примерно в трети случаев — и хуже именно так, как важно: повторял ошибку, вызвавшую эскалацию, полируя прозу вокруг неё.

Эскалация — это не повторная попытка. Это свежая попытка лучшего исполнителя. Отправляйте исходный запрос.

Что это дало

На 1000 запросов при нашем распределении:

                Все-сильные     Сначала-дешёвая + шлюз
Запросов на дешёвой   0               950
Эскалировано           —               143
Запросов к сильной    1000            193
Стоимость              $14.50          $4.16

Минус 71%. 81% запросов полностью обслуживаются моделью, которая стоит в десять раз дешевле, а 5% принудительно идут на сильную модель и вообще не попадают в роутер. Принудительный список короткий, и это политическое решение, а не измерение: всё, что пользователь отправляет клиенту, всё, что пишет в продакшен, и всё, что связано с генерацией кода в билдере. Это никогда не касается дешёвой модели, что бы ни сказал шлюз.

Когда так делать не стоит

  • Низкий объём. Если меньше нескольких сотен тысяч запросов в месяц, 71% счёта не стоит нового компонента на горячем пути. Лучше договоритесь о цене за место.
  • Длинные цепочки вызова инструментов. Ошибки накапливаются между шагами, а шлюзы видят только один шаг за раз. Дешёвая модель, правильная в 85% случаев на один вызов, правильна в 44% на пять вызовов.
  • Если у вас есть видимый пользователю ретрай. Если интерфейс уже показывает спиннер, удвоение хвоста хуже, чем оплата счёта.
  • Регулируемый или аудируемый вывод. «Какая модель это создала» становится вопросом, на который нужно отвечать для каждого запроса. Логируйте Result.Model с первого дня, если есть шанс, что вы в этой категории — ретрофит мучителен.

Версия, которую можно запустить на этой неделе

Вам не нужна вся система, чтобы получить большую часть выгоды. Вот план:

  1. Выберите одну высоконагруженную, не-стримовую, структурированную задачу. Заголовки, извлечение полей, тегирование. Одну.
  2. Добавьте один шлюз: проверку по схеме, которая у вас уже есть.
  3. Погоняйте в тени день. Запускайте обе модели, отдавайте сильную, логируйте, где шлюз бы сработал. Этот шаг пропускают, а он показывает, будет ли частота эскалации 15% или 60%.
  4. Переключите с Result.Model в логах и kill switch в конфиге.
  5. Измеряйте частоту эскалации по типам запросов — это число, на котором живёт вся схема, и оно подскажет, какую задачу двигать следующей.

Главный аргумент для дешёвых моделей в 2026 году не в том, что они стали достаточно хороши, чтобы заменить frontier. А в том, что они стали настолько дёшевы, что проверка их качества теперь бесплатна.

#LLM#оптимизация расходов#роутер#дешёвая модель#шлюз проверки
Al
Редакция Algolit

Пишем про алгоритмы, подготовку к собеседованиям и карьеру в IT — так, чтобы было понятно и полезно.

Хочешь закрепить знания на практике?

Решай задачи на Algolit — интерактивная платформа для обучения

Начать бесплатно →