AI-инжиниринг
Rate Limiting для AI API: token bucket, sliding window, per-user quotas
Цели урока
- Понять разницу между RPM и TPM rate limiting для LLM API
- Реализовать Token Bucket алгоритм с двумя измерениями: запросы и токены
- Построить per-user quota систему с Redis для справедливого распределения ресурсов
- Создать NestJS Guard, объединяющий global rate limit и per-user quotas
OpenAI rate limit: 10,000 RPM для tier 3. Звучит много. Один пользователь с automation-скриптом - 600 запросов в минуту. 17 таких - и весь аккаунт лежит. Rate limiting для AI - это не про 429 ошибки, это про token budget по пользователям.
- OpenAI tier system: tier 1 даёт 500 RPM и 30,000 TPM. Один RAG-запрос с 8K контекстом = 8,000 TPM. После 3 таких запросов одновременно - лимит исчерпан для всех
- Anthropic ограничивает tier 1 до 50 RPM и 40,000 TPM с суточным лимитом в 1000 запросов - без per-user квот один активный пользователь обнулит день
- ChatGPT использует per-user rate limiting: 'You've reached the limit on messages' - это тот же token budget, только в интерфейсе
- Vercel AI SDK включает встроенный rate limiter с Redis-backed token counting - индустрия признала проблему стандартной
От сетевых алгоритмов к квотам LLM
Rate limiting появился задолго до AI. Алгоритмы **token bucket** и **leaky bucket** пришли из классического networking и traffic shaping, где они сглаживали всплески и удерживали средний темп на роутерах и свитчах. Те же алгоритмы теперь управляют LLM API в виде лимитов RPM (requests per minute) и TPM (tokens per minute). Особенность для AI в том, что стоимость и нагрузка растут от числа токенов, а не только от числа запросов, поэтому старые bucket'ы применяют к токен-бюджету на пользователя, а не только к частоте запросов.
Предварительные знания
Почему rate limiting для AI API отличается от обычного
OpenAI tier 3: 10,000 RPM. Звучит как много. Один пользователь с automation-скриптом - 600 запросов в минуту. 17 таких пользователей - и весь аккаунт лежит. Но вот что интересно: проблема не в количестве запросов. Проблема в **токенах**.
Традиционный rate limiting считает запросы - 100 req/min, 1000 req/hour. Для LLM API это грубая метрика. Один запрос с 50,000 токенов контекста потребляет в 100 раз больше ресурсов, чем запрос с 500 токенами. Провайдеры вводят **TPM (tokens per minute)** - и именно он становится настоящим bottleneck. Rate limiting для AI - это не про 429 ошибки. Это про **token budget** по пользователям.
| Провайдер | Tier | RPM | TPM | RPD |
|---|---|---|---|---|
| OpenAI (GPT-4o) | Tier 1 | 500 | 30,000 | - |
| OpenAI (GPT-4o) | Tier 5 | 10,000 | 30,000,000 | - |
| Anthropic (Claude Sonnet) | Tier 1 | 50 | 40,000 | 1,000 |
| Anthropic (Claude Sonnet) | Tier 4 | 4,000 | 400,000 | - |
| Google (Gemini 1.5) | Free | 15 | 1,000,000 | 1,500 |
Три слоя защиты в AI-приложении: 1. **защита от провайдера** - чтобы не получать 429 ошибки от OpenAI 2. **per-user квоты** - чтобы один пользователь не исчерпал лимит для всех 3. **budget protection** - финансовый потолок, выше которого не подниматься. Каждый слой решает свою задачу. Ни один нельзя пропустить.
**Retry - не замена rate limiting.** Retry спасает от спорадических 429, но при систематическом превышении лимита создаёт нарастающую очередь. 100 запросов → 70 429-ок → retry через 2 секунды → ещё 70 запросов → лимит исчерпан снова. Нужен proactive rate limiting до отправки запроса.
Почему стандартный rate limiting по количеству запросов (RPM) недостаточен для LLM API?
Token Bucket для LLM: ограничение по токенам, а не запросам
Token Bucket - алгоритм с историей. Ведро наполняется с постоянной скоростью. Каждый запрос берёт из ведра ровно столько, сколько стоит. Ведро пусто - запрос ждёт или уходит. Звучит просто, но есть нюанс: для LLM нужны **два ведра одновременно** - одно для RPM, другое для TPM. Первое защищает от storm of requests, второе - от одного запроса на 128K токенов.
Проблема: в момент отправки запроса количество **output токенов неизвестно** - модель ещё не начала генерацию. Стандартное решение - оценка: `system_tokens + user_tokens + max_tokens` (или среднее по истории запросов). После получения ответа `release()` возвращает неиспользованный кредит в ведро. Это сохраняет точность при систематически завышенных оценках.
Как token bucket обрабатывает ситуацию, когда фактическое потребление токенов оказалось меньше оценки?
Per-User Quotas с Redis: справедливое распределение ресурсов
Глобальный rate limit защищает от 429 провайдера. Но не от ситуации, когда один пользователь с automation-скриптом съедает 80% месячного бюджета за ночь. Per-user квоты - это **справедливость как архитектурное решение**. Лимит делится между пользователями, а не принадлежит тому, кто успел первым.
Redis - стандартный выбор для distributed rate limiting: атомарные операции, TTL на ключах, горизонтальное масштабирование. Ключи по userId + дата, сброс в полночь. Sliding window counter vs fixed window - для токенного бюджета достаточно fixed window: дневной лимит проще объяснить пользователю, чем скользящее окно.
Ответ при превышении квоты должен содержать **информативные заголовки**: `X-RateLimit-Remaining-Requests`, `X-RateLimit-Remaining-Tokens`, `X-RateLimit-Reset`. Стандарт де-факто - именно так делают OpenAI и GitHub API. Клиент получает точную информацию для graceful degradation вместо гадания по статус-коду.
Почему per-user quota использует adjustTokenUsage() после получения ответа от LLM?
NestJS Guard: rate limiting для AI endpoints
NestJS Guard - идеальное место для rate limiting: выполняется до controller, имеет доступ к request context, блокирует запрос до бизнес-логики. Guard для AI endpoints объединяет два слоя: global rate limit (защита от 429 провайдера) и per-user quota. Порядок важен - global идёт первым, иначе per-user пропустит запрос, который потом упадёт о лимит OpenAI.
В какой последовательности NestJS Guard проверяет rate limits?
Rate limiting для AI - это защита от DDoS и ботов
Rate limiting для AI - это в первую очередь управление token budget и стоимостью, а не защита от атак
DDoS-защита работает на уровне IP и количества запросов. Для LLM главная угроза другая: один легитимный пользователь с большим контекстом или automation-скриптом может потратить месячный бюджет за часы. Проблема не в злом умысле - проблема в том, что токены стоят денег, а их потребление непропорционально количеству запросов. Sliding window по RPM не спасает, когда один запрос = 50,000 токенов = 50 долларов.
Rate Limiting для AI API
- LLM API ограничены по RPM и TPM. TPM - реальный bottleneck: один запрос с 50K токенов исчерпывает минутную квоту
- Token Bucket с двумя измерениями: request bucket (RPM) + token bucket (TPM). release() возвращает неиспользованный кредит
- Per-user quotas через Redis: дневные лимиты по запросам и токенам, разные тарифы (free/pro/enterprise), adjustTokenUsage() для точности
- NestJS Guard: global rate limit → per-user quota → выполнение. Rate limit headers в каждом ответе
Что дальше
Rate limiting защищает от перерасхода и обеспечивает fair access. Следующая задача - понять, работает ли AI-система корректно, через evaluation и тестирование.
- Evaluation: как тестировать LLM — Rate limiting - количественный контроль. Evaluation - качественный: правильно ли отвечает модель
- Cost Management — Rate limiting и budget alerts - два механизма защиты бюджета, работающие в связке
- Error Handling для LLM — 429 ошибки от rate limit - один из типов LLM-специфичных ошибок для graceful handling
Связанные уроки
- aie-05-api-integration — Rate limiting оборачивает клиента провайдера
- aie-29-cost-management — Лимиты защищают бюджет расходов
- aie-32-error-handling-llm — Обрабатываем 429 через backoff и retry
- aie-31-evaluation — Измеряем пропускную способность под давлением лимитов
- net-62-rate-limiting — Тот же token-bucket на сетевом уровне