vLLM: локален inference сървър за много потребители
Ollama е за разработка и малък екип. Когато десетки хора питат модела едновременно, ти трябва vLLM. Тук разбираме двата механизма, които го правят бърз — PagedAttention и continuous batching — и вдигаме OpenAI-съвместим сървър на x86 и на ARM64 машина.
vllm/vllm-openai е за amd64 и arm64; NVIDIA пуска свой образ за GB10 (DGX Spark / ASUS Ascent GX10). Старото „ограничена поддръжка“ отпада.Команди:
vllm serve вместо python -m vllm.entrypoints.openai.api_server; Python 3.10–3.13 (беше 3.9–3.12); инсталация с uv.AWQ: флагът
--quantization awq иска AWQ модел — примерите вече ползват такъв; хранилището casperhansen/… вече не е достъпно; AutoAWQ е изоставен в полза на llm-compressor.Факти: блокът на PagedAttention е фиксиран брой токени (не „~16 KB“); „24ד е спрямо HF Transformers, а статията дава 2–4× спрямо FasterTransformer и Orca; Ollama не е чист FIFO — има
OLLAMA_NUM_PARALLEL; връзката към NGC е поправена.Числата за скорост в примерите са илюстративни, не са мерени от нас — ⚠️ измери на своя хардуер.
01Какво ще научиш
- Кога Ollama е достатъчен и кога е време за vLLM — по брой едновременни потребители, не по мода.
- Как PagedAttention и continuous batching освобождават паметта и видеокартата за повече заявки.
- Как се инсталира vLLM на x86, на ARM64 (GB10) и в Docker.
- Три начина за работа: OpenAI-съвместим сървър, Python за пакетна обработка, смяна на Ollama в LangChain с един ред.
- Кога квантизация (AWQ, FP8) и какво да проверяваш при избор на модел.
- Как да настроиш Ollama за няколко паралелни заявки, докато vLLM още не ти трябва.
02Преди да започнеш
- Минат Блок 0 (как работи LLM, AI стекът, лабораторията).
- Linux машина с NVIDIA видеокарта (compute capability 7.5 или по-нова) — или ARM64 машина с GB10 и 128 GB обща памет. vLLM не върви директно под Windows (само през WSL).
- Python 3.10–3.13; препоръчително
uvза средата. - Акаунт в Hugging Face 🌐 глобален, ако ще ползваш „заключени“ модели като Llama (приемаш лиценза и ползваш токен). Примерите тук ползват Qwen, който не е заключен.
- Ollama 🔒 локално — за сравнението и за стъпка 6.
03Стъпки
-
Ollama или vLLM — кога кое
Двата инструмента решават различни задачи. Ollama е инструмент за разработчика — лесен старт, добро управление на модели. vLLM е двигател за обслужване — направен да държи много заявки наведнъж. Олимпийски бегач срещу джип: всеки е най-добър на своя терен. Честа архитектурна грешка е да ползваш грешния за ситуацията.
Критерий Ollama vLLM Кога кое Инсталация 1 команда pip/uv или Docker Ollama за старт Паралелни заявки ограничено — OLLAMA_NUM_PARALLEL(по подразбиране 1 на модел)continuous batching, стотици последователности vLLM при много едновременни потребители Памет за контекста заделя по NUM_PARALLEL × контекстPagedAttention — динамично vLLM при много сесии OpenAI API ✓ съвместим ✓ съвместим (по-пълен) и двата работят с LangChain Квантизация GGUF (llama.cpp) AWQ, GPTQ, FP8 и др. vLLM за AWQ/FP8 ARM64 (GB10) ✓ ✓ (aarch64 пакети и образи — вж. стъпка 3) и двата — обновено 01.10.2026 Наблюдение минимално вградени Prometheus метрики vLLM за production ✅ПравилоРазработка и малък екип → Ollama. Production API или RAG с много едновременни потребители → vLLM. Решавай по реалния товар (колко души наведнъж, колко дълъг контекст), не „по принцип“.🏥Примерен сценарий (илюстрация, не наш измерен случай)Болница пуска RAG за протоколи на НЗОК върху Ollama с настройки по подразбиране. Когато петима лекари питат наведнъж, заявките чакат една след друга и отговорът стига до десетки секунди. Със същия хардуер и модел, но на vLLM, заявките се обработват заедно и чакането пада многократно. Изводът: Ollama не е „виновен“ — направен е за друго. -
PagedAttention и continuous batching — защо vLLM е бърз
Докато генерира, моделът пази KV кеш (ключове и стойности) за всеки токен от контекста. Наивният подход заделя място за целия възможен контекст на всяка заявка още в началото — дори отговорът да е кратък. Голяма част от паметта остава заделена, но празна, и нови заявки не се побират.
PagedAttention (UC Berkeley, SOSP 2023) взима идеята за виртуалната памет от операционните системи: KV кешът се дели на блокове с фиксиран брой токени, които се заделят при нужда и не е нужно да са един до друг. Губи се място само в последния блок на всяка заявка — по данни на екипа на vLLM под 4%. Свободната памет отива за още едновременни заявки.
Наивно заделяне PagedAttention Как се заделя максималният контекст — предварително блок по блок, докато заявката расте Загуба на памет голяма (заделено, но неизползвано) само в последния блок (<4%) Резултат малко заявки се побират много повече заявки на същия хардуер Continuous batching е вторият механизъм. При статичен batch всички чакат най-дългата заявка: ако едната генерира 500 токена, а другата 10, втората седи без работа. С continuous batching завършилата заявка излиза и на нейно място влиза нова на всяка итерация — видеокартата не стои празна.
📄Какво точно твърдят източницитеБлогът на vLLM (2023): до 24× по-висок throughput спрямо HuggingFace Transformers и до 3,5× спрямо TGI. Научната статия (Kwon и съавт., SOSP 2023): 2–4× спрямо FasterTransformer и Orca при същото забавяне. Реалната печалба зависи от товара — мери при себе си. -
Инсталация: x86, ARM64 (GB10) и Docker
x86 + NVIDIA. Официалният път днес е с
uv— той сам избира правилния PyTorch за драйвера ти.bash · x86 или ARM64 Linux · инсталация# Проверки преди инсталация python3 --version # 3.10–3.13 nvidia-smi # видеокартата и драйверът се виждат # Среда и инсталация (препоръчан път от документацията на vLLM) uv venv --python 3.12 --seed source .venv/bin/activate uv pip install vllm --torch-backend=auto python -c "import vllm; print(vllm.__version__)"ARM64 с GB10 (NVIDIA DGX Spark, ASUS Ascent GX10 и подобни). До скоро това беше слабото място. Към 01.10.2026 PyPI има aarch64 пакети на vLLM, а NVIDIA поддържа готов образ за тези машини — най-сигурният път, защото е сглобен за точната архитектура.
bash · GB10 · образът на NVIDIA# Етикетът се сменя — вземи актуалния от NGC или от наръчника на NVIDIA за DGX Spark export VLLM_TAG=26.05.post1-py3 # етикетът в наръчника към 01.10.2026 docker pull nvcr.io/nvidia/vllm:${VLLM_TAG} docker run -d --gpus all -p 8000:8000 --name vllm-server \ nvcr.io/nvidia/vllm:${VLLM_TAG} \ vllm serve Qwen/Qwen2.5-7B-Instruct-AWQDocker, общият образ.
vllm/vllm-openai:latestвече е и за amd64, и за arm64.bash · Docker · официалният образ на vLLMdocker run --gpus all --ipc=host -p 8000:8000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --name vllm-server \ vllm/vllm-openai:latest \ --model Qwen/Qwen2.5-7B-Instruct-AWQ \ --max-model-len 16384 \ --served-model-name qwen-7b # Тест веднага след старта curl http://localhost:8000/v1/models⚠️Капан: заключени моделиМоделите на Meta (Llama) в Hugging Face са заключени: трябва да приемеш лиценза и да подадеш токен (HF_TOKEN). Без това свалянето спира с грешка 401. Токенът не се пише в скриптове, които споделяш. -
Първи сървър — три начина на работа
Начин 1 — OpenAI-съвместим HTTP сървър. Това е начинът за production: всяко приложение, което говори с OpenAI, говори и с него.
bash · vllm serve с основните настройки# Моделът е AWQ — vLLM разпознава квантизацията от конфигурацията му vllm serve Qwen/Qwen2.5-7B-Instruct-AWQ \ --port 8000 \ --max-model-len 16384 \ --max-num-seqs 128 \ --gpu-memory-utilization 0.90 \ --enable-prefix-caching \ --served-model-name qwen-7b # --max-num-seqs колко последователности наведнъж в една итерация # --enable-prefix-caching преизползва общ начален текст (системния промпт) # --served-model-name името, с което клиентите викат модела # --trust-remote-code само за модели със собствен код и само ако им вярваш # Въпрос с curl curl http://localhost:8000/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{ "model": "qwen-7b", "messages": [ {"role": "system", "content": "Ти си AI асистент на малка фирма."}, {"role": "user", "content": "Обясни PagedAttention в 3 изречения."} ], "temperature": 0.2, "max_tokens": 300 }' # Поточно (за чат интерфейс): добави "stream": trueНачин 2 — Python за пакетна обработка. Когато имаш списък с въпроси и не ти трябва сървър: даваш ги всички наведнъж и vLLM сам ги подрежда.
python · batch_inference.pyfrom vllm import LLM, SamplingParams import time llm = LLM( model="Qwen/Qwen2.5-7B-Instruct-AWQ", max_model_len=8192, enable_prefix_caching=True, gpu_memory_utilization=0.90, # 10% резерв ) params = SamplingParams(temperature=0.2, top_p=0.9, max_tokens=500, repetition_penalty=1.05) PROMPTS = [ "Обясни разликата между LoRA и пълно дообучение.", "Напиши пример за RAG верига с LangChain.", "Какво е PagedAttention и защо е важно?", "Как се изчислява косинусово сходство?", ] t0 = time.time() outputs = llm.generate(PROMPTS, params) elapsed = time.time() - t0 tokens = sum(len(o.outputs[0].token_ids) for o in outputs) print(f"{len(PROMPTS)} заявки · {tokens} токена · {elapsed:.1f} s · {tokens/elapsed:.0f} tok/s") for p, o in zip(PROMPTS, outputs): print("\n?", p[:60]); print(o.outputs[0].text[:200])Начин 3 — смяна на Ollama в LangChain. vLLM говори OpenAI API, затова в RAG кода сменяш само адреса на модела. Embeddings могат да останат на Ollama.
python · LangChain: Ollama → vLLMfrom langchain_openai import ChatOpenAI # ПРЕДИ: llm = ChatOllama(model="qwen2.5:7b", temperature=0.2) llm = ChatOpenAI( base_url="http://localhost:8000/v1", api_key="not-needed", # vLLM не иска ключ, ако не си му задал model="qwen-7b", temperature=0.2, max_tokens=500, ) # chain = ({"context": retriever, "question": RunnablePassthrough()} # | prompt | llm | StrOutputParser()) ← смяна само тук # Бързо сравнение: едни и същи 10 заявки към двата сървъра import time, requests def bench(url, model, n=10): t0 = time.time() for _ in range(n): requests.post(url, json={"model": model, "max_tokens": 80, "messages": [{"role": "user", "content": "Обясни RAG в 50 думи."}]}) return time.time() - t0 print("Ollama:", bench("http://localhost:11434/v1/chat/completions", "qwen2.5:7b")) print("vLLM: ", bench("http://localhost:8000/v1/chat/completions", "qwen-7b"))⚠️Числата не са наши измерванияСтарата версия на урока показваше „42 s срещу 14 s“ за 10 заявки — това е илюстрация, не резултат от наш тест. Последователните заявки почти не показват предимството на vLLM; то се вижда при паралелни заявки. Пусни сравнението с няколко нишки и запиши собствените си числа. -
Квантизация за production — AWQ, GPTQ, FP8
AWQ (Lin и съавт., MIT, 2023) е 4-битова квантизация, която намира най-важните тегла (според реалните активации) и ги пази по-точно. Затова губи по-малко качество от обикновена 4-битова квантизация при същата памет.
Формат Бита Памет за 7B модел Кога BF16 (без квантизация) 16 ~14 GB само теглата машини с много памет (напр. 128 GB обща) AWQ 4-bit 4 ~4–5 GB видеокарти с малко памет; добро качество GPTQ 4-bit 4 ~4–5 GB алтернатива на AWQ FP8 8 ~7–8 GB нови видеокарти с хардуерна поддръжка на FP8 GGUF Q4_K_M 4 ~4–5 GB само Ollama / llama.cpp ⚠️Без общи проценти за качествоПроцентите „качество спрямо fp16“ и „по-бърз с 1,2–1,5ד от старата версия махнахме — не намерихме източник, който да ги потвърждава общо за всички модели. Паметта в таблицата е ориентировъчна. Качеството зависи от модела и задачата; сравнявай на своите данни.python · сваляне на готов AWQ модел и старт# Търси готови AWQ версии: huggingface.co/models?search=awq # Официални AWQ версии пускат напр. Qwen; за Llama 3.1 — hugging-quants from huggingface_hub import snapshot_download path = snapshot_download("Qwen/Qwen2.5-7B-Instruct-AWQ", local_dir="./models/qwen2.5-7b-awq") from vllm import LLM llm = LLM(model=path, dtype="float16", # AWQ ядрата работят с fp16 max_model_len=32768, gpu_memory_utilization=0.85, enable_chunked_prefill=True) # по-равно забавяне при дълги промптове # Същото от командния ред: # vllm serve ./models/qwen2.5-7b-awq --max-model-len 32768🔧Сам ли да квантизираш?Библиотеката AutoAWQ е изоставена; екипът на vLLM е поел функцията в llm-compressor. Ако няма готова AWQ версия на твоя модел, квантизирай с llm-compressor — но първо потърси готова. -
GB10 с 128 GB обща памет — стратегия
Машини като DGX Spark и ASUS Ascent GX10 имат 128 GB памет, обща за процесора и видеокартата. Това позволява големи модели без квантизация, но паметта е по-бавна от тази на големите сървърни видеокарти. За малък екип или пилотен курс често стига добре настроен Ollama; vLLM (от образа на NVIDIA) е следващата стъпка, когато едновременните потребители растат.
bash · Ollama за няколко паралелни заявки (systemd)sudo mkdir -p /etc/systemd/system/ollama.service.d/ sudo tee /etc/systemd/system/ollama.service.d/override.conf >/dev/null <<'EOF' [Service] Environment="OLLAMA_NUM_PARALLEL=4" Environment="OLLAMA_MAX_LOADED_MODELS=2" Environment="OLLAMA_KEEP_ALIVE=30m" Environment="OLLAMA_FLASH_ATTENTION=1" EOF sudo systemctl daemon-reload && sudo systemctl restart ollama ollama ps # кои модели са заредени # OLLAMA_NUM_PARALLEL паралелни заявки на модел (по подразбиране 1) # паметта расте с NUM_PARALLEL × дължината на контекста # OLLAMA_FLASH_ATTENTION Ollama го включва сам, където може; =1 го налагаpython · проверка: 8 паралелни заявкиimport requests, concurrent.futures, time URL = "http://localhost:11434/v1/chat/completions" # или :8000 за vLLM MODEL = "qwen2.5:7b" def one(i): t0 = time.time() requests.post(URL, json={"model": MODEL, "max_tokens": 50, "messages": [{"role": "user", "content": f"Въпрос {i}: какво е AI?"}]}) return time.time() - t0 t0 = time.time() with concurrent.futures.ThreadPoolExecutor(max_workers=8) as ex: r = list(ex.map(one, range(8))) print(f"общо {time.time()-t0:.1f}s · средно {sum(r)/len(r):.1f}s · последователно би било ~{sum(r):.1f}s")⛔Не пускай модела гол в интернетНито Ollama, нито vLLM по подразбиране искат парола. Пред тях сложи обратен прокси (напр. nginx) с ограничение на заявките и вход — или ги дръж само във вътрешната мрежа. Как се сглобява това — в следващата част.🎯Кога да минеш от Ollama на vLLMОстани на Ollama: разработка, тестове, малък екип, няколко едновременни заявки.
Мини на vLLM: много едновременни потребители, нужда от FP8/AWQ и метрики, договорени срокове за отговор. ⚠️ Точния праг измери на своя хардуер — старата версия даваше „8 потребители на 70B модел“, което не сме проверили.
04Проверка
1. RAG система с 30 едновременни потребители отговаря бавно на Ollama с настройки по подразбиране. Кой механизъм на vLLM ще помогне най-много?
2. Защо AWQ губи по-малко качество от обикновена 4-битова квантизация?
3. На какво дели PagedAttention KV кеша?
4. Искаш vLLM на ARM64 машина с GB10 (към 01.10.2026). Кой е най-сигурният път?
05Какво следва
06Източници
- vLLM — Quickstart — инсталация с uv,
vllm serve, OpenAI-съвместим сървър на порт 8000. - vLLM — инсталация за GPU — изисквания (Linux, Python 3.10–3.13, compute capability ≥ 7.5), aarch64.
- vllm в PyPI — версия 0.30.0 към 01.10.2026, пакети за x86_64 и aarch64.
- NVIDIA DGX Spark playbooks — vLLM — образът
nvcr.io/nvidia/vllmза GB10. - NVIDIA NGC — vLLM контейнер — актуалните етикети.
- Kwon и съавт. — Efficient Memory Management for LLM Serving with PagedAttention (SOSP 2023) — раздели 3 и 4 са ядрото.
- Блогът на vLLM (2023) — 24× спрямо HF Transformers, загуба под 4%.
- Lin и съавт. — AWQ: Activation-aware Weight Quantization.
- vLLM — квантизация (AWQ, FP8 и др.) · llm-compressor.
- Qwen2.5-7B-Instruct-AWQ · Llama 3.1 8B AWQ (hugging-quants) — готови AWQ модели.
- Ollama FAQ —
OLLAMA_NUM_PARALLEL,OLLAMA_MAX_LOADED_MODELS, Flash Attention.