Когда GPU-кластер с vLLM задыхается от пиковых нагрузок, классический Rate Limiting и блокировка пользователей — это худший UX из возможных. А что если не отбрасывать запросы, а заставить саму языковую модель «сжать» свои промпты и стать предельно лаконичной, выдавая только самую суть? В этой статье мы разбираем архитектуру LazyGate — open-source шлюза, который в фоновом режиме читает метрики видеокарты и с помощью системных промптов динамически регулирует «болтливость» нейросети, кардинально спасая метрику Time-to-First-Token.
Деплой больших языковых моделей (LLM) — это всегда боль, когда дело доходит до пиковых нагрузок. В классических web-сервисах при высоких RPS мы просто включаем балансировщик, а если всё горит — жестко режем запросы HTTP 429 Too Many Requests.
Но в мире генеративного AI отбрасывать запросы клиентов очень дорого: пользователь уже подождал, пока загрузится чат, написал длинный промпт, нажал Enter и… получил ошибку. А масштабирование GPU-кластера занимает минуты, которых у нас нет.
В этой статье мы покажем, как подход “Динамической лени” (Dynamic Degradation) позволяет сохранить доступность сервиса при 100% утилизации GPU, не отбрасывая запросы, а заставляя саму модель быть более “краткой”. Для этого мы написали свой open-source шлюз LazyGate.
Идея проста: мы ставим перед нашим vLLM сервером легковесный ASGI-прокси на базе FastAPI + httpx. Этот прокси-шлюз выполняет одну ключевую задачу — постоянно опрашивает драйверы NVIDIA (или эмулятор в режиме разработки), получая метрику нагрузки на GPU.
Мы определили три состояния кластера:
Normal Load (<75%): Обычный режим. Модель может писать стихи, долго рассуждать и вдаваться в детали (max_tokens: 100%).
Tired Load (75-90%): Система начинает испытывать сложности (очередь continuous batching растет). Прокси “на лету” подкидывает в массив messages системный промпт: “Отвечай кратко”, а max_tokens урезается до 70%.
Extremely Lazy (>90%): Аварийный режим. Чтобы максимально быстро очистить очередь KV-кэша, шлюз инжектит жесткий промпт: “Provide extremely short answers only. No explanations. Code only if asked.” и режет токены до 30%.
Читать нагрузку видеокарты при каждом HTTP запросе через интерфейс Python pynvml — верная смерть для Event Loop’а. Поэтому мы вынесли это в фоновую asyncio задачу.
# Из нашего hardware.py
async def _monitor_loop(self):
while True:
# Чтение реальной нагрузки (либо синусоиды для mock режима)
new_load = self._fetch_real_load()
# EMA (Экспоненциальное скользящее среднее) чтобы побороть "дребезг" на порогах
alpha = 0.3
self._current_load = (new_load * alpha) + (self._current_load * (1 - alpha))
await asyncio.sleep(self.update_interval)
Таким образом, для каждого входящего роута получение метрики — это просто чтение из памяти:
monitor.current_load.
vLLM используется в основном со стримингом токенов (stream=True). Если бы наш прокси буферизировал ответ, мы бы убили главную фичу — Time-to-First-Token (TTFT).
Именно поэтому мы используем потоковую передачу данных через StreamingResponse:
# main.py
if is_stream:
async def stream_generator():
async with http_client.stream("POST", url, json=body, headers=proxy_headers) as response:
async for chunk in response.aiter_bytes():
yield chunk
return StreamingResponse(stream_generator(), media_type="text/event-stream")
Мы не хотели вручную переписывать Pytest-тесты каждый раз, когда добавляем новый профиль нагрузки в config.yaml. Так как в разработке мы применяем AI-инструменты, мы написали скрипт tester_agent.py.
Это “внутренний Агент”, который сам читает конфиг YAML со списком порогов, идет в файл с Pytest-тестами через Abstract Syntax Trees (AST) и ищет, покрыт ли каждый сценарий (Normal, Tired, Lazy). Если он находит “дыру”, скрипт автоматически дописывает код нового теста в файл test_main.py и запускает конвейер заново!
Использование промптинга для динамической модификации сложности вычислений (LLM Routing) — намного более гибкий подход, чем жесткий Rate-limiting.
В ближайших планах — добавить в шлюз отслеживание Token Velocity () вместо прямого пинга NVML, чтобы понимать реальную пропускную способность очередей vLLM.
Код проекта полностью открыт под лицензией для академического и некоммерческого использования на нашем GitHub. Присоединяйтесь!
| # | Наименование новости | Тональность | Информативность | Дата публикации |
|---|---|---|---|---|
| 1 | Как заставить LLM считать точно: генерация кода вместо генерации ответов | 0 | 11.55 | 28-03-2026 |
| 2 | Собственная облачная LLM на 16 ГБ VRAM — часть 1: базовая сборка, tools и MCP | -1 | 14.47 | 09-03-2026 |
| 3 | MCP vs Thin MCP: где AI агенты теряют скорость | 0 | 9.91 | 03-04-2026 |
| 4 | TileRT - Tile-Based Runtime for Ultra-Low-Latency LLM Inference | 0 | 35 | 28-06-2026 |
| 5 | Запускаем AI-ассистента на бесплатном CPU: Qwen2.5 + Gradio + Hugging Face Spaces | 0 | 17.1 | 07-02-2026 |
| 6 | Сепаратор для логов. Сжимаем логи для контекста LLM без потери читаемости | 0 | 11.92 | 06-05-2026 |
| 7 | Почему классический CI/CD не справляется с LLM (и какие release gates мы построили, чтобы это исправить) | 0 | 7 | 04-07-2026 |
| 8 | Как я построил guardrails, которые не дали моему AI-агенту пойти вразнос | 0 | 9.2 | 16-06-2026 |
| 9 | LLM как декодер в ASR: опыт адаптации SOTA архитектуры для спонтанной русскоязычной речи | 0 | 9.94 | 22-04-2026 |
| 10 | local-llm-server 0.35.1 | 0 | 7 | 20-07-2026 |