Skip to content

RCA(根本原因)

アラートが発火すると、Ongrid は LLM worker を spawn して incident-investigator ペルソナでグラフカーネル ReAct エージェントを 駆動し、ツールを呼んで証拠を集め、構造化レポートを investigation_reports に書き戻します。

レポートは SPA の /alerts/incidents/:id ページの発火系列の隣に描画 されます —— 人間の SRE は「白紙のプロンプトから始める」必要がありません。

HLD-013

現行パイプラインは HLD-013 の Phase 1+2 因果モデルをランディングします。 素朴な「何が発火したかを要約」アプローチ(PR-2)は、operator が求めて いたのはアラームテキストの recap ではなく 0 号病人 —— カスケードを 始めた特定のプロセス / コンテナ / 行 —— だと分かった時点で置き換え られました。

ライフサイクル

text
incident.fire (isNew=true)
    └─ alert.Usecase.RecordFiring
        └─ Investigator.InvestigateAsync(incident)
            └─ Enqueue → Gate 1-3 (severity, inflight, semaphore)
                └─ repo.Create(pending row)   ← UI shows "investigating…"
                └─ go run(reportID, incident, dedupKey, locale)
                    ├─ spawner.SpawnWorker(incident-investigator persona)
                    ├─ worker drives the ReAct loop with tools
                    ├─ Pass-2 structured extraction (cheap model)
                    └─ repo.MarkReady(report fields)  ← UI shows ready

InvestigateAsync が alert.Usecase の呼ぶ公開 seam です —— usecase.go:301 参照。

ゲート

worker が spawn される前に 3 つのゲートがフィルタします。各拒否は status=skipped 行として永続化されるので、SPA は「いつまでも開始されない」 ではなく operator に理由を表示します。

ゲートデフォルトミス時の挙動
重要度フロアConfig.MinSeveritywarningサイレントスキップ、行を書かない
プロセス内 inflightincident_id ごと)常時オンサイレント coalesce
同時実行キャップConfig.MaxConcurrent5skipped: concurrency limit reached (N workers in flight)

同時実行キャップは LLM プロバイダーのレート制限を守り、100 件のインシデント が同時に発火したときの RAM を制限します。キャップ超えの呼び出し元は キューではなく明示的な skipped 行を得ます。

Worker

ランタイムは chatruntime.SpawnRequest で worker を spawn します。

go
// internal/manager/biz/alert/investigator/usecase.go:571
worker, err := uc.spawner.SpawnWorker(ctx, chatruntime.SpawnRequest{
    AgentName:   uc.cfg.AgentName, // "incident-investigator"
    Prompt:      prompt,
    Background:  false,
    SessionKind: "investigation",
})

プロンプトは renderAlertPrompt が描画し、以下を含みます:

  • インシデントメタデータ(rule、severity、device_id、value、threshold、 summary)。
  • 明示的な開始指示:Start with correlate_incident to pull metrics + logs + traces + topology around the fire window.
  • ハード予算:ツール呼び出し最大 10 回、呼び出し #7 までにレポート 書き出しを始めること。この予算は user メッセージに置かれます —— フロンティアでないモデル(GLM、DeepSeek)は system メッセージより user メッセージの制約に従いやすいからです。これがないと eino MaxStep キャップ に毎回ぶつかっていました。
  • ロケールディレクティブ —— ペルソナの暗黙の言語を operator の UI ロケールで上書きします(ロケール伝播は モデル / ルーティング を参照)。

ツール予算 + サルベージ

eino ReAct グラフは総ステップ数をキャップします。worker が最終回答を 書かずキャップを使い切ったとき、investigator は部分的な軌跡をサルベージ します:

  1. MessageReader.ListMessages(sessionID, limit=100) で全ターンを取得。
  2. assistant + tool メッセージを連結して合成「分かったこと」markdown に する。
  3. サルベージを同じ Pass-2 抽出器に流す。
  4. レポートは ready でマークされ、低信頼度ノートが前置されます: 工作器超出最大步数预算(exceeds max steps);以下为根据已收集工具结果的局部分析,置信度偏低。

サルベージなしでは operator は使えるデータなしの status=failed を見ました —— worker は通常 10+ ツールを呼んで答えを集めていたのに、合成ターンを 書かなかっただけです。

構造化抽出(Pass 2)

worker の最終 assistant メッセージは markdown です —— SPA は構造化 フィールドが必要です。2 度目の安価な LLM 呼び出しが以下を抽出します:

  • root_cause —— 1 段落の TL;DR。
  • affected_window —— 症状スパンの開始 / 停止時刻。
  • pinpointed_target —— 変化した特定のプロセス / コンテナ / ファイル。
  • related_alerts —— 同時発火インシデント(RelatedAlertQuerier 経由)。
  • evidence —— 箇条書きのソース引用(PromQL 出力、ログ行など)。
  • suggested_actions —— operator が実行可能な次のステップ。
  • confidence + confidence_factors
  • tool_call_count —— UI が worker が実際に発動したツール数を表示できる よう chat_messages から読み戻す(ハードコードの 0 ではなく)。

Config.SummarizerProvider / Config.SummarizerModel でモデル + プロバイ ダーを設定可能。デフォルトタイムアウト 30 秒(短いプロンプト、短い返信、 ツールループなし)。

抽出器が未配線またはエラーのとき、フォールバックは worker の markdown 上で firstParagraphOneLine を使って root_cause を埋め、markdown 全文 を逐語で findings_md として送ります。

Bold-header トラップ

firstParagraphOneLineusecase.go:846) は純粋な markdown 足場(見出し、divider、完全に bold のセクションタイトル) をスキップするので root_cause**现象** ではなく文として読めます。 以前のバグは先頭の ** だけを剥がして末尾のペアを残していました —— 同じ関数で修正済み。

手動再トリガー

http
POST /v1/alerts/incidents/{id}/investigation
Accept-Language: en

ForceEnqueue は手動パスを走らせます:

  1. このインシデントに対して現在実行中の worker を停止(ベストエフォート —— 再起動後に worker_id が陳腐化していれば警告して続行)。
  2. 直前の investigation_reports 行をハード削除(前回 force-enqueue からのソフト削除行をカバーし、ユニーク incident_id インデックスが 次の Create を拒否しないように)。
  3. inflight ガードを解放。
  4. EnqueueWithAccept-Language からパースしたロケールで呼び、再生成 されたレポートが operator の UI 言語で戻ってくるようにする。

重要度フロアは引き続き適用されます —— info レベルインシデントの手動 トリガーは severity below floor エラーを返します。

起動時バックフィル

新規インストールは鶏と卵問題に当たります:構造化 RCA チェーンは少なくとも 1 つの LLM プロバイダーが設定されているときのみ配線されますが、operator が プロバイダーを追加する前にインシデントは発火しえます。BackfillUnstartedIncidents が起動時に走り、ListIncidentsWithoutReport(since, limit) を walk し、 ウィンドウ内で発火したものを再 enqueue します。通常のゲートは引き続き 適用されるので、解決済みインシデントとフロア超えインシデントはスキップ されます。

配線は cmd/ongrid/main.go を参照 —— 起動時に 24 時間ウィンドウで走り ます。

関連

  • アラート —— 何が発火するか。
  • トポロジー —— investigator のブラスト半径ウォークが使う expand_topology が公開するもの。
  • スキル —— correlate_incidentget_incident_detailquery_promqlsearch_logs:worker が呼ぶツール。
  • モデル / ルーティング —— 調査ごとのロケール + プロ バイダーがどう解決されるか。
  • モデル / バジェット —— 調査コストを制限するグローバル な日次トークンキャップ。