docker compose определяет .env для подстановки ${VAR} по каталогу
compose-файла (deploy/), а не по текущей директории — repo-root .env,
который использует install.sh и вся документация, молча не подхватывался.
Это и была причина "WARN: LIVEKIT_API_KEY not set" на боевом сервере:
секреты были в .env, но compose их не видел и подставлял небезопасные
дефолты (см. таблицу разведки сервера).
Теперь ставшие обязательными ${VAR:?} в docker-compose.yml (см. предыдущий
коммит) без этого немедленно проваливали бы конфиг на любой из
документированных команд. Добавлен `--env-file .env`/"$ENV_FILE" ко всем
вызовам docker compose в install.sh и в командах из README/docs.
install.sh дополнительно: ensure_default (аналог ensure_secret без генерации
секрета) для новых не-секретных параметров nginx/coturn/livekit
(NGINX_SERVER_NAMES, NGINX_CERT_NAME, LIVEKIT_USE_EXTERNAL_IP,
LIVEKIT_NODE_IP, TURN_EXTERNAL_IP) — дефолты только для локальной
разработки, не перезаписывают значения, заданные вручную на боевом
сервере. Плюс вызов deploy/render-templates.sh перед сборкой/подъёмом
стека.
122 lines
7.7 KiB
Markdown
122 lines
7.7 KiB
Markdown
# Настройка локального LLM-сервера (Qwen3.5, профили compose `llm`/`llm-gpu`)
|
||
|
||
Плагин суммаризации `qwen_local` (`backend/core/plugins/qwen_local.py`)
|
||
обращается к OpenAI-совместимому серверу `llama.cpp`. Модель и параметры —
|
||
единый источник истины ADR-004 (`docs/architecture/adr/004-ai-tier-matrix.md`)
|
||
и константная матрица `backend/services/ai_tiers.py`. В обычной установке всё
|
||
описанное ниже делает `install.sh` (см. `docs/deploy/dev-setup.md`) — этот
|
||
раздел актуален для ручного/точечного запуска профиля без инсталлятора.
|
||
|
||
## 1. Компоненты
|
||
|
||
| Сервис | Образ | Профиль | Назначение |
|
||
|---|---|---|---|
|
||
| `llm-models-init` | `busybox` | `llm`/`llm-gpu` | фиксирует владельца тома `llm-models` (обходит дефект прав доступа) |
|
||
| `llm-model-init` | `curlimages/curl` | `llm`/`llm-gpu` | однократное скачивание GGUF-модели и tokenizer.json уровня AI |
|
||
| `llm` | `ghcr.io/ggml-org/llama.cpp:server-b<build>` | `llm` | CPU-инференс (`/v1/chat/completions`), уровни `min`/`medium` |
|
||
| `llm-gpu` | `ghcr.io/ggml-org/llama.cpp:server-cuda-b<build>` | `llm-gpu` | GPU-инференс, уровень `max` (ADR-004: GPU обязателен) |
|
||
|
||
Файлы:
|
||
- `deploy/llm/download-model.sh` — генерализованный скрипт скачивания
|
||
(параметризован `LLM_MODEL_FILE`/`LLM_MODEL_URL`/`LLM_MODEL_MIN_SIZE`/
|
||
`LLM_TOKENIZER_FILE`/`LLM_TOKENIZER_URL`; их пишет `install.sh` по
|
||
выбранному пресету — см. `install.sh --help`).
|
||
- `deploy/docker-compose.yml` — сервисы `llm-models-init`/`llm-model-init`/
|
||
`llm`/`llm-gpu`, volume `llm-models`, монтирование
|
||
`llm-models:/models/qwen:ro` в сервис `worker` (там же считаются токены
|
||
чанкером — `QwenTokenCounter`).
|
||
|
||
## 2. Модели по уровням AI (ADR-004)
|
||
|
||
| Уровень | Модель | Квант | Файл (`LLM_MODEL_FILE`) | Источник GGUF (`LLM_MODEL_URL`) |
|
||
|---|---|---|---|---|
|
||
| `min` (пресет 3) | Qwen3.5-4B-Instruct | Q4_K_M ≈ 2,6 ГиБ | `qwen3.5-4b-instruct-q4_k_m.gguf` | `unsloth/Qwen3.5-4B-GGUF` |
|
||
| `medium` (пресет 4) | Qwen3.5-9B-Instruct | Q4_K_M ≈ 5,3 ГиБ | `qwen3.5-9b-instruct-q4_k_m.gguf` | `unsloth/Qwen3.5-9B-GGUF` |
|
||
| `max` (пресет 5) | Qwen3.5-35B-A3B-Instruct (MoE) | Q4_K_M ≈ 20,5 ГиБ | `qwen3.5-35b-a3b-instruct-q4_k_m.gguf` | `unsloth/Qwen3.5-35B-A3B-GGUF` |
|
||
|
||
`tokenizer.json` (переименован под `LLM_TOKENIZER_FILE` — нужен
|
||
`QwenTokenCounter`, `backend/core/summarization/tokens.py`) берётся из
|
||
официальных репозиториев `Qwen/Qwen3.5-<размер>` (публичные, без gate;
|
||
GGUF-репозитории `Qwen/…-GGUF` — gated, поэтому источник GGUF — публичное
|
||
зеркало `unsloth/…-GGUF`, файлы идентичны по содержимому квантизации).
|
||
|
||
Имена файлов на диске (`LLM_MODEL_FILE`/`LLM_TOKENIZER_FILE`) — КОНТРАКТ с
|
||
детектом доступности уровня AI (`backend/services/ai_levels.py` через
|
||
`backend/services/ai_tiers.py::TIERS[level].model_files`): именно эти пути
|
||
проверяются на «модель скачана» в админке.
|
||
|
||
Скачивание идемпотентно (проверка по наличию и минимальному размеру файла,
|
||
`LLM_MODEL_MIN_SIZE`) — повторный запуск на уже заполненном томе ничего не
|
||
перекачивает. Ручной запуск (прогреть volume заранее):
|
||
|
||
```bash
|
||
LLM_MODEL_FILE=qwen3.5-4b-instruct-q4_k_m.gguf \
|
||
LLM_MODEL_URL=https://huggingface.co/unsloth/Qwen3.5-4B-GGUF/resolve/main/Qwen3.5-4B-Q4_K_M.gguf \
|
||
LLM_TOKENIZER_FILE=qwen3.5-4b-instruct.tokenizer.json \
|
||
LLM_TOKENIZER_URL=https://huggingface.co/Qwen/Qwen3.5-4B/resolve/main/tokenizer.json \
|
||
docker compose -f deploy/docker-compose.yml --env-file .env --profile llm run --rm llm-model-init
|
||
```
|
||
|
||
## 3. Запуск профиля
|
||
|
||
CPU (уровни `min`/`medium`, пресеты 3/4):
|
||
|
||
```bash
|
||
docker compose -f deploy/docker-compose.yml --env-file .env \
|
||
--profile media --profile transcribe --profile llm up -d
|
||
```
|
||
|
||
GPU (уровень `max`, пресет 5 — GPU обязателен):
|
||
|
||
```bash
|
||
docker compose -f deploy/docker-compose.yml --env-file .env \
|
||
--profile media --profile transcribe-gpu --profile llm-gpu up -d
|
||
```
|
||
|
||
Проверка готовности (порт `8080` — `llm`, `8081` — `llm-gpu` на хосте;
|
||
внутри docker-сети оба доступны как `llm:8080`/`llm-gpu:8080`, `llm-gpu`
|
||
также отвечает под алиасом `llm` — см. комментарий в `deploy/monitoring/prometheus.yml`):
|
||
|
||
```bash
|
||
curl http://localhost:8080/health
|
||
# пока модель грузится: {"error":{"code":503,"message":"Loading model",...}}
|
||
# сервер готов: {"status":"ok"}
|
||
```
|
||
|
||
## 4. Включение провайдера `qwen_local`
|
||
|
||
Дефолт репозитория в `config/plugins.yaml` — `summarizer.provider: "null"`
|
||
(безопасно для dev-окружений без LLM-сервера). Уровень `min` включается по
|
||
образцу закомментированного примера в `config/plugins.yaml`; уровни
|
||
`medium`/`max` собираются автоматически из `TIERS` (`backend/services/ai_tiers.py`)
|
||
при выборе уровня AI в админке — руками их прописывать не нужно (см.
|
||
`backend/services/instance_settings.py::_apply_tier_overrides`).
|
||
|
||
После правки `config/plugins.yaml` (уровень `min`, ручной dev-сценарий)
|
||
перезапустить `worker`:
|
||
|
||
```bash
|
||
docker compose -f deploy/docker-compose.yml --env-file .env up -d --force-recreate worker
|
||
```
|
||
|
||
## 5. Проверка вручную
|
||
|
||
1. Поднять профиль `llm`/`llm-gpu`, дождаться `docker compose ps` → healthy.
|
||
2. `curl http://localhost:8080/health` → `{"status":"ok"}`.
|
||
3. Включить уровень AI в админке (или `qwen_local` в `config/plugins.yaml`
|
||
для ручного dev-сценария уровня `min`).
|
||
4. Прогнать пайплайн на тестовом сеансе (см. `docs/plugins/summarizer.md`)
|
||
и убедиться, что `conference_sessions.summary_data` заполняется.
|
||
|
||
## 6. Ресурсы, thinking-режим и мониторинг
|
||
|
||
- Требования CPU/RAM/GPU по пресетам — `docs/architecture/adr/004-ai-tier-matrix.md`.
|
||
- `LLAMA_ARG_CTX_SIZE=16384` — с запасом на чанк до 8000 токенов + промпт +
|
||
вывод (per-tier `max_tokens_map`/`max_tokens_reduce`, ADR-004).
|
||
- Thinking-режим (семейство Qwen3.5) отключается флагом `LLAMA_ARG_REASONING=off`
|
||
(актуальная замена `--chat-template-kwargs '{"enable_thinking":false}'` из
|
||
ADR-004 — та же семантика, но без деприкейшен-варнинга в логе на каждый
|
||
запуск, проверено через find-docs по `common/arg.cpp` проекта llama.cpp).
|
||
- `/metrics` (`LLAMA_ARG_ENDPOINT_METRICS=1`) собирает Prometheus, профиль
|
||
compose `monitoring` — `deploy/monitoring/prometheus.yml`, job `llm`.
|