Model serving — это организация надёжного, масштабируемого и предсказуемого инференса больших моделей (LLM) в проде. Хороший сервисинг обеспечивает стабильные SLO (p50/p95/пиковая латентность, tokens/s, доступность), экономичность (GPU-утилизация, стоимость ответа) и безопасность (guardrails, защита от инъекций). Техническая основа — правильная компоновка движка инференса, планировщика, кэшей и масштабирования.
Связанные страницы: KV cache, Prefill cache, Top-k, Top-p, Контекстное окно, Evals, RAG-хаб, Prompt injection.
SLO и ключевые метрики
| Метрика | Что означает | Комментарии по целям |
|---|---|---|
| Latency p50/p95/p99 | задержка ответа | Мерите отдельно префилл и декод; p95 важнее среднего |
| Tokens/s (decode) | скорость генерации | На пользователя/на GPU/на ноду; влияет планировщик и кэш |
| Throughput (req/s) | пропускная способность | Зависит от батчинга и параллелизма |
| Acceptance rate | доля принятых запросов | Управляется Admission Control (очереди/квоты) |
| GPU util / memory | утилизация и запас | Следите за фрагментацией KV и OOM |
| Cost/answer | стоимость ответа | Бюджетная метрика, связана с tokens/out и TCO |
Мини-цели: стабильный p95, отсутствие head-of-line blocking, предсказуемый tokens/s при росте конкуренции.
Архитектуры и движки инференса
Один узел (single-GPU/мульти-GPU) *Плюсы:* простота, низкая латентность. *Минусы:* предел памяти/модели, ручное шардирование.
Кластер (Kubernetes/Ray/мульти-узел) *Плюсы:* автомасштабирование, смешение профилей моделей. *Минусы:* сложность планировщика и сети.
Типовые движки/серверы
- vLLM-класс: непрерывный батчинг, paged KV, совместимость OpenAI API.
- TGI-класс: ориентир на стабильный прод, хорошая телеметрия.
- TensorRT-LLM / Triton: ядро высокопроизводительных фьюзов/Kernel-оптимизаций на NVIDIA.
- llama.cpp-класс (CPU/Mac/Mobile): лёгкий рантайм, GGUF/квантование для edge.
Выбор зависит от: размера контекста, ожиданий по p95, наличия мульти-тенантной очереди и бюджета.
Планирование: префилл и декод
Инференс LLM делится на две разные по характеру фазы:
- Prefill — «заливка» промпта батчем (математика → GEMM, хорошо параллелится).
- Decode — токен-за-токеном, memory-bound, упирается в скорость доступа к KV.
Современные планировщики используют:
- Непрерывный батчинг (continuous batching) — динамически подмешивают новые запросы между шагами декода, не дожидаясь «опустошения» батча.
- Sequence packing — плотная укладка коротких запросов с масками.
- Paged KV — блочная организация кэша, чтобы избегать копирований и фрагментации.
- Admission control — лимит на одновременные последовательности, «кредит» токенов, таймауты очереди.
Цель — максимально заполнять GPU, не разрушая p95.
Кэширование: KV и префиксы
- KV cache хранит K/V уже пройденных токенов; растёт линейно по длине контекста и веткам (beam).
- Prefill cache позволяет шарить общий префикс между сессиями (системные инструкции/рамки ответа).
- Политики: горячие префиксы — на GPU, холодные — на CPU; TTL/LRU, запрет на шаринг между доверенными/недоверенными потоками.
Считайте память: bytes ≈ layers × seq_len × n_kv_heads × head_dim × 2 × sizeof(dtype).
Параллелизм и шардирование модели
| Вид | Суть | Когда применять |
|---|---|---|
| Data parallel | дубли модели на разных GPU | Масштаб по пользователям, просто |
| Tensor parallel | шард слоёв/голов по GPU | Большие модели, треб. быстрая связь (NVLink) |
| Pipeline parallel | слои по стадиям/узлам | Очень большие модели; усложняет планировщик |
| Sequence/Context parallel | деление по длине/позициям | Длинные окна; тонко с RoPE/позициями |
Практически: стремитесь влезть в один ускоритель с квантованием/Low-RAM весами; если нет — Tensor-parallel c быстрой межсвязью.
Квантование и оптимизации
- Weights: INT8/INT4 (GPTQ/AWQ/RTN), FP8/BF16/FP16.
- KV cache: INT8/FP8 — экономит память без заметной потери качества для многих задач.
- Fused kernels / FlashAttention: ускоряют префилл/внимание.
- PagedAttention: доступ к KV страницами.
- Activation checkpointing: компромисс память↔скорость в префилле.
На проде квантование — инструмент вместимости и стоимости, но валидируйте качество на своих evals.
Декод: скорость и качество
Выбор стратегий влияет на латентность и стиль ответа:
- Спекулятивный декод (draft-модель, проверка «большой») — даёт 2–3× ускорение на длинных ответах при хорошей валидации.
- Assisted/Lookahead — вариации с дешёвыми предсказателями токенов.
- Beam-search редко нужен для open-ended; увеличивает память и p95.
Для JSON/строгих форматов снижайте «смелость» и добавляйте пост-валидацию.
Мульти-тенантность и справедливость
Проблемы: разные длины контекстов, «жадные» запросы, HoLB (head-of-line blocking).
- Time-slicing декода: «кванты» по токенам на запрос.
- Квоты и бюджет токенов: лимит prefill_tokens и decode_tokens на клиента/ключ.
- Отсечка больших контекстов: честные ошибки «слишком длинно», рекомендации сокращения.
- Приёмка: остановка на очереди, ранний отказ при переполнении, «Warm pool» для мгновенного старта.
Стриминг и API-контракты
- Протоколы: HTTP SSE для токен-стрима, gRPC/WebSocket для низкой задержки.
- Контракты: совместимость с OpenAI API (chat/completions, функции), JSON-форматы, токен-usage.
- Функции/инструменты: строгое JSONSchema, валидация до вызова.
- Версионирование: model_id, prompt_template_id, tokenizer_id — отдельные сущности.
Наблюдаемость и эксплуатация
Минимальный Observability-набор:
- Метрики: префилл/декод-время, tokens/s, принятые/отклонённые, OOM, KV-футпринт, GPU-util.
- Трейсинг: спаны «admission → prefill → decode → stream out», контекст запроса (анонимизирован).
- Логи: ошибки, таймауты, отказы, эвакуации с GPU.
- Алерты: рост p95/p99, резкий спад tokens/s, частые OOM/почти OOM, снижение acceptance rate.
Для качества — онлайн-evals/канареечные запросы, feedback-петля «плохих» кейсов.
Масштабирование и стоимость
- Autoscaling: HPA/KEDA по tokens/s, GPU-util, очереди; anti-thrash (гистерезис).
- MIG / профили GPU: логическое деление A100/H100 под разные модели.
- Spot/прерываемые: удешевляют, но требуют graceful drain (дослать поток/сохранить KV-префикс).
- Bin-packing: смешивайте «тяжёлые» и «лёгкие» модели; избегайте «девственно пустых» карт.
- Edge/CPU-offload: часть KV/весов на CPU; компромисс задержки.
Считайте Cost/answer = ($/час × доля часу на ответ) / ответы; бенчмарки на реальных промптах.
Безопасность и комплаенс
- Guardrails (политики/валидаторы): проверка форматов, фильтры PII/токсичности, правило «данные ≠ инструкции».
- Изоляция кэшей (префиксы/KV) по уровням доверия; маскирование логов.
- Региональные требования к данным и аудит (лог-хранение, политика доступа).
Интеграция с RAG и инструментами
- На вход подавайте очищенные и цитируемые чанки (ретривер/переранжирование — см. retriever, vector index).
- Контракты функций: белые списки, статическая схема, dry-run, квоты на действия.
- Рамки промптов короткие, без секретов; длинные стили — через prefill cache.
Плейбук внедрения (пошагово)
- Цели и SLO: p95, tokens/s, стоимость; целевые окна контекста и длины ответов.
- Бенчсценарии: 50–200 реальных промптов; измерьте базовые метрики на одном узле.
- Движок: выберите класс (vLLM/TGI/TensorRT-LLM/llama.cpp) под вашу модель и железо.
- Квантование/вместимость: попробуйте INT8/INT4 для весов и KV, проверьте качество по evals.
- Планировщик: включите непрерывный батчинг, лимиты очереди, max_tokens на клиента.
- Кэши: подключите prefill cache для общих прологов; настройте блочный KV.
- Стриминг: SSE/API-совместимость, функции с JSONSchema.
- Observability: метрики/трейсы/логи + алерты по p95, OOM, acceptance.
- Autoscaling: HPA по очереди и tokens/s; warm-pool.
- Безопасность: guardrails, фильтры, разделение кэшей, подписи весов; red-team по инъекциям/утечкам.
- Непрерывные evals: nightly регрессы; release-gate по SLO/безопасности.
Частые ошибки (и как их исправить)
- Один гигантский батч: p95 «гуляет», пользователи с короткими запросами страдают. → Непрерывный батчинг, admission-кредиты.
- Общий префикс не кэшируется: переплата префилла. → Prefill cache и пул «горячих» префиксов.
- KV-фрагментация и копирования → падение tokens/s. → Paged KV, выравнивание блоков, дефраг-пул.
- Агрессивное квантование ломает код/арифметику. → Смешанные режимы: INT8 KV + BF16 на последних слоях.
- Нет лимитов на токены → «залипание» последовательностей. → max_new_tokens, бюджет на клиента, ранняя остановка.
- Отсутствие наблюдаемости: «летим вслепую». → Метрики/трейсы с разрезом prefill/decode.
- Сырые промпты в логах → утечки. → Маскирование/ролевая сегрегация логов.
- Beam-search по умолчанию → память×N. → Для open-ended держать greedy/top-k/p.
Мини-FAQ
Как увеличить tokens/s без ухудшения качества?
Непрерывный батчинг + Paged KV + FlashAttention; аккуратное квантование весов/KV; спекулятивный декод с быстрой «черновой» моделью.
Что делать, если p95 рвано растёт при пиках?
Включить admission control (кредиты на токены), ограничить prefill_tokens, держать warm pool и авто-масштабирование по длине очереди/tokens/s.
Чем помочь длинным контекстам без OOM?
Квантование KV, «скользящее окно», offload дальнего префикса на CPU, sequence-/context-parallel при необходимости.
Почему greedy иногда быстрее и «чище»?
k=1 убирает сэмплирование и экономит выбор; для кода/форматов это снижает ошибки и ускоряет декод (см. Top-k).
Можно ли делить один GPU между несколькими моделями?
Да, через MIG/временную мультиплексцию, но следите за изоляцией кэшей и стабильностью p95; маленькие модели лучше «упаковывать» вместе.
Нужен ли RAG, чтобы снизить галлюцинации?
В проде почти всегда. Но RAG требует чистого ретрива и цитирования (см. RAG-хаб, retriever).
Мини-глоссарий
- Prefill — батч-прогон промпта до генерации.
- Decode — по-токенная генерация с опорой на KV.
- Continuous batching — динамическое смешивание запросов между шагами декода.
- Paged KV — блочный кэш для уменьшения копирований/фрагментации.
- Speculative decoding — ускорение за счёт «черновой» модели.
- SLO — целевые показатели качества сервиса (латентность, стабильность, стоимость).
