Когда выбирать локальную модель, когда платный API, подводные камни каждого пути, как разворачивать (vLLM/llama.cpp/Ollama) и как строить сервис с моделью: очередь, параллелизация.
Цель урока
На доске за 5 минут: таблица «local vs API» под банк, стек разворачивания (vLLM + GGUF/Ollama), схема сервиса с очередью и параллельными воркерами.
1. Local vs API: таблица решений
Критерий
Paid API (OpenAI/Anthropic)
Local model
Данные / резидентность
Данные уходят провайдеру (no-train контракт, регион)
Fine-tune, квантизация, свой стек — полный контроль
Масштабирование
Провайдер масштабирует сам
Ты: GPU, vLLM, автоскейлинг
Отказоустойчивость
Риск rate limit / провайдер 500 / цена
Твой SRE, твои GPU, твой обслуживание
Когда точно API
Нужно лучшее качество (фронтир) — а банковский каре требует точности.
Объём маленький: $5/мес API дешевле, чем аренда GPU.
Скорость запуска: не ждёшь закупки железа.
Когда точно local
Данные нельзя выносить из контура (персональные данные, тайны банка).
Объём большой и стабильный: 10M токенов/день × API = дорого, GPU окупается.
Нужна кастомизация: fine-tune под домен (твои Карты так жили).
Оффлайн / edge: нет доступа к сети.
Гибрид — то, что любят спрашивать
Прод-паттерн: роутер модель → дешёвая локальная на простые запросы, API на сложные. Пример каре: FAQ и статус карты — локальная 7B; сложный отказ с юридическими нюансами — API.
Судья — всегда сильная модель (часто API), прод — дешёвая. Это из урока 0006.
2. Подводные камни local
OOM GPU — модель не влезла в память. Лечится квантизацией, меньшим max_model_len, gpu-memory-utilization.[96]
Квантизация ≠ бесплатно — Q4 быстрее и меньше, но качество падает. Для кода/технического — Q5/Q6, для чата — Q4_K_M.[97][98]
Throughput vs latency — vLLM жертвует чуть-чуть латентностью ради батчинга. Один запрос может быть медленнее, чем 100 параллельных — в сумме быстрее.[96]
KV cache — память под кэш внимания растёт с длиной контекста и числом параллельных запросов. Это не «модель не влезла», а «запросы не влезли».[96]
Холодный старт — загрузка 70B модели = минуты. Автоскейлинг должен знать про warm pool.
Обслуживание — ты владелец: обновления, патчи, бэкапы, мониторинг GPU.
3. Подводные камни API
Rate limits — лимиты запросов/минуту; нужен ретрай с экспоненциальной задержкой.
Стоимость на масштабе — 1M токенов/день × фронтир = заметные деньги. Считай до, не после.
Данные уходят — даже с no-train контрактом данные пересекают границу. Для персональных данных банка — вопрос юристов.
Vendor lock-in — промпты, тулы, форматы заточены под одного провайдера. Абстрагируйся: OpenAI-совместимый интерфейс везде (vLLM тоже умеет).
Провайдер 500 / деградация — нужен fallback (второй провайдер или local) и circuit breaker.
4. Как разворачивать локальную модель
Три стека, от продакшна к прототипу:
Стек A. vLLM — продакшн, высокий throughput
OpenAI-совместимый сервер, PagedAttention (блочный KV cache), continuous batching (смешивает prefill и decode), tensor parallelism для 30B–70B на нескольких GPU, prefix caching для повторяющихся промптов, Prometheus-метрики на /metrics.[96]
# 7B–13B на одной GPU
vllm serve meta-llama/Llama-3-8B-Instruct \
--gpu-memory-utilization 0.9 \
--max-model-len 8192 \
--enable-prefix-caching
# 70B на 4 GPU с квантизацией AWQ
vllm serve TheBloke/Llama-2-70B-AWQ \
--tensor-parallel-size 4 \
--quantization awq \
--gpu-memory-utilization 0.95
# клиент — обычный OpenAI SDK, только base_url другой
from openai import OpenAI
client = OpenAI(base_url="http://localhost:8000/v1", api_key="EMPTY")
Стек B. llama.cpp / GGUF — CPU, Apple Silicon, edge
GGUF-формат с квантизацией (Q4_K_M — чат, Q5_K_M/Q6_K — код). Работает на CPU, Metal (Apple), CUDA. Для прототипов и edge; на продакшн-GPU уступает vLLM по throughput.[97][98]
# из Hugging Face Hub, OpenAI-совместимый сервер
llama-server -hf bartowski/Llama-3.2-3B-Instruct-GGUF:Q4_K_M
# выбор квантизации: Q4_K_M чат, Q5_K_M/Q6_K код, Q3/IQ — только если память совсем впритык
Простой запуск открытых моделей локально или в их cloud, OpenAI-совместимый API, интеграции с редакторами. Не для тяжёлого продакшна (меньше контроля над батчингом и кэшем), но идеален для dev-окружения.[95]
Что выбрать (фраза на собеседовании)
«Для продакшн-инференса открытых моделей — vLLM: continuous batching и PagedAttention дают на порядок выше throughput, чем наивный transformers. Для прототипа и edge — llama.cpp/GGUF. Ollama — для dev. TGI был стандартом, но теперь в maintenance mode — его авторы сами направляют на vLLM/SGLang. Формат везде OpenAI-совместимый — переключение local/API меняет только base_url».
Факт про TGI (важно, свежий): Hugging Face перевела TGI в maintenance mode и рекомендует vLLM, SGLang, llama.cpp, MLX.[94]
5. Сервис с моделью: очередь и параллелизация
Схема, которую рисуешь на доске:
Клиент → API-слой (FastAPI) → Очередь → Воркеры (N) → LLM (vLLM) → ответ
Зачем очередь, если vLLM уже батчит
vLLM батчит внутри себя, но его очередь — в памяти процесса. Упал процесс — потерял запросы.
Разделение масштабирования: веб-слой и воркеры растут независимо.
Параллелизация: сколько воркеров
Воркер = процесс, который берёт задачу из очереди и шлёт в LLM. Один воркер на одну активную генерацию — или используй асинхронность.
vLLM сам обрабатывает N параллельных запросов (max-num-seqs). Оптимум: воркеров столько, чтобы vLLM был загружен, но очередь не росла бесконечно.
Правило: не плодить воркеров больше, чем параллельных слотов в LLM-сервере — иначе очередь просто перекладывается.
Асинхронный клиент (asyncio + openai AsyncClient) позволяет одному воркеру держать несколько in-flight запросов.
Backpressure и таймауты
Если очередь растёт — это сигнал: или добавить воркеров, или поднять rate limit, или это атака. Мерить длину очереди и age (возраст старейшей задачи).
Таймаут на каждый LLM-вызов (например, 30–60 с). Retry с экспоненциальной задержкой на 429/5xx.
Circuit breaker: если LLM деградировал — быстро фейлить, а не копить очередь.
6. Как разворачивать очередь тулов
Тулы — это тоже сервисы, и у них своя «очередь» вопросов: параллельность, идемпотентность, таймауты.
Отдельный сервис на тул, не всё в одном процессе
Тул = внутренний API (в банке так и есть): cards/block, payments/transfer. Агент зовёт их через адаптер, тулы не разделяют процесс с LLM-сервером. Так можно: масштабировать тул независимо (блокировки карт — отдельно от FAQ), ставить свои таймауты и rate limits, переиспользовать между агентами.
Очередь для долгих тулов
Тул, который работает секунды/минуты (OCR пачки, генерация отчёта), — не синхронный вызов из агентного цикла. Паттерн: агент кладёт задачу в очередь → получает job_id → опрашивает статус → забирает результат. Идемпотентность обязательна: retry воркера не должен выполнить действие дважды (idempotency_key).
Параллелизация тулов
Data-тулы (поиск, чтение) — можно параллелить смело: они read-only.
Action-тулы (перевод, блокировка) — НЕ параллелить вслепую: состояние меняется. Очередь на action с идемпотентностью.
Ограничение параллелизма на тул: Redis semaphore / rate limiter. Иначе 50 параллельных блокировок карт от одного агента.
HITL в очереди тулов
Деньги и PII: агент кладёт задачу, статус awaiting_approval, очередь ждёт решения человека (отдельный приоритетный канал), потом выполняет или отменяет. Это связывает урок 0007 (HITL) с очередью.