Назад к блогу

Опубликовано 30 июля 2026 г.

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

LangGraphAIOpsJunosLLM AgentsAutomation

Почему не скрипт

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

Скрипт такого не переживёт. Он остановится при первом же деплое сервиса, и вместе с ним пропадёт информация о том, что порт уже деактивирован и к нему едет бригада.

Поэтому сценарий построен как граф состояний на LangGraph, а состояние графа хранится в чекпойнтах PostgreSQL. После рестарта сервиса контекст сохраняется, и процесс продолжает работу с того узла, на котором остановился.

Решение 1: LLM не подключается к устройствам

Дать модели доступ к CLI очень заманчиво, но делать этого не стоит. Обязанности в системе строго разделены:

  • детерминированный код подключается по NETCONF/PyEZ, разбирает вывод и применяет конфигурацию;
  • модель работает только с текстом и компактной JSON-выжимкой диагностики, по которой составляет заключение для инженера.

Все изменения в сети проходят через код, который можно прочитать и покрыть тестами. Модель влияет на формулировки и на выводы вроде «похоже на обрыв волокна», но commit она не выполняет.

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

Решение 2: commit confirmed

Junos умеет сам откатывать конфигурацию:

# таймер автоотката ставится на самом устройстве
cu.commit(confirm=10, comment="incident <ticket>: deactivate port")
# ... инженер подтверждает в интерфейсе ...
cu.commit()  # фиксация; без неё устройство откатится само

Это меняет поведение системы при сбоях. Если оркестратор упадёт, потеряет связь или ошибётся, порт вернётся в исходное состояние без нашего участия, потому что таймер работает на самом устройстве. Человек при этом остаётся в контуре, ведь подтверждает изменение именно он.

Решение 3: сначала dry-run

Первый этап проекта намеренно запущен без единого реального commit. Вместо применения конфигурации оркестратор вычисляет diff и записывает в лог, что собирался сделать:

WOULD COMMIT on <device-a>:
[edit interfaces xe-0/0/0]
+   inactive: gigether-options

Этот этап выглядит скучно, но пользы от него много. За время работы в режиме dry-run становится видно, где парсер спотыкается о нестандартные описания тикетов, где диагностика приходит к ложному выводу и каких пар концов линка не хватает в инвентаре. Всё это лучше узнать из лога, чем из инцидента.

Что пока не решено

Формат описания тикета не зафиксирован контрактом. Его формирует шаблон системы мониторинга, и этот шаблон меняется. Парсер справляется со всеми встреченными вариантами, а если список интерфейсов оказывается пустым, останавливается и зовёт человека. Этот принцип, пожалуй, главный для всей системы: автоматика имеет право отказаться от работы.