Я работаю по ИП, поэтому не только пишу код, но и поддерживаю как DevOps свои проекты у заказчика. Эта история началась банально: я собирался в отпуск и хотел оптимизировать часть процессов, которые в повседневной жизни занимают время — чтобы не дёргать клиентов из-за вопросов по ошибкам, которые я мог не увидеть во время отдыха. Пусть локальная моделька сама разгребает типовое. Думал: запущу OpenClaw, подключу к локальной модели — и поеду спокойно
Через две недели — вместо отпуска — я сидел и обучал Qwen3 на Nvidia RTX 3090 24GB, потому что ни одно из существующих решений у меня не работало. Дальше расскажу, как до этого докатился — с цифрами, граблями и бенчмарками.
Точка входа: OpenClaw и боль с локальными моделями
Поставил OpenClaw — open-source агентный фреймворк. Если вы не знакомы с этим чудом, то простыми словами это прослойка между tool calling модели и скриптами (скиллами), которые может вызывать ваш ИИ. Плюс удобный планировщик типа cron — может по расписанию слать промт.
У меня несколько ПК, прокинул его на один с RTX 4080 16GB + 64GB DDR5 + Core i9. Задумка была такая: модель должна по расписанию через OpenClaw соединиться по SSH с удалённым сервером, пролистнуть docker ps, и если есть контейнеры, которые перезагружаются — взять оттуда логи, проанализировать и прислать в Телеграм с обоснованием «что это».
Я как и многие разрабы ведусь на хайп, читаю статьи, исследования. Это меня и подвело. В статьях — красивые цифры и «годнота», в реальности — мир влажных фантазий. Что произошло: я прогнал кучу моделей, начиная со старых gpt-oss до последнего qwen3.6, который мог себе позволить на ollama даже с очень низкой скоростью. И тут начало вылезать неприятное. Половина моих реальных кейсов локальные модели просто не вытягивали: tool calling через раз, галлюцинации в ответах, обрезанный output, путаница с аргументами функций.
В итоге картина простая: без серьёзных платных ИИ — а именно codex от OpenAI, который предлагается из коробки по oAuth, ну и Антропик (очень дорогой через консоль) — остальное мусор. Некая замануха: ты пробуешь локальные модельки для OpenClaw, обжигаешься, потом не страдаешь фигнёй и переезжаешь на платный. Что в свою очередь рождает массу сомнений — VPN (не только с нашей стороны), безопасность данных (а если я работаю с паспортными данными или ещё с чем). В итоге решил запилить сам.
Шаг 1: бенчмарк 16 локальных моделей
Слово «САМ» — оно очень смелое, потому что самому запилить с нуля модель типа Qwen — задача неподъёмная, поэтому я решил не придумывать велосипед, а взять готовый вариант и дообучить его до нужного состояния. Прежде чем пилить дообучение, надо понять, что вообще существует из локальных моделей и какая из них реально работает на моих задачах. Делаю бенчмарк из трёх реальных кейсов и замера скорости:
Code Review — даю заведомо багованный Java-код Jitsi-клиента (SQL injection, сравнение строк через
==, небезопасный Random, HTTP вместо HTTPS, ресурс-leak, пустой catch). Модель должна найти все баги и предложить fix. 0–10 баллов.Финансовая аналитика — план погашения кредитов методом лавины с помесячной разбивкой на 12 месяцев. Проверяет математику и следование инструкциям. 0–10 баллов.
HTML страница — генерация визуального таймлайна погашения 8 кредитов. Проверяет вёрстку, адаптивный дизайн, обработку данных. 0–10 баллов.
Бонус за скорость 0–5 баллов: ≥100 t/s = 5, ≥60 = 4, ≥30 = 3, ≥15 = 2, >0 = 1.
Стенд: RTX 4080 16GB, 64GB DDR5, Core i9, Ollama 0.20.7. Прогнал все актуальные модели, которые помещаются:
# | Модель | Код | HTML | Кредит | Скорость | ИТОГО | t/s | Tool calling |
|---|---|---|---|---|---|---|---|---|
1 | 🥇 gemma4:latest | 10 | 10 | 9 | 5 | 34 | 108 | ✅ |
2 | 🥈 phi4-mini:latest | 10 | 10 | 8 | 5 | 33 | 208 | ❌ |
3 | 🥉 deepseek-r1-tool-calling:14b | 10 | 10 | 9 | 4 | 33 | 61 | ❌ |
4 | qwen3:8b | 9 | 10 | 8 | 5 | 32 | 102 | ✅ |
5 | deepseek-r1:14b | 10 | 9 | 9 | 4 | 32 | 65 | ❌ |
6 | phi4:latest | 8 | 10 | 9 | 4 | 31 | 67 | ❌ |
7 | lfm2:latest | 8 | 10 | 9 | 4 | 31 | 70 | ❌ |
8 | glm-4.7-flash:latest | 9 | 10 | 9 | 2 | 30 | 20 | ✅ |
9 | devstral:latest | 8 | 10 | 9 | 1 | 28 | 14 | ✅ |
10 | qwen3:30b-a3b | 9 | 9 | 7 | 2 | 27 | 25 | ✅ |
11 | mistral-small:latest | 8 | 9 | 9 | 1 | 27 | 14 | ✅ |
12 | nemotron-3-nano:latest | 8 | 10 | 7 | 2 | 27 | 21 | ✅ |
13 | gpt-oss:latest | 9 | 10 | 2 | 5 | 26 | 123 | ✅ |
14 | qwen3:32b | timeout | 1 | 1 | 5 | ❌ | ||
Но не всё так гладко. Для агентной работы (а я хотел именно агента, не просто chat) обязательно нужен tool calling. Из 14 протестированных это умели только 7. И из них в категории «10/10 на коде + быстрая» — только gemma4.
Шаг 2: первая попытка дообучения, и почему 4080 16GB — мало
Решил: беру gemma4:latest как базу, дообучаю QLoRA-лорой на своих DevOps-кейсах, получаю свой домашний AI-ассистент.
На бумаге норм. На практике — не помещается в 16GB VRAM 4080. QLoRA с активациями + градиентами в 4-битном квантовании требует ~22–24 GB. Раздать на CPU offload — обучение шло бы неделями, с потерей всего смысла «локальное дообучение». Простыми словами: запустить уже обученную LLM — она отлично работает и помещается в 16GB, а вот дообучать и формировать адаптеры — места уже не хватает.
Опустился на qwen3:8b. 8B модель реально влезает в 4080, обучение проходит за ночь. Прогнал свой первый датасет (~1000 hand-crafted DevOps-трейсов), получил oni:v3. Запустил.
И вот тут второе расстройство: 8B мало для агентной работы. На простых задачах работает, на multi-step типа «отрефактори этот docker-compose с учётом всех 12 требований» — теряет нить. Не хватает параметров, чтобы держать контекст и принимать сложные решения.
Я мог:
Смириться с 8B и считать «лучше что есть».
Снять VM с большим объёмом памяти на GPU, дообучить нормальную модель, потом смержить и запустить на 4080, как я и хотел.
Купить ещё одну видеокарту.
Выбрал вариант 2.
Шаг 3: VM с RTX 3090 и обучение Qwen3-14B
Снял на ночь VM с RTX 3090 24GB. Снова сделал бенчмарк, добавив простой тест на создание файла на сервере. Тут уже выиграл ранее пропущенный мной Qwen3-14B. На 24 гигах помещается с QLoRA r=64 — хороший баланс «достаточно большая, чтобы тянуть агентные задачи» + «обучение проходит за разумное время».
Попросил Claude запилить сиды на дырявые моменты в шагах тестирования. Перетащил датасет, запустил обучение. Через ~6 часов получил oni:v6. Стало заметно лучше: tool calling стабильный, контекст держится, multi-step задачи решает.
Понимая, что OpenClaw уже может что-то типа «закинь мне с frontend-контейнера последние логи с ошибками за 3 часа», я решил отказаться от достойного по цене intelion.cloud VM (мы с ним прожили 4 дня за 44 рубля в час, дешевле и качественнее не нашёл) и купил себе домой с авито старенький Dell с 3090. Теперь обучение полностью дома, никаких VM, никаких облачных зависимостей. Точно соответствует моему изначальному принципу: ничего не должно зависеть от VPN или зарубежной инфраструктуры.
Шаг 4: качество датасета — главное, что я недооценивал
На oni:v6 я провёл сравнение head-to-head с базовым qwen3.6:27b (более крупная свежая модель из коробки) на конкретной задаче — multi-file scaffold TODO web app: backend Django REST, frontend Vue+Vite, docker-compose, README. Промт был такой: «Создай мне приложение: TODO на фронте Vue, бэк на Django, и подними мне на локалке в docker-compose».
Получилось интересно:
qwen3.6:27b (cloud) | oni:v6 (local 4080) | |
|---|---|---|
Время | 3 мин 54 с | 49 с |
Шагов агента | 21 (max=20, упёрся) | 19 |
Файлов создано | 15 (но docker-compose.yml ✗) | 14 (compose.yml ✓) |
Честность отчёта | ❌ сфабриковал docker-compose.yml | ✅ отчёт совпадает с диском |
И вот это уже серьёзно. qwen3.6:27b в финальном отчёте написал «успешно создал TODO web app» и привёл содержимое docker-compose.yml — красивый YAML, четыре строки описания сервисов, volumes, ports. А файла на диске нет. Вызова write_file в логе агента не было. Модель в финальном ответе сфабриковала результат, которого не существует.
Это так называемый «phantom final answer» — когда модель описывает то, чего не делала. В продакшене это убивает доверие: ты думаешь «всё готово», идёшь в папку, делаешь docker compose up — и получаешь «no such file». Если ошибку видно сразу — её можно починить. А если ассистент врёт, что её нет — починить нельзя.
Мой oni:v6 наоборот: знал Django хуже (забыл cors_headers, settings.py был куцый), но не врал про результаты. Что записал — про то и отчитался. Плюс в 4.8 раза быстрее на 4080, чем 27B на 3090 через тоннель.
Когда я давал тест, я не ждал от своей модели, что она запилит хоть что-то — мне просто нужен был честный ответ или решение на уровне песочницы. Я и до этого видел, как модельки на локалке при разработке простого приложения путаются в роутинг-файлах. То есть тест был не про «вот посмотрите, модель 14b побеждает 27b» — это бред — а про разницу в подходе. qwen3.6:27b в это сравнение попал как рекомендация от друга-ИИшника со словами «там tool-calling проработан лучше».
Так вот, для агента честность — главное. Для LLM-чата — наоборот, 27B будет полным, развёрнутым. Но для self-running агента, который реально выполняет задачи и должен честно отчитываться — фантюненный 14B работает лучше базового 27B.
Шаг 5: текущее состояние — base-7.v2
Ниже я вложил плод моих страданий. Повёлся на полный набор сидов от Magicoder, которые лежат на HF — там много обучающих датасетов, но они общие и ломают логику маленького агента. Поэтому следующую статью я хотел написать как раз про дистилляцию данных с общих ресурсов под агента — это отдельная тема.
За неделю мой датасет вырос:
180 hand-written seed templates в YAML (типовые DevOps-задачи)
14 generation templates (превращают seed → multi-turn agent trace)
45 hand-curated «эталонных» полных трейсов (для самых сложных паттернов: anti-pattern fix, honest failure, multi-file scaffold)
= ~1900 трейсов после paraphrase-вариаций
Текущий чемпион — oni:base-7.v2, обученный на этом датасете с 2 эпохами Unsloth QLoRA r=64 на RTX 3090. Метрики:
Stage 1 SSH (22 теста): 15/22 (68%) Realworld stepped (5 этапов): 4/5 (80%, честный fail на 5-м) Phase 3 multi-file scaffold: работает Скорость inference: 25-30 t/s на 3090
Это не идеально. Multi-step SSH chains (port-forward, scp с проверкой, обратный туннель) проседают. Реальный nginx через SSH+sudo тоже тяжело. До production-ready ещё доводить — но базовый агент уже даёт пользу. Это значит, что не «два раза я сделал ничего», а «выполняю задачи, но говорю, если у меня не выходит» (уровень джуна: не получилось — сказал). Что я считаю прорывом!
Самый загадочный баг по дороге: Qwen3 thinking mode
Один баг сожрал у меня 3 ночи — расскажу подробно, потому что ловушка типичная для тех, кто фантюнит.
Модель обучилась хорошо (eval_loss 0.4), но в Ollama выдавала 2/22 на бенчмарке. Сижу, смотрю на вывод: модель пишет осмысленно, но не выполняет инструкции. Адаптер кривой? Шаблон чата сломан?
Оказалось — Qwen3 family по умолчанию пишет в thinking-поле, а не в response. То есть весь num_predict уходит в <think>...</think> блок, в response — пусто. Модель «думает», но не «отвечает».
Решение — TEMPLATE override в Modelfile с предзакрытым <think></think>:
TEMPLATE """{{- if .System }}<|im_start|>system {{ .System }}<|im_end|> {{ end }}{{- range .Messages }}<|im_start|>{{ .Role }} {{ .Content }}<|im_end|> {{ end }}<|im_start|>assistant <think> </think> """
Без этого: 2/22. С этим: 15/22. Один параметр в Modelfile = разница в 13 баллов на бенчмарке.
Если будете фантюнить Qwen3 family — обязательно проверьте этот Modelfile-фикс. Через Ollama API параметр называется think: false:
requests.post(f"{OLLAMA_URL}/api/generate", json={ "model": "oni:base-7.v2", "prompt": prompt, "stream": False, "think": False, # ← критично для Qwen3 family "options": {"num_ctx": 16384, "num_predict": 4000} })
Что выложил в open source
Весь датасет, скрипты, бенчмарки, методология — лежит на GitHub:
👉 github.com/makarsuperstar/oni-devops-traces
Что там:
data/own_anchors/train.jsonl— 1867 hand-crafted multi-turn agent трейсов в JSONL форматеdata/distilled_*— distilled subsets (распилка из Magicoder через локальный teacher gemma4:31b — об этом в следующей статье)scripts/distill.py— скрипт дистилляции (~250 строк, только requests + pyyaml)scripts/run_benchmark.py— composite scoring 8 метрик format compliancemeta/few_shot_reference.jsonl— 5 эталонных трейсов для few-shot promptingmeta/benchmark_results.md— полный отчёт teacher-бенчмаркаDATASET_FORMAT.md— спецификация форматаREPRODUCE.md— пошаговый гайд как пересобрать с нуля
Что я бы сделал иначе с самого начала
Сразу пошёл бы на 14B+ базу. Время, потраченное на qwen3:8b в попытках «уместить в 4080» — потеряно зря. Если задача агентная (multi-step + tool calling), 8B параметров просто мало. Лучше снять VM на ночь, чем месяц мучиться на маленькой модели.
Не начинал бы с HuggingFace mix raw. Это разрушило 3 итерации. Если фантюнишь агента — данные должны быть в формате агента (multi-turn). Иначе модель учится противоречию между single-turn и multi-turn.
TEMPLATE override в Modelfile — с первого дня. 3 ночи на диагностику «обучилось, но не работает» — этого можно было избежать одной строкой.
Сразу делал бы composite scoring. Я долго смотрел на loss + субъективную оценку. Когда добавил композитный 8-метричный scoring (json parses, has messages, tool call present, final answer present, verification before final и т.д.) — стало возможно сравнивать модели объективно.
Не игнорировал бы баланс датасета. Распределение трейсов по seed-ам у меня было от 1 до 30. Это означало 30× перекос. Из-за него base-8 начал hallucinate success на realworld step. Урок: кап на seed — обязателен.
Что дальше
Distillation pipeline — расширяем датасет через локальный teacher LLM (без облачных API), переоборудуем существующие открытые датасеты в наш agent-формат. Уже выложено
distilled_bash_pipes(243 трейса) иdistilled_django(199 трейсов). В очереди — express, microservices, ssh, docker_advanced, kubernetes, ci_cd, postgres. Подробнее в следующей статье.Train base-10 на сбалансированных + distilled данных. Цель — Stage 1 SSH 19+/22, realworld 5/5 honest.
Telegram-бот @oni_devops_bot для публичного demo (через 2–3 недели, когда модель станет «не стыдно показывать»). Free 10 запросов/день.
OpenClaw integration — ради чего всё начиналось. Конфиг + Modelfile в репо, любой может подключить свою oni к openclaw-серверу.
Если интересно следить
Я веду building-in-public канал с короткими заметками про процесс, грабли, числа.
Репо с данными и скриптами: github.com/makarsuperstar/oni-devops-traces
В следующей статье — детально про SeedBuilder pipeline (как мы локально дистиллируем существующие датасеты в agent format) и сравнение 4 кандидатов на teacher-модель: Qwen 2.5 Coder vs Qwen 3.6 vs Gemma 4 vs DeepSeek Coder V2.
Если делаете что-то похожее, или хотите помочь с PR (например, добавить distillation из своего домена — Terraform, Ansible, Kubernetes-specific) — буду рад. Или просто скачайте JSONL и расскажите как он повёл себя у вас в SFT — фидбек ценнее звёзд.
— makarsuperstar
