В первой части разобрали, как обращения из Mattermost попадают в n8n, классифицируются по категориям и отправляются в нужную ветку обработки. Под капотом классификатора живёт LLM с доступом к Mattermost через MCP — она читает контекст треда и определяет, к чему относится новый запрос. Параллельно работают вспомогательные sub-workflow:

  • attachmentsAnalyzer — разбирает скриншоты и текстовые логи;

  • httpProbeTool — корректно проверяет доступность ендпоинтов, не роняя цепочку с агентом;

  • errorReporter — страхует на случай, когда падает сам workflow и автор обращения остаётся в неведении.

На примере ассистента по CI/CD-проблемам показал, как из этих кирпичиков собирается ветка обработки. Сегодня — три оставшиеся ветки. Каждая решает свой класс задач, но архитектурно построена по одному и тому же шаблону, благодаря чему любую новую ветку можно тиражировать за пару часов, а не выдумывать каждый раз велосипед с нуля.

В этой части:

  • Расследователь инцидентов — самая капризная категория, у неё самый низкий процент автономного разрешения и самые интересные подводные камни;

  • Менеджер задач — обработка запросов на модификацию инфраструктуры с автоматическим заведением тикетов в Jira;

  • Консультант по вопросам инфраструктуры — ответы на «а где у нас настроен X?» с хитростью в виде автогенерации README в IaC-репозиториях.

Все workflow и системные промпты выложены отдельно — ссылка в конце статьи.

Расследователь инцидентов

Это, пожалуй, самая «капризная» из всех веток. В категорию incident у нас попадает практически любая ситуация, когда «что-то отвалилось»: от подвисшего Postgres до 502-х на ingress-контроллере, от внезапных OOM в каком-нибудь consumer'е до загадочных «у меня все запросы тормозят, а у соседа нет». Спектр огромный, общего рецепта нет — поэтому процент полностью автономного разрешения здесь самый низкий из всех веток.

Но даже когда автоматика не закрывает проблему до конца, собранное досье экономит дежурному инженеру минут 10–15 на старте: уже подтянуты горящие алерты, уже посмотрены метрики и логи, уже сформулированы гипотезы. Когда тебя выдернули в дежурство в субботу утром, эти 10 минут — иногда разница между «успел спокойно проснуться» и «уже отвечаешь в чат, держа кофе в одной руке».

Входные данные

На входе sub-workflow ожидает ту же структуру, что и CI/CD-ассистент из первой части. Чтобы не пришлось скакать между статьями — короткое описание полей:

{
  "message": "Текст обращения в чате",
  "post_id": "ID поста — нужен, чтобы ответить именно в этот тред",
  "channel_id": "ID канала Mattermost",
  "channel_name": "Название канала, прокидывается в промпт для контекста",
  "user_name": "Автор обращения, упоминается в финальном ответе",
  "user_id": "ID пользователя",
  "file_ids": ["ID вложений, если есть"],
  "category": "incident",
  "confidence": 0.95,
  "summary": "Краткое описание от классификатора",
  "is_thread": true,
  "thread_root_id": "ID корневого сообщения треда",
  "on_call_user": "Имя дежурного — пригодится при эскалации"
}

Первым делом отрабатывает attachmentsAnalyzer — знакомый по первой части sub-workflow, который разбирает скриншоты и логи. Для его вызова достаточно передать file_ids. Если вложений нет, ветка с пустыми attachments проходит через Merge-ноду, и пайплайн не валится из-за отсутствия данных.

Сбор переменных в SetVars

Дальше нода SetVars собирает всё, что понадобится и в системном промпте агента, и при отправке ответа обратно в Mattermost. Здесь стоит остановиться, потому что эти переменные — фактически параметризация системного промпта без необходимости его править руками:

  • K8S_CLUSTERS — список доступных контекстов (у нас два: dev и prod, оба в DigitalOcean Frankfurt);

  • K8S_NAMESPACE — основной namespace с прод-нагрузкой;

  • GITHUB_ORG — название организации в GitHub, чтобы агент не пытался искать код вообще везде;

  • prometheus_uid и loki_uid — UID datasource в Grafana, без них агенту неоткуда узнать, куда стучаться за метриками и логами;

  • reply_root_id — ID поста, в который агент будет отвечать (либо корневой пост треда, либо сам пост обращения).

Эти значения потом подставляются в системный промпт через {{ $('SetVars').first().json.* }}. Когда поднимется новый кластер или сменится namespace, достаточно поправить значения в одной ноде — а не лезть редактировать большой текст промпта.

Запрос к агенту

Пользовательский промпт собирается из той же конструкции, что и в CI/CD-ассистенте:

Investigate incident from {{ $json.user_name }} in channel {{ $json.channel_name }}{{ $json.is_thread ? ’ (message in thread, thread_root_id=’ + $json.thread_root_id + ’ — read the story through Mattermost get_thread first)’ : ‘’ }}

{{ $json.message }}{{ $json.attachments_context && $json.attachments_context.trim().length > 0 ? ‘\n\nAdditional information from attachments:\n’ + $json.attachments_context : ‘’ }}

В нём три блока: исходное сообщение, явное указание прочитать историю треда (если запрос пришёл из треда), и контекст из вложений, если они были.

В качестве модели использую GPT-5.5 от OpenAI. На этой задаче Opus показывают сопоставимое качество — выбор скорее по привычке. Что действительно влияет на результат — это не модель, а полнота системного промпта и набор инструментов.

Инструменты, к которым агент имеет доступ

Чтобы агент мог разобраться в инциденте, он должен видеть инфраструктуру глазами инженера. У нас дежурный при разборе обычно идёт так: «что говорят алерты → что в логах сервиса → что в метриках → какие были релизы → что в инфраструктурном коде». Под каждый шаг подключен соответствующий MCP-инструмент:

  • Kubernetes MCP — посмотреть поды, события, прочитать логи контейнера. В системном промпте отдельно прописано: pods_log для одного и того же пода больше двух раз не вызывать. Без этого ограничения агент любит зацикливаться в попытках «уточнить ещё разок».

  • Grafana MCP — запросы в Prometheus (query_prometheus) и Loki (query_loki_logs). Здесь же — поиск по дашбордам, если агент хочет дать в ответе ссылку на готовую панель.

  • DigitalOcean MCP — нужен, когда инцидент касается уровня инфраструктуры: App Platform, дроплеты, кластер DOKS, балансировщики.

  • GitHub MCP — посмотреть последние коммиты, прогоны Actions, открытые PR. Особенно полезен в сценарии «всё сломалось ровно после деплоя» — а такие сценарии у нас, как и у всех, не редкость.

  • Mattermost MCP — только на чтение. Используется в основном для get_thread в самом начале расследования. Отправку ответа агенту не доверяем — это делает отдельная нода после получения output. Если что-то пойдёт не так на этапе постинга, тред просто останется без финального ответа, но execution не упадёт.

  • Qdrant Vector Store — база знаний по нашей инфраструктуре: описание компонентов, связи сервисов, конвенции именования, полезные лейблы. Используется, когда агент натыкается на имя незнакомого ему сервиса и хочет понять, где он живёт и с кем общается.

И отдельной строкой — prometheusAlertSearch. Это самописный sub-workflow, который агент дёргает почти всегда в первые 2–3 шага расследования. О нём стоит рассказать подробнее.

Alert tool: prometheusAlertSearch

Идея простая: львиная доля жалоб от разработчиков по сути дублирует уже горящий в Prometheus алерт. «Postgres что-то тормозит» обычно прилетает ровно в тот момент, когда у нас уже минут десять как горит PostgresHighLatency. Логично сначала проверить, не находится ли причина прямо на поверхности — и только потом лезть копать логи.

Sub-workflow принимает на вход ключевые слова и режим сопоставления:

{
  "keywords": ["postgres", "pgbouncer"],
  "match_mode": "any"
}

Внутри идёт запрос к /api/v1/alerts Прометея, и среди горящих алертов выбираются те, у которых ключевые слова встречаются в названии, лейблах или аннотациях. match_mode: "any" (по умолчанию) — это OR между ключевыми словами, "all" — AND.

Подключается к агенту через ноду toolWorkflow как обычный MCP-tool. Описание для агента критически важно — без него агент не понимает, когда и как этим тулом пользоваться:

Search currently firing alerts in Prometheus by keywords. Use early in

investigation to find correlated active alerts across the platform.

Input:

- keywords (array of strings, required): lowercase substrings matched

against alert names, all label keys/values, and all annotation values.

Examples: ["postgres"], ["http","5xx"], ["kafka","redpanda"].

- match_mode (string, optional): "any" (default, OR) or "all" (AND).

Returns: { ok, total_firing, matched_count, returned_count, truncated,

alerts:[...] }

Each alert has alertname, severity, labels, summary, description,

activeAt, value. Output capped at 25 alerts — if truncated=true,

refine keywords.

Пример подключения tool

Без явного указания «вызывай это в первые 2–3 шага» агент любит сначала уйти в логи, потом в события кластера, потом ещё куда-нибудь — и только в конце вспомнить про алерты. Жёсткое указание в описании тула заметно меняет поведение.

Формат ответа

В системном промпте описан строгий формат вывода:

### Что произошло

Краткое описание сути инцидента

### Хронология событий

События за 10 минут до проявления проблемы

### Возможные причины

Не более двух гипотез

### Что попробовать

Пошаговые действия по каждой гипотезе

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

Среднее время обработки одного обращения около пары минут, а потребление на уровне 50 000 токенов . По стоимости — копейки на фоне затрат на инженера, особенно если учесть, что часть этих обращений раньше требовала созвон, а не просто переписку.

Пример использования

Менеджер задач

Категория «модификация инфраструктуры» — это запросы вида «выкатите нам новый сервис», «дайте доступ к Grafana», «добавьте бакет под аналитику». Полностью автоматизировать такое нельзя: почти всегда нужно согласование, оценка и просто человеческое внимание. Но что точно можно автоматизировать — это превращение свободного текста в нормально оформленный тикет.

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

На вход — та же структура с полями от классификатора. Дальше — знакомая последовательность: разбор вложений через attachmentsAnalyzer, сбор переменных в SetVars, передача в LLM-агента.

Выходной формат

Особенность здесь — в строго заданной структуре ответа. Агент должен вернуть JSON:

{
  "summary": "<область>: <что нужно сделать>",
  "description": "<1-3 предложения с деталями>",
  "label": "<одно из направлений>"
}

label определяется из ограниченного словаря: kubernetes, monitoring, network, access, database, ci-cd и так далее. Это упрощает дальнейший роутинг задач по компетенциям инженеров и сбор аналитики «кто чем у нас занят и сколько».

Чтобы агент написал нормальные summary и description, у него есть доступ к:

  • Qdrant Vector Store — посмотреть, как у нас в принципе устроена область, к которой относится запрос. Это нужно, чтобы агент не выдумывал новое название там, где уже есть существующее.

  • Mattermost MCP — если запрос пришёл в тред, прочитать предысторию. Часто люди уточняют детали именно в треде, а в первом сообщении пишут одну строчку.

  • HTTP Request — на случай, если в запросе есть ссылка на чей-то PR, документ в Confluence или внешнюю спеку.

Валидация и создание задачи

После получения JSON идёт валидация: проверяем наличие обязательных полей и допустимое значение label. Если что-то не так — отправляется fallback-сообщение «не получилось сформулировать задачу автоматически, посмотри сам» и дежурный обрабатывает руками. Если всё в порядке — задача уходит в Jira через встроенную ноду n8n.

Пример настройки
Jira node
Jira node
Create Jira API token
Create Jira API token

В Mattermost обратно прилетает короткое сообщение с названием задачи и ссылкой на неё. Дальше дежурный видит готовый тикет в нужном label, и принимает решение: брать в работу, переназначать или уточнять.

Важный момент: автоматическое создание задачи не отменяет ручной валидации. Иногда классификатор ошибается и распознаёт инцидент как модификацию (особенно когда автор пишет в стиле «нам нужно, чтобы у нас работал X» вместо «у нас не работает X»). Поэтому в системном промпте отдельно прописано правило: если из текста непонятно, нужна ли вообще новая работа или это про существующее — задавать вопрос «уточни, что именно нужно сделать» отдельным сообщением, а задачу не создавать. Дешёвая мера, которая заметно снижает количество мусорных тикетов.

Пример использования

Консультант по вопросам инфраструктуры

Последний на сегодня workflow — самый «спокойный». В категорию «вопрос» попадают запросы вида «где у нас настроен лимит коннектов в pgbouncer?», «куда складываются логи alloy с дроплетов?», «какой регион у бакета assets-prod?». Иногда от новеньких в команде, иногда — от тех же DevOps-инженеров, которые забыли, где что лежит. (Бывает, не буду врать.)

Структура почти один в один с инцидент-расследователем: та же входная JSON-структура, та же цепочка с разбором вложений, тот же SetVars. Из инструментов: GitHub MCP, Mattermost MCP, Kubernetes MCP, Grafana MCP, DigitalOcean MCP, Qdrant Vector Store и HTTP Request для подгрузки официальной документации компонентов, когда в коде нужного параметра нет и нужно поискать default'ы у вендора.

Останавливаться на тех же узлах не буду — расскажу про одну хитрость, без которой агент уперся бы в стену довольно быстро.

Skill, который пишет README в IaC-репозиториях

Изначальная гипотеза была такая: даём агенту GitHub MCP и базу знаний в Qdrant — и он сам разберётся. На практике оказалось, что в репозиториях IaC структура почти всегда неочевидная: где-то Terragrunt вперемешку с Helmfile, где-то Ansible playbooks с inventories через два подкаталога, где-то модули Terraform разложены по понятным только нам именам. Агент тратил кучу токенов и времени просто на то, чтобы понять, куда вообще смотреть.

Решение пришло из формата Claude Code Skills: я написал отдельный skill, который запускается локально в IDE и генерирует/обновляет README в каждом инфраструктурном репозитории. Skill читает структуру каталогов, выявляет точки входа и описывает их в едином формате. Получается что-то такое:

## Overview

## Repository layout
| Directory | Tooling | Purpose |
|-----------|---------|---------|

## terraform/
Описание структуры директорий

### Resource catalog
| Cloud | Region | Units |
|-------|--------|-------|
### How to run

## ansible/
Описание инвенторя, плейбуков, ролей
### Galaxy requirements
### How to run

## helm/
Структура helmfile кода
### Environments and kube contexts
| Helmfile environment | kubeContext |
|----------------------|-------------|

### Helm repositories

| Name | URL | OCI |
|------|-----|-----|
### App catalog
| Release | Chart | Version | Namespace |
|---------|-------|---------|-----------|

### How to run

## manifests/
| Environment | Subfolders |
|-------------|------------|

### File inventory
### How to run

## .github/
### Workflows
| Product | Files |
|---------|-------|
### Secrets consumed

## Local tooling
Список необходимых утилит с версиями необходимые для работы

В каждом репозитории README обновляется при значимом изменении структуры — есть pre-commit hook, который перезапускает skill.

С точки зрения агента это меняет всё: первым делом он читает README.md через get_file_contents, понимает структуру, и дальше уже идёт в нужный подкаталог за конкретным файлом. Количество вызовов GitHub MCP сократилось примерно в 3 раза, а качество ответов заметно выросло — особенно по вопросам вида «где конфигурируется X».

Сам skill выложу в репозитории — он простой, легко адаптируется под чужую структуру.

Пример использования

Что получилось в сумме

После того как все три ветки въехали в продакшен и пару месяцев пожили в боевом режиме:

  • Расследователь инцидентов — закрывает 40% обращений полностью; в остальных случаях дежурный получает на руки готовый разбор с гипотезами и экономит 10–15 минут на старте.

  • Менеджер задач — практически все запросы на модификацию доходят до Jira с осмысленным summary и проставленным label; ручные правки нужны редко, обычно по тексту описания.

  • Консультант — закрывает 70% вопросов без участия инженера; для оставшихся ответ агента всё равно полезен как стартовая точка для дежурного.

  • Совокупная стоимость при нашем потоке держится в районе $250 в месяц на оплату LLM. С учётом того, что система работает 24/7 и не уходит в отпуск — это смешные деньги.

Планы на будущее

Что в очереди на ближайшие месяцы:

  • Расширить инструментарий агентов до уровня «действия, а не только чтение» — аккуратно дать доступ к перезапуску подов, применению готовых манифестов, restart'ам systemd-сервисов. Понятно, что это территория с минами, поэтому делать буду через явное подтверждение от инженера в Mattermost — без подтверждения никакие изменения не уезжают.

  • Добавить обработку анонсов техработ — пока такие сообщения просто помечаются и игнорируются, но их можно было бы пушить в отдельный канал-дайджест с автоматическим саммари «что планируется на этой неделе».

  • Подключить отдельную ветку для security-вопросов — со своей базой знаний по нашим compliance-документам и политике безопасности.

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

Заключение

Если коротко: AI-агенты под управлением n8n с MCP-инструментами оказались очень рабочим способом снять с дежурного инженера значительную часть рутины. Не серебряная пуля — но что-то близкое к скромному, но трудолюбивому стажёру, который работает круглые сутки и стоит как пара обедов в Wolt.

Главное — не пытаться сразу автоматизировать всё. Лучше сделать одну ветку, плотно пожить с ней, понять её слабые места — и только потом распространять подход на остальные категории. У меня от первого работающего CI/CD-ассистента до полного набора из всех веток прошло около трёх месяцев — и я об этом не жалею.

Репозиторий с всеми описанными workflow: https://github.com/javdet/automagicops-workflows


А у вас какая категория обращений лучше всего автоматизируется? И сталкивались ли с ситуацией, когда AI-агент уверенно поставил неправильный диагноз и увёл инженера не в ту сторону? Поделитесь в комментариях — особенно интересно сравнить, у кого где грабли.