Перейти к содержимому
Ступень 1111.2 Проектирование ML-систем

Разбор: LLM-платформа внутри компании

Проектируем по схеме из 11.2.1 внутреннюю LLM-платформу для десятков команд — шлюз с OpenAI-совместимым API, маршрутизация между своими моделями и внешними API, квоты и учёт расходов, кэш, пул vLLM, выкатка через эвал-гейт, guardrails и логи без персональных данных — и считаем, сколько пользователей выдержит одна машина 4× RTX 4080.

УРОКstage-11/11-2-system-design/05-case-llm-platform.mdx2 чУрок 5 из 5
После урока вы сможете
  • Спроектировать внутреннюю LLM-платформу по девяти шагам и объяснить роль каждого компонента — шлюза, маршрутизатора, квот, кэша, пула инференса, guardrails, наблюдаемости
  • Описать выкатку новой модели через эвал-гейт, теневой запуск и канарейку с откатом через алиасы
  • Посчитать, сколько запросов и пользователей выдержит одна машина 4× RTX 4080 при заданных длинах запросов и SLO, и распределить модели по кластеру
  • Назвать главные компромиссы платформы и изложить решение за 45 минут собеседования

Через год после первых экспериментов с LLM в средней компании картина похожа: сорок команд, у каждой свой ключ внешнего API прямо в коде, свои повторы, свой кэш и свой способ логировать. Никто не знает, сколько компания тратит на токены и какая команда отправляет наружу паспортные данные клиентов. Провайдер снимает модель с поддержки — и половина сервисов ломается в один день. А ещё каждая команда просит себе отдельную видеокарту.

Ответ — внутренняя LLM-платформа: один вход ко всем моделям, свои и внешние, с контролем качества, денег и безопасности. Это продукт, пользователи которого — инженеры. Задача частая и на собеседовании: на ней видно, понимает ли кандидат инференс (ступень 7) и продакшен (модуль 8.4) одновременно. Пройдём её по девяти шагам из урока 11.2.1, а на шаге 8 посчитаем, сколько пользователей выдержит одна машина 4× RTX 4080.

Уточняющие вопросы: сколько команд и сценариев; какие модели нужны (свои, внешние); есть ли в запросах персональные данные и коммерческая тайна; какие требования к задержке; какое железо есть сейчас; кто платит за токены. Допущения, которые фиксируем вслух:

ПараметрДопущение
Команды40 сейчас, около 100 через год
Сценариичат-ассистенты, RAG по документам, суммаризация и классификация обращений, помощь с кодом, ночные пакетные задачи
Моделисвои открытые (Qwen3-8B, Qwen3-32B, эмбеддинги BGE-M3) и 2–3 внешних API
Пользователидо 10 000 сотрудников через приложения команд
SLO интерактиваp95 TTFT ≤ 2 с, TPOT ≤ 50 мс; доступность шлюза 99.9%
Данныечасть запросов содержит персональные данные (ПДн) и коммерческую тайну
Железотри машины 4× RTX 4080 32 ГБ, бюджет на рост

Чего платформа не делает: не обучает свои базовые модели и не строит продукты за команды. Это ограничение масштаба, и его стоит проговорить: иначе разговор уйдёт в претрейн.

У платформы нет «своей» выручки: её ценность — скорость команд и контроль рисков. Поэтому цепочка метрик выглядит так:

УровеньМетрикаЗачем
Бизнесбизнес-метрикаEN business metricТо, ради чего строят систему: выручка, конверсия, удержание, доля вопросов, решённых без оператора. ML-метрики (точность, recall@k) полезны, только если связаны с ней.время от заявки команды до первого вызова в продакшене; число сценариев в продакшенеплатформа ускоряет, а не тормозит
Принятиедоля LLM-трафика компании, идущего через платформуесли команды ходят в обход — платформа неудобна
Деньгистоимость 1 млн токенов по моделям; расходы команд против бюджета; экономия от кэша и маршрутизацииглавный аргумент для руководства
Надёжностьдоступность; p95 TTFT и TPOT по классам; доля ответов 429 и 5xxцелевой уровень сервисацелевой уровень сервиса (SLO)EN service level objectiveИзмеримая цель, например «p95 TTFT меньше 2 с при 8 пользователях». По ней планируют железо и поднимают тревоги.
Качестводоля сценариев с эвал-набором; результаты эвал-гейтаплатформа не может мерить качество за команды, но может требовать эвалы
Защитныеутечки ПДн во внешние API (цель — ноль), инциденты инъекцийзащитные метрикизащитная метрикаEN guardrail metricМетрика, которая не должна ухудшиться при выкатке новой версии, даже если главная растёт: задержка, доля жалоб, утечки персональных данных, стоимость запроса.

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

У платформы три вида данных. Логи запросов — с маскированием ПДн, сроком хранения и доступом по ролям; из них же команды собирают эвал-наборы. Статистика потребления — токены по командам, моделям и часам: по ней видно пиковый коэффициент и рост, это вход для планирования мощности. Эвал-наборы сценариев — версионированные золотые наборызолотой набор (тест-набор)EN golden datasetВерсионированный набор входов с эталонными ответами или способом проверки, на котором прогоняют каждую версию промпта или модели., которые команда приносит при подключении; без них платформа не сможет проверить, не сломает ли новая модель чужой сценарий.

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

Бейзлайн — текущее положение: команды ходят во внешние API напрямую. Измерьте его: сколько ключей, сколько денег в месяц, сколько инцидентов. Платформа v0 — готовый открытый LLM-шлюзLLM-шлюзEN LLM gateway (AI gateway)Единая точка входа ко всем моделям компании с API, совместимым с OpenAI: ключи и квоты команд, маршрутизация, кэш, учёт токенов и денег, логи, guardrails. Примеры с открытым кодом — LiteLLM, Higress, One API. (например, LiteLLM) перед внешними API и одним сервером vLLM, за две недели: ключи команд, логи, учёт расходов. Всё остальное — приращения к работающей системе, и каждое должно улучшать метрики шага 2. Решение «строить шлюз самим или взять готовый» — первый компромисс разговора; разумный ответ — взять готовый и дописывать своё только там, где его не хватает.

flowchart LR
T["Команды:<br/>SDK openai, свой base_url"] --> GW["Шлюз: ключи, лимиты,<br/>OpenAI-совместимый API"]
GW --> GR["Guardrails:<br/>ПДн, инъекции"]
GR --> R{"Маршрутизатор:<br/>алиас, класс данных,<br/>цена, доступность"}
R --> C[("Кэш")]
R --> POOL["Пул vLLM:<br/>fast, smart, embed,<br/>LoRA команд"]
R --> EXT["Внешние API<br/>(без ПДн)"]
GW -.-> OBS["Наблюдаемость:<br/>метрики, трейсы,<br/>расходы команд"]
GATE["Эвал-гейт:<br/>новые модели"] --> POOL
LLM-платформа: всё, что общее для команд, вынесено в один слой перед моделями

Команды уже умеют работать с SDK openai (урок 8.1.1), поэтому шлюз говорит на OpenAI-совместимом APIOpenAI-совместимый APIEN OpenAI-compatible APIHTTP-интерфейс в формате OpenAI Chat Completions (/v1/chat/completions). Его отдают Ollama, llama-server, vLLM, SGLang, а также DeepSeek и Qwen в облаке — код клиента не меняется.: /v1/chat/completions, /v1/embeddings, стриминг. Переход на платформу — заменить base_url и ключ. Платформа устроена по принципу мультиарендностимультиарендностьEN multi-tenancyОдна система обслуживает многих независимых потребителей (команды, клиентов) с раздельными ключами, квотами, данными и логами, чтобы один арендатор не мешал другим и не видел их данных.: у каждой команды свои ключи на окружение (dev, prod) с владельцем, свои квоты и свои логи, и одна команда не видит запросов другой; лимиты — «ведро токенов»«ведро токенов» (token bucket)EN token bucketАлгоритм лимитов. В ведре до C жетонов, оно пополняется со скоростью r в секунду, запрос забирает жетоны. Допускает короткие всплески и держит среднюю скорость. не только по запросам, но и по токенам в минуту (урок 8.4.3); таймауты и повторы — только до первого токена. Шлюз не хранит состояния и работает минимум в двух экземплярах за балансировщиком: иначе он единая точка отказа для всей компании.

Команды вызывают не модели, а алиасы: fast (Qwen3-8B), smart (Qwen3-32B или внешняя модель), embed, code. За алиасом платформа может сменить модель после эвал-гейта — команды не правят код. Кому нужна стабильность, закрепляет конкретную версию на срок поддержки (например, полгода). маршрутизация между моделямимаршрутизация между моделямиEN model routingВыбор модели для каждого запроса по задаче, сложности, цене и доступности: простое — маленькой своей модели, сложное — большой или внешней; при отказе — запасная модель (fallback). решает по правилам:

ПравилоПример
Класс данныхключ с ПДн или коммерческой тайной — только свои модели
Цена и сложностькаскад: сначала fast, при низкой уверенности или провале валидатора — smart (урок 11.1.5)
Длина контекстадлинный документ — модель и реплика с большим контекстом
Доступностьпровайдер отвечает ошибками — запасной провайдер или своя модель (fallback)

У каждой команды бюджет: мягкий порог (уведомление на 80%) и жёсткий (429 с понятным сообщением). Классы приоритета: интерактив важнее пакетных задач; пакетные идут ночью на простаивающих картах. Учёт расходов по командамучёт расходов по командамEN chargeback (cost allocation)Распределение затрат общей платформы между командами-потребителями по их токенам или GPU-часам. Без него общий ресурс выглядит бесплатным и расходуется без меры. нужен и для своих GPU: общий ресурс без цены кажется бесплатным. Внутренняя цена токена — стоимость GPU-часов, делённая на токены. Пример с условной ценой 0.5 долл. за час карты: машина из четырёх карт стоит 2 долл. в час; при полной загрузке RAG-сценарием (1 405 выходных токенов в секунду, расчёт на шаге 8) это 0.40 долл. за миллион выходных токенов, при средней загрузке 30% — 1.32 долл. Так команды видят, во что обходится их сценарий, а платформа — когда пора покупать следующую машину.

Три уровня. Точный кэш ответов в шлюзе — для детерминированных запросов (temperature=0); ключ включает модель, её версию, сообщения и параметры. Префикс-кэш в vLLM (урок 7.1.2) — общий системный промпт сценария считается один раз; чтобы он срабатывал, запросы с одинаковым началом выгодно направлять на одну реплику. Семантический кэшсемантический кэшEN semantic cacheВозвращает сохранённый ответ на запрос, близкий по смыслу (косинус эмбеддингов выше порога). Опасен ложными попаданиями — «включить» и «отключить» очень похожи. — только по запросу команды и после проверки порога на размеченных парах: в уроке 8.4.3 вы видели, как «включить» и «отключить» оказываются «похожими».

Модели крутятся в vLLM с непрерывным батчингомнепрерывный батчингEN continuous batching (iteration-level scheduling)Состав батча пересматривается после каждого шага decode — закончившиеся запросы уходят, новые встают на их место. Идея системы Orca (2022), стандарт всех современных серверов. и PagedAttention. Модель, которая помещается в одну карту, запускаем репликами (параллелизм по данным); тензорный параллелизмтензорный параллелизмEN tensor parallelism — только когда модель не влезает или не укладывается в TTFT: на PCIe без NVLink связь между картами съедает часть выигрыша (урок 7.1.2). Дообученные адаптеры команд обслуживаются поверх одной базовой модели: vllm serve Qwen/Qwen3-8B --enable-lora --lora-modules support=./adapters/support .... LoRALoRA (низкоранговая адаптация)EN low-rank adaptation (LoRA) с рангом 16 на всех проекциях Qwen3-8B — около 44 млн параметров, примерно 90 МБ против 16 ГБ базы, так что десять команд с адаптерами стоят почти как одна модель. Эмбеддинги и реранкер — на отдельной карте, чтобы длинные генерации не задерживали поиск.

На входе — поиск ПДн (регулярные выражения для телефонов, почты, номеров карт с проверкой Луна; распознавание имён) и решение по классу данных: для внешнего API — замаскировать (заменить на метки и вернуть значения в ответ) или заблокировать; для своих моделей — пропустить, но в логах хранить замаскированный текст. Для RAG и агентов — проверка промпт-инъекцийпромпт-инъекцияEN prompt injectionАтака, при которой инструкции злоумышленника попадают в контекст модели (напрямую или через веб-страницу, письмо, файл) и перехватывают управление. (урок 8.3.4). На выходе — ПДн и запрещённый контент. Сырые тексты, если они вообще нужны, лежат отдельно, с коротким сроком хранения и журналом доступа. Требования к персональным данным задаёт закон (в России — 152-ФЗ); платформа делает соблюдение возможным, а юридические выводы — работа юристов. Подробно — раздел «Guardrails и персональные данные» урока 8.4.3.

Метрики в разрезе «команда × модель»: запросы, входные и выходные токены, p50/p95/p99 TTFT и TPOT, ошибки по кодам, доля попаданий в кэш, деньги. Со стороны GPU — загрузка, заполненность KV-кэша, длина очереди: vLLM отдаёт их на эндпоинте /metrics в формате Prometheus. Идентификатор запроса проходит через шлюз до модели, так что трейс (урок 8.4.2) показывает, где ушло время.

Шаги 6–7. Выкатка новой модели через эвал-гейт

Заголовок раздела «Шаги 6–7. Выкатка новой модели через эвал-гейт»

Офлайн- и онлайн-оценка у платформы сливаются в один конвейер выкатки:

flowchart LR
N["Новая модель<br/>или версия"] --> G{"Эвал-гейт"}
G -->|"провал"| X["Не выходит,<br/>отчёт командам"]
G -->|"прошла"| SH["Теневой запуск:<br/>копия трафика"]
SH --> CA["Канарейка: 5%<br/>трафика алиаса"]
CA --> FULL["Алиас переключён<br/>на 100%"]
FULL -.->|"тревога"| RB["Откат: алиас назад"]
Новая модель проходит гейт, тень и канарейку; откат — переключение алиаса

Эвал-гейтэвал-гейтEN evaluation gate (quality gate)Автоматическое условие выкатки новой модели или промпта: прогон эвал-набора и порогов качества, безопасности, задержки и цены. Не прошла — в продакшен не попадает. — набор автоматических условий, записанных заранее: на эвал-наборе каждой команды, подписанной на алиас, качество не хуже текущей версии в пределах доверительного интервала (парный бутстрэп, урок 7.3.2); набор безопасности пройден; p95 задержки при плановой нагрузке укладывается в SLO (нагрузочный тест); цена токена не выросла сверх оговорённого. Потом теневой запусктеневой запускEN shadow deploymentКопия реальных запросов отправляется новой версии, но её ответы пользователю не показывают — только записывают и сравнивают. — копия реальных запросов идёт новой модели, ответы только записываются и сравниваются; затем канарейкаканареечный выпускEN canary releaseНовая версия сначала получает малую долю трафика (например, 5%); её онлайн-метрики сравнивают со старой, и только потом трафик переключают целиком. на 5% и полное переключение. Откат — вернуть алиас, это секунды. Команды заранее получают отчёт гейта по своим наборам: смена модели — изменение и для них.

Шаг 8. Сервинг: сколько пользователей выдержит одна машина

Заголовок раздела «Шаг 8. Сервинг: сколько пользователей выдержит одна машина»

Считаем по схеме из урока 11.2.1 и домашней работы s11-hw04-capacity. Интерактивный RAG-сценарий: промпт 2000 токенов, ответ 400, SLO TTFT ≤ 2 с и TPOT ≤ 50 мс, Qwen3-8B в INT8 на одной RTX 4080 32 ГБ (716.8 ГБ/с, 97.5 TFLOPS — паспорт штатной карты, урок 5.7.1).

  1. Память. Веса — 8.2 ГБ, KV-кэш запроса — 144 КиБ на токен × 2 400 токенов ≈ 354 МБ. Под кэш остаётся 32⋅0.9−1−8.2≈19.632 \cdot 0.9 - 1 - 8.2 \approx 19.6 ГБ: помещается 55 запросов одновременно.
  2. TTFT. Prefill: 2⋅8.19⋅109⋅2000/(97.5⋅1012⋅0.5)≈0.672 \cdot 8.19 \cdot 10^9 \cdot 2000 / (97.5 \cdot 10^{12} \cdot 0.5) \approx 0.67 с — в SLO.
  3. TPOT и батч. Шаг decode при батче BB — чтение весов и BB кэшей. Но пока реплика ведёт BB ответов, она успевает сделать prefill каждого из них, и эти паузы ложатся на те же 400 токенов: TPOT(B)=tшаг(B)+B⋅0.67 с400.\text{TPOT}(B) = t_{\text{шаг}}(B) + B \cdot \frac{0.67\ \text{с}}{400}. При B=17B = 17 шаг — 19.8 мс, паузы — 28.6 мс, итого 48.4 мс; при B=18B = 18 уже больше 50 мс. Батч ограничивает не память (55), а TPOT (17), и больше половины TPOT — чужие prefill.
  4. Пропускная способность реплики. Запрос живёт в реплике 400⋅48.4 мс≈19.4400 \cdot 48.4\ \text{мс} \approx 19.4 с; по закону Литтлазакон ЛиттлаEN Little's lawL = λ·W — среднее число запросов в системе равно интенсивности их поступления, умноженной на среднее время в системе. при 17 одновременных запросах реплика обслуживает 17/19.4≈0.8817 / 19.4 \approx 0.88 запроса в секунду.
  5. Машина. Четыре реплики — 3.5 запроса в секунду, с запасом 0.7 — 2.46 запроса в секунду в пик.
  6. Пользователи. Пусть активный сотрудник задаёт 20 вопросов в рабочий день, а пиковый коэффициент к среднему за сутки — 4. Машина выдерживает 2.46⋅86 400/4≈53 0002.46 \cdot 86\,400 / 4 \approx 53\,000 запросов в сутки, то есть около 2 650 активных пользователей.

Те же формулы для других сценариев (одна машина, 20 запросов на пользователя в день, пик ×4, налог на связь при tp > 1 — 30%):

СценарийПромпт / ответМодель, формат, tpБатчЗапросов/с в пикПользователей
Чат500 / 300Qwen3-8B INT8, tp = 1539.9≈ 10 700
RAG2000 / 400Qwen3-8B INT8, tp = 1172.46≈ 2 650
RAG2000 / 400Qwen3-8B BF16, tp = 1121.72≈ 1 850
Длинные документы8000 / 500Qwen3-8B INT8, tp = 290.51≈ 550
RAG2000 / 400Qwen3-32B INT4, tp = 270.50≈ 540

Выводы, которые стоит сказать вслух. Длина промпта — главный рычаг: вчетверо длиннее промпт — вчетверо меньше пользователей, а на 8 000 токенах одна карта уже не укладывается в TTFT (prefill 2.7 с) и нужен tp = 2. Квантование весов в INT8 даёт +40% пользователей. Модель 32B обходится примерно впятеро дороже 8B на запрос — значит, smart нужен с квотами и каскадом. Все числа — потолок на салфетке; реальные движки дают 50–85% от него, поэтому до запуска делаем нагрузочный тест и калибруем коэффициенты.

Кластер из трёх машин на старте:

МашинаЧто на нейЗачем
14 реплики Qwen3-8B INT8 (fast) с LoRA-адаптерами командосновной интерактив: ≈ 2 650 пользователей RAG или ≈ 10 700 чата
2Qwen3-32B INT4, tp = 2, две реплики (smart)сложные задачи, ≈ 0.5 запроса в секунду в пик — только по квотам
32 реплики fast, эмбеддинги и реранкер, 1 карта под эвалы и канарейкурост, запас на отказ машины, выкатка

Если все 10 000 сотрудников станут активными пользователями RAG, для fast понадобится около четырёх машин — это план роста, и подтверждать его будет статистика потребления, а не прогноз. Ночью интерактив почти стоит — пакетные задачи команд (разметка, суммаризация архивов) получают освободившиеся карты. Внешние API — для задач, где своих моделей не хватает, и как переливной клапан в пик, но только для запросов без ПДн.

Тревоги: ошибки шлюза выше 1%, p95 TTFT выше SLO дольше 10 минут, заполненность KV-кэша выше 90% (движок начнёт вытеснять запросы), бюджет команды на 80%. Раз в месяц — планирование мощности: рост токенов по командам и пиковый коэффициент из статистики потребления. Раз в квартал — обновление моделей за алиасами через гейт. Отдельно — опрос команд: что мешает подключиться. Если доля трафика в обход платформы растёт, проблема не в командах, а в платформе.

РазвилкаВыборЦенаПередумаю, если…
Свой шлюз или готовыйготовый открытый (LiteLLM, Higress)зависимость от чужого коданужны функции, которых нет и не будет
Алиасы или конкретные моделиалиасы плюс закрепление версийплатформа отвечает за совместимостькоманды массово закрепляют версии — гейт плохо ловит регрессии
Реплики или tpреплики, tp только по необходимостименьше память и медленнее prefill на репликуTTFT не укладывается или модель не влезает
Строгие или мягкие квотымягкие с уведомлением, жёсткие для пакетных задачриск перерасходабюджет компании урезан
Полные логи или минимуммаскированные логи, сырые — коротко и под замкомсложнее отладкарегулятор или служба безопасности требует иного

На собеседовании держите тайминг из 11.2.1: 5 минут на вопросы (команды, сценарии, ПДн, железо), 5 — на метрики платформы, 3–4 — на данные и бейзлайн, 10 — на архитектуру с диаграммой, 5 — на эвал-гейт и выкатку, 8 — на расчёт мощности, 3 — на мониторинг, остаток — на компромиссы. Самый сильный момент — фраза вида «одна машина держит около 2 650 пользователей RAG, а длинные документы впятеро дороже — поэтому ввожу квоты и tp = 2 только для них».

Проверь себя6 вопросов
1 Зачем платформе давать командам алиасы моделей (fast, smart), а не имена конкретных моделей?
2 Реплика обслуживает 0.88 запроса в секунду, на машине 4 реплики, запас загрузки 0.7. Сколько запросов в секунду машина выдержит в пик? Округлите до сотых.
3 Машина выдерживает 2.46 запроса в секунду в пик, пиковый коэффициент к среднему за сутки — 4, пользователь задаёт 20 запросов в день. Сколько пользователей выдержит машина? Округлите до десятков.
4 Политика: персональные данные и коммерческая тайна не уходят во внешние API. Какие запросы маршрутизатор может отправить во внешний API? Выберите все.Можно выбрать несколько ответов
5 Почему в расчёте RAG-сценария батч реплики ограничила TPOT, а не память?
6 В каком порядке выкатывать новую модель за алиасом?
  • LLM-платформа — продукт для инженеров: шлюз с OpenAI-совместимым API, маршрутизация по алиасам, классу данных, цене и доступности, квоты и учёт расходов, кэш, пул vLLM с репликами и LoRA команд, guardrails и логи без ПДн, наблюдаемость.
  • Новая модель выходит через эвал-гейт на наборах всех команд, тень и канарейку; откат — переключение алиаса.
  • Одна машина 4× RTX 4080 с Qwen3-8B INT8 держит около 2 650 активных пользователей RAG (промпт 2000, ответ 400, TPOT ≤ 50 мс) или около 10 700 пользователей короткого чата; главные рычаги — длина промпта, квантование, размер модели.
  • Готовые шлюзы (LiteLLM, Higress, One API) закрывают половину работы; эвал-гейт, guardrails под ваши данные и расчёт мощности — ваша часть.

Дальше: «Как устроен найм в ML» — роли, уровни и этапы собеседований, на одном из которых вам и предложат спроектировать такую систему.