Skip to content

Incident investigator

incident-investigator — самый глубокий из воркеров Ongrid. Coordinator диспетчирует на него всякий раз, когда пользователь хочет корневую причину, а не просто резюме симптома. Персона идёт по причинной цепочке от наблюдаемого алерта обратно к первоисточнику («нулевой пациент» — 0 号病人 в промпте персоны) и возвращает структурированный отчёт.

Это персона за RCA

Конвейер причинного RCA HLD-013 (тестовая среда, выкатка в мае 2026) построен на этой персоне. Когда /incident/<id> → Get RCA запускается, manager порождает этого воркера с incident id, заранее вписанным в промпт.

Когда coordinator его выбирает

Файл персоны декларирует триггер-паттерны. Цитируем frontmatter дословно:

yaml
when_to_use: |
  coordinator 在用户问以下场景时 spawn 本 worker:
    • "这条告警的根因是什么 / 到底是谁导致的"
    • "incident 123 怎么排查 / 受影响范围 / 持续多久"
    • "这个告警是不是误报 / 跟上次那个相关吗"
    • "这台机器 mem 飙了,看一下"

Перевод: всякий раз, когда пользователь спрашивает почему, а не что. Поставка investigator — это причинная цепочка обратно к источнику, а не «текущее состояние» снапшот.

Набор инструментов

В whitelist персоны — фильтр рантайма срезает всё остальное:

yaml
tools:
  - query_knowledge        # KB / vault / uploads
  - get_incident_detail
  - query_incidents
  - correlate_incident     # metric+log+trace pulled together
  - query_change_events    # config / rule / device mutations
  - query_promql
  - query_logql
  - query_traceql
  - get_edge_summary
  - query_alert_rules
  - query_devices
  - get_host_load
  - get_host_processes
  - expand_topology
  - find_topology_node
  - host_find_large_files
  - host_du_summary
  - host_stat_file

disallowed_tools:
  - execute_skill
  - host_restart_service
  - run_shell

permission_mode: read-only

Ключевые следствия:

  • Read-only. Никаких host_restart_service, execute_skill, run_shell. Если расследование заключает, что сервис нужно перезапустить, оно возвращает это как предложение coordinator; coordinator диспетчирует specialist-ops с мутирующим намерением, а reviewer гейтит его.
  • Топология и change-events. Уникальное преимущество перед specialist — это expand_topology + find_topology_node (обход графа сервисов обратно к upstream-источникам) и query_change_events (корреляция симптомов с недавними мутациями конфигов / правил / устройств вокруг fired_at алерта).
  • Межхостового bash и per-host пробов нет. Они живут на specialist (specialist-network, specialist-compute, specialist-disk); investigator координирует поверх плоскости данных observability.

5-шаговый workflow

Тело персоны кодирует workflow. Каждое расследование запускает:

  1. KB первым (обязательно). Как только incident_id под рукой, query_knowledge ровно один раз с natural-language запросом, составленным из имени правила + симптома (например, "swap_high 告警怎么排查"). Попадание (score ≥ 0.6) означает следовать playbook; финальный ответ несёт цитирование (参考 KB: <title>). Промах — переходим к шагу 1.
  2. Симптом + blast radius. get_incident_detail для имени правила / severity / target / fired_at / labels. Это конец причинной цепочки (эффект), а не корневая причина. Не останавливайтесь здесь.
  3. Таймлайн. correlate_incident подтягивает metric + log + trace для того же incident-окна одним вызовом. Сортируйте по fired_at / времени первого отклонения. Самый ранний сигнал — кандидат-источник; downstream high-CPU / high-latency обычно эффект, а не причина. «Самый громкий» ≠ «самый ранний».
  4. Один причинный шаг вверх. Выберите один инструмент с чёткой целью:
    • Что изменилось?query_change_events(around_ts=fired_at). Изменения со стороны продукта часто оказываются нулевым пациентом.
    • Зависимости?expand_topology upstream (не blast radius downstream) / find_topology_node.
    • Цепочка вызывающего трейса?query_traceql, чтобы найти источник самого медленного спана.
    • Первая ошибка?query_logql grep по device_id для самой ранней ERROR / PANIC / OOM до fired_at.
    • Кто первым двинулся?query_promql, чтобы найти метрику, которая отклонилась первой.
  5. Рекурсия. Считайте upstream-кандидата новой текущей точкой. Повторяйте шаг 3 до одного из:
    • Достигли дна — в-системе нет upstream. Лист — это процесс / единичное изменение / внешняя зависимость = нулевой пациент.
    • Сигнал исчерпан — дальше нельзя. Сообщите «достигнут глубочайший слой + какой сигнал позволил бы продолжить».
  6. Валидация. Предложенная корневая причина должна объяснить всю downstream-цепочку — быть темпорально раньше симптома, по масштабу и направлению согласованной. Если нет — понизьте до «гипотезы» и так и скажите.

Бюджет 18 инструментов — копай глубоко, не крутись

Персона форсирует строгую дисциплину итерации. Из тела:

你有 ~18 个工具调用预算(够上溯 4-6 层)。深挖允许,但死分支立刻砍

  • 工具返回空(result:[] / streams:[]):第一次空可换思路;第二次 空立刻停这条线,换方向或就此上溯为止。
  • 同一工具失败 / 空 ≥2 次 → 必须换工具或换方向,禁止反复换表达式空转 (v0.7.51-55 的失败都栽在这).
  • 每一步都要朝"再上溯一层"前进 — 调之前问自己 "这步能让我更接近源头吗"。
  • 上溯到 4-5 层仍未触底、或预算用到 ~15:停,输出"目前最深一层 + 缺失 信号",别为凑满空转。

Ограничение max_turns: 40 в frontmatter — это жёсткий потолок; граф eino считает MaxStep = MaxIterations*2+2, так что 40 → MaxStep=82 → грубо 41 ChatModel ходов. Бюджет 18 инструментов — более мягкое руководство в промпте; ограничение — это runtime safety net.

Мёртвые ветки — не обсуждается

Промпт investigator делает это явным правилом «не делайте этого», потому что ранние evals v0.7.51-55 видели, как воркер сжигал 30+ ходов, переставляя PromQL-выражения, пытаясь заставить пустой range-запрос вернуть данные. Один пустой результат — это информация; второй пустой результат означает, что вы уже узнали то, что узнаете — поворачивайте или поднимайтесь.

Формат вывода

Финальный ответ coordinator — дословно Markdown такой формы:

markdown
**根因(0 号病人)**
{One-line patient zero — process / change / upstream service+node /
config. Concrete identifiers (pid + cmdline / service name / change
key) — this is the source of pinpoint_target. If we didn't hit bottom:
"未触底,最深到 X;要继续需 Y 信号".}

**因果链**
{Source → … → alert symptom. One line per hop, each with "why this
caused the next" plus evidence (PromQL / LogQL / trace span /
process line).}

**现象**
{1-2 sentences: when did it start / which host / what crossed
threshold / for how long.}

**置信度与验证**
{High / medium / low + reason. Plus: "what query or action would
further validate or falsify this root cause".}

Coordinator синтезирует это в ответ, обращённый к пользователю (один язык, один абзац, цитирование, если KB-попадание). Сырой Markdown investigator также сохраняется как Result воркера в сессии — UI RCA показывает его дословно под раскрытием Reasoning.

E2E-тест F1

F1 — это сквозной eval, который прогоняет эту персону против засеянного инцидента на тестовой среде. Форма:

  1. Засеять синтетический инцидент swap_high на node-01 (device_id=7) с процессом searxng, прижатым к 95% RSS на 30 минут.
  2. Замонтировать конвейер алертов так, чтобы инцидент сработал с ожидаемыми labels + fired_at.
  3. Диспетчировать investigator с prompt = "rca incident 1234".
  4. Утверждать, что финальный ответ содержит:
    • правило swap_high упомянуто в 现象.
    • процесс searxng идентифицирован в 根因(0 号病人) с pid + командной строкой.
    • минимум 2 причинных шага в 因果链 (симптом → upstream).
    • цитирование (参考 KB: …), ЕСЛИ засеянный KB имеет соответствующий playbook.

Первая версия HLD-013 провалила F1, потому что default_provider не был установлен в БД — resolver откатился на openai с именем модели glm, chat-модель завалилась, и воркер вернул пустой анализ. Урок: F1 также покрывает LLM resolver как side-effect, поэтому это канонический гейт «RCA реально соединён end-to-end?».

Распространённые причины, по которым он останавливается раньше времени

Персона честно возвращает «нулевой пациент не достигнут», когда:

  • Причина вне кластера — DNS-провайдер, upstream API, питание. query_change_events не видит инфра-изменения вне области manager.
  • Trace-данных нет — TraceQL-запросы не возвращают спанов для релевантного сервиса. Investigator не может пройти цепочку вызывающего без трейсов; он сообщает «缺失 trace 信号».
  • Лог-строка, которая указала бы на триггер, ротирована. Loki retention < времени до первого расследования. Investigator говорит об этом и рекомендует расширить retention для повторных расследований.

Эта честность — by design. Уверенный неправильный ответ хуже, чем «мы дошли до сигнала X и нужен Y, чтобы продолжить».

Тюнинг

Что реально менять в форке этой персоны:

  • Добавить доменно-специфичные KB-попадания — напишите playbook для ваших частых режимов отказа, отгрузите их через vault, KB-first шаг investigator их обнаружит.
  • Подогнать whitelist инструментов — добавьте варианты query_traceql-, если ваш стек трейсинга не Tempo, или удалите host_du_summary, если вы не хотите, чтобы investigator диспетчировал инспекцию диска инлайн (по умолчанию делегирование на specialist-disk через собственный вывод воркера — но персона несёт инструменты, чтобы инспектировать сам, когда быстро).
  • Затянуть бюджет — если стоимость токенов вашей модели важна, опустите max_turns до 25-30. Мягкий бюджет 18 вызовов в теле персоны и так держит большинство расследований ниже этого.

См. Кастомные агенты, как смонтировать ваш форк поверх встроенного.