Отправка рабочего nginx.conf во внешний сервис ради подсказки означает передать неизвестному лицу структуру инфраструктуры: внутренние адреса, имена хостов, порты, пути и логику маршрутизации. Даже при добросовестном провайдере это лишняя копия конфигурации в чужом журнале. Локальная модель ставится на собственный сервер, работает офлайн и разбирает конфиг, не покидая машину. Ниже собран порядок действий и пределы её полномочий.

Почему нейросети нужен напарник в виде штатного валидатора

Первое, с чем стоит разобраться до установки чего бы то ни было: модель не проверяет конфигурацию, она её комментирует. Разница принципиальна.

У Nginx и systemd уже есть встроенные средства проверки, и они детерминированы. Nginx разбирает файл целиком, строит внутреннее дерево и сообщает точную строку с ошибкой:

nginx -t

Для systemd есть свой аналог, который разбирает юнит и сообщает о нераспознанных директивах:

systemd-analyze verify /etc/systemd/system/myservice.service

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

Отсюда рабочая схема. Сначала прогон штатным валидатором, и только потом разбор остатка моделью. Валидатор ловит синтаксис, модель объясняет смысл, предлагает упрощения и находит места, где конфигурация формально верна, но ведёт себя не так, как задумано.

Пример полезного вывода, который валидатор не даст никогда: в блоке location стоит proxy_pass без завершающего слэша при rewrite правил, и запросы уходят на бэкенд с удвоенным путём. Синтаксис при этом безупречен.

Установка локального движка моделей и выбор размера под железо

Ollama ставится одной строкой и поднимает локальный сервис на порту 11434:

curl -fsSL https://ollama.com/install.sh | sh
systemctl enable --now ollama

Проверка работоспособности занимает несколько секунд:

curl -s http://127.0.0.1:11434/api/tags

Выбор модели упирается в объём оперативной памяти. Ориентиры для семимиллиардных моделей такие: вариант с четырёхбитным квантованием весит около 4,7 ГБ и требует порядка 6 ГБ памяти в работе, трёхмиллиардная модель укладывается в 2 ГБ, полуторамиллиардная в гигабайт. Для разбора конфигов на сервере с 8 ГБ памяти разумно взять модель среднего размера из ветки, обученной на коде.

ollama pull qwen2.5-coder:7b
ollama run qwen2.5-coder:7b "Объясни одной фразой, что делает директива proxy_buffering off"

Отдельно стоит сказать про скорость. На процессоре без видеокарты семимиллиардная модель выдаёт несколько токенов в секунду, и развёрнутый ответ на большой конфиг собирается минуту или две. Это терпимо для разового разбора и неприемлемо для интерактивной работы. Для интерактива берут модель меньшего размера, а тяжёлую оставляют для ночного аудита.

Проверить, что ничего не уходит наружу, легко: достаточно отключить сетевой интерфейс и повторить запрос. Локальная модель продолжит отвечать.

Подключение ShellGPT к локальному бэкенду вместо внешнего API

ShellGPT это обёртка, которая превращает модель в обычную консольную команду. Устанавливается через менеджер пакетов Python:

python3 -m venv ~/.venv/sgpt
~/.venv/sgpt/bin/pip install shell-gpt
ln -s ~/.venv/sgpt/bin/sgpt ~/.local/bin/sgpt

Конфигурация лежит в ~/.config/shell_gpt/.sgptrc, и для локальной работы в ней достаточно поменять три строки:

DEFAULT_MODEL=qwen2.5-coder:7b
LLM_PROVIDER=ollama
OLLAMA_API_BASE_URL=http://127.0.0.1:11434

После этого запрос уходит не во внешний сервис, а на локальный порт. Проверяется одной командой:

sgpt "чем отличается location /api от location /api/ при proxy_pass"

Полезных режимов у инструмента два. Первый, обычный запрос с текстом в аргументе. Второй, потоковая обработка входа, когда текст подаётся через конвейер:

cat /etc/nginx/nginx.conf | sgpt "найди места, где отсутствует ограничение размера тела запроса"

Второй режим удобнее для разбора файлов, поскольку конфигурацию не приходится вставлять в командную строку вручную, а значит, она не попадает в историю оболочки.

История команд отдельная тема. Конфиг с внутренними адресами, отправленный аргументом, навсегда остаётся в ~/.bash_history, а передача через конвейер этот след убирает.

Разбор конфигурации Nginx конвейером из трёх шагов

Одиночный запрос к модели редко даёт полезный результат, потому что модели не хватает контекста о том, что уже проверено. Рабочий конвейер состоит из трёх шагов и оформляется небольшим скриптом:

#!/usr/bin/env bash
set -euo pipefail

CONF="${1:-/etc/nginx/nginx.conf}"

echo "== Шаг 1. Штатная проверка синтаксиса =="
if ! nginx -t -c "$CONF" 2>&1; then
  echo "Синтаксис не прошёл, дальше разбирать нечего"
  exit 1
fi

echo "== Шаг 2. Проверка на утечку внутренних адресов в запрос =="
sgpt --no-cache "Перечисли только те строки конфигурации ниже, где наружу отдаются \
внутренние адреса, версии ПО или служебные заголовки. Если таких нет, ответь НЕТ.

$(cat "$CONF")"

echo "== Шаг 3. Замечания по логике =="
sgpt --no-cache "Конфигурация прошла nginx -t. Укажи места, где поведение может \
отличаться от ожидаемого: порядок location, отсутствие слэша в proxy_pass, \
лишние rewrite, неочевидный приоритет правил. Отвечай списком строк файла.

$(cat "$CONF")"

Детали здесь не косметические. Флаг --no-cache отключает сохранение запроса в кэше инструмента и снижает число копий конфигурации на диске. Упоминание пройденной проверки в третьем запросе избавляет модель от соблазна перечислять синтаксические придирки, которые уже отработал валидатор.

Первый шаг с выходом по ошибке важен по другой причине. Если синтаксис сломан, любые рассуждения модели о логике бессмысленны, а время на их генерацию тратится впустую.

Размер конфига стоит держать в пределах окна контекста. Для модели с окном в 32 тысячи токенов это несколько тысяч строк, чего обычно хватает.

Реальный nginx.conf редко хранит всё в одном месте. Обычно в нём несколько строк include, а содержимое разложено по каталогам conf.d и sites-enabled. Скормить такой файл модели целиком значит отправить ей текст без включённых частей, и разбор окажется бесполезным.

Штатный дамп показывает конфигурацию после раскрытия всех включений:

nginx -T 2>/dev/null > /tmp/nginx-full.conf
wc -l /tmp/nginx-full.conf
grep -c 'server_name' /tmp/nginx-full.conf

Флаг -T выводит всё дерево с пометками о том, какой файл откуда взят. Пометки помогают понять, к какому файлу относится найденное замечание.

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

Проверка юнитов systemd и поиск типовых ошибок в таймерах

С systemd картина похожая, но со своей спецификой. Ошибки в юнитах чаще не синтаксические, а семантические: служба стартует раньше, чем поднимается сеть, или перезапускается бесконечно из-за неверной политики.

Штатная проверка:

systemd-analyze verify /etc/systemd/system/backup.service

Разбор моделью даёт больше, если скормить ей сразу и юнит, и вывод проверки, и текущее состояние:

{
  cat /etc/systemd/system/backup.service
  echo "---"
  cat /etc/systemd/system/backup.timer
  echo "---"
  systemctl status backup.service --no-pager -l || true
  echo "---"
  journalctl -u backup.service -n 50 --no-pager
} | sgpt "Найди причину, по которой служба не отрабатывает по расписанию. \
Укажи конкретные директивы и предложи исправление."

Подача журнала вместе с конфигом повышает качество ответа. Модель видит не абстрактный юнит, а реальные сообщения о завершении с кодом.

Типовые находки в таких разборах повторяются. Отсутствие After=network-online.target у службы, которая обращается к сетевому хранилищу. Persistent=true в таймере, из-за которого пропущенный запуск компенсируется сразу после включения машины. Restart=always без RestartSec, превращающий падающую службу в генератор нагрузки.

Ни одно из этих замечаний не является синтаксической ошибкой, и штатный валидатор про них молчит.

Где проходит граница полномочий модели и как её не перейти

Локальная модель не хуже внешней по уровню фантазии, и в этом главная опасность. Она так же уверенно придумывает директивы, которых не существует в используемой ветке, и так же склонна предлагать решения из чужой версии.

Практическое следствие простое: предложенные правки применяются только через проверочный прогон, а не напрямую в рабочий файл. Безопасный порядок выглядит так.

cp /etc/nginx/nginx.conf /tmp/nginx.conf.candidate
# правки вносятся в копию
nginx -t -c /tmp/nginx.conf.candidate

Только после успешной проверки копия заменяет рабочий файл, и делается это через systemctl reload, а не restart, чтобы не обрывать текущие соединения.

Остальные ограничения удобно держать перед глазами коротким перечнем:

  1. Секреты модели не показывают, токены и пароли к бэкендам выносят в отдельные файлы с правами 600, а в конфигурации оставляют только ссылки на них, иначе результат разбора осядет в терминальной прокрутке и в отчётах;
  2. Вердикт о несуществовании директивы перепроверяют по справочнику своей ветки, поскольку модель обучена на снимке знаний определённой давности, а stable 1.30.6 и mainline 1.31.8 различаются набором параметров;
  3. Правку применяют к копии файла и только после успешного nginx -t переносят на рабочее место, а службу перечитывают через reload, а не через restart, чтобы не обрывать текущие соединения;
  4. Модель спрашивают о конкретике, а не в целом, потому что вопрос про таймауты в одном блоке location даёт разбор, а просьба посмотреть конфиг целиком возвращает общие рассуждения.

Ответ локальной модели стоит проверять так же придирчиво, как и ответ внешнего сервиса. Помогает понижение температуры генерации до нуля, тогда на один и тот же вопрос модель отвечает одинаково, и расхождение между прогонами перестаёт маскировать неуверенность.

Признак качества ответа простой: модель ссылается на конкретные строки и директивы, а не рассуждает в общем. Нет упоминаний реального содержимого файла значит запрос сформулирован слишком широко.

Второй приём, встречный вопрос. После получения совета стоит попросить обоснование и указать, к каким последствиям приведёт правка. Модель, которая уверенно выдумала директиву, на таком вопросе обычно начинает противоречить себе, и это заметно сразу.

Третий приём, повторный прогон через день. Настоящая находка воспроизводится, а выдуманная часто меняет формулировку или исчезает.

Встраивание проверки в ежедневный рабочий процесс

Основная выгода появляется, когда проверка становится частью рутины.

Первый уровень, git. Конфигурации хранятся в репозитории, а применение изменений идёт через фиксацию с понятным сообщением. Откат в этом случае занимает одну команду, а история отвечает на вопрос, кто и зачем поменял таймаут.

Второй уровень, проверка перед фиксацией. Хук вызывает валидатор и не даёт записать сломанную конфигурацию:

#!/usr/bin/env bash
# .git/hooks/pre-commit
for f in $(git diff --cached --name-only | grep -E '\.conf$'); do
  nginx -t -c "$(pwd)/$f" || exit 1
done

Третий уровень, периодический аудит по расписанию. Раз в сутки cron запускает скрипт, который собирает список конфигурационных файлов, прогоняет их через валидатор и через модель, а результат складывает в текстовый отчёт:

0 4 * * * /usr/local/sbin/conf-audit.sh >> /var/log/conf-audit.log 2>&1

Ценность аудита не в поиске поломок, их ловит валидатор, а в накопленной истории замечаний. Через месяц видно, какие части конфигурации модель стабильно считает подозрительными, и это повод разобрать их руками.

Локальный помощник в консоли не заменяет знание предмета. Он снимает рутинную часть работы и подсвечивает места, куда стоит посмотреть внимательнее, при этом конфигурация остаётся внутри собственного периметра и не уходит в чужие журналы. Для сервера, который обслуживает реальные проекты, это свойство оказывается важнее удобства.