
В первой части разобрали, как обращения из 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.
Пример настройки
В 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-агент уверенно поставил неправильный диагноз и увёл инженера не в ту сторону? Поделитесь в комментариях — особенно интересно сравнить, у кого где грабли.





















