Дашборд провайдера - это не бюджет: жёсткие лимиты расходов для AI-автоматизаций
Лимиты провайдера срабатывают поздно, режут всю организацию разом и ничего не знают о ваших клиентах. Рассказываю, какой слой «резерв, потом расчёт» я ставлю перед каждым вызовом LLM, прокси и платного API.
Pavel Duglas
AI Automation & MVP Architect
Раз в несколько месяцев мне пишет кто-то из фаундеров: «Проснулись, а счёт в четыре раза больше, чем в прошлом месяце. Что произошло?» Ответ почти всегда скучный. Агент застрял в цикле ретраев, cron-задача прогнала один и тот же батч двенадцать раз, или один пользователь на бесплатном тарифе выяснил, что продукт дёргает топовую модель на каждое нажатие клавиши. Никто ничего не ломал специально. Просто в системе не было понятия «стоп, на сегодня хватит».
Большинство команд уверены, что такое понятие у них есть: в дашборде провайдера же стоит месячный лимит. Нет, не есть. Лимит в дашборде - это пожарная сигнализация в соседнем доме. Ниже покажу бюджетный слой, который я встраиваю в каждую AI-автоматизацию, агента и парсер ещё до того, как они попадут в прод.
Почему лимиты провайдера вас не спасут
Лимиты на стороне провайдера полезны как последний рубеж. Но как основной механизм контроля они не работают, и причин четыре.
Они запаздывают. Статистика использования на большинстве платформ обновляется с задержкой в минуты, а иногда и дольше. Агент, который гоняет 20 параллельных запросов с большим контекстом, за это время успевает сжечь приличную сумму.
Они действуют на всю организацию. Когда лимит срабатывает, падает всё сразу: продовый чат-бот, внутренние инструменты, ночное обогащение данных. Вы хотели остановить один сбесившийся воркфлоу, а положили всю компанию.
Они не знают ваш бизнес. Провайдер понятия не имеет, что клиент А платит 29 долларов в месяц, а клиент Б - 900. Он не в курсе, что саммари документа должно стоить центы, и если одно стоит 14 долларов, это баг.
Они покрывают только одного вендора. Мои автоматизации редко тратят деньги на один API. Типичный пайплайн парсинга платит за резидентные прокси, за сервис решения капчи, за LLM для извлечения данных и иногда за модель эмбеддингов. Проект на BAS в 50 потоков может тихо выкачать баланс антикапчи вообще без участия LLM.
Поэтому лимит должен жить в вашем коде, ровно в той точке, где деньги вот-вот будут потрачены.
Иерархия бюджетов
Я думаю о бюджетах как о вложенных областях. Каждый платный вызов обязан уложиться во все из них одновременно.
Лимит на один вызов
Самая маленькая область. Всегда ставьте max_tokens на каждый запрос. Без исключений. Оценивайте входные токены до отправки и отказывайте, если один только вход превышает разумный размер для этой задачи. Если промпт для классификации вдруг пришёл со 180k токенов контекста, значит, что-то сломалось выше по цепочке. Правильная реакция - громко упасть, а не заплатить.
Бюджет на запуск
Один прогон агента, одно выполнение пайплайна, один обработанный документ. Именно эта область ловит циклы. Каждому запуску я выдаю бюджет в зависимости от типа задачи: «обогащение лида - максимум $0.40», «исследовательский агент - максимум $3». Упёрся в бюджет - останавливается и отчитывается, что успел сделать.
Бюджет на клиента
Дневной и месячный, привязанный к тарифу. Это то, что защищает маржу и не даёт одному активному пользователю стать вашей главной статьёй расходов. Заодно здесь стоимость превращается в продуктовое решение: что увидит клиент, упёршись в лимит, - это вопрос UX, а не инфраструктуры.
Бюджет на воркфлоу
У каждой автоматизации свой дневной лимит. Ночная синхронизация не должна съедать бюджет ассистента, с которым общаются клиенты.
Глобальный бюджет
Ваш собственный жёсткий потолок на день, заметно ниже лимита провайдера. Когда он достигнут, некритичные воркфлоу встают на паузу, а вам приходит алерт. Лимит провайдера остаётся страховкой сверху.
Сначала резерв, потом расчёт
Наивная реализация проверяет «потратили меньше лимита?» и делает вызов. Это ломается при первой же конкурентности. Двадцать воркеров одновременно читают «потрачено $9.80 из $10», все решают, что всё нормально, и в итоге выходит $16.
Работает тот же паттерн, что и в карточных платежах: сначала холдируем, потом списываем.
- До вызова оцениваем максимально возможную стоимость: входные токены плюс
max_tokensпо цене выходных токенов модели. Для прокси или капчи берём известную цену за единицу. - Атомарно резервируем эту сумму во всех областях, к которым относится вызов. Если хоть одна область вылезет за лимит, резерв не проходит и вызов не делается.
- Делаем вызов.
- Рассчитываемся по фактической стоимости из usage в ответе. Разницу освобождаем.
- Если вызов упал и так и не рассчитался, резерв истекает по таймауту и списывается в зарезервированном объёме. Пессимистично, зато безопасно.
Минимальная версия на Postgres выглядит так:
CREATE TABLE budget_scope (
scope_key text PRIMARY KEY, -- 'run:8f2a', 'customer:412:2026-09-14', 'global:2026-09-14'
cap_micros bigint NOT NULL,
spent_micros bigint NOT NULL DEFAULT 0,
reserved_micros bigint NOT NULL DEFAULT 0
);
-- резерв в одной области; выполняется для каждой области в одной транзакции
UPDATE budget_scope
SET reserved_micros = reserved_micros + $1
WHERE scope_key = $2
AND spent_micros + reserved_micros + $1 <= cap_micros
RETURNING scope_key;
Если хотя бы для одной области апдейт не вернул строку, откатываем транзакцию и отказываем в вызове. Деньги храним целыми микродолларами, никаких float. В высоконагруженных системах я выношу горячие счётчики в Redis и делаю ту же проверку с инкрементом атомарно через Lua-скрипт, а итоговые списания пишу в Postgres как append-only леджер для отчётности.
Ключевое свойство: в кодовой базе ровно одна функция имеет право вызывать платный API, и она всегда идёт через резерв. Никаких прямых вызовов SDK, разбросанных по проекту. Если разработчик или кодинг-агент добавил фичу, которая дёргает модель напрямую, на код-ревью это должно восприниматься так же, как склейка SQL-запроса из строк.
Что происходит, когда лимит достигнут
Лимит без заранее определённого поведения - это просто новый вид даунтайма. Поэтому реакцию для каждой области я решаю заранее.
Лимит на вызов: сразу отказ, в лог пишем размер входа и источник. Почти всегда это баг выше по цепочке.
Бюджет на запуск: аккуратно останавливаем запуск. Сохраняем частичный результат, помечаем статусом budget_exhausted (отдельно от failed) и показываем это. Часто правильное решение - не поднять бюджет, а сделать задачу меньше.
Бюджет на клиента: деградируем, а не падаем. Переключаемся на модель подешевле, переводим обработку в очередь вместо мгновенной или честно показываем «дневной лимит исчерпан» с предложением перейти на тариф выше. Это ещё и отличный сигнал для ценообразования: клиенты, которые каждую неделю упираются в лимит, подсказывают, каким должен быть ваш следующий тариф.
Бюджет на воркфлоу: ставим воркфлоу на паузу, уведомляем владельца, всё остальное работает дальше.
Глобальный бюджет: паузим всё некритичное, оставляем живыми клиентские сценарии платящих пользователей за счёт выделенной части бюджета, будим человека.
Эта выделенная часть важна. Обычно я держу 20-30% глобального дневного бюджета только под пользовательские запросы платящих клиентов, чтобы взбесившаяся фоновая задача не оставила продукт без ресурса.
Циклы - это в первую очередь проблема денег
Большая часть неожиданных счетов - это агенты и пайплайны, которые повторяют одно и то же. Бюджеты рано или поздно ловят циклы, но можно ловить их раньше и дешевле.
- Считайте шаги, а не только доллары. У запуска агента есть максимальное число вызовов инструментов. Запуск, который сделал 40 шагов на задаче, где обычно нужно 6, сломан, даже если каждый шаг стоил копейки.
- Делайте отпечатки повторяющихся вызовов. Хешируйте модель, последний вызов инструмента и его аргументы. Если один и тот же хеш встретился трижды за запуск, останавливайтесь. Агент, который бесконечно повторяет один и тот же неудачный поисковый запрос, - классика.
- Следите за скоростью трат. Алерт, если воркфлоу за 10 минут потратил больше, чем обычно тратит за день. Это ловит ситуацию, когда каждый отдельный запуск укладывается в бюджет, но баг в планировщике запустил их тысячу.
Не забывайте про расходы вне LLM
В моих проектах по парсингу и на BAS модель часто вообще не главная статья расходов. Прокси тарифицируются за гигабайт, антикапча за тысячу решений, некоторые API данных за запрос, а генерация видео и картинок за результат, причём по таким ценам, что текстовые токены кажутся бесплатными.
Тот же бюджетный слой покрывает всё это. У каждого платного ресурса есть цена за единицу в конфиге, и он проходит через ту же функцию резерва и расчёта. В BAS я оборачиваю платные действия в функцию, которая сначала стучится во внутренний HTTP-эндпоинт за резервом и только потом идёт дальше. Поток, которому резерв не дали, аккуратно останавливается, а не долбит целевой сайт дорогим прокси-трафиком, который всё равно уйдёт в никуда.
Если у вас Telegram-бот поверх API генерации, именно это отделяет забавный пет-проект от неожиданного инвойса. Резервируйте на пользователя на день и прямо пишите ему, когда обновится лимит.
Как сделать оценки достаточно точными
Идеальные оценки не нужны. Нужны оценки, которые никогда не бывают заниженными.
- Для входа используйте токенизатор провайдера или близкое приближение и добавляйте 10%.
- При резерве всегда считайте, что
max_tokensбудет израсходован полностью. Расчёт потом всё поправит. - Держите таблицу цен в конфиге, с версией и датой. Цены поменялись - правите один файл, а не двенадцать сервисов.
- Для инструментов с плавающей стоимостью резервируйте худший реалистичный сценарий и списывайте фактическую сумму.
Завышенная оценка замораживает бюджет всего на несколько секунд, зато гарантирует, что лимит действительно жёсткий.
План внедрения, который можно начать сегодня
- Найдите все места в коде, где вызывается платный API. Пустите их все через одну функцию.
- Добавьте
max_tokensи проверку размера входа в каждый вызов LLM. - Добавьте агентам лимит шагов на запуск и отпечатки повторяющихся вызовов.
- Запустите леджер на неделю в режиме только логирования: пишем резервы, ничего не блокируем. Смотрим реальные распределения по воркфлоу и клиентам.
- Поставьте лимиты примерно в 3 раза выше наблюдаемого p99 и включите блокировку.
- Определите поведение деградации для каждой области и сделайте сообщения для пользователей.
- Выставьте свой глобальный лимит ниже лимита провайдера, а лимит провайдера оставьте как последнюю страховку.
Когда всё это работает, инвойса больше не боишься. А главное, агентам можно доверить больше, потому что худший сценарий теперь - это цифра, которую вы выбрали сами, а не та, которую обнаружили в конце месяца.
Вопросы и ответы
Может, проще поставить лимит в дашборде провайдера пониже и не городить свой слой?
Нет. Лимит провайдера срабатывает с задержкой, отключает сразу всю организацию и ничего не знает о ваших клиентах, тарифах и воркфлоу. К тому же он покрывает только одного вендора, а прокси, антикапча и другие API остаются без контроля. Свой лимит должен быть основным, а лимит провайдера - страховкой сверху.
Не слишком ли сложно для MVP делать резерв и расчёт?
Для MVP хватит одной таблицы в Postgres и одной функции-обёртки над платными вызовами, это пара часов работы. Начните с max_tokens, лимита шагов агента и бюджета на клиента в день. Redis, леджер и тонкая деградация нужны позже, когда появится нагрузка. А вот одна точка входа для всех платных вызовов нужна с первого дня, потом её внедрять гораздо больнее.
Как выбрать начальные значения лимитов, если статистики ещё нет?
Включите леджер на неделю в режиме только логирования, ничего не блокируя. Соберите реальные распределения стоимости по типам запусков, воркфлоу и клиентам, затем поставьте лимиты примерно в 3 раза выше p99. Для бюджета на клиента отталкивайтесь от цены тарифа: расходы на AI не должны съедать маржу даже у самого активного пользователя.
Похожие статьи
Сделаю под ключ
Соберу ИИ-агента под реальную задачу
С инструментами, памятью и логами, чтобы он работал в проде, а не только в демо.
от 70 000 ₽ · 1-2 недели