Webrium
Подписаться

Повторы, которые удваивают счёт

Разбираемся, почему счёт за API языковых моделей растёт при неизменном продукте, как повторные попытки превращаются в умножение затрат и что чинить на самом деле

Шесть столбцов растут ступеньками от первой попытки к шестой: каждая повторная попытка оплачивается полностью, и счёт за один и тот же вызов вырастает кратноПлатим за попытки

Почему счёт растёт, когда продукт стоит на месте

Знакомая картина: месяц закрыт, счёт за API ощутимо выше предыдущего, а в продуктовом дашборде ничего не поменялось — столько же пользователей, столько же доведённых до конца сценариев. Первая гипотеза обычно техническая и почти всегда неверная: «наверное, вырос тариф». Тариф не менялся. Изменилось количество попыток за каждым ответом.

За этой ошибкой стоит привычка из классического бэкенда. Там неудачный запрос почти ничего не стоит: потратили немного процессора, получили ошибку, пошли дальше. Мы приучены считать, что неудача бесплатна. В API языковых моделей единица биллинга — не результат, а попытка: провайдер списывает за токены, которые обработал, а не за те, за которые вы получили пригодный ответ.

Отсюда арифметика. Пусть типичный вызов несёт контекст размером C — подставьте своё: системный промпт, история диалога, выборка из базы знаний, описания инструментов. Если операция проходит со второй попытки, вход оплачивается дважды: 2×C вместо C. Если попыток четыре — множитель четыре. Всё это время пользователь не получил ничего, а монитор будет думать, что всё в порядке: операция ведь завершилась успешно, просто «чуть медленнее».

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

Первый ход: подкрутить политику повторов

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

Не помогает, потому что ручка не на той оси. Настройки повторов управляют временем: когда случится следующая попытка. Деньги списываются в момент начала попытки и от пауз между ними не зависят. Можно идеально разнести повторы по календарю — и так же стабильно оплачивать каждый.

Предохранитель? Он останавливает поток неудач, и счёт перестаёт расти. Вместе с ним перестаёт работать и фича: предохранитель не отличает «дорого» от «сломано», он просто рвёт цепь. Это не лечение — выдёргивание пациента из розетки.

Бывает и второй первый ход: перевести всё на модель подешевле, «чтобы компенсировать». Арифметика может выйти обратной: дешёвый вариант чаще проваливает проверку ответа, провал запускает повтор, повтор стоит денег. Цена попытки упала, число попыток выросло — произведение само по себе не уменьшается.

Где попытки умножаются, а не складываются

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

Вложенные ретраи. HTTP-клиент со своими повторами, поверх него SDK со своими, поверх фреймворк агента, повторяющий шаги, и вдогонку ваш собственный код «ну попробуем ещё разок» на случай плохого ответа. Каждый слой по отдельности безобиден и написан из лучших побуждений. Вместе они образуют умножение: если внешний слой делает A попыток и в каждой запускает внутренний с B попытками, вниз уходит A×B вызовов, а не A+B. Причём внешний слой о внутреннем не знает: для него это была одна попытка. Пара слоёв, каждый из которых в отдельности выглядел разумной осторожностью, — и вот вам «вдруг вдвое» при неизменном продукте.

Глубина агента. В многошаговом агенте контекст тянется за шагами: в запросе на шаге k лежит всё, что накопилось к этому моменту. Повтор шага заново оплачивает всю эту сумму. Повтор на последнем шаге длинной цепочки стоит примерно как вход всей цепочки — ещё раз. А если фреймворк при ошибке начинает цепочку сначала, одна неудача на финише удваивает стоимость прохода целиком. Про счётчики в агенте у нас был отдельный материал — там речь шла о бюджете времени; с деньгами механика та же, только ставки выше.

Таймаут, который убивает самое дорогое. Долгие ответы и таймауты ходят в паре: чем длиннее генерация, тем ближе она к лимиту времени на запрос. Оборванная по таймауту генерация — это токены, которые модель уже породила; списывает ли их провайдер — вопрос тарификации, проверьте у своего. Если списывает, таймаут превращается в кнопку «заплатить и не получить», а повтор — в подписку на неё. Дальше хуже: типичный API не умеет докатывать оборванную генерацию, повтор начнётся с первого токена. И под нож идут не случайные запросы, а самые длинные ответы — то есть самые дорогие. Отбор по стоимости, чистая механика, без злого умысла.

Систематический провал валидации. Модель вернула невалидный JSON — сработал повтор. Повтор помогает ровно тогда, когда провал случаен и со второй попытки модель справилась бы. Если же провал систематический — схема, которую модель не тянет, ответ, обрезанный лимитом длины, двусмысленный промпт, инструмент, который отвечает не в том формате, — каждый повтор покупает вам ту же ошибку по полной цене. Это уже не ретрай — лотерея с известной ставкой и нулевым выигрышем. Классика жанра: лимит длины обрезает JSON на полуслове, повтор обрезается ровно там же. Лечится лимитом или схемой; политика повторов тут ни при чём.

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

Что считать и что чинить

Начните с одной цифры, код подождёт. Возьмите за период все списанные вызовы и все ответы, доведённые до пользователя, и поделите одно на другое. Получится «попыток на ответ»: единица — вы платите ровно за то, что получаете; двойка — каждый ответ обходится вдвое дороже номинала. Чтобы это посчитать, каждой логической операции нужен идентификатор, который протаскивается через все попытки, включая тихие повторы HTTP-клиента, если они у вас есть. Без идентификатора делить нечего. Тот же показатель можно взять в токенах: все входные токены за период, делённые на входные токены успешных ответов.

Дальше разложите неудачи по классам, потому что лечение у них разное:

  • Транспортные ошибки — серверные сбои, лимиты частоты запросов. Повтор оправдан, бэкофф уместен. Единственный класс, где учебная политика повторов работает как написано.
  • Таймауты на генерации. Чинить здесь надо таймаут: лимит времени должен соотноситься с ожидаемой длиной ответа, иначе вы платите за обрыв.
  • Провалы валидации. Перед повтором спросите себя: провал случайный или систематический? Случайный — повторяйте. Систематический — правьте промпт, схему или лимит; повтор здесь просто сжигает бюджет.

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

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

И для агентов: повторяйте шаг с усечённым контекстом; полную перезагрузку цепочки оставьте на крайний случай.

Позиция редакции

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

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

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

С чем это связано

Метка над заголовком говорит, зачем туда идти: продолжить тему, перейти к практике или увидеть возражение.

Счёт за инференс, разогнанный повторами вызовов, растёт и пробивает порог бюджета, зажигая сигнал тревогисчёт с повторамимесяц
Предыстория

Сколько стоит инференс: считаем до счёта, а не после

Прежде чем разбираться с повторами — модель расчёта инференса, где главный множитель не размер модели, а доля повторов. Своими числами её можно заполнить за час.

Цепочка из пяти шагов агентного цикла; последний шаг, внутри которого тянется ожидание, пересекает пунктирную границу бюджета времени — лимит шагов времени не обюджет временишаг с ожиданием
Практика

Бюджет времени в многошаговом агенте

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

Семь ячеек-кандидатов, одна выделена: каждое новое слово ответа машина заново выбирает из всей таблицы чиселвся таблица чиселновое слово
Просто

Откуда берётся счёт за искусственный интеллект

Если термины мешают, та же история рассказана без них: почему у нейросети нет готовых ответов, из чего складывается цена и где экономия на своих словах заходит в тупик.

Из потока запросов векторный поиск выигрывает не на всехзапросыпо смыслу
Усиление

Векторная база: когда она окупается, а когда хватит обычного поиска

Тот же счёт в часах, но про хранилище: когда векторный поиск окупается, а когда достаточно обычного полнотекстового.

Письмо по вторникам

Один разбор недели и короткий список того, что изменилось. Без дайджестов на сорок ссылок.

Начните вводить — материалы появятся здесь.

выбрать · Enter открыть · Esc закрыть