規格書 v2.0 新增: - §十一 四階段細化實施步驟(階段1~4各含驗收清單) - 階段1: CD解鎖+debounce+alertname+冷啟動Playbook+KM向量化(9步) - 階段2: DB Migration+classify_alert_early+outcome寫入(5步) - 階段3: 分診站+SSH路由+TYPE-1/E/F+action解析+risk_level(Tier3,7步) - 階段4: KMConversionService+手動修復記錄(4步) - §十二 防偏差守則(不跳步驟/Tier3授權/不改範圍/異常立刻報告) ADR-073 更新:架構轉型截圖定案(舊架構中斷→新架構分診飛輪) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
15 KiB
ADR-073: AIOps 飛輪完整盤點 — 問題根因 + 長期解決方案
狀態: Accepted — 等待統帥批准實作(細化步驟已完成)
日期: 2026-04-12 (台北時間)
更新: 2026-04-12 晚 — 加入架構轉型截圖定案 + 四階段細化實施步驟
作者: Claude Sonnet 4.6 + 首席架構師
觸發: 資料庫審計顯示飛輪從未真正運轉
完整規格: docs/superpowers/specs/2026-04-12-aiops-complete-flywheel-repair-design.md(§十一 細化步驟、§十二 防偏差守則)
架構轉型核心(2026-04-12 截圖定案)
舊架構(中斷路徑):
告警 → LLM 瞎目(alertname=NULL)→ 目標不明 Unknown → KM 空白
新架構(飛輪 V2.2):
告警 → 分診工作站(decision_manager 入口)
├─ TYPE-1(資訊)→ 純文字通知,不進修復流程
├─ TYPE-4D(Drift)→ Drift 卡片,不進修復流程
└─ TYPE-3(需修復)→ MCP 上下文 → LLM 推理 → 自動執行 → KM 向量化
關鍵洞察:分診工作站從 telegram_gateway 前移至 decision_manager 入口,是防止「HostBackupFailed 產生 kubectl scale deployment unknown」的架構根治。
一、飛輪設計圖(截圖定案版)
正常模式(自主運行)
監控觀察 → 接入去重 → 環境診斷 → 推理匹配 → 執行燒橇 → 學習固化
↑ ↓
└─────────────────── 飛輪閉環 ──────────────────────────────┘
六個節點定義:
| 節點 | 功能 | 對應程式碼 |
|---|---|---|
| 監控觀察 | Prometheus/Alertmanager 接收告警,Fingerprint 去重 | webhooks.py alertmanager_webhook |
| 接入去重 | Debounce 30 分鐘,相同 fingerprint 只建一個 Incident | webhooks.py fingerprint + debounce |
| 環境診斷 | MCP 收集真實環境狀態(SSH/K8s/Prometheus) | decision_manager._collect_mcp_context() |
| 推理匹配 | 查 KM Playbook → 有匹配直接執行;無匹配呼叫 LLM 推理 | decision_manager._dual_engine_analyze() |
| 執行燒橇 | 透過 MCP Provider 執行修復動作 | decision_manager._auto_execute() |
| 學習固化 | 成功→生成 Playbook;失敗→生成 Anti-Pattern;寫入 KM | decision_manager._generate_playbook_draft_if_new() |
人工介入模式(需人工路徑)
推理匹配
├→ TYPE-3(高風險審核): AI 找到方案但動作風險高 → 人工批准 → 執行 → 學習固化
└→ TYPE-4(根因確認): AI 無法判斷 → 人工手動處理 → 記錄手動步驟 → 生成新 Playbook
人工介入只發生在兩種情況:
risk_level = critical或 DESTRUCTIVE_PATTERNS 命中 → TYPE-3 卡片confidence < 0.5或 MCP 全失敗 → TYPE-4 卡片,人工確認後轉 Playbook
二、資料庫實際數據(2026-04-12 統計)
Incidents(132 筆)
| 欄位 | 數值 | 正常應有 |
|---|---|---|
| severity P3 | 108(82%) | P3 應是少數 |
| severity P2 | 18(14%) | |
| severity P0 | 6(4%) | |
| status INVESTIGATING | 87(66%) | 應該接近 0 |
| status RESOLVED | 45(34%) | |
| alertname | 全部 NULL | 應有 alertname |
| alert_category | 全部 NULL | 應有分類 |
| notification_type | 全部 NULL | 應記錄卡片類型 |
| vectorized | 全部 False | 應 True(已向量化進 KM) |
| outcome | 全部 null | 應記錄修復結果 |
Approvals(380 筆)
| status | 數量 | 說明 |
|---|---|---|
| EXECUTION_FAILED | 211(55%) | 超過一半執行失敗 |
| APPROVED | 132(35%) | 已核准,未執行或執行中 |
| REJECTED | 30(8%) | 人工拒絕 |
| EXPIRED | 5 | 超時未處理 |
| EXECUTION_SUCCESS | 2(0.5%) | 實際成功修復只有 2 次 |
Top Actions(失敗根因)
| action | 數量 | 問題 |
|---|---|---|
| 未知操作 |(空) | 93 | action 解析失敗,完全不知道要做什麼 |
| 重新啟動 postgres-primary-0 | 30 | 同一問題反覆重啟,未解決根因 |
| 重新啟動 postgresql-native | 25 | 同上 |
| 重新啟動 harbor-core-7d4b8c9f5-xk2m3 | 21 | Pod hash 寫死,每次不同 |
| OBSERVE | 16 | LLM 判斷無法修復,只觀察 |
Playbooks = 0
Knowledge Entries(699 筆)
- INCIDENT_CASE: 690(有案例,但全部未向量化,無法 RAG 查詢)
- RUNBOOK: 7
- BEST_PRACTICE: 2
三、飛輪每個節點的實際狀態 vs 設計目標
節點 1:監控觀察 ✅ 部分正常
- Prometheus → Alertmanager → webhooks.py 接收 ✅
- 問題:alertname 存入 signals JSONB,但
signals->0->>'alertname'全為 NULL,代表 signals 結構不對
節點 2:接入去重 ⚠️ 部分異常
- Fingerprint 機制存在 ✅
- 問題:debounce window 只有 5 分鐘,Prometheus 每 1 分鐘 fire 一次,5 分鐘後重新建 Incident,導致同一問題產生多個 Incident,反覆發 Telegram
節點 3:環境診斷 ❌ 未運作
_collect_mcp_context()已實作(commit c439277)- 問題:
8be87b0的修復程式碼 CD 失敗未部署,目前 Pod 是舊 image(a86ecf3),該 image 沒有_collect_mcp_context() - MCP Providers 雖然 enabled,但沒有在分析前被呼叫
節點 4:推理匹配 ❌ 嚴重問題
- LLM 分析有在跑(Nemotron NIM)✅
- 問題 A:Playbooks = 0,
evaluate_auto_repair()在 Playbook 匹配步驟永遠失敗,100% 走人工審核 - 問題 B:
signalsJSONB 裡 alertname = NULL,LLM 拿到的是空告警名稱 - 問題 C:target_resource 解析失敗(DockerContainerUnhealthy → target = alertname or unknown)
- 問題 D:LLM 習慣性回傳
risk_level = high,但 YAML 規則的 risk 沒有覆蓋 LLM 結果
節點 5:執行燒橇 ❌ 從未成功(0.5%)
_auto_execute()三層安全守衛存在 ✅- 問題 A:93 次
未知操作|→ action 解析失敗,LLM 回傳的 action 包含|分隔符,解析邏輯出錯 - 問題 B:
target = unknown或target = alertname→ 安全守衛攔截 → 發❌ 自動修復失敗(而非 TYPE-4 卡片) - 問題 C:K8s deployment 名稱含 hash(
harbor-core-7d4b8c9f5-xk2m3),K8s 驗證失敗 - 問題 D:Docker/Host 層告警(DockerContainerUnhealthy / HostBackupFailed)走 K8s 路徑,根本路徑就錯
節點 6:學習固化 ❌ 完全失效
_generate_playbook_draft_if_new()已實作(commit 7eb49f9)✅- 問題 A:Playbooks 仍為 0,代表觸發條件從未成立,或有 exception 被靜默吞掉
- 問題 B:690 筆 INCIDENT_CASE 全部
vectorized = False,RAG 查詢永遠找不到歷史案例 - 問題 C:outcome / target_resource / alert_category 全為 NULL,KM 記錄的案例沒有分類,無法被有效查詢
- 問題 D:重複發生的告警(postgres-primary-0 被重啟 30 次)沒有任何學習,每次重新走全流程
四、與設計定案的落差對照表
| 設計定案要求 | 實際狀況 | 落差等級 |
|---|---|---|
| 告警 → 自動修復閉環 | Playbooks=0,100% 人工審核 | 🔴 P0 |
| 重複告警不重複觸發 | 5 分鐘 debounce,每次重建 | 🔴 P0 |
| 失敗 → 學習 → 建立規則 | 690 案例未向量化,Playbook 從未生成 | 🔴 P0 |
| KM 三段資料完整 | outcome/vectorized 全 NULL | 🔴 P0 |
| target 動態解析(Docker/Host) | target = unknown/alertname | 🔴 P0 |
| NO_ACTION 告警不顯示按鈕 | HostBackupFailed 仍有六個按鈕 | 🟠 P1 |
| 通知類型正確分類 | notification_type 全 NULL | 🟠 P1 |
| severity/risk_level 正確 | P3 佔 82%,risk 被 LLM 覆蓋 | 🟠 P1 |
| 人工修復後轉 Playbook | 無 handle_manual_fix_done 呼叫 | 🟠 P1 |
| 前端顯示飛輪狀態 | 截圖顯示 Playbook=0,飛輪靜止 | 🟡 P2 |
五、長期解決方案(按優先順序)
P0-A:修復 CD + 部署新 image
問題:8be87b0 的三大修復未上線
解法:確認 Harbor 有 8be87b0 的 web image;若沒有,重新觸發 build
驗收:Pod image = 8be87b0,_collect_mcp_context 存在於 /app/src
P0-B:修復 signals 結構 — alertname 存入正確欄位
問題:signals->0->>'alertname' = NULL
解法:webhooks.py 在建 Incident 時,確認 signal 的 alertname 正確寫入 JSONB;或新增獨立欄位 alertname VARCHAR(100)
驗收:新 Incident 的 alertname 不為 NULL
P0-C:debounce window 延長到 30 分鐘
問題:5 分鐘 debounce → 同一問題每 5 分鐘重新建 Incident + 發 Telegram
解法:webhooks.py fingerprint debounce window 從 5 分鐘 → 30 分鐘
驗收:同一告警 30 分鐘內只有 1 個 INVESTIGATING Incident
P0-D:冷啟動 Playbook 生成
問題:Playbooks = 0,自動修復永遠失敗
解法:一次性腳本,讀取 approval_records 裡 EXECUTION_SUCCESS 的 2 筆 + APPROVED 的 top 10,用 LLM 生成初始 Playbook 草稿,寫入 DB 標記 APPROVED
驗收:Playbooks >= 10,evaluate_auto_repair 至少有匹配
P0-E:690 筆 INCIDENT_CASE 向量化
問題:vectorized = False,RAG 永遠找不到歷史案例
解法:一次性腳本,批次向量化所有 vectorized=False 的 knowledge_entries
驗收:vectorized = True,RAG 查詢返回相關案例
P0-F:Docker/Host 層告警走 SSH MCP 路徑
問題:DockerContainerUnhealthy → K8s 路徑 → deployment 不存在 → 失敗
解法:_auto_execute() 判斷 alertname prefix(Docker*/Host*)→ 走 SSH MCP ssh_docker_restart;不走 K8s 路徑
驗收:DockerContainerUnhealthy 觸發 → SSH MCP 執行 → 成功
P1-A:action 解析修復(93 筆「未知操作|」)
問題:LLM 回傳含 | 的 action,解析後為空
解法:_push_decision_to_telegram() 的 action 佔位符替換邏輯,加 | 分隔符清理
驗收:新的 approval_records action 不出現「未知操作|」
P1-B:NO_ACTION → TYPE-1(無按鈕)
問題:HostBackupFailed 仍有六個按鈕
解法:classify_notification() 加判斷:suggested_action == "NO_ACTION" → TYPE-1
驗收:HostBackupFailed Telegram 卡片無操作按鈕
P1-C:outcome / alert_category / notification_type 寫入
問題:三個欄位全 NULL,KM 無法分類
解法:
_auto_execute()完成後寫incident.outcomewebhooks.py建 Incident 時寫alert_category(從 YAML 規則取)_push_decision_to_telegram()發完寫notification_type
P1-D:risk_level YAML 優先覆蓋 LLM
問題:LLM 習慣性回傳 high,YAML 規則的 risk 沒有覆蓋
解法:_dual_engine_analyze() 中,若 YAML 規則有 risk 值,優先用 YAML 的,不採用 LLM 的
驗收:HostHighCpuLoad(YAML risk=medium)→ Telegram 顯示 MEDIUM
P2-A:前端飛輪狀態即時顯示
問題:截圖顯示飛輪靜止,Playbook=0,成功率 0.5%,需人工介入
解法:前端飛輪元件(已有)連接以下 API:
GET /api/v1/stats/flywheel→ 返回六節點狀態(活躍/靜止/錯誤)GET /api/v1/stats/summary→ 返回 Playbook 數量、成功率、今日處理數- WebSocket 推送:每次節點狀態變化即時更新
驗收:飛輪動畫跟隨真實告警流轉動;Playbook 數量即時更新
P2-B:前端顯示人工介入路徑
問題:截圖第二張「需人工介入」時,飛輪顯示 TYPE-4(根因確認)和 TYPE-3(高風險審核)路徑
解法:飛輪元件增加「顯示人工干預路徑」toggle(已有),觸發時:
- 紅色虛線顯示從「推理匹配」到「人工處理中心」的路徑
- TYPE-3 / TYPE-4 標籤顯示在對應節點旁邊
六、實作優先順序與批准等待
以下所有變更等待首席架構師批准後才開始實作
Sprint ADR-073-A(本週):讓飛輪真的動起來
| # | 任務 | 風險等級 | 估計工時 |
|---|---|---|---|
| 1 | CD 修復 + 8be87b0 部署確認 |
Tier 2 | 30 分鐘 |
| 2 | signals alertname NULL 修復 | Tier 2 | 1 小時 |
| 3 | debounce 5→30 分鐘 | Tier 2 | 30 分鐘 |
| 4 | 冷啟動 Playbook 生成腳本(一次性) | Tier 2 | 2 小時 |
| 5 | 690 筆 KM 向量化腳本(一次性) | Tier 1 | 1 小時 |
Sprint ADR-073-B(下週):修復路由 + 通知品質
| # | 任務 | 風險等級 | 估計工時 |
|---|---|---|---|
| 6 | Docker/Host 告警 → SSH MCP 路徑 | Tier 3 | 3 小時 |
| 7 | action 解析「未知操作 | 」修復 | Tier 2 |
| 8 | NO_ACTION → TYPE-1 無按鈕 | Tier 2 | 1 小時 |
| 9 | outcome/alert_category/notification_type 寫入 | Tier 3 | 2 小時 |
| 10 | risk_level YAML 優先 | Tier 2 | 1 小時 |
Sprint ADR-073-C(再下週):前端飛輪即時化
| # | 任務 | 風險等級 | 估計工時 |
|---|---|---|---|
| 11 | /api/v1/stats/flywheel API |
Tier 1 | 2 小時 |
| 12 | 前端飛輪元件連接真實 API | Tier 2 | 3 小時 |
| 13 | WebSocket 即時推送 | Tier 2 | 2 小時 |
Sprint ADR-073-B 補充(整合 ADR-071 工作序,2026-04-12 更新)
| # | 任務 | 風險等級 | ADR-071 對應 |
|---|---|---|---|
| B-DB | DB Migration: incidents +9 欄位 | Tier 1 | ADR-071-A |
| B1 | 檢傷分類站前移(decision_manager 入口) | Tier 3 | ADR-071-A0+B |
| B2 | TYPE-1 純資訊卡片 | Tier 2 | ADR-071-C |
| B3 | KMConversionService(RESOLVED→KM→向量化) | Tier 2 | ADR-071-G |
| B4 | TYPE-4 手動修復記錄 | Tier 2 | ADR-071-H |
Sprint ADR-074(監控補全,依賴 ADR-073-A/B 完成後)
| # | 任務 | 告警名稱 | 嚴重度 |
|---|---|---|---|
| M1 | 飛輪健康度 Prometheus Exporter + 告警規則 | FlywheelPlaybookZero / FlywheelExecutionSuccessLow |
P0 Critical |
| M2 | 主機間網路 Blackbox probe | HostNetworkPartition |
P0 Critical |
| M3 | CoreDNS 解析失敗監控 | CoreDNSResolutionFailed |
P0 Critical |
| M4 | Gitea CI/CD 管線失敗 webhook | GiteaCIPipelineFailed |
P0 Critical |
| M5 | 備份還原週排程測試 | BackupRestoreTestFailed |
P0 Critical |
| M6 | Docker 188 容器詳細健康 | DockerContainerUnhealthyDetailed |
P1 Warning |
| M7 | Redis Streams 積壓 | RedisStreamBacklogHigh |
P1 Warning |
| M8 | PostgreSQL 磁碟增長率 | PostgreSQLDiskGrowthRate |
P1 Warning |
| M9 | Gemini API 錯誤率 | GeminiAPIErrorRateHigh |
P1 Warning |
完整規格:
docs/superpowers/specs/2026-04-12-aiops-complete-flywheel-repair-design.md
七、驗收標準(飛輪真正運轉的指標)
完成後 7 天內,以下指標必須達到:
| 指標 | 當前 | 目標 |
|---|---|---|
| Playbooks 數量 | 0 | ≥ 20 |
| EXECUTION_SUCCESS 率 | 0.5% | ≥ 30% |
| 重複告警同 Incident 率 | < 10% | ≥ 70% |
| KM vectorized | 0% | ≥ 90% |
| alertname NULL | 100% | 0% |
| 飛輪節點有活動的告警 | 0 | ≥ 1 筆/小時 |