Перейти к содержимому
NEWЧат с 15 ИИ-моделями — попробуйте бесплатно / имейте совесть, когда будете делиться или копировать
>AISTUDY_

Модуль 4.2 · Урок 1

Локальная инфраструктура

45 мин
ПрактикаУстановка
Содержание
4.2 / Урок 1 из 3

Введение

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

  • Использовать разные модели для разных субагентов
  • Работать без интернета
  • Соблюдать конфиденциальность данных
  • Избежать зависимости от облачных провайдеров

Сценарий:

graph TD
    A[Пользователь] -->|Запрос| B[Python-агент]
    B -->|HTTP API| C[llama-server / Ollama / vLLM]
    C -->|Загрузка| D[Файл модели GGUF]
    D -->|Инференс| C
    C -->|Ответ| B
    B -->|Результат| A

    style A fill:#f8fafc,stroke:#4f46e5,stroke-width:2px
    style B fill:#f8fafc,stroke:#2563eb,stroke-width:2px
    style C fill:#f8fafc,stroke:#059669,stroke-width:2px
    style D fill:#f8fafc,stroke:#64748b,stroke-width:2px

Почему локальный деплой?

Основные преимущества

АспектОблакоЛокально
Стоимость$0.01-0.10 за 1K токеновОдин раз на оборудование
КонфиденциальностьДанные на серверах провайдераПолная изоляция
ИнтернетОбязателенНе требуется
КонтрольПривязаны к условиям APIПолная свобода
СкоростьСетевые задержкиМинимальные задержки

Особенности для России

  • Санкции: OpenAI, Claude API недоступны напрямую
  • Суверенитет данных: Чувствительные данные остаются на вашей машине
  • Надёжность: Не зависите от перебоев в интернете

llama.cpp и llama-server

Что это?

llama.cpp — высокоэффективная C++ реализация Llama для запуска моделей на CPU/GPU. llama-server — встроенный HTTP API.

Установка на Linux

# Клонируем репозиторий
git clone https://github.com/ggerganov/llama.cpp.git
cd llama.cpp

# Компилируем с поддержкой GPU (CUDA для NVIDIA)
mkdir build && cd build
cmake .. -DGGML_CUDA=ON
make -j$(nproc)

# Или без GPU (только CPU):
# cmake ..
# make -j$(nproc)

Скачивание модели в формате GGUF

Модели в формате GGUF оптимизированы для llama.cpp (меньший размер, быстрая загрузка).

# Скачиваем модель Qwen2.5-Coder (компактная, отличная для кодинга)
huggingface-cli download Qwen/Qwen2.5-Coder-7B-Instruct-GGUF \
  qwen2.5-coder-7b-instruct-q4_k_m.gguf --local-dir ./models

# Или используем curl:
curl -L -o models/qwen-7b.gguf \
  https://huggingface.co/Qwen/Qwen2.5-Coder-7B-Instruct-GGUF/resolve/main/qwen2.5-coder-7b-instruct-q4_k_m.gguf

Запуск llama-server с OpenAI-совместимым API

# Базовый запуск
cd ~/llama.cpp/build/bin
./llama-server -m ../models/qwen-7b.gguf \
  --port 8080 \
  --n-gpu-layers 33 \
  --ctx-size 4096

# Параметры:
# --port 8080           - порт для API
# --n-gpu-layers 33     - сколько слоёв отправить на GPU
# --ctx-size 4096       - размер контекста
# -ngl                  - alias для --n-gpu-layers

Тестирование API

Когда сервер запущен, можно делать запросы как к OpenAI:

# Обычное завершение текста
curl -X POST http://localhost:8080/v1/completions \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "Напиши функцию на Python для сортировки:",
    "max_tokens": 100,
    "temperature": 0.7
  }'

# Chat API (как ChatGPT)
curl -X POST http://localhost:8080/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "local",
    "messages": [
      {"role": "user", "content": "Привет! Что такое рекурсия?"}
    ],
    "temperature": 0.7,
    "max_tokens": 200
  }'

Горячая замена моделей

Преимущество llama-server — можно менять модели без перезагрузки сервера.

# Запускаем сервер с первой моделью
./llama-server -m models/qwen-7b.gguf --port 8080

# В другом терминале загружаем другую модель (она стоит в очереди)
# llama-server автоматически выгружает старую из памяти

# Или используем slot management (если включены слоты):
curl -X POST http://localhost:8080/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "models/mistral-7b.gguf",
    "messages": [{"role": "user", "content": "Hello"}],
    "max_tokens": 100
  }'

Оптимизация для разных железок

NVIDIA GPU (CUDA):

cmake .. -DGGML_CUDA=ON -DCUDA_ARCH=native
make -j$(nproc)

AMD GPU (ROCm):

cmake .. -DGGML_HIPBLAS=ON
make -j$(nproc)

Intel GPU:

cmake .. -DGGML_ONEAPI=ON
make -j$(nproc)

macOS (Apple Silicon):

cmake .. -DGGML_METAL=ON
make -j$(nproc)

Ollama — альтернатива

Что это?

Ollama — более удобный интерфейс для llama.cpp с встроенной системой управления моделями.

Установка

# На Linux
curl -fsSL https://ollama.com/install.sh | sh

# На macOS
# Скачать из https://ollama.com

# На Windows
# Скачать .exe

Базовое использование

# Скачать и запустить модель
ollama run qwen2.5-coder

# Ollama автоматически:
# 1. Скачает модель
# 2. Запустит локальный сервер (по умолчанию :11434)
# 3. Откроет интерактивный чат

# Запустить только сервер (без интерактива)
ollama serve

# Все модели хранятся в ~/.ollama/models/

API Ollama

# Chat API (совместимо с OpenAI)
curl -X POST http://localhost:11434/api/chat \
  -H "Content-Type: application/json" \
  -d '{
    "model": "qwen2.5-coder",
    "messages": [
      {"role": "user", "content": "Как работает рекурсия?"}
    ],
    "stream": false
  }'

# Обычное завершение (generate)
curl -X POST http://localhost:11434/api/generate \
  -H "Content-Type: application/json" \
  -d '{
    "model": "qwen2.5-coder",
    "prompt": "def fibonacci(n):",
    "stream": false
  }'

Modelfile — кастомные конфигурации

Создаём Modelfile для своего кастомного агента:

Modelfile -- кастомная конфигурация модели Ollama

dockerfile
Нажмите на строку — увидите объяснение

Запуск:

ollama create my-agent -f Modelfile
ollama run my-agent

Сравнение llama-server vs Ollama

Параметрllama.cppOllama
УстановкаНужно компилироватьОдин скрипт
APIOpenAI-совместимыйСобственный + OpenAI
Управление моделямиРучноеАвтоматическое
ПроизводительностьЧуть вышеНемного ниже
КастомизацияБольше контроляМеньше возможностей
Идеален дляProductionРазработка

vLLM — для production с высокой пропускной способностью

Особенности

  • PagedAttention — эффективная работа с памятью
  • LoRA поддержка — многие адаптеры одновременно
  • Batch обработка — сотни запросов в очереди
  • Streaming — ответы в реальном времени

Установка

pip install vllm

# С поддержкой CUDA
pip install vllm[cuda12]

Запуск сервера

python -m vllm.entrypoints.openai.api_server \
  --model qwen/Qwen2.5-7B-Instruct \
  --tensor-parallel-size 2 \
  --gpu-memory-utilization 0.9 \
  --port 8000

# Параметры:
# --tensor-parallel-size    - сколько GPU использовать
# --gpu-memory-utilization  - сколько памяти GPU занять (0-1)

API вызовы (совместимо с OpenAI)

curl -X POST http://localhost:8000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "qwen/Qwen2.5-7B-Instruct",
    "messages": [
      {"role": "user", "content": "Объясни асинхронность в Python"}
    ],
    "temperature": 0.7,
    "max_tokens": 150
  }'

Выбор модели для разных задач субагентов

Архитектура многоагентной системы

Главный агент (координатор)
+-- Классификатор задач (1-3B модель)
+-- Агент кодирования (7-13B модель)
+-- Агент анализа (30B модель)
+-- Агент планирования (7B модель)

Рекомендуемые модели и их параметры

Маленькие модели (1-3B) — Классификация, маршрутизация

Задачи:
- Определить тип запроса пользователя
- Выбрать нужного агента
- Быстрое предварительное заключение

Рекомендуемые:
- Qwen2.5-1.5B-Instruct-GGUF (~700MB)
- Phi-3-mini-4k-instruct (~2GB)
- TinyLlama-1.1B (~700MB)

Параметры запуска:
./llama-server -m models/qwen-1.5b.gguf \
  --port 8081 \
  --n-gpu-layers 20 \
  --ctx-size 2048

Средние модели (7-13B) — Кодирование, рассуждение

Задачи:
- Генерация кода
- Анализ текста
- Сложные рассуждения

Рекомендуемые:
- Qwen2.5-Coder-7B-Instruct-GGUF (~5GB)
- DeepSeek-V3 (~5GB)
- Llama-4-8B-Instruct (~6GB)
- Mistral-7B-Instruct-v0.3 (~5GB)

Параметры запуска:
./llama-server -m models/qwen-7b.gguf \
  --port 8082 \
  --n-gpu-layers 33 \
  --ctx-size 4096 \
  --batch-size 32

Большие модели (30-70B) — Сложные задачи

Задачи:
- Глубокий анализ
- Сложное планирование
- Многошаговые рассуждения
- Перевод и трансформация

Рекомендуемые:
- Qwen2.5-32B-Instruct-GGUF (~20GB)
- DeepSeek-V3-MoE (~40GB)
- Llama-4-70B-Instruct (~45GB)
- Mistral Large (~48GB)

Параметры запуска:
./llama-server -m models/qwen-32b.gguf \
  --port 8083 \
  --n-gpu-layers 60 \
  --ctx-size 8192 \
  --batch-size 16

Таблица выбора модели

ЗадачаМодельРазмерПортRAM
МаршрутизацияQwen2.5-1.5B700MB80812GB
КодированиеQwen2.5-Coder-7B5GB80828GB
АнализQwen2.5-7B-Instruct5GB80828GB
ПланированиеLlama-4-8B6GB80828GB
Сложное рассуждениеQwen2.5-32B20GB808324GB

Сетевая архитектура: Host + VM

Диаграмма

+-----------------------------------------------------+
|                    Хост-машина                       |
|                  (GPU, высокий CPU)                  |
|                                                      |
|  +----------------------------------------------+   |
|  |       llama-server (несколько портов)         |   |
|  |                                               |   |
|  |  :8081 -> Qwen2.5-1.5B (маршрутизация)       |   |
|  |  :8082 -> Qwen2.5-7B (основной агент)        |   |
|  |  :8083 -> Qwen2.5-32B (сложные задачи)       |   |
|  +----------------------------------------------+   |
+-----------------------------------------------------+
           ^
      HTTP API (localhost:808x)
           v
+-----------------------------------------------------+
|              VM (Ubuntu 22.04)                       |
|         (Агент, низкий CPU, без GPU)                 |
|                                                      |
|  +----------------------------------------------+   |
|  |   Python Агент (используется OpenAI SDK)      |   |
|  |                                               |   |
|  |   # Роутер: выбирает модель                   |   |
|  |   # Выполнение: отправляет в llama-server     |   |
|  |   # Результат: получает ответ                 |   |
|  +----------------------------------------------+   |
+-----------------------------------------------------+

Настройка SSH туннеля для сетевого доступа

Если VM нужна сетевая изоляция:

# На VM: подключаемся к хосту через SSH туннель
ssh -L 8081:localhost:8081 \
    -L 8082:localhost:8082 \
    -L 8083:localhost:8083 \
    user@host-machine

# Теперь на VM может использовать:
# http://localhost:8081  (как если бы это был localhost)

Docker Compose для мультимодельного сервиса

Создаём docker-compose.yml:

Docker Compose для мультимодельного сервиса

yaml
Нажмите на строку — увидите объяснение

Аналогично определяются сервисы agent (средняя модель на порту 8082) и reasoning (большая модель на порту 8083). Каждый сервис получает свой GPU через CUDA_VISIBLE_DEVICES и блок deploy.resources с резервацией GPU. Все сервисы объединены в сеть ai-agents.

Запуск:

docker compose up -d
docker compose ps

Nginx для маршрутизации моделей

Если нужен единый entry point с маршрутизацией:

graph LR
    Client[Клиент] -->|:8080| LB[Nginx<br/>балансировщик]
    LB -->|/v1/router/| S1[llama-server<br/>:8081<br/>Qwen 1.5B]
    LB -->|/v1/agent/| S2[llama-server<br/>:8082<br/>Qwen 7B]
    LB -->|/v1/reasoning/| S3[llama-server<br/>:8083<br/>Qwen 32B]

    style Client fill:#f8fafc,stroke:#1e293b,stroke-width:2px
    style LB fill:#f8fafc,stroke:#4f46e5,stroke-width:2px
    style S1 fill:#f8fafc,stroke:#059669,stroke-width:2px
    style S2 fill:#f8fafc,stroke:#2563eb,stroke-width:2px
    style S3 fill:#f8fafc,stroke:#dc2626,stroke-width:2px

Конфигурация nginx

nginx.conf:

upstream router {
    server localhost:8081;
}

upstream agent {
    server localhost:8082;
}

upstream reasoning {
    server localhost:8083;
}

server {
    listen 8080;
    server_name _;

    # Маршрутизатор — для определения типа задачи
    location /v1/router/ {
        proxy_pass http://router;
        proxy_set_header Content-Type application/json;
    }

    # Основной агент — для большинства задач
    location /v1/agent/ {
        proxy_pass http://agent;
        rewrite ^/v1/agent/(.*)$ /v1/$1 break;
    }

    # Сложные задачи
    location /v1/reasoning/ {
        proxy_pass http://reasoning;
        rewrite ^/v1/reasoning/(.*)$ /v1/$1 break;
    }

    # Дефолтный маршрут
    location /v1/completions {
        proxy_pass http://agent;
    }

    location /v1/chat/completions {
        proxy_pass http://agent;
    }
}

Запуск:

nginx -c /path/to/nginx.conf

Практический пример: Полная настройка

Структура проекта

ai-agents-local/
+-- docker-compose.yml         # Мультимодельный сервис
+-- nginx.conf                 # Маршрутизация
+-- agent.py                   # Главный агент
+-- subrouters/
|   +-- task_classifier.py    # Маршрутизатор задач
|   +-- code_agent.py         # Агент кодирования
|   +-- analysis_agent.py     # Агент анализа
+-- models/                    # GGUF модели (скачанные)
|   +-- qwen-1.5b.gguf
|   +-- qwen-7b.gguf
|   +-- qwen-32b.gguf
+-- README.md

Установка с нуля (Linux/macOS)

#!/bin/bash
# setup.sh

# 1. Клонируем llama.cpp
git clone https://github.com/ggerganov/llama.cpp.git
cd llama.cpp

# 2. Компилируем с GPU поддержкой
mkdir build && cd build
cmake .. -DGGML_CUDA=ON
make -j$(nproc)
cd ../..

# 3. Создаём директорию моделей
mkdir -p models

# 4. Скачиваем модели
echo "Загружаем модели (может занять время)..."
huggingface-cli download Qwen/Qwen2.5-1.5B-Instruct-GGUF \
  qwen2.5-1.5b-instruct-q4_k_m.gguf --local-dir ./models

huggingface-cli download Qwen/Qwen2.5-Coder-7B-Instruct-GGUF \
  qwen2.5-coder-7b-instruct-q4_k_m.gguf --local-dir ./models

huggingface-cli download Qwen/Qwen2.5-32B-Instruct-GGUF \
  qwen2.5-32b-instruct-q4_k_m.gguf --local-dir ./models

# 5. Запускаем сервисы
docker compose up -d

echo "Готово! Сервисы запущены на портах 8081-8083"

Запуск:

chmod +x setup.sh
./setup.sh

Агент с маршрутизацией моделей (Python)

agent.py:

import openai
import json
from typing import Literal

# Клиент openai v1.x для локального сервера
from openai import OpenAI

# Модели на разных портах
MODELS = {
    "router": "http://localhost:8081/v1",
    "agent": "http://localhost:8082/v1",
    "reasoning": "http://localhost:8083/v1",
}

def get_client(base_url: str) -> openai.OpenAI:
    """Создаём клиент для конкретного сервера."""
    return openai.OpenAI(api_key="sk-local", base_url=base_url)

def classify_task(user_input: str) -> Literal["routing", "coding", "reasoning"]:
    """
    Определяем тип задачи маленькой моделью.
    """
    client = get_client(MODELS["router"])
    response = client.chat.completions.create(
        model="local",
        messages=[
            {
                "role": "system",
                "content": """Определи тип задачи пользователя.

Ответь одним словом:
- routing: если нужно определить что-то простое
- coding: если нужно написать/изменить код
- reasoning: если нужны сложные рассуждения"""
            },
            {"role": "user", "content": user_input}
        ],
        temperature=0.1,
        max_tokens=10
    )

    task_type = response.choices[0].message.content.strip().lower()
    return task_type if task_type in ["routing", "coding", "reasoning"] else "routing"

def execute_agent(task_type: str, user_input: str) -> str:
    """
    Выполняем задачу нужной моделью.
    """
    if task_type == "coding":
        base_url = MODELS["agent"]
        system_prompt = "Ты эксперт по программированию. Пиши чистый, эффективный код."
    elif task_type == "reasoning":
        base_url = MODELS["reasoning"]
        system_prompt = "Ты логик и аналитик. Разбирай сложные концепции пошагово."
    else:
        base_url = MODELS["agent"]
        system_prompt = "Ты полезный ассистент."

    client = get_client(base_url)
    response = client.chat.completions.create(
        model="local",
        messages=[
            {"role": "system", "content": system_prompt},
            {"role": "user", "content": user_input}
        ],
        temperature=0.7,
        max_tokens=500
    )

    return response.choices[0].message.content

def main():
    """
    Главная функция агента.
    """
    user_input = input("Ваш запрос: ")

    print("\nАнализирую запрос...")
    task_type = classify_task(user_input)
    print(f"Тип задачи: {task_type}")

    print(f"\nВыполняю ({task_type})...")
    result = execute_agent(task_type, user_input)

    print(f"\nРезультат:\n{result}")

if __name__ == "__main__":
    main()

Запуск:

pip install openai
python agent.py

Пример использования:

Ваш запрос: Напиши функцию для сортировки массива

Анализирую запрос...
Тип задачи: coding

Выполняю (coding)...

Результат:
def sort_array(arr):
    """Быстрая сортировка O(n log n)"""
    if len(arr) <= 1:
        return arr
    pivot = arr[len(arr) // 2]
    left = [x for x in arr if x < pivot]
    middle = [x for x in arr if x == pivot]
    right = [x for x in arr if x > pivot]
    return sort_array(left) + middle + sort_array(right)

Мониторинг и отладка

# Проверка статуса сервисов
docker compose ps

# Логи конкретного сервиса
docker compose logs -f agent

# Проверка API напрямую
curl -X POST http://localhost:8082/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "local",
    "messages": [{"role": "user", "content": "Привет!"}]
  }' | jq .

# Использование памяти
nvidia-smi

# Скорость обработки (benchmark)
time curl -s http://localhost:8082/v1/chat/completions \
  -d '{"model":"local","messages":[{"role":"user","content":"test"}]}' | jq .

Оптимизация производительности

Параметры llama-server

# Для CPU (медленнее, но универсально):
./llama-server -m model.gguf \
  --port 8080 \
  --threads $(nproc)  # все ядра

# Для GPU (быстрее):
./llama-server -m model.gguf \
  --port 8080 \
  --n-gpu-layers 33   # больше слоёв на GPU
  --batch-size 64     # параллельная обработка

# Для низкой памяти:
./llama-server -m model.gguf \
  --port 8080 \
  --n-gpu-layers 10   # меньше на GPU
  --batch-size 8      # меньше параллелизма
  --ctx-size 1024     # меньше контекст

Квантизация моделей

Используйте GGUF модели с квантизацией:

graph LR
    FP16[FP16<br/>16 бит] --> Q8[INT8<br/>8 бит] --> Q4[INT4<br/>4 бита]

    FP16 -.->|Качество: максимум| Q_FP16[Память: 14 GB<br/>Скорость: низкая]
    Q8 -.->|Качество: высокое| Q_Q8[Память: 7 GB<br/>Скорость: средняя]
    Q4 -.->|Качество: хорошее| Q_Q4[Память: 4 GB<br/>Скорость: высокая]

    style FP16 fill:#fef2f2,stroke:#dc2626,stroke-width:2px
    style Q8 fill:#fefce8,stroke:#ca8a04,stroke-width:2px
    style Q4 fill:#f0fdf4,stroke:#059669,stroke-width:2px
    style Q_FP16 fill:#f8fafc,stroke:#e2e8f0,stroke-width:1px
    style Q_Q8 fill:#f8fafc,stroke:#e2e8f0,stroke-width:1px
    style Q_Q4 fill:#f8fafc,stroke:#e2e8f0,stroke-width:1px

Обозначения уровней квантизации:

УровеньБитыНазначение
q3_k_m~3Маленькие, быстрые, низкое качество
q4_k_m~4Баланс размера и качества
q5_k_m~5Хорошее качество
q8_0~8Почти оригинал
fp1616Оригинальная точность

Батчинг запросов

Отправляйте несколько запросов одновременно:

import concurrent.futures

def batch_requests(queries):
    client = openai.OpenAI(api_key="sk-local", base_url="http://localhost:8080/v1")
    with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor:
        futures = [
            executor.submit(client.chat.completions.create,
                model="local",
                messages=[{"role": "user", "content": q}]
            )
            for q in queries
        ]
        return [f.result() for f in futures]

# Использование
queries = ["Что такое AI?", "Как работает ML?", "Что такое агент?"]
results = batch_requests(queries)

Чек-лист для продакшена

  • GPU правильно настроена (nvidia-smi показывает активность)
  • Модели скачаны и в GGUF формате
  • llama-server/Ollama запущены на разных портах
  • Nginx маршрутизирует запросы корректно
  • Agent код обрабатывает ошибки сети
  • Логирование настроено (какие модели использовались)
  • Backup моделей сделан
  • Memory limits установлены в Docker
  • API кэширование включено (Redis опционально)
  • Мониторинг активирован (grafana/prometheus)

Заключение

Вы теперь знаете, как:

  1. Запустить локальные модели с llama.cpp/Ollama
  2. Менять модели для разных субагентов
  3. Организовать многомодельную инфраструктуру
  4. Маршрутизировать запросы через nginx
  5. Оптимизировать память и производительность

Следующий шаг: Интегрируйте это в вашу систему агентов из предыдущих уроков.

Полезные ссылки

Скачать урок

Есть идея или нашли ошибку?

// Обсуждение

Можно писать анонимно. Укажите email, чтобы получать уведомления об ответах.