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 号病人 —— カスケードを 始めた特定のプロセス / コンテナ / 行 —— だと分かった時点で置き換え られました。
ライフサイクル
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 readyInvestigateAsync が alert.Usecase の呼ぶ公開 seam です —— usecase.go:301 参照。
ゲート
worker が spawn される前に 3 つのゲートがフィルタします。各拒否は status=skipped 行として永続化されるので、SPA は「いつまでも開始されない」 ではなく operator に理由を表示します。
| ゲート | デフォルト | ミス時の挙動 |
|---|---|---|
重要度フロア(Config.MinSeverity) | warning | サイレントスキップ、行を書かない |
プロセス内 inflight(incident_id ごと) | 常時オン | サイレント coalesce |
同時実行キャップ(Config.MaxConcurrent) | 5 | skipped: concurrency limit reached (N workers in flight) 行 |
同時実行キャップは LLM プロバイダーのレート制限を守り、100 件のインシデント が同時に発火したときの RAM を制限します。キャップ超えの呼び出し元は キューではなく明示的な skipped 行を得ます。
Worker
ランタイムは chatruntime.SpawnRequest で worker を spawn します。
// 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 は部分的な軌跡をサルベージ します:
MessageReader.ListMessages(sessionID, limit=100)で全ターンを取得。- assistant + tool メッセージを連結して合成「分かったこと」markdown に する。
- サルベージを同じ Pass-2 抽出器に流す。
- レポートは
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 トラップ
firstParagraphOneLine(usecase.go:846) は純粋な markdown 足場(見出し、divider、完全に bold のセクションタイトル) をスキップするので root_cause が **现象** ではなく文として読めます。 以前のバグは先頭の ** だけを剥がして末尾のペアを残していました —— 同じ関数で修正済み。
手動再トリガー
POST /v1/alerts/incidents/{id}/investigation
Accept-Language: enForceEnqueue は手動パスを走らせます:
- このインシデントに対して現在実行中の worker を停止(ベストエフォート —— 再起動後に worker_id が陳腐化していれば警告して続行)。
- 直前の
investigation_reports行をハード削除(前回 force-enqueue からのソフト削除行をカバーし、ユニークincident_idインデックスが 次の Create を拒否しないように)。 - inflight ガードを解放。
EnqueueWithをAccept-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_incident、get_incident_detail、query_promql、search_logs:worker が呼ぶツール。 - モデル / ルーティング —— 調査ごとのロケール + プロ バイダーがどう解決されるか。
- モデル / バジェット —— 調査コストを制限するグローバル な日次トークンキャップ。