ADR-023: Локальный LLM-стек — Qwen 2.5 14B на Windows + reverse SSH tunnel
Контекст
Для BR 6.3 (AI-агент через REST) нужен LLM с function-calling, который понимает русский язык. Бизнес-ограничение: не использовать платный Claude API (см. ADR-022).
Доступное железо для хостинга: домашняя машина Алексея на Windows 11 с AMD Ryzen 7 9800X3D + NVIDIA RTX 5070 (12 GB GDDR7 VRAM, Blackwell) + 32 GB RAM.
Сервисы ERP живут на VPS erp-test.nirbi.ru (Ubuntu 24.04). Им нужно обращаться к LLM из бэкенда.
Рассмотренные варианты
Модель
| Модель | VRAM Q5_K_M | Function calling | Русский | Вердикт |
|---|---|---|---|---|
| Qwen 2.5 14B Instruct | ~10 GB | ★★★★★ | ★★★★★ | ✅ выбран |
| Qwen 2.5 7B Instruct | ~6 GB | ★★★★ | ★★★★ | Fallback если 14B медленный |
| Mistral Nemo 12B | ~9 GB | ★★★★ | ★★★ | Альтернатива, русский слабее |
| Llama 3.3 70B / Mixtral 8x22B | 40-80 GB | ★★★★★ | ★★★★ | Не влезают в 12 GB |
| Vikhr-Nemo-12B (RU) | ~9 GB | ★★★ | ★★★★★ | FC слабее Qwen |
Inference engine
| Опция | Плюсы | Минусы |
|---|---|---|
| llama.cpp server (через ollama-фронт или прямо) | Установлено, работает, GGUF Q5_K_M | API не полностью OpenAI-compatible на tool_calls |
| vLLM в WSL2 | +30% скорости, FP8 на Blackwell | Сложнее setup, требует prebuilt wheel под CUDA 12.8 |
| TGI HuggingFace | Хорош для prod | Перебор для одной машины |
Транспорт VPS ↔ LLM-хост
| Опция | Тест | Результат |
|---|---|---|
| Tailscale (mesh VPN) | Установили, авторизовали | ⚠️ Direct path WSL→VPS работает, обратно (VPS→WSL) — handshake не пробивает Hyper-V symmetric NAT; DERP relay не помог |
Reverse SSH (autossh -R) | Установили, тест с inference | ✅ Стабильно, 22 tok/s, переживает heavy curl |
| Cloudflare Tunnel | Не пробовали | Альтернатива на будущее, нужен домен |
Решение
Стек:
- Модель —
qwen2.5:14b-instruct-q5_k_m(~10 GB GGUF) - Хост — Windows 11 → WSL2 Ubuntu 24.04 (systemd) → llama.cpp-server (через ollama-runtime), слушает
127.0.0.1:11434 - Транспорт —
autossh -M 0 -f -N -R 11434:localhost:11434 root@185.152.93.77(с WSL2 на VPS, через SSH key) - На VPS — Ollama-compatible API доступно на
http://localhost:11434(потребители обращаются напрямую; самописныйerp-llm-gatewayотменён 2026-05-13, см. ADR-022 rev 2)
Параметры:
OLLAMA_KEEP_ALIVE=24h— модель не выгружается из VRAMOLLAMA_NUM_PARALLEL=2— две параллельные генерации- autossh
ServerAliveInterval=20, ServerAliveCountMax=3— авто-восстановление туннеля
Измеренные метрики (2026-05-13):
- Cold load 14B Q5_K_M в VRAM: ~10 сек
- Steady-state generation: 20-22 tok/s (целевой диапазон 20-35)
- Function calling работает корректно — модель возвращает
<tool_call>{json}</tool_call>в content; парсинг — на стороне потребителя (OpenClaw framework, см. ADR-022 rev 2)
Последствия
Положительные
- ✅ 0 ₽ inference cost после железа
- ✅ Данные не покидают периметр (для production-сценария важно)
- ✅ 20-22 tok/s достаточно для интерактивного агента (короткий ответ 100-300 ток за 5-15 сек)
- ✅ Минимум зависимостей — нет публичных API, нет proxy-провайдеров
Отрицательные
- ❌ Зависимость от одной физической машины (домашняя Windows Алексея) — single point of failure
- ❌ После закрытия машины — деградация AI-функций ERP (admin agent падает)
- ❌ Network latency 100-200ms через autossh (домашний роутер → интернет → VPS)
- ❌ Не масштабируется горизонтально — одна машина = один параллельный пользователь LLM
Риски
- ⚠️ Windows reboot / отключение — autossh поднимется, но Ollama в WSL2 нет (зависит от systemd внутри WSL и автозапуска WSL). Митигация: PowerShell-task на boot, который делает
wsl -d Ubuntu -- systemctl restart ollama. - ⚠️ Пропускная способность апстрима — RTX 5070 выдаёт 22 tok/s одного потока. 2-3 параллельных запроса просядут до 8-12 tok/s каждый. Митигация: rate-limit на стороне потребителя, очередь.
- ⚠️ GGUF runtime version mismatch — обнаружен факт что версия
0.23.2(по/api/version) — это llama.cpp-fork, не стабильный Ollama (где было бы 0.5.x). Tool_calls приходят в content, не в structured поле. Митигация: парсер<tool_call>...</tool_call>блоков на стороне потребителя (OpenClaw).
Производственная миграция (после демо)
После 29.05.2026, если AI-функционал решено оставить:
- Перенести модель на выделенный GPU-сервер (vast.ai / immers.cloud / собственный сервер с 1× RTX 4090 24 GB) — больше VRAM для 32B моделей и параллельных пользователей
- Перейти на Cloudflare Tunnel или прямой ingress (TLS + аутентификация по token) вместо reverse SSH
- Возможно — fallback на платный API (Yandex GPT, GigaChat) для пиковых нагрузок
Ссылки
- ADR-022 — OpenClaw агент архитектура
- Windows LLM Host Setup — runbook для развёртывания
- BR 6.3 (active)