Запуск моделей локально: Ollama, LM Studio, llama.cpp, vLLM, требования к железу
Ollama, LM Studio, llama.cpp, vLLM — выбор инструмента для локальной разработки и production
Локальный запуск LLM позволяет запускать языковые модели на собственном железе без зависимости от облачных API. Это даёт:
Инструменты:
| Инструмент | Назначение | Сложность |
|---|---|---|
| Ollama | Быстрый старт, CLI + API | Низкая |
| LM Studio | GUI для разработки | Низкая |
| llama.cpp | CPU inference, embedding | Средняя |
| vLLM | Production serving | Высокая |
Ollama — самый простой способ запустить LLM локально. Автоматически скачивает модели, предоставляет REST API и CLI.
# macOS
brew install ollama
# Linux
curl -fsSL https://ollama.ai/install.sh | sh
# Windows
# Скачать с https://ollama.ai/download# Запуск модели (автоматическая загрузка)
ollama run llama3.1
# Доступные команды в CLI
>>> /help # справка
>>> /set parameter temperature 0.8
>>> /set system Ты — помощник для Python разработчиков
>>> /bye # выход
# Список загруженных моделей
ollama list
# Удаление модели
ollama rm llama3.1
# Копирование модели
ollama cp llama3.1 my-llama3.1# Llama 3.1 (Meta)
ollama run llama3.1 # 8B
ollama run llama3.1:70b # 70B
# Mistral
ollama run mistral # 7B
ollama run mistral-nemo # 12B
# Gemma (Google)
ollama run gemma2 # 9B
ollama run gemma2:27b # 27B
# Code модели
ollama run codellama # Code Llama
ollama run deepseek-coder # DeepSeek Coder
# Специализированные
ollama run llava # Vision (image + text)
ollama run nomic-embed-text # Embeddingsimport ollama
# Простой запрос
response = ollama.chat(
model='llama3.1',
messages=[{
'role': 'user',
'content': 'Напиши функцию на Python для сортировки списка'
}]
)
print(response['message']['content'])
# Streaming
stream = ollama.chat(
model='llama3.1',
messages=[{'role': 'user', 'content': 'Расскажи о Python'}],
stream=True
)
for chunk in stream:
print(chunk['message']['content'], end='', flush=True)# Запуск сервера
ollama serve
# API доступен на http://localhost:11434import requests
# Генерация
response = requests.post(
'http://localhost:11434/api/generate',
json={
'model': 'llama3.1',
'prompt': 'Напиши hello world на Python',
'stream': False
}
)
print(response.json()['response'])
# Chat API (OpenAI-compatible)
response = requests.post(
'http://localhost:11434/api/chat',
json={
'model': 'llama3.1',
'messages': [
{'role': 'user', 'content': 'Привет!'}
],
'stream': False
}
)
print(response.json()['message']['content'])# Создание Modelfile
cat > Modelfile << EOF
FROM llama3.1
# System prompt
SYSTEM """
Ты — опытный Python разработчик.
Отвечай кратко, с примерами кода.
Используй best practices и type hints.
"""
# Параметры
PARAMETER temperature 0.2
PARAMETER top_p 0.9
# Embeddings для контекста
PARAMETER num_ctx 4096
EOF
# Создание кастомной модели
ollama create python-assistant -f Modelfile
# Использование
ollama run python-assistant "Как создать декоратор?"from langchain_community.chat_models import ChatOllama
from langchain_core.messages import HumanMessage
llm = ChatOllama(
model="llama3.1",
temperature=0.2,
base_url="http://localhost:11434"
)
response = llm.invoke([HumanMessage(content="Напиши функцию factorial")])
print(response.content)
# Streaming
for chunk in llm.stream([HumanMessage(content="Объясни OOP")]):
print(chunk.content, end='')LM Studio — GUI приложение для локальных LLM. Удобно для разработки и тестирования.
1. Скачать с https://lmstudio.ai/
2. Установить и запустить
3. Search models → выбрать модель (Llama, Mistral, etc.)
4. Выбрать quantization (Q4_K_M recommended)
5. Download
6. Chat interface готова
# LM Studio запускает локальный сервер
# http://localhost:1234/v1
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:1234/v1",
api_key="not-needed" # не требуется
)
response = client.chat.completions.create(
model="local-model", # имя загруженной модели
messages=[
{"role": "system", "content": "Ты — Python помощник"},
{"role": "user", "content": "Напиши quicksort"}
],
temperature=0.2
)
print(response.choices[0].message.content)llama.cpp — C++ реализация LLM inference с поддержкой GGUF. Максимальная эффективность на CPU.
git clone https://github.com/ggerganov/llama.cpp
cd llama.cpp
# macOS
make
# Linux с CUDA
make LLAMA_CUDA=1
# Windows (CMake)
cmake -B build
cmake --build build --config Releasepip install llama-cpp-pythonimport llama_cpp_python as llama
# Загрузка модели
llm = llama.Llama(
model_path="./models/llama-3.1-8b-Q4_K_M.gguf",
n_ctx=4096, # размер контекста
n_threads=8, # CPU threads
n_gpu_layers=0, # 0 = CPU, >0 = GPU offload
verbose=False
)
# Генерация
output = llm(
"Расскажи о Python",
max_tokens=256,
temperature=0.7,
top_p=0.9,
stop=["\n\n"]
)
print(output["choices"][0]["text"])
# Streaming
for chunk in llm(
"Напиши функцию",
max_tokens=512,
stream=True
):
print(chunk["choices"][0]["text"], end='')from llama_cpp import Llama
llm = Llama(
model_path="./models/llama-3.1-8b-Q4_K_M.gguf",
n_ctx=4096
)
# Создание chat completion
output = llm.create_chat_completion(
messages=[
{"role": "system", "content": "Ты — Python разработчик"},
{"role": "user", "content": "Напиши decorator для кэширования"}
],
max_tokens=512,
temperature=0.2
)
print(output["choices"][0]["message"]["content"])# Частичный offload на GPU
llm = llama.Llama(
model_path="./models/llama-3.1-8b-Q4_K_M.gguf",
n_gpu_layers=35, # количество слоёв на GPU
n_ctx=4096
)
# Полный offload (если помещается)
llm = llama.Llama(
model_path="./models/llama-3.1-8b-Q4_K_M.gguf",
n_gpu_layers=-1, # все слои на GPU
main_gpu=0 # индекс GPU
)# Базовая генерация
./main -m models/llama-3.1-8b-Q4_K_M.gguf \
-p "Расскажи о Python" \
-n 256 \
--temp 0.7
# Chat режим
./main -m models/llama-3.1-8b-Q4_K_M.gguf \
-i \
--color
# С системным промптом
./main -m models/llama-3.1-8b-Q4_K_M.gguf \
--system-prompt "Ты — Python разработчик" \
-p "Напиши функцию" \
-n 512
# Batch processing
./main -m models/llama-3.1-8b-Q4_K_M.gguf \
-f prompts.txt \
-n 256 \
-o output.txtvLLM — оптимизированный serving engine для production. В 2-4x быстрее HuggingFace Transformers.
pip install vllm# Запуск сервера
python -m vllm.entrypoints.api_server \
--model meta-llama/Llama-2-7b-chat-hf \
--port 8000 \
--host 0.0.0.0
# С квантованием
python -m vllm.entrypoints.api_server \
--model mistralai/Mistral-7B-Instruct-v0.2 \
--quantization awq \
--port 8000
# С несколькими GPU
python -m vllm.entrypoints.api_server \
--model meta-llama/Llama-2-70b-chat-hf \
--tensor-parallel-size 4 \
--port 8000from vllm import LLM, SamplingParams
# Инициализация
llm = LLM(
model="meta-llama/Llama-2-7b-chat-hf",
gpu_memory_utilization=0.9,
tensor_parallel_size=1
)
# Параметры генерации
sampling_params = SamplingParams(
temperature=0.7,
top_p=0.9,
max_tokens=256,
stop=["\n\n"]
)
# Генерация
prompts = [
"Напиши функцию на Python",
"Объясни что такое OOP",
"Как работать с asyncio?"
]
outputs = llm.generate(prompts, sampling_params)
for output in outputs:
print(f"Prompt: {output.prompt}")
print(f"Generated: {output.outputs[0].text}\n")from openai import OpenAI
# vLLM предоставляет совместимый API
client = OpenAI(
base_url="http://localhost:8000/v1",
api_key="not-needed"
)
response = client.chat.completions.create(
model="meta-llama/Llama-2-7b-chat-hf",
messages=[
{"role": "user", "content": "Привет!"}
]
)
print(response.choices[0].message.content)
# Streaming
stream = client.chat.completions.create(
model="meta-llama/Llama-2-7b-chat-hf",
messages=[{"role": "user", "content": "Расскажи о Python"}],
stream=True
)
for chunk in stream:
if chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end='')vLLM использует PagedAttention и continuous batching:
Traditional batching:
Request A: [████████████████]
Request B: [ ] ← ждёт завершения A
Request C: [ ] ← ждёт завершения A
vLLM continuous batching:
Request A: [██][██][██][██]
Request B: [██][██][██][██] ← добавляется dynamically
Request C: [██][██][██][██] ← добавляется dynamically
| Модель | FP16 | GGUF Q4_K_M | GPU (GPTQ 4-bit) |
|---|---|---|---|
| 7B | 14GB | 4.2GB | 6GB |
| 13B | 26GB | 8GB | 10GB |
| 34B | 68GB | 20GB | 24GB |
| 70B | 140GB | 40GB | 48GB |
Для разработки (7-13B модели):
Для production (13-34B модели):
Для больших моделей (70B+):
import torch
def check_gpu_compatibility():
if not torch.cuda.is_available():
print("CUDA не доступен — только CPU inference")
return
gpu_name = torch.cuda.get_device_name(0)
gpu_memory = torch.cuda.get_device_properties(0).total_memory / 1024**3
print(f"GPU: {gpu_name}")
print(f"VRAM: {gpu_memory:.1f}GB")
# Рекомендуемые модели
if gpu_memory >= 40:
print("Можно запускать: 7B, 13B, 34B (Q4), 70B (Q2)")
elif gpu_memory >= 24:
print("Можно запускать: 7B, 13B (Q4), 34B (Q2)")
elif gpu_memory >= 12:
print("Можно запускать: 7B, 13B (Q3)")
elif gpu_memory >= 8:
print("Можно запускать: 7B (Q4)")
else:
print("Мало VRAM — используйте CPU с GGUF или облачные API")
check_gpu_compatibility()from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
import ollama
from typing import List, Optional
app = FastAPI()
class ChatRequest(BaseModel):
message: str
model: str = "llama3.1"
temperature: float = 0.7
max_tokens: int = 512
conversation_history: List[dict] = []
class ChatResponse(BaseModel):
response: str
model: str
tokens_per_second: Optional[float] = None
@app.post("/chat", response_model=ChatResponse)
async def chat(request: ChatRequest):
import time
start = time.time()
messages = request.conversation_history + [
{"role": "user", "content": request.message}
]
try:
response = ollama.chat(
model=request.model,
messages=messages,
options={
"temperature": request.temperature,
"num_predict": request.max_tokens
}
)
elapsed = time.time() - start
tokens = len(response["message"]["content"].split())
return ChatResponse(
response=response["message"]["content"],
model=request.model,
tokens_per_second=tokens / elapsed if elapsed > 0 else None
)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))from fastapi import FastAPI
from pydantic import BaseModel
import llama_cpp_python as llama
from typing import Optional
app = FastAPI()
# Глобальная модель (lazy loading)
_llm: Optional[llama.Llama] = None
def get_llm() -> llama.Llama:
global _llm
if _llm is None:
_llm = llama.Llama(
model_path="./models/llama-3.1-8b-Q4_K_M.gguf",
n_ctx=4096,
n_threads=8
)
return _llm
class GenerationRequest(BaseModel):
prompt: str
max_tokens: int = 256
temperature: float = 0.7
top_p: float = 0.9
class GenerationResponse(BaseModel):
text: str
tokens_per_second: float
@app.post("/generate", response_model=GenerationResponse)
async def generate(request: GenerationRequest):
import time
start = time.time()
llm = get_llm()
output = llm(
request.prompt,
max_tokens=request.max_tokens,
temperature=request.temperature,
top_p=request.top_p
)
elapsed = time.time() - start
text = output["choices"][0]["text"]
tokens = len(text.split())
return GenerationResponse(
text=text,
tokens_per_second=tokens / elapsed if elapsed > 0 else 0
)
@app.on_event("startup")
async def startup_event():
# Preload модель при старте
get_llm()from fastapi import FastAPI
from fastapi.responses import StreamingResponse
import ollama
import json
app = FastAPI()
class StreamRequest(BaseModel):
message: str
model: str = "llama3.1"
@app.post("/chat/stream")
async def chat_stream(request: StreamRequest):
async def generate():
stream = ollama.chat(
model=request.model,
messages=[{"role": "user", "content": request.message}],
stream=True
)
for chunk in stream:
content = chunk["message"]["content"]
yield f"data: {json.dumps({'content': content})}\n\n"
yield "data: [DONE]\n\n"
return StreamingResponse(
generate(),
media_type="text/event-stream"
)Нужен ли production serving с высокой throughput?
├─ Да → vLLM
│
└─ Нет → Для чего?
├─ Быстрый старт / разработка → Ollama
├─ GUI для тестирования → LM Studio
├─ CPU inference / embeddings → llama.cpp
└─ Custom integration → llama.cpp Python
| Инструмент | CPU | GPU | Streaming | API | Сложность |
|---|---|---|---|---|---|
| Ollama | ✅ | ✅ | ✅ | ✅ | Низкая |
| LM Studio | ✅ | ✅ | ✅ | ✅ | Низкая |
| llama.cpp | ✅ | ✅ | ✅ | ❌ | Средняя |
| vLLM | ❌ | ✅ | ✅ | ✅ | Высокая |
Вы изучили основные инструменты для локального запуска LLM:
В следующей теме вы изучите Оценку LLM — метрики качества моделей.
Вопросы ещё не добавлены
Вопросы для этой подтемы ещё не добавлены.