TL;DR
Historical Frequency Profiling (профилирование исторической частоты) — техника подготовки данных и промпта для диагностики инцидентов, которая добавляет к каждому аномальному сигналу его базовую частоту срабатывания в штатном режиме (Expected Outlier Baseline, r). Вместо того чтобы скармливать модели голые пики метрик и ошибки из логов, метод показывает, как часто этот же сигнал «фонит», когда система полностью здорова.
Когда в системе случается сбой, нейросеть видит десятки алертов и почти всегда обвиняет тот сервис, у которого самая большая цифра отклонения в метриках, даже если абзацем выше в собственных рассуждениях доказала, что проблема пришла от другого узла по цепочке вызовов. Причина в слепоте к базовой норме: фоновые процессы (например, плановая очистка памяти или пересчёт кэша) постоянно дают резкие скачки и ключевые слова error в логах. Без пометки о том, что для конкретного сервиса такой всплеск — обычная рутина, модель путает громкий фоновый шум с первопричиной аварии.
Метод решает проблему в два шага на уровне данных и промпта: сначала считает статистику отклонений по длительному безаварийному периоду (а не за пять минут до сбоя), а затем дописывает к каждой метрике и строке лога в промпте процент r — в скольких процентах спокойных окон этот сигнал тоже пробивал порог. Дополнительно при использовании обучающих few-shot примеров (примеров правильного разбора в промпте) из них маскируют реальные названия сервисов, чтобы модель перенимала логику проверки графа зависимостей, а не копировала имя виновника из образца.
Схема метода
ШАГ 1 (Офлайн / подготовка данных): Замер метрик, логов и трейсов в спокойные периоды → расчёт базовой частоты шума r (%) для каждого сигнала
ШАГ 2 (В промпте инцидента): Подача аномалий в формате [Сервис / Сигнал · отклонение z · частота в норме r%] + граф зависимостей → диагноз по редким в норме сигналам и цепочке вызовов
ШАГ 3 (Если в промпте есть few-shot пример): Замена реальных имён сервисов в примере на нейтральные маски (Service_A, Service_B) → защита от слепого копирования ответа
Шаги 2 и 3 выполняются внутри одного промпта при разборе инцидента; шаг 1 рассчитывается заранее по историческим логам или мониторингу.
Пример применения
Задача: В финтех-сервисе оплаты заказов (аналог СБП-шлюза и корзины в крупном российском маркетплейсе) в пятницу вечером начали отваливаться платежи. Дежурный инженер собирает выгрузку из мониторинга (Prometheus/Grafana и логи) и отдаёт LLM-ассистенту, чтобы быстро найти виновный микросервис и не перезапускать вслепую здоровые узлы.
Промпт:
Ты — инженер диагностики распределённых систем. В кластере оформления заказов маркетплейса произошёл сбой в окне 19:00–19:30. Найди компонент-первопричину (root cause) и тип сбоя.
Граф вызовов (кто кого вызывает):
Frontend → Checkout → [CartService, PaymentGateway, BonusEngine]
CartService → RedisCluster
BonusEngine → PostgresLoyalty
Аномальные сигналы в окне инцидента (z — сила выброса в сигмах, r — как часто этот сигнал превышает порог в спокойные дни без сбоев):
Метрики:
- BonusEngine / jvm_heap_used: z = 28.4 · r = 94.0%
- BonusEngine / gc_pause_ms: z = 19.1 · r = 89.5%
- PaymentGateway / upstream_timeout_rate: z = 14.2 · r = 0.2%
- Checkout / http_5xx_count: z = 31.0 · r = 0.4%
Логи:
- BonusEngine ×14: "[GC (Allocation Failure) PSYoungGen]" · r = 86.0%
- PaymentGateway ×4: "Connection pool exhausted while waiting for acquirer socket" · r = 0.0%
- Checkout ×22: "Upstream request failed: PaymentGateway timeout" · r = 0.3%
Трейсы:
- PaymentGateway: latency ×8.4 vs normal
- Checkout: latency ×7.9 vs normal
- BonusEngine: latency ×1.1 vs normal
Инструкция разбора:
1. Отсей сигналы с высокой базовой частотой в норме (r > 20%) — это рутинный фоновый шум компонента, даже если у них огромный z.
2. По графу вызовов проследи направление распространения ошибки от нижних сервисов к верхним (от вызываемого к вызывающему).
3. Перед финальным ответом проверь: совпадает ли выбранный компонент с самой нижней точкой зарождения редкой аномалии в графе вызовов, и не приписал ли ты ему метрику соседнего сервиса.
Результат:
Модель не поведётся на огромные значения z = 28.4 и 14 записей Allocation Failure у сервиса BonusEngine, потому что увидит метку r = 86–94% (штатная сборка мусора). Далее по графу вызовов она свяжет ошибки Checkout (z = 31.0) с таймаутами нижестоящего PaymentGateway (r = 0.0% в логах и рост задержки в 8.4 раза) и точно назовёт первопричиной исчерпание пула соединений в PaymentGateway, не противореча собственному разбору цепочки зависимостей.
Почему это работает
У языковых моделей — особенно компактных, которые компании разворачивают локально ради приватности логов — есть сильное смещение к самым громким числам. Даже когда модель в блоке рассуждений (Chain-of-Thought) правильно выписывает граф зависимостей и замечает, что сбой идёт снизу вверх от базы данных или шлюза, в финальной строчке ответа она всё равно срывается и называет тот сервис, у которого в промпте написано самое большое число z или больше всего строк со словом error.
При этом сама модель отлично умеет сопоставлять контекст редкости события и причинно-следственные связи в графе, если дать ей точку отсчёта. Когда рядом с грозным выбросом z = 30 явно написано r = 97% (сигнал шумит почти в каждом десятом окне без всяких аварий), числовой якорь теряет свою магическую силу. Модель переключает внимание на сигналы, где r близок к нулю — то есть на события, которые действительно случаются только во время аварий.
Второй критический механизм связан с тем, как LLM используют few-shot примеры (образцы прошлых разборов) в промпте. Вместо того чтобы копировать алгоритм рассуждений, модель часто цепляется за конкретные названия сервисов из примера и вставляет их в ответ к новому инциденту. Маскирование имён компонентов в обучающем примере (Component_A, Component_B) убирает этот соблазн и заставляет модель опираться только на структуру анализа.
Рычаги управления промптом:
- Окно расчёта базовой нормы (r) → считай среднее и разброс по нескольким дням штатной работы, а не за 5 минут до аварии (короткое окно не захватывает периодические циклы вроде очистки памяти и даёт сплошные ложные срабатывания).
- Имена в few-shot примерах → всегда заменяй реальные названия таблиц, подов и сервисов в примерах промпта на абстрактные метки (Node_1, Service_X), иначе модель будет угадывать старого виновника.
- Финальная сверка с графом → требуй от модели явно выписать цепочку Источник → Жертва перед словом Ответ:, чтобы заблокировать скачок к максимальному z в последней строке.
Шаблон промпта
Контекст системы: в системе {название_системы} зафиксирован инцидент в интервале {окно_времени}.
Задача: определить компонент-первопричину, тип сбоя и время начала.
Граф зависимостей компонентов:
{граф_вызовов}
Аномальные сигналы в окне инцидента (z — отклонение от нормы, r — доля безаварийных окон, в которых сигнал также превышает порог):
[Метрики]
{список_метрик_с_z_и_r}
[Логи]
{список_логов_с_количеством_и_r}
[Трейсы]
{список_трейсов_с_кратностью_задержки_к_норме}
Пример правильного хода рассуждений (имена компонентов замаскированы):
- Кандидат Service_A имеет высокий z, но r > 80% — это рутинное колебание в штатном режиме.
- Кандидат Service_B зависит от Service_C. У обоих редкие аномалии (r < 2%), но по графу вызовов сбой в Service_C вызывает каскадную задержку в Service_B.
- Вывод: первопричина — Service_C.
Выполни диагностику строго по шагам:
1. Раздели кандидатов на фоновый шум (высокий r) и реальные улики сбоя (низкий r).
2. Проанализируй распространение сбоя по графу зависимостей среди кандидатов с низким r.
3. Проверь, что все названные тобой метрики действительно принадлежат выбранному компоненту, а финальный ответ совпадает с корнем цепочки распространения.
Что подставлять в плейсхолдеры:
- {название_системы} и {окно_времени} — контур и таймслот аварии.
- {граф_вызовов} — список связей вида Сервис А → Сервис Б.
- {список_метрик_с_z_и_r}, {список_логов_с_количеством_и_r}, {список_трейсов_с_кратностью_задержки_к_норме} — отфильтрованные сигналы с добавленной исторической частотой r в спокойные периоды.
🚀 Быстрый старт — вставь в чат:
Вот шаблон Historical Frequency Profiling для поиска первопричины сбоя по логам и метрикам. Адаптируй под мою задачу: [твоя задача].
Задавай вопросы, чтобы заполнить поля.
[вставить шаблон выше]
LLM спросит, какие компоненты входят в твою систему, кто кого вызывает и какие метрики у тебя обычно «фонят» по расписанию (кроны, бэкапы, сборка мусора) — потому что без разделения штатных периодических пиков и редких аномалий модель выберет самый шумный сервис вместо реального источника аварии.
Ограничения
⚠️ Необходимость предрасчёта базовой статистики: Промпт не узнает сам, как ведёт себя система без аварий. Чтобы передать параметр
rи кратность задержки трейсов к норме, нужно заранее посчитать статистику срабатываний алертов на архивных безаварийных окнах.
⚠️ Потолок обычного промптинга на малых моделях: На компактных локальных моделях (около 4B параметров) одно лишь добавление инструкций и контекста в промпт не убирает до конца привычку выбирать максимальный
z-балл вопреки собственному анализу графа. В исследовании для полного устранения этого разрыва потребовалось дополнительное обучение модели методом Biased-Decision Correction (DPO) на парах её собственных ответов, где начало рассуждений совпадало, а финальный вердикт расходился.
⚠️ Уязвимость к неmasked few-shot примерам: Если вставить в промпт реальный пример прошлого постмортема с настоящими названиями сервисов, а новая авария произошла в другом узле, точность диагностики резко падает (ниже, чем вообще без примера), так как модель начинает копировать имя старого виновника.
Как исследовали
Команда из университета Ханьян (Южная Корея) решила выяснить, почему локальные малые модели (SLM до 20B параметров вроде Qwen3-4B и Gemma 3) на задачах диагностики микросервисов OpenRCA выдают точность полного совпадения меньше 4%, а флагманы вроде Claude Opus 5 и GPT-5.6 Sol — около 10.5–10.6% (при стоимости до \$42 за 1000 инцидентов). Сначала авторы проверили сам входной промпт на банковском датасете Bank (10 дней телеметрии, 136 инцидентов) и обнаружили поразительный факт: из-за того, что стандартные системы считают норму по коротким 5-минутным окнам перед аварией, плановая сборка мусора JVM в Tomcat01 пробивает порог $3\sigma$ в 97% абсолютно здоровых окон, GC-логи помечаются как ошибки в 82–87% здоровых окон, а трейсы IG01/IG02 — в 97–98%. В результате настоящий виновник аварии присутствует среди кандидатов в 99% случаев, но имеет максимальный балл аномалии всего в 8% инцидентов! Переход на расчёт среднего и дисперсии по всему безаварийному периоду сразу снизил долю ложных тревог по памяти JVM с 97% до 4%.
Затем исследователи собрали 640 траекторий рассуждений модели Qwen3-4B (по 8 на каждый из 80 инцидентов) и разметили их с помощью двух независимых судей на базе Claude Opus 5. Оказалось, что в 100% случаев модель честно проверяет несколько кандидатов и анализирует граф распространения сбоя, но при выборе финального ответа в 76% траекторий ссылается просто на большой балл аномалии, в 68% случаев противоречит собственному выводу о распространении сбоя, а в 66% — приписывает выбранному сервису чужую метрику! Когда в промпт добавили эталонный few-shot разбор от DeepSeek-R1, вскрылась ещё одна ловушка: если в примере был тот же виновник, точность Top-1 взлетала с 16% до 56%, но если виновник в примере отличался, точность проваливалась с 16% до 9%, потому что в 44% ответов модель тупо копировала имя сервиса из примера (а при маскировании имён в примере точность честно росла до 21%). Объединив профилирование частоты в норме (Historical Frequency Profiling) и дообучение на точках ветвления решений (Biased-Decision Correction через DPO), авторы подняли точность 4B-модели на датасете Bank до 48.7% против 18.5% у 14-миллиардной OpsAgent-14B и обошли конкурентов на 8 датасетах (включая перенос на новые системы без переобучения).
Адаптации и экстраполяции
🔧 Техника: Пометка базовой частоты
r%в бизнес-аналитике и мониторинге рекламы → защита от паники на регулярных колебанияхПриём Expected Outlier Baseline отлично переносится из DevOps в регулярные промпты для анализа продаж, рекламных кабинетов (Яндекс.Директ, VK Реклама) или продуктовых воронок. Если подавать LLM таблицу с падением конверсии или ростом CPA, добавляй к каждой метрике колонку
Доля таких отклонений в обычные дни (r%):Кампания: Ретаргетинг_Корзина | Рост CPA за час: +45% (z = 3.4) | Частота таких скачков в обычные дни: r = 78% (каждый вечер после 22:00) Кампания: Поиск_Бренд | Падение CTR за час: -38% (z = 4.1) | Частота таких скачков в обычные дни: r = 0.5%Это мгновенно отучает модель бить тревогу из-за суточной сезонности и фокусирует разбор на реальных поломках (например, слетевшей посадочной странице).
Ресурсы
[Fault-Free Profiles and Decision-Level Training for Root Cause Analysis via Small Language Models] [arXiv:2611.02416v2, OpenRCA benchmark] [Kyoohyun Lee, Woohyeok Park, Taeyoon Kim, Sungyong Baik, Kyungyong Lee — Hanyang University, Republic of Korea]
