Защита AI-API от перегрузок: Rate Limiting и квоты токенов для LLM

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

Что ломается без rate limiting в AI-сервисах

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

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

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

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

Что даёт управление лимитами: снятая нагрузка и отказы

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

Доля отказов перестаёт зависеть от чужого трафика

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

Пик проходит деградацией, а не отказом

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

Задержка ответа перестаёт «плавать»

Лимит на длину контекста и число одновременных генераций снимает борьбу за вычислительные ресурсы. Интерактивные запросы получают приоритет над пакетными, поэтому время ответа на 95-м процентиле остаётся в согласованном коридоре даже при полной загрузке.

Ёмкость распределяется равномерно

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

Режимы ограничения: лимиты запросов, квоты токенов, очереди

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

Поэтому режимы не выбирают «один из», а выстраивают в порядок: что проверяется первым, что уходит в очередь, кому достаётся остаток ёмкости.

Вариант Когда подходит Чем ограничен
Лимит по запросам Внешние интеграции с предсказуемым размером вызова Не отражает стоимость: тяжёлый и короткий запрос считаются одинаково
Квота по токенам Основной бюджет сценариев, где длина промпта и ответа плавает Нужен точный учёт; при потоковой отдаче расход виден по факту
Параллельные сессии Интерактивные интерфейсы и агенты, где важна отзывчивость Ограничивает одновременность, а не суммарный объём
Буферизация запросов Фоновые и пакетные задачи без требования немедленного ответа Ответ приходит с задержкой, нужны таймаут и политика отказа
Приоритеты ключей Несколько потребителей разной критичности на общей ёмкости Нужен реестр ключей; неприоритетные получают отказ, а не ждут вечно

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

Этапы внедрения: от замера нагрузки до регламента

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

  1. Профиль нагрузки. Собираем статистику за несколько недель, включая хотя бы один пиковый период. Отдельно помечаем, какие обращения критичны для бизнес-процесса, а какие можно отложить в очередь.
  2. Расчёт порогов. Определяем лимиты послойно — на ключ, на пользователя, на весь сервис, — с запасом до насыщения модели, чтобы ограничитель срабатывал раньше, чем начнёт расти время ответа.
  3. Настройка ограничителя. Выбираем алгоритм под характер трафика (скользящее окно или пополняемое ведро) и вводим раздельные квоты на число запросов и на объём токенов: короткий вызов и длинный контекст расходуют ресурс несопоставимо.
  4. Обработка отказов. Ограничение должно быть предсказуемым: понятный код ответа, указание, когда повторить, приоритетная очередь для критичных операций.
  5. Проверка под пиком. Прогоняем профиль, повторяющий реальный пик с запасом, и фиксируем, на каких значениях появляются отказы и не задевают ли они добросовестных клиентов.
  6. Регламент эксплуатации. Передаём документ: как менять квоты, где смотреть метрики, при каких отклонениях поднимать лимит, как выдать временное исключение.

Отдельно договариваемся о пересмотре порогов — при смене сценариев или росте числа клиентов цифры устаревают быстрее, чем конфигурация.

Что передаём заказчику: конфигурации, схемы, регламенты

Отдаём не описание «как надо», а рабочий комплект, по которому дежурный инженер и разработчик работают без нас: файлы, применяемые прямо в контуре, и регламенты под конкретные инциденты.

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

Комплект обновляем вместе с сервисом: иначе регламент расходится с контуром, и на инциденте никто не знает, где правда.

Кейс: пиковый наплыв пользователей без отказов

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

Свою долю вносили и повторные отправки при таймауте: пользователь нажимал кнопку ещё раз, приложение ставило вторую задачу к тому же узкому месту. Итог — 7,8% ответов с ошибкой и p95 задержки 12 секунд, а в пиковые двадцать минут доля отказов доходила до 15%: люди уходили, не дождавшись ответа.

Что выставили на входе

Ограничение частоты сделали двухуровневым: token bucket на пользователя с правом на короткий всплеск и скользящее окно на 60 секунд для сглаживания. Сверху — суточная квота токенов по каждому тарифу и приоритетная очередь для платных обращений. Общий предохранитель на провайдера размыкается при росте ошибок и не даёт добить его повторами.

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

Метрики после

  • Отказы: 7,8% → 0,4% от всех запросов.
  • Задержка p95: 12 секунд → 2,6 секунды; всплеск до 60 запросов в секунду проходит без деградации.
  • Доля ответов из кэша в пике: 22%.
  • Разбор накопившейся очереди после сбоя провайдера — 90 секунд вместо ручной перезаливки.

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

Что делать, если AI-шлюз уже упирается в потолок?

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

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

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

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

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

Частые технические сомнения по лимитам и квотам

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

Клиенты заметят, что их стали ограничивать?

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

Можно ли задать разные правила для команд и продуктов?

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

Лимиты легко обойти?

Счётчики живут на стороне сервиса, а не в клиенте, поэтому правка кода приложения на них не влияет. Ключи выдаются и отзываются централизованно, аномалии по одному ключу видны в метриках.

Придётся ли переписывать код приложения?

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

Обсудим вашу нагрузку — вернёмся с оценкой и планом

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

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

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

Опишите задачу и пришлите то, что есть по нагрузке, — вернёмся с оценкой и планом.

Направления AI-разработки

Последние работы

  • Разработка сайта компании B2B ADVANCE
    Разработка сайта компании B2B ADVANCE
    1543
  • Разработка веб-приложения для компании FEEDME
    Разработка веб-приложения для компании FEEDME
    1394
  • Разработка веб-сайта для компании БЕЛФИНГРУПП
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    1081
  • Разработка интернет магазина для компании FURNORO
    Разработка интернет магазина для компании FURNORO
    1374
  • Разработка логотипа компании B2B Advance
    Разработка логотипа компании B2B Advance
    816
  • Разработка веб-приложения для компании Enviok
    Разработка веб-приложения для компании Enviok
    1142