Моя домашняя сеть уже давно перестала быть просто «вайфаем в квартире». Сейчас этот зоопарк состоит из: нескольких роутеров, прошитых под на OpenWrt, NAS, МФУ, а также IoT-устройств (робот-пылесос, умные лампочки, бризер, кондиционер, три канарейки, черепаха, золотые рыбки и старый глупый хомяк). А на роутерах ещё куча сервисов крутятся (например, https-over-dns и сервис-который-нельзя-называть).

В какой-то момент я поймал себя на мысли, что трачу кучу времени на рутину: копаться в заметках, вспоминать команды для OpenWrt, вручную проверять сервисы; понял, что выступаю для своей сети сисадмином на побегушках. Так я и решил настроить локально Qwen3.6-35B-A3B + несколько инструментов.

В этой статье я расскажу о MVP моего домашнего Skynet-а: настроенный агент умеет сам ходить по SSH на роутеры, проверять статус сервисов — и сам поддерживает документацию по сети (на основе подхода llm wiki). Чтобы не бояться, что LLM случайно уронит сеть, я выстроил строгие правила: все деструктивные действия требуют моего подтверждения, а любые скрипты, которые агент напишет, я запускаю только после ручного ревью (не самый оптимальный вариант — но для MVP хватило).

Итак, далее вас ждёт пошаговая инструкция для новичков, как поднять подобную систему в вашем контуре. И пример, как с её помощью поднять себе РКН следить за состоянием принтера по протоколу SNMP.

И сразу скажу: я фанат Powershell, так как эта командная оболочка работает и под Windows, и под Linux и построена на основе горячо любимого мною .NET. Поэтому все дальнейшие действия (и генерируемые скрипты) будут приведены для кроссплатформенного Powershell.

Шаг 1. Устанавливаем Node.js и Qwen CLI — даём ИИ-сисадмину «тело»

Зачем это делать: Сам по себе LLM-бэкенд (будь то локальный llama.cpp или облачный API) — это просто мощный, но изолированный «мозг в колбе». Он умеет только думать и генерировать текст. Чтобы наш будущий ИИ-сисадмин начал работать с файлами, выполнять команды и вообще взаимодействовать с внешним миром, ему нужен механизм контролируемого взаимодействия с компьютером. У меня в системе данную роль выполняет Qwen CLI (он ещё упоминается под названием Qwen Code). Это консольная утилита, которая превращает LLM в полноценного агента, способного вызывать внешние инструменты (в моём случае — ходить по SSH через MCP).

Для её работы нам понадобится среда выполнения JavaScript — Node.js. Мне нужна была версия под Windows - поэтому я скачал установщик LTS-версии с сайта Node.js (он, ко всему прочему, поставит и пакетный менеджер npm).

Теперь открываем консоль (я открыл PowerShell) и ставим Qwen CLI через npm:

npm install -g @qwen-code/qwen-cli

После успешной установки просто вводим команду qwen. Утилита выведет красивый ASCII-логотип, проверит окружение и запустит мастер первоначальной настройки.

Важно: не торопитесь проходить мастер настройки! Почему — будет описано далее.

Первый запуск Qwen CLI
Первый запуск Qwen CLI

Развилка: Облако или локальный сервер?

Здесь вам нужно принять решение, где будет «думать» ваш сисадмин.

Вариант А — Облачная модель. Если вы планируете просто подключить недорогую облачную модель (DeepSeek, Qwen, Anthropic, OpenAI и т.п.) — тогда пройдите мастер настройки до конца: выберите провайдера, введите API-ключ, и сразу переходите к Шагу 3, пропуская установку llama.cpp.

Вариант Б — Локальная модель (мой выбор). Если вы строите полностью автономного и приватного сисадмина, который ничего не отправляет в облако — ничего не вводите в мастере. Нажмите дважды Ctrl+C, чтобы выйти из Qwen CLI. затем переходите к Шагу 2.

Итак, по итогам первого шага у вас либо полностью настроенный облачный агент, либо установленный CLI-клиент, готовый к подключению локальных «мозгов».

Шаг 2. Устанавливаем llama.cpp и запускаем модель — локальные «сердце» и «мозг»

Зачем это делать: Если на прошлом шаге вы выбрали путь приватности и автономности, то сейчас понадобится «мозг» (локальная модель) и «сердце» (то, в чём запускаем). Для запуска модели я использовал llama.cpp. Llama.cpp написана на C++ (язык сам по себе низкоуровневый) плюс содержит множество оптимизаций, что позволяет позволяет запускать модели с минимальным оверхедом.

Для начала эксперимента нам не нужно хардкора и сборки llama.cpp из исходников. Идём на страницу релизов llama.cpp, качаем свежий прекомпилированный архив, в моём случае для Windows с поддержкой CUDA, и просто распаковываем его. Я создал папку C:\llama.cpp и скинул всё туда.

Скачиваем и запускаем модель локально

Не стану долго рассказывать о всех тонкостях — ибо сам пользовался инструкцией уважаемого хабровчанина @rAnto из его статьи про локальный запуск Qwen3.6 35B-A3B на RTX 4070. Добавлю лишь, что в моём случае пришлось смириться с контекстом ~ 240к (вместо 262к) из-за «всего лишь» 8Гб видеопамяти.

Я использовал 4-битный квант MXFP4 (от unsloth) и запускал модель с помощью следующей команды:

C:\llama.cpp\llama-server -m '.\Qwen3.6-35B-A3B-MXFP4_MOE.gguf' --host 127.0.0.1 --port 8080 --no-mmap -ngl 99 -ncmoe 99 -fa 1 -ctk q8_0 -ctv q8_0 -b 2048 -ub 512 --cache-ram 0 -t 12

Личный опыт: Если вам понравится вариант с локальным агентом, в дальнейшем обязательно соберите llama.cpp из исходников (можно проконсультироваться с любой OpenSource LLM касательно параметров сборки под ваше железо). В моём случае компиляция под себя оправдалась. Для запуска тяжёлой Qwen3.6-35B-A3B квантованием MXFP4 на ноутбуке c с характеристиками AMD Ryzen 9 7845HX/32Гб RAM/RTX 5070 Mobile 8Gb стоковый llama.cpp выдавал до 170 токенов/сек входных и 12 выходных. А скомпилированный из исходников (в стоковом не было оптимизации под Ryzen) ускорился до 200 и 20 токенов/сек соответственно.

Проверяем работу

Если запуск прошёл без ошибок, у вас в терминале замелькают логи загрузки. Как только сервер отрапортует о готовности, открывайте браузер, введите в строке адреса http://127.0.0.1:8080/ и пробуйте задать модели вопрос:

Проверка запуска локальной модели через встроенный web-сервер llama.cpp
Проверка запуска локальной модели через встроенный web-сервер llama.cpp

Если в браузере модель вам отвечает, осталось лишь связать её с Qwen CLI, чтобы агент наконец-то начал понимать нас.

Снова запустите Qwen CLI, далее пошаговая инструкция для актуальной на момент написания статьи версии Qwen CLI (0.21.10):

  • [Шаг 1/6] Выберите "Custom Provider", затем "OpenAI-compatible";

  • [Шаг 2/6] Введите http://localhost:8080/ (или иной адрес, который вы указали в команде запуска llama.cpp);

  • [Шаг 3/6] В качестве API key введите любой ключ (в моей команде запуска настройки API-ключа нет, поэтому сервер будет отвечать что бы вы не ввели на этом шаге);

  • [Шаг 4/6] Укажите имя модели (у меня это Qwen3.6-35B-A3B);

  • [Шаг 5/6] Пока пропустите, нажав Enter

  • [Шаг 6/6] У вас получится примерно такой файл:

{
  "ui": {
    "autoModeAcknowledged": true
  },
  "env": {
    "QWEN_CUSTOM_API_KEY_OPENAI_HTTP_LOCALHOST_8080_AA9A3524234A": "1"
  },
  "modelProviders": {
    "openai": [
      {
        "id": "Qwen3.6-35B-A3B",
        "name": "Qwen3.6-35B-A3B",
        "baseUrl": "http://localhost:8080/",
        "envKey": "QWEN_CUSTOM_API_KEY_OPENAI_HTTP_LOCALHOST_8080_AA9A3524234A"
      }
    ]
  },
  "security": {
    "auth": {
      "selectedType": "openai"
    }
  },
  "model": {
    "name": "Qwen3.6-35B-A3B",
    "baseUrl": "http://localhost:8080/"
  }
}

Как закончите его изучение, нажмите Enter чтобы Qwen CLI перешёл в режим диалога. Спросите модель о чём-нибудь для проверки и убедитесь, что она также корректно отвечает:

Первый запрос к модели через Qwen CLI
Первый запрос к модели через Qwen CLI

Первое ручное изменение конфигурации

Важно: размер контекстного окна у модели Qwen3.6 262 тысячи токенов, по умолчанию же Qwen CLI считает, что контекстное окно у модели 1 миллион токенов. измените эту настройку прямо в файле конфигурации Qwen.

Закройте Qwen CLI, затем откройте в текстовом редакторе, поддерживающем подсветку форматирования JSON файл %USERPROFILE%\.qwen\settings.json - туда сохранилась конфигурация с шага 6. Добавьте в параметры модели размер её контекста и увеличенный timeout (на моем железе модель думала по нескольку минут прежде, чем начать отвечать):

Добавляем дополнительные параметры в Qwen CLI
Добавляем дополнительные параметры в Qwen CLI

Не забудьте про выделенную запятую!

Если вы где-то ошибетесь в файле конфигурации, при следующем запуске Qwen CLI пересоздаст файл с параметрами по умолчанию, а изначальное содержимое переместит в файл settings-backup.json. Тогда снова закройте Qwen CLI, исправьте ошибку, затем сохраните файл под именем settings.json.

Если же вы всё сделали правильно — поздравляю! У вас уже есть локальный агент, способный взаимодействовать с файлами на вашем компьютере. Остальсь лишь научить его работать с другими устройствами.

Шаг 4. Настраиваем SSH MCP Sessions — тянем «руки» агента к роутерам

Зачем это делать: Теперь, когда у нас есть локальный агент, нужно научить его выполнять команды на роутерах. Сам по себе он этого не сумеет (надеюсь), поэтому мы научим его с помощью инструмента ssh-mcp-sessions. Это локальный сервер, который даёт Qwen CLI возможность подключаться к устройствам, поддерживающим SSH.

ssh-mcp-sessions умеет:

  • хранить профили хостов, к которым возможно подключение (в %USERPROFILE%\.ssh-mcp\hosts.json);

  • открывать постоянные SSH-сессии (при переключении между диалогами сохраняются открытая директория и переменные окружения);

  • выполнять команды и возвращать результат обратно агенту.

Устанавливаем:

Открываем PowerShell и ставим пакет ssh-msp-sessions:

npm install -g ssh-mcp-sessions

После установки выполните команду

npx ssh-mcp-sessions

Если вы видите текст SSH MCP Server running on stdio, то сервер запустился успешно, нажмите Ctrl+C чтобы остановить его, затем откройте (или создайте, если его нет) файл %USERPROFILE%\.ssh-mcp\hosts.json.

Пример файла с параметрами подключения к основному роутеру (в файле я его назвал Router-Main) с IP-адресом 192.168.1.1, доступом по ssh по ключу (приватный ключ без пароля):

{
  "hosts": [
    {
      "id": "Router-Main",
      "host": "192.168.1.1",
      "port": 22,
      "username": "root",
      "keyPath": "~/.ssh/openwrt.id_rsa"
    }
  ]
}

Теперь снова отредактируем конфиг-файл Qwen CLI чтобы добавить вы него информацию об ssh-mcp-sessions:

Добавляем ssh-mcp-sessions в Qwen CLI
Добавляем ssh-mcp-sessions в Qwen CLI

И снова внимательно проверьте структуру JSON и не теряйте запятые!

Проверяем работу: запускаем qwen и вводим в нём команду /mcp. Если в ответе появилась утилита ssh-mcp-sessions, значит Qwen CLI «видит» инструмент и теперь сможет отправлять команды на роутеры:

Успешное подключение ssh-mcp
Успешное подключение ssh-mcp

Шаг 5. Краткое описание LLM Wiki и моя адаптация под домашнюю сеть

Зачем это делать: Теперь пришло время рассказать, как организованы знания о локальной сети. Объяснять каждый раз досконально что делать — муторно и неудобно. И тут на помощь приходит концепция llm wiki, которую в апреле 2026 года опубликовал Андрей Карпатый (сооснователь OpenAI, бывший глава AI в Tesla).

Идея llm wiki в виде трёх пунктов

  1. Пишем основную информацию, с которой мы работаем, в формате Markdown.

  2. Далее просим LLM построить по этим данным уже свою структурированную коллекцию Markdown-файлов.

  3. И затем, при выполнении запросов, пусть LLM аккумулирует в этой базе знаний полученную в процессе работы информацию.

Приведу пример как это работает у меня:

  • В изначальном описании своей сети я перечисляю все устройства (кроме основного роутера) указывая только их MAC-адреса.

  • Далее я даю команду агенту узнать на роутере IP-адреса всех устройств - и прошу запомнить информацию о том, как это сделать. Агент записывает это в отдельный Markdown-файл.

  • И далее я могу обращаться к устройствам лишь по именам. А агент, в созданной им карточке устройства посмотрит по имени MAC адрес, а по известному ему алгоритму узнает IP-адрес устройства.

В общем и целом подход llm wiki — это процесс постоянного накопления агентом знаний, их извлечение при необходимости, а также поддержание знаний в актуальном состоянии в виде набора текстовых файлов: с перекрёстными ссылками и зафиксированными противоречиями (при их наличии).

Архитектура LLM Wiki — три слоя

  1. Raw-слой (исходники) — ваши исходные документы: статьи, конфиги, инструкции. Эти файлы читаются, но никогда не изменяются LLM. Это — источник информации для модели.

  2. Схема — файл QWEN.md или AGENTS.md, который объясняет LLM, как устроена вики, какие правила именования и какие рабочие процессы. Это ключевой файл — именно он превращает LLM из обычного чат-бота в дисциплинированного редактора вики.

  3. Wiki-слой — сгенерированные LLM Markdown-файлы:

    • index.md — каталог всех страниц;

    • log.md — хронология изменений;

    • папки с концепциями, сущностями и источниками.

Как llm wiki работает у меня

Я слегка адаптировал эту идею под свою домашнюю сеть. Вот структура папок, которую использую:

llm-wiki\
├── raw\                                   # неизменяемые исходники (READ ONLY для AI)
│   ├── devices\                           # ПОСТОЯННЫЕ атрибуты устройств
│   │   ├── router-main.md                 # hostname, MAC, тип ОС, способ доступа
│   │   └── ...
│   ├── services\                          # информация о сервисах
│   │   ├── https-dns-proxy.md             # название, описание, на каком устройстве запущен
│   │   └── ...
│   ├── manuals\                           # инструкции по работе с ОС/сервисами
│   │   ├── Справка по командам OpenWRT.md
│   │   └── ...
│   └── checkups\                          # описания проверок работы сети
│       ├── Ежедневная проверка.md
│       └── ...
├── compiled\                              # wiki-страницы (AI пишет сюда)
│   ├── index.md                           # каталог всех страниц
│   ├── devices\                           # ДИНАМИЧЕСКОЕ состояние устройств
│   │   ├── router-main.md                 # IP-адрес, дата последней проверки
│   │   └── ...
│   ├── backups\                           # резервные копии настроек устройств или сервисов
│   │   └── ...
│   └── log.md                             # журнал всех действий
└── QWEN.md                                # инструкция для LLM по структуре вики

Три основные операции

1. Ingest (поглощение). Если я создаю новый Markdown-файл в папке raw\ (или правлю существующий), то после даю команду агенту через Qwen CLI:

Выполни Ingest.

LLM сама найдёт новые и изменённые файлы в папке raw\, выделит ключевые факты, создаст или обновит страницы в compiled\, обновит index.md и log.md, а затем выполнит qmd embed для обновления поискового индекса (об индексе расскажу чуть позже).

2. Change (изменение). Команда, когда нужно внести изменения в сети (или в wiki):

Change. Собери все статически назначенные IP-адреса (данные находятся на роутере router-main) и сохрани их в wiki

Агент сначала проверяет уже записанную в wiki информацию, затем подключается по SSH к указанному роутеру, выполняет команды и сохраняет результат в compiled\.

3. Lint (проверка). Периодически после изменений следует просить агента проверить согласованность данных в вики:

Выполни Lint.

Агент ищет противоречия между страницами, находит страницы без входящих ссылок, обнаруживает устаревшие утверждения и предлагает исправления.

[СКРИНШОТ 6: Пример структуры папок llm-wiki в проводнике Windows]

Шаг 6. Установка qmd - локального поисковика по Markdown-заметкам

Зачем это делать: при описанном выше подходе нас ждет неочевидная проблема: в некоторый момент данных в llm wiki может оказаться настолько много, что они перестанут помещаться в контекст агента. Плюс для каждого запроса агенту придётся читать все файлы, которые связаны с поставленной задачей, целиком.

Чтобы этого избежать, мы можем добавить в систему утилиту qmd — для полнотекстового поиска по указанным markdown‑файлам. С её помощью агент сможет быстро определить, в каком файле находится нужная ему информация, — или даже получить только её, не читая весь файл.

Для установки открываем PowerShell и устанавливаем пакет qmd через npm:

npm install -g @tobilu/qmd

Затем, там же в powershell, создадим папку, в которой будет находиться наша llm wiki, и добавим ее в индексируемую коллекцию:

mkdir "C:\HabrLlmWiki"
qmd collection add "C:\HabrLlmWiki" --name HabrLlmWiki

далее переходим в папку с llm wiki и вызываем первое построение индекса:

cd "C:\HabrLlmWiki"
qmd embed

При первом запуске qmd скачает несколько GGUF-моделей (эмбеддинг-модель, реранкер) — это займёт пару минут и потребует интернета.

Модели qmd кэшируются в %USERPROFILE%\.cache\qmd\models\ и занимают примерно 2 ГБ. Если места на диске мало — можно отключить реранкинг, выполнив в powershell команду $env:QMD_RERANK_MODE = "off" перед запуском qmd embed и qwen. Подробнее смотрите в документации к qmd.

Так как наша база знаний пока пуста, в выводе команды qmd embed должно быть указано, что проиндексировано 0 документов. Это нормально — далее мы наполним вики содержимым.

Для информации: изначально я настраивал qmd в режиме mcp сервера (также, как ssh-mcp-sessions). Однако на практике оказалось, что моей модели использовать qmd проще через вызов команды в консоли, чем через MCP, так как при использовании mcp постоянно были какие-то проблемы с относительными путями. И плюс команды qmd не меняют содержимого файлов, поэтому я разрешил их безусловное исполнение в папке с wiki.

Шаг 7. Промпты

Самые нетерпеливые могут скачать сразу весь архив с примером, о котором поведаю дальше, из моего репозитория на github.

Файл QWEN.md

Этот файл должен находиться в корне llm wiki; если вы используете не Qwen CLI, переименуйте его в AGENTS.md или иначе, как требует ваше ПО. Этот

файл загружается при каждом запуске Qwen CLI и определяет, как агент должен себя вести. В нём описана уже известные вам структура папок (речь про raw/ и compiled/), список действий (Ingest, Lint, Change). А также общие правила работы, к примеру:

1. Прежде чем менять конфигурацию устройства, читай его карточку из `raw/devices/`
   и извлекай необходимые данные из соответствующего ОС manual-а (оптимально через
   qmd, но также можно и полным прочтением файла).
2. Для поиска исторических данных (например, какие IP уже используются) используй
   `qmd search` по `compiled/`.
3. При изменении состояния (IP, установка пакета, включение сервиса) –
   **обязательно** обновляй `compiled/devices/` и пиши запись в `compiled/log.md`.
4. При Ingest обновляй `compiled/index.md`, сверяй фактические данные с карточками
   и создавай страницы для новых устройств.
5. При обнаружении неизвестной ОС – создавай в `compiled/` страницу `unknown-os.md`
   с перечнем устройств и запроси у пользователя добавление мануала по ней.

Шаг 8. Практическая задача: получаем состояние МФУ через SNMP

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

Для примера расскажу, как, не обладая глубокими познаниями в работе принтеров по сети, я вместе с ИИ научился следить за состоянием моего домашнего лазерного МФУ через протокол SNMP.

Сразу спойлер — процесс потребовал одновременной работы двух облачных бесплатных LLM (ИИ-поиск Google и Qwen3.8-Max), потому что локальная модель на этом этапе ещё не справлялась.

Скриншот из диалога с ИИ поиском Google, когда мы вместе пытались разобраться, почему код не работает
Скриншот из диалога с ИИ поиском Google, когда мы вместе пытались разобраться, почему код не работает

Задача казалась несложной:

  1. Поиск в гугле способов опроса состояния принтера: уровня картриджей, статуса печати (готов, печатаю, ошибка) и прочей основной информации.

  2. В результате было найдено решение — опрос принтера по протоколу SNMP v2c, и ИИ выдал код, как через COM-объект в Windows PowerShell выполнить такой опрос:

$snmp = New-Object -ComObject olePrn.OleSNMP
$PrinterIP = "192.168.1.10"   # IP-адрес принтера
$Community = "public"         # Укажите SNMP Community (по умолчанию public)
$OidSysName = ".1.3.6.1.2.1.1.5.0" # Базовый OID для имени устройства

# Проверяем SNMP v2c (версия = 2)
try {
    $snmp.Open($PrinterIP, $Community, 2, 3000)
    $result = $snmp.Get($OidSysName)
    $snmp.Close()
    Write-Host "Принтер поддерживает SNMP v2c. Имя устройства: $result" -ForegroundColor Green
    return
} catch {
    $snmp.Close()
}

Запомните этот код. И особенно комментарии к нему!

Я проверил — код успешно работал и выводил имя принтера.

  1. Дальше попробовал найти .NET-библиотеку, не привязанную к Windows, которая умеет работать по протоколу SNMP — нашлась Lextm.SharpSnmpLib.

  2. Отдав облачному Qwen 3.8 ссылку на код библиотеки и информацию о производителе модели моего принтера, я получил краткую инструкцию с основными OID для запроса и примеры скриптов на PowerShell, как получить значение по OID (get) и как получить список доступных значений OID (walk).

  3. Скопировав этот список уже в локальный md-файл, я попросил модель выполнить Ingest и затем опросить принтер (его IP-адрес уже был записан в compiled-части wiki).

Локальная LLM успешно прочла новый документ, собрала скрипт, запустила — и он не сработал. Покрутившись некоторое время, она сообщила, что «принтер не отвечает потому что нет связи с ним по сети» (это была неправда). При этом запросы данных через COM-объект продолжали отлично работать!

Дальше в течение нескольких часов я, а также обе облачные модели, изучали исходный код библиотеки Lextm.SharpSnmpLib, ругали Windows Firewall, искали проблему в вызовах библиотеки C# из PowerShell, так и сяк «крутили» код — и в итоге ИИ-поиск Google таки разобрался в проблеме.

Причина проблемы была в том, что мой принтер … (барабанная дробь) … всё это время общался со мной по SNMP 1 версии! Объект olePrn.OleSNMP то ли сам как-то это понимал, то ли просто игнорировал указание, что общаться надо по 2 версии протокола. Но в результате пакеты через него шли в формате SNMPv1, и тогда всё работало. А библиотека Lextm.SharpSnmpLib отправляла, как её и просили, пакеты 2 версии — и принтер их тупо игнорировал.

В итоге я заменил справку по общению с принтером по SNMP и примеры скриптов. После этого локальная модель обновила скрипт мониторинга — и тут он сработал уже без ошибок.

Успешный опрос принтера агентом по локальной сети
Успешный опрос принтера агентом по локальной сети

Заключение

Раньше поддержкой устройств и сервисов приходилось заниматься вручную — и это занимало слишком много времени. Использование агента сначала требовало больше времени, но сейчас, с учётом накопленных в llm wiki скриптов автоматизации, экономия уже налицо: система по сути начала следить сама за собой.

Смог бы я пройти путь самостоятельного написания того же домашнего скрипта мониторинга принтера? 100% нет, у меня не хватило бы времени на изучение кода библиотеки или протокола SNMP. Если бы это была рабочая задача — там, конечно, выбора бы не было. Но именно упорство LLM помогло разработать мне это и многие другие домашние скрипты.

О дальнейшем развитии системы я расскажу в следующей статье (надеюсь, уже в хабе DIY) — пока писалась эта, в системе уже появились новые устройства.

Если у вас будут вопросы — пишите, буду рад на них ответить. Ещё раз приведу ссылку на репозиторий с примером моей llm wiki.

Всем хорошего дня!