Почему одного RPM мало
Запрос на 200 и 20 000 токенов имеют разную стоимость prefill. Длинный ответ занимает decode slot дольше короткого. Поэтому admission decision учитывает requests, tokens, concurrency и текущую очередь.
RPM и TPM
Requests per minute защищает control plane, tokens per minute - вычислительную мощность и счёт. Внешние провайдеры могут применять оба лимита одновременно. Считайте input заранее, а output резервируйте по max_tokens или историческому percentile.
Token bucket
Bucket накапливает ограниченное число разрешений и позволяет короткий burst, сохраняя среднюю скорость. Отдельные buckets можно вести для запросов и токенов, но решение должно быть атомарным, чтобы параллельные workers не превысили лимит.
Per-tenant fairness
Глобальный limiter не мешает одному клиенту занять всю мощность. Добавьте tenant quota, concurrency cap и weighted fair queue. Привязывайте лимит к проверенной identity, а не к переданному пользователем заголовку.
Admission control и очередь
Если прогнозируемый запрос нарушит SLO, лучше быстро отказать, чем принять его в бесконечную очередь. Ограничьте queue length и wait time, возвращайте 429 или 503 согласно причине и добавляйте Retry-After там, где оценка честна.
Retries без шторма
Используйте exponential backoff с jitter, общий deadline и максимум попыток. Не повторяйте запрос автоматически после неизвестного результата без idempotency key: модель или tool мог уже выполнить действие.
Graceful degradation
При дефиците мощности можно уменьшить max output, отключить дорогой reranker, выбрать разрешённую более дешёвую модель или перейти в async batch. Любая деградация должна быть видна клиенту и проверена evals.
Наблюдаемость
Логируйте accepted/rejected, причину, tenant, оценённые и фактические tokens, queue wait и retry count. Алерт должен ловить не только 429, но и рост очереди, fairness skew и расход бюджета.