Тикет «не работает оплата» раньше означал для меня маленький квест. Открыть Sentry. Потом Kibana. Потом Grafana. Потом базу. Потом Jira. Потом через десять минут поймать себя на мысли, что ты уже не помнишь, зачем вообще открыл четвёртую вкладку.
Я хотел автоматизировать это обычным кодом. Написать аккуратный сервис, красиво завернуть API, сделать кнопочку, жить спокойно. Но в итоге написал markdown.
И вот это меня до сих пор немного раздражает.
Потому что в задачах класса «сходи в три системы, собери улики, принеси черновой вывод» SKILL.md внезапно дал лучший ROI, чем ещё один PHP-скрипт. Не потому что markdown магический. Просто цена ошибки здесь умеренная, а цена ручной рутины раздражающе высокая.
Сразу честная рамка: это не история про «AI заменил on-call». Это история про полуавтоматический черновик расследования. Иногда очень полезный. Иногда тупящий. Иногда даже нагло уверенный в ерунде.
Но полезный достаточно, чтобы я продолжил этим пользоваться.
Что получилось на практике
У меня получился AI-оркестратор расследования: по короткому описанию проблемы он сам решает, куда идти - в логи, в Sentry, в базу, в Jira или в APM, а потом собирает первый отчёт. В рабочем проекте этот skill называется research-billing, но в статье я буду называть его просто расследовательским оркестратором.
По моим рабочим заметкам и памяти картина пока такая:
Метрика | Оценка |
|---|---|
Вручную | обычно 15-20 минут, 5 вкладок, много копипаста между системами |
Через расследовательский оркестратор | обычно 3-5 минут до первого чернового отчёта |
Полезный первый черновик | примерно 13-14 случаев из 20 |
Неудачные кейсы | 6-7 случаев из 20, потом всё равно дожимаю руками |
Где проваливается чаще | когда проблема сидит в логике кода, а не в данных |
Деньги | заметно дешевле потерянного времени, но это не zero-cost история |
И да, это не бенчмарк. Я не запускал секундомер на каждое расследование и не строил красивую табличку в Notion. Следующие 20 кейсов хочу уже замерять аккуратно. Но даже грубых цифр хватило, чтобы понять: схема окупает себя не в теории, а в реальной рутине.
Три вещи, которые я сначала свалил в одну кучу
Я сначала смешал три сущности:
SKILL.mdили skill — workflow-инструкция: когда вызываться, что делать, какой формат ответа нуженsubagents — параллельные исполнители с собственным context window
MCP— способ дать агенту инструменты и внешние интеграции
В моём кейсе расклад такой:
research-billing -> skill DB / Logs / Error -> subagents Sentry / ELK / Jira -> внешние системы MCP -> опциональный способ подключить инструменты
MCP не заменяет SKILL.md. Он даёт агенту руки. Skill задаёт порядок, ограничения и критерий полезного результата.
И важная деталь: SKILL.md уже не выглядит локальным хаком одного инструмента. Anthropic оформил этот подход как open standard Agent Skills, а OpenAI пишет, что skills в ChatGPT, Codex и API следуют тому же формату.
Что я собрал
В реальном Laravel-проекте это у меня выглядит как набор skills в .claude/skills/:
research-billing— оркестратор расследованияdb-billing— production БД, только чтение и готовые SQL-шаблоныkibana-billing— ELK и поисковые шаблоны поx-request-id,invoice_id, exception classsentry-billing— issues, events и стектрейсыjira-billing— тикеты и комментарииnewrelic-billingиgrafana-billing— метрики и аномалииbilling-parallel-review— тот же паттерн, но уже для code review
А сверху лежит research-billing как диспетчер.
Он получает текст вроде заказ 87291 не оплачивается, 404, вытаскивает идентификаторы, выбирает маршрут, определяет временное окно и запускает первую волну расследования.
Схематично это выглядит так:
research-billing: "заказ 87291, 404" │ ┌────┴────┐ │ Парсинг │ -> entity_id=87291, keyword=404 │ роутинг │ -> DB + Logs + Sentry └────┬────┘ │ ┌────┼──────────────┐ ▼ ▼ ▼ ┌─────┐ ┌──────┐ ┌──────────┐ │ DB │ │ Logs │ │ Sentry │ │ SQL │ │ ELK │ │ API │ └──┬──┘ └──┬───┘ └────┬─────┘ │ │ │ └───────┼──────────┘ ▼ ┌──────────────┐ │ Синтез отчёта│ └──────────────┘
Важный нюанс: расследовательский оркестратор у меня не «умеет всё сам». Он скорее похож на диспетчера. Его работа - правильно разобрать вход, не забыть нужные источники, дождаться всех ответов и потом аккуратно склеить вывод.
Архитектура в файлах
Вот что особенно важно: в рабочем репо у меня вообще нет обязательной отдельной папки .claude/agents/. Субагенты можно держать прямо внутри оркестратора как inline prompts для Agent tool. Структура получается такой:
.claude/ ├── skills/ │ ├── research-billing/ │ │ └── SKILL.md │ ├── sentry-billing/ │ │ └── SKILL.md │ ├── kibana-billing/ │ │ └── SKILL.md │ ├── db-billing/ │ │ └── SKILL.md │ ├── jira-billing/ │ │ └── SKILL.md │ ├── newrelic-billing/ │ │ └── SKILL.md │ ├── grafana-billing/ │ │ └── SKILL.md │ └── billing-parallel-review/ │ └── SKILL.md
Почему это удобно:
в одном месте лежит и маршрутизация, и правила запуска
знания по каждому источнику не размазаны по одному гигантскому prompt'у
project-specific эвристики живут рядом с интеграцией, а не в голове автора статьи
тот же паттерн потом можно повторить для других задач, не только для расследования инцидентов
Это оказалось намного легче поддерживать, чем один монолитный CLAUDE.md на всё сразу.
Кусок оркестратора research-billing
Самый полезный кусок в этой истории не /sentry, а именно диспетчер. И в моём случае он уже заметно умнее, чем просто «если 404, сходи в три места». Упрощённо он выглядит так:
--- name: research-billing description: Investigate billing problems - broken links, failed payments, 404 errors, duplicate invoices, SD/WEBDEV tickets with issues. disable-model-invocation: true --- ## Step 1. Extract identifiers and keywords Вытащи из текста: - slug из billing URL - invoice_id - deal_id - request_id - transaction_id - jira ticket - keywords вроде 404, expired, duplicate, rejected ## Step 2. Choose route and time window | Input | Spawn | |------|-------| | URL invoice slug | db-investigator + log-investigator + code-investigator | | invoice_id / deal_id | db-investigator + log-investigator + error-investigator | | transaction_id | db-investigator + log-investigator | | jira ticket | jira-investigator, затем вторая волна по найденным ID | | only text / no IDs | error-investigator + log-investigator + db-investigator | Time window: - explicit date -> date +- 2h - "вчера" -> previous day - default -> last 30 days ## Step 3. Launch CRITICAL: launch all required subagents in one message, not sequentially. ## Step 4. Optional second wave If first wave finds invoice but root cause is still unclear: - center logs around invoice created_at +- 1h - run code-investigator for routing or bank logic - run metrics-investigator if everything looks clean but issue reproduces Maximum 2 waves. ## Step 5. Synthesize Format: 1. Что произошло 2. Хронология 3. Корневая причина 4. Что делать 5. Источники
Смысл в том, что skill задаёт эвристику выбора, порядок волн, стратегию времени и формат выхода.
Контракт между агентами
Самое неприятное, что может сделать субагент, - принести «умный абзац» вместо структурированного результата. Поэтому в реальном skill'е у меня не один общий JSON-контракт, а серия жёстких markdown-шаблонов по ролям.
Например:
## DB Investigator — Результаты Инвойс: - id, status, amount, deal_id - payment_type_id - payment_type_option_id Аномалии в данных: - NULL FK-поля - невалидные комбинации Ключевые факты: - ...
## Log Investigator — Результаты Хронология событий: - [timestamp] severity — message Ошибки: - [timestamp] Exception: ... Ключевые факты: - ...
## Error Investigator — Результаты Релевантные ошибки: - Issue #ID: title - culprit - stacktrace: file:line in function Ключевые факты: - ...
Это банально, но работает. Синтезатору не надо каждый раз заново угадывать, где факт, где вывод, а где фантазия модели. Он видит ролевые секции и собирает финальный отчёт уже поверх них.
По сути я заставил субагентов делать две вещи:
отделять сырые улики от интерпретации
приносить факты в форме, которую можно склеить без повторного гадания
Кейс, после которого я понял, что штука реально полезная
Прилетает тикет: «Клиент жалуется, страница оплаты отдаёт 404».
Я запускаю расследовательский оркестратор на таком описании:
research-billing: заказ 87291 не оплачивается, 404
Дальше skill вытаскивает 87291 как entity_id, слово 404 как ключевой симптом и решает запустить три линии расследования одновременно.
DB Investigator находит запись в базе: статус expired, ссылка на оплату была создана 5 дней назад, TTL - 72 часа.
Log Investigator смотрит Elasticsearch и находит пачку 404 по /pay/expired-slug. Slug в запросах не совпадает с текущим. Клиент реально ходит по старой ссылке.
Error Investigator идёт в Sentry и видит, что ошибок уровня error нет, зато есть warning про истёкшую ссылку.
Главный агент собирает это в один вывод: баг не в коде оплаты, а в том, что клиент открыл старый URL. То есть это не «сломалась система», а «мы отдаём слишком жестокий 404 там, где пользователю нужна человеческая страница с объяснением».
Раньше на такой кейс у меня уходило минут 15-20 и пять ручных переходов между системами. Со skill'ом - несколько минут до первого внятного черновика.
Не до окончательной истины. Именно до первого внятного черновика. И в этом, если честно, вся магия.
Чтобы было понятно, какой артефакт я считаю хорошим выходом, вот упрощённый вид итогового отчёта:
Problem: Заказ 87291: клиент получает 404 на странице оплаты. Evidence: - DB: статус expired, ссылка создана 5 дней назад, TTL 72 часа - Logs: 14 запросов к /pay/expired-slug, slug не совпадает с текущим - Sentry: error нет, есть warning "Payment link expired" Conclusion: Корневая причина не в падении платёжного сценария, а в использовании старой ссылки. Recommendation: Показывать страницу с объяснением вместо голого 404 или дать механизм продления ссылки. Confidence: high Open questions: - Нужно ли автоматически перевыпускать ссылку для этой категории заказов?
Вот это уже можно читать, спорить с этим, пересылать коллеге или докручивать руками. Свободный поток слов без структуры - нельзя.
И ещё одна вещь, которая всплыла уже не из теории, а из живого research-billing. Очень часто агент по умолчанию хочет обвинить внешний сервис: «банк не ответил», «сломались credentials», «какой-то нестабильный callback».
А реальная эвристика у меня сейчас жёстче:
сначала проверь целостность данных инвойса
потом уже обвиняй внешнюю интеграцию
В skill'е это дошло до прямого правила: если у инвойса payment_type_option_id = NULL, то проблема часто вообще не в банке, а в том, что код потом падает на чём-то вроде paymentTypeOption->type, уходит в catch и маскируется под 404.
То же самое с кодовым расследованием: если страница отдаёт 404, полезно смотреть не только catch, который редиректит на 404, а тело try, где реально могла случиться ошибка. Это мелочь, но именно такие project-specific правила и делают skill из красивой демки рабочим инструментом.
Какие формулировки реально сделали это стабильным
Вот здесь было самое интересное. Проблема оказалась не в API и не в Bash. Проблема оказалась в том, что агент любит понимать «параллельно» как «ну да, как-нибудь потом».
Ниже места, где один неточный абзац ломал мне всю схему.
Проблема | Слабая формулировка | Что реально сработало |
|---|---|---|
Агенты ходят по очереди | «Запусти всё параллельно» |
|
Агент преждевременно останавливается | «Если данных хватит, сделай вывод» |
|
Логи съедают весь контекст | «Посмотри логи по ошибке» |
|
Временное окно либо слишком узкое, либо слишком широкое | «Поищи за последнее время» |
|
Первая волна не дала причины | «Копни глубже» |
|
Агент слишком рано обвиняет внешний сервис | «Проверь банк / credentials» |
|
Sentry теряет важные события | «Покажи релевантные issues» |
|
Итог разваливается в свободный текст | «Сделай краткий отчёт» |
|
Меня это сначала бесило. Казалось, что я занимаюсь не автоматизацией, а дрессировкой очень умного стажёра.
Потом стало ясно, что в таких workflow побеждает не «умный промпт», а операционная дисциплина. Не «поищи важное», а конкретный лимит. Не «копни глубже», а триггер для второй волны. Не «наверное банк», а сначала проверка данных инвойса. По сути это уже не prompt engineering, а мини-SOP для вероятностной системы.
Вот кусок реального skill'а
Чтобы это не выглядело как эзотерика, вот упрощённый фрагмент одного из моих скиллов.
--- name: sentry description: Use when investigating production errors in Sentry. disable-model-invocation: true --- # Sentry Токен лежит в `.env` как `SENTRY_TOKEN`. 1. Сначала выгрузи top unresolved issues за последние 24 часа. 2. Если environment не сработал как `prod`, попробуй `production`. 3. Никогда не скрывай issues уровня `critical`. 4. Если результатов больше 10, покажи первые 10 и общее количество остальных. Пример запроса: ```bash curl -s -H "Authorization: Bearer $SENTRY_TOKEN" \ "https://sentry.example.com/api/0/projects/org/project/issues/?environment=prod&statsPeriod=24h&sort=freq&limit=10" ``` Для стектрейса используй: ```bash curl -s -H "Authorization: Bearer $SENTRY_TOKEN" \ "https://sentry.example.com/api/0/issues/<ID>/events/latest/" ```
По сути skill - это не код интеграции, а текстовый playbook поверх API. Он не даёт агенту забыть шаги и каждый раз заново «изобретать» критерий полезного ответа.
Почему не PHP-скрипт, не MCP и не просто большой system prompt
Это три вопроса, которые прилетают первыми.
Почему не обычный код?
Потому что ценность здесь не в самом факте вызова API, а в неоднозначной glue-логике: вытащить ID из человеческого текста, выбрать маршрут, распараллелить источники и собрать черновой вывод.
Почему не MCP?
Потому что MCP и skills решают разные задачи. MCP отвечает на вопрос «какие инструменты доступны агенту». Skill отвечает на вопрос «как именно действовать в этом сценарии».
Почему не CLAUDE.md или system prompt?
Потому что глобальные инструкции грузятся всегда, даже когда они не нужны. Skill хорош тем, что поднимается по требованию. Чем больше always-on контекста, тем выше шанс, что агент начнёт следовать не тем правилам не в тот момент.
Если совсем коротко, у меня это выглядит так:
CLAUDE.md- для общих правил репозиторияSKILL.md- для конкретного workflowMCP- для подключения инструментовsubagents - для параллельного выполнения
Когда я разложил это по полочкам, система перестала казаться магией и стала казаться нормальной инженерной конструкцией. Вероятностной, да. Но всё равно инженерной.
Где AI-оркестрация начинает буксовать
Теперь плохая новость.
Всё это хрупко.
Skill - это промпт. Промпт - вероятностная штука. Один и тот же сценарий может в понедельник отработать идеально, а в среду внезапно решить, что в Sentry идти уже не обязательно.
Дебажить такое удовольствие сомнительное. У тебя нет нормального stacktrace. Нет человеческого «почему я так решил». Есть только вывод и ощущение, что где-то на шаге 4 агент пошёл не туда, но доказать ты это можешь только косвенно.
Но до того, как вся схема окончательно обрастает хаками, у неё есть три типовых фейла.
Фейл 1: проблема в кодовой логике, а не в данных.
Логи чистые, база выглядит нормальной, Sentry молчит, а корень сидит в условии на 20 строк в сервисе или контроллере. Такой кейс расследовательский оркестратор почти всегда недоразбирает, потому что у него перед глазами в первую очередь операционные источники, а не reasoning по коду.
Фейл 2: источники противоречат друг другу.
Например, логи уже обновились, а Sentry ещё нет. Или в Jira описан старый симптом, а пользователь уже воспроизводит новый. Если не заставить синтезатор явно отмечать противоречия, он очень любит выбрать одну версию и подать её как истину.
Фейл 3: вход грязный.
В тикете два ID, старый slug, кусок текста из прошлого кейса и формулировка «всё упало». В таких случаях ошибка часто начинается ещё на маршрутизации: агент бежит не в те системы и тратит весь контекст не туда.
И ещё есть неприятный эффект роста.
Сначала SKILL.md маленький и прекрасный. Потом туда добавляется edge case. Потом ещё один. Потом лимит на логи. Потом special case для Jira-тикетов. Потом запрет фильтровать critical. Потом строгий шаблон синтеза. И внезапно у тебя уже не «лёгкий слой оркестрации», а markdown-монстр, который сам требует сопровождения.
Для себя я сформулировал очень трезвый критерий. Подход начинает терять смысл, если:
SKILL.mdлезет сильно за 300-500 строкв нём хаков уже больше, чем полезных инструкций
ошибка начинает стоить дорого
один и тот же сценарий нужно выполнять детерминированно, а не «обычно неплохо»
В этот момент проблема уже не в том, что у тебя «мало промпта». Проблема в том, что сценарий перестал быть хорошим кандидатом для skill-based orchestration в текущем виде.
И это, кстати, гораздо полезнее признать вовремя, чем бесконечно усиливать markdown словами CRITICAL, MUST, NEVER, ALWAYS и делать вид, что от этого вероятностная система внезапно стала программой.
Вывод
В 2026 полезная автоматизация всё чаще выглядит не как «сразу писать отдельный сервис», а как «собрать для агента хороший workflow, дать ему инструменты и заставить приносить структурированный результат».
Это удобно, потому что за вечер можно собрать штуку, которая реально экономит время.
И тревожно, потому что сложность никуда не исчезает. Она просто переезжает в markdown, frontmatter, оговорки, stop conditions и контракты между агентами.
Для меня SKILL.md + subagents + API - это не “недокод” и не временная заглушка. Это отдельный тип инженерной автоматизации. Он отлично подходит там, где:
нужно собрать факты из нескольких источников
цена ошибки умеренная
первый полезный черновик ценнее идеальной детерминированности
И он начинает плохо работать там, где:
нужен гарантированный сценарий
ошибка дорогая
сам workflow уже не даёт выигрыш от вероятностной оркестрации
У меня сейчас в проекте живут два режима.
Несколько узких skills как «умные шпаргалки» по API.
И два оркестратора, которые временами умничают, временами тупят, но всё равно покупают мне несколько очень спокойных минут на каждом расследовании.
И вот это, кажется, для меня главный вывод. В AI-автоматизации ценность часто появляется не там, где ты “убрал человека”, а там, где ты радикально сократил путь от симптома к первому внятному объяснению.
Если нужно быстро собрать сигналы, сопоставить факты и выдать черновой вывод, skills и субагенты уже сейчас работают как взрослый инструмент.
Не идеальный. Не универсальный. Но вполне настоящий.
