|
Professor Seleznov
|
 В первой части разобрали, как обращения из 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

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