From 09982fdfaa4762c3b98fc8f96f0866bbc67d1be3 Mon Sep 17 00:00:00 2001 From: OG T Date: Sat, 11 Apr 2026 20:04:50 +0800 Subject: [PATCH] =?UTF-8?q?docs(session6):=20Telegram=20=E5=85=A8=E9=9D=A2?= =?UTF-8?q?=E5=AF=A9=E8=A8=88=20+=20ADR-072=20Bug=20=E6=B8=85=E5=96=AE=20+?= =?UTF-8?q?=20=E8=A6=8F=E6=A0=BC=E6=95=B4=E5=90=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - LOGBOOK: Session 6 Redis DB10 審計結果(8個系統性問題,P0-P2分級) - ADR-072: AIOps 閉環 Bug 修復清單(drift_interpreter/deployment_name/KM vectorization等) - 規格文件 v2.2: 確認 Sprint A/B/C + MCP 1-4 + ADR-071 全部完成,標記下一步為 ADR-072 Co-Authored-By: Claude Sonnet 4.6 --- docs/LOGBOOK.md | 28 + docs/adr/ADR-072-aiops-bug-fixes.md | 102 ++ ...6-04-10-infra-rebuild-sprint-abc-design.md | 1154 ++++++++++++++++- 3 files changed, 1277 insertions(+), 7 deletions(-) create mode 100644 docs/adr/ADR-072-aiops-bug-fixes.md diff --git a/docs/LOGBOOK.md b/docs/LOGBOOK.md index 2ddf2346..5f586b8b 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -6,6 +6,34 @@ --- +## 📍 當前狀態 (2026-04-11 深夜 — Session 6 Telegram 全面審計 + 規格整合) + +### Session 6 Redis DB10 全面審計發現 (2026-04-11 深夜) + +**審計範圍**:62 個 DecisionToken、112 個 Incidents、Telegram 告警統計 + +| 發現 | 數字 | 根因 | +|------|------|------| +| DecisionToken error | 24/62 | deployment_name = "unknown"(無 component label)→ safety guard 攔截 | +| DecisionToken ready(無人審核)| 15/62 | approval flow UUID 問題 | +| 所有告警 = P3/High | 100% | Prometheus rules 未設 severity label | +| incident type = "custom" | 69/112 | alertname_to_type 只有 9 筆 | +| KM vectorized = False | 108/112 | RAG 知識庫未累積 | +| drift_interpreter 失敗 | 100% | nvidia.chat() 返回 NvidiaProviderResult 非 4-tuple | +| outcome/verification_result = null | 全部 | post-repair 驗證未寫入 | + +**ADR-072 Bug 清單建立**:8 個問題,P0/P1/P2 分級 + +**規格文件整合確認**: +- `docs/superpowers/specs/2026-04-10-infra-rebuild-sprint-abc-design.md` v2.1 +- Sprint A/B/C + MCP Phase 1-4 + ADR-071 A-J 全部 ✅(與 LOGBOOK 吻合) +- `project_master_workplan.md` 已更新(原來錯誤顯示 🔲) +- 執行順序確認無邏輯衝突(Section 7 依賴鏈正確) + +**下一步**:ADR-072 P0 Bug 修復(drift_interpreter + deployment_name resolution) + +--- + ## 📍 當前狀態 (2026-04-11 — MCP 全驗收完成 + Bug 修復) ### MCP 全驗收 + Bug 修復 (2026-04-11 深夜) diff --git a/docs/adr/ADR-072-aiops-bug-fixes.md b/docs/adr/ADR-072-aiops-bug-fixes.md new file mode 100644 index 00000000..2111ee66 --- /dev/null +++ b/docs/adr/ADR-072-aiops-bug-fixes.md @@ -0,0 +1,102 @@ +# ADR-072: AIOps 閉環 Bug 修復清單 + +> **狀態**: 進行中 +> **建立**: 2026-04-11 (台北時間) +> **背景**: Session 6 對 Redis DB10 + 程式碼全面審計發現 8 個系統性問題 + +--- + +## 背景 + +ADR-070/071 實作完成後,Session 6 對 62 個 DecisionToken、112 個 Incidents 及 Telegram 告警進行全面審計, +發現 MCP 雖已實作但閉環仍未真正生效。本 ADR 記錄所有發現與修復方案。 + +--- + +## Bug 清單 + +### P0 — 立即修復(閉環核心失效) + +#### BUG-001: drift_interpreter 永久失敗 +- **位置**: `apps/api/src/services/drift_interpreter.py:112` +- **症狀**: 所有 K8s 漂移解析失敗,`too many values to unpack (expected 4)` +- **根因**: `response_text, success, _tokens, _cost = await nvidia.chat(...)` — nvidia_provider 已重構為返回 `NvidiaProviderResult` 物件,非 4-tuple +- **修復方案**: 改用 `drift_narrator_service`(ADR-067 Phase 30,qwen2.5:7b-instruct,已存在)做漂移摘要,完全繞過 nvidia_provider +- **影響**: 所有 K8s config drift 告警無法生成可讀摘要 + +#### BUG-002: 自動修復 deployment name 全為 "unknown" +- **位置**: `apps/api/src/services/decision_manager.py:_auto_execute()` + `apps/api/src/services/incident_service.py:extract_affected_services()` +- **症狀**: 24/62 DecisionToken = error,safety guard 攔截(`"unknown" in action`) +- **根因**: HostHighCpuLoad / DockerContainerUnhealthy 等主機層告警只有 `{alertname, host}` label,無 `component`/`job`/`pod` → `extract_affected_services()` 返回 `[]` → target = "unknown" +- **修復方案**: 在 `_auto_execute()` 執行前,呼叫 K8s MCP `k8s_describe_pod` / `kubectl get pods` 根據 alert 類型動態查詢受影響 Pod,填入 target +- **影響**: 所有非 K8s 源頭告警的自動修復全部失敗 + +#### BUG-003: nemotron_validation 接受無效 deployment name +- **位置**: `apps/api/src/services/decision_manager.py:_nemoclaw_second_opinion()` +- **症狀**: ``、`HostHighCpuLoad`、`unknown` 等無效值通過 schema 驗證 +- **根因**: 只做 JSON schema 結構驗證,不驗證 deployment name 是否真實存在於 K8s +- **修復方案**: 加入 K8s MCP `k8s_describe_pod` 存在性檢查,若 deployment 不存在則拒絕並重新分析 +- **影響**: 無效的修復指令被誤判為合法 + +--- + +### P1 — 本 Sprint 修復(功能性問題) + +#### BUG-004: KM vectorization 108/112 = False +- **位置**: `apps/api/src/services/km_conversion_service.py` +- **症狀**: RAG 知識庫未累積,飛輪自癒閉環(ADR-068)學習效果為零 +- **根因**: KM 轉換服務呼叫後未確保 embedding 寫入 pgvector(Session 6 確認 `vectorized=False`,`persisted_to_pg=False`) +- **修復方案**: 修復 `convert()` 後的 embedding 寫入流程,確保 `nomic-embed-text` 768d 向量化並持久化 + +#### BUG-005: 15 ready decisions 無人審核 +- **位置**: incident_id → approval UUID 對應流程 +- **症狀**: 15 個 `status=ready` 的 DecisionToken 長期無審核,Telegram 未發出審核卡片 +- **根因**: approval flow 的 `incident_id` UUID 對應問題 +- **修復方案**: 診斷 `incident_id` 查詢鏈,修復 Telegram 審核卡片未發送的問題 + +#### BUG-006: outcome/verification_result 全 null +- **位置**: `apps/api/src/services/decision_manager.py:_push_auto_repair_result()` +- **症狀**: 所有已完成修復無 outcome 記錄 +- **根因**: post-repair 驗證邏輯未寫入 DB outcome 欄位 +- **修復方案**: 修復 `_push_auto_repair_result()` 確保 outcome + verification_result 寫入 + +--- + +### P2 — 下 Sprint 修復(數據品質問題) + +#### BUG-007: 所有告警 severity = P3/High(label 缺失) +- **位置**: `k8s/monitoring/alerts-unified.yml` +- **症狀**: 所有 62 個 DecisionToken 對應告警都是 P3(mapping: no label → P3) +- **根因**: Prometheus 告警規則大部分未設 `severity` label +- **修復方案**: 按告警類型補充 `severity: critical|high|warning|info` label + +#### BUG-008: 69/112 incidents type = "custom" +- **位置**: `apps/api/src/api/v1/webhooks.py:1103`(`alertname_to_type` 只有 9 筆) +- **症狀**: 大部分告警無法觸發對應 Playbook +- **根因**: `alertname_to_type` mapping 只覆蓋 9 種 K8s 告警,其他全部 → "custom" +- **修復方案**: 整合 ADR-064 Rule Engine,從 YAML 規則動態推斷告警類型,取代靜態 9 筆 mapping + +--- + +## 修復順序 + +``` +BUG-001 (drift_interpreter) → BUG-002 (deployment_name) → BUG-003 (nemotron_validation) +→ BUG-004 (KM vectorization) → BUG-005 (approval flow) → BUG-006 (outcome) +→ BUG-007 (severity labels) → BUG-008 (alertname_to_type) +``` + +--- + +## 驗收標準 + +| Bug | 驗收 | +|-----|------| +| BUG-001 | K8s config drift 告警 → Telegram 出現可讀摘要(非錯誤訊息)| +| BUG-002 | HostHighCpuLoad 告警 → DecisionToken status = completed(非 error)| +| BUG-003 | 無效 deployment name → nemotron 拒絕並重新分析 | +| BUG-004 | 新 Incident 完成後 vectorized = True + persisted_to_pg = True | +| BUG-005 | ready decisions → Telegram 發出審核卡片 | +| BUG-006 | 修復完成後 outcome + verification_result 不為 null | +| BUG-007 | 新告警 severity label 正確 → P0/P1/P2 分級出現 | +| BUG-008 | HostHighCpuLoad → incident_type = "host_cpu"(非 "custom")| diff --git a/docs/superpowers/specs/2026-04-10-infra-rebuild-sprint-abc-design.md b/docs/superpowers/specs/2026-04-10-infra-rebuild-sprint-abc-design.md index e8e95bcf..b604ca0f 100644 --- a/docs/superpowers/specs/2026-04-10-infra-rebuild-sprint-abc-design.md +++ b/docs/superpowers/specs/2026-04-10-infra-rebuild-sprint-abc-design.md @@ -1,10 +1,11 @@ # AWOOOI 基礎設施完整重建計畫 ## Sprint A + B + C — 正式設計規格文件 -> **文件版本**: v1.3 -> **建立日期**: 2026-04-10 -> **狀態**: 待實施 -> **ADR**: ADR-069(本文件為 ADR 核心規範) +> **文件版本**: v2.2 +> **建立日期**: 2026-04-10 +> **更新日期**: 2026-04-11 (Session 6 整合更新) +> **狀態**: ✅ Sprint A/B/C + MCP Phase 1-4 + ADR-071 A-J 全部完成 — 進入 ADR-072 Bug 修復階段 +> **ADR**: ADR-069(基礎設施重建)+ ADR-070(全自動 AIOps 閉環)+ ADR-072(Bug 修復) > **作者**: Claude Sonnet 4.6 + 首席架構師審查通過 --- @@ -1207,6 +1208,15 @@ argocd app create awoooi-prod \ | 設定漂移偵測 | `ansible-playbook site.yml --check` → 顯示 drift | | CD 新流程 | git push → ArgoCD 自動同步(無需手動 kubectl set image)| +### Sprint B 完成後 MCP Phase 1 驗收標準 + +| 能力 | 驗收方式 | +|------|---------| +| NIM 退場確認 | `AI_ROUTER` 中 NemotronProvider 已停用,無 Tool Calling 呼叫到 NIM | +| K8s MCP 強化 | PodRestartingTooMuch 告警 → AI 呼叫 `k8s_get_pod_logs` 取得真實 log → 決策 | +| 無幻覺驗證 | DecisionToken.target 永遠是真實 deployment 名稱,不出現 `unknown` / `` | +| rollout 驗證 | 執行 restart 後 AI 呼叫 `k8s_watch_rollout` 確認成功,而非 sleep 猜測 | + --- ## 六、Sprint C — 災難備援 @@ -1340,11 +1350,27 @@ echo "[$DATE] Backup completed" >> $LOG | K3s 設定被改 | `git revert + push` → ArgoCD 同步 | < 5 分鐘 | | Nginx conf 錯誤 | `ansible-playbook nginx-sync.yml` | < 5 分鐘 | +### Sprint C 完成後 MCP Phase 3 驗收標準 + +| 能力 | 驗收方式 | +|------|---------| +| ArgoCD MCP | ArgoCDSyncFailed 告警 → AI 呼叫 `argocd_get_app_status` → 自動 sync or rollback | +| Sentry MCP | AWOOOIApiDown + Sentry Error 同時發生 → AI 呼叫 `sentry_get_recent_errors` → 關聯分析 | +| MCP Phase 4 | HostHighCpuLoad 告警 → SSH MCP 查程序 → AI 動態提煉 symptom_pattern → KM 記錄含指標快照 | +| 動態規則 | 新型態告警(無 Playbook)→ AI 生成草稿 → Telegram 推送審核通知 | + --- ## 七、完整執行順序(無邏輯衝突) +> 包含 ADR-070 全自動 AIOps 閉環 MCP Phase 工作項目(§14) +> +> **2026-04-11 Session 6 狀態確認**: Sprint A ✅ / Sprint B ✅ / Sprint C ✅ / MCP 1-4 ✅ / ADR-071 A-J ✅ +> **下一步**: ADR-072 Bug 修復(BUG-001~008,見 `docs/adr/ADR-072-aiops-bug-fixes.md`) + ``` +══════════════ SPRINT A ══════════════ + A-0-1: 110 擴充 Swap ← 獨立,最先,防 OOM ↓ A-0-2: 修復 snuba crash loop ← 依賴 Swap 先擴充 @@ -1371,20 +1397,124 @@ A-9: 安裝 188 keepalived MASTER ← 依賴 A-5(nginx 收斂後) ↓ [Sprint A 全部驗收] ↓ + +══════════════ ADR-071 第一批(Sprint A 完成後立即開始,不依賴 MCP)══════════════ + +ADR-071-D: 狀態機守衛 ← 最優先,防止舊卡片被誤點執行 + handle_callback() 前加 incident.status 檢查 + ↓ +ADR-071-A: DB Migration ← incidents 表 +7 欄位,alert_operation_log +5 event_type + ↓ +ADR-071-B: 通知分類器 ← classify_notification() 插入現有 send_approval_card() 前 +ADR-071-C: TYPE-1 純資訊卡片 ← 同步,info/success 告警無按鈕 +ADR-071-E: TYPE-3 按鈕動態組合 ← 依 alert_category 選按鈕組合 +ADR-071-F: Config Drift 專屬卡片 ← TYPE-4D,Diff 截斷處理 + ↓ +ADR-071-G: KMConversionService ← Incident RESOLVED → 自動轉 KM + 向量化 + ↓ +ADR-071-H: TYPE-4 手動修復記錄 ← Bot 對話 → 草稿 Playbook → KM + ↓ +[ADR-071 第一批驗收] + → Backup.harbor info → 純文字,無按鈕 + → 點已解決卡片 → 回覆「已解決」並移除按鈕 + → database 告警 → 無 [重啟][擴容] 按鈕 + → Incident RESOLVED → knowledge_entries 自動新增 + +══════════════ MCP PHASE 2(Sprint A 完成後立即開始,不等 Sprint B)══════════════ + +MCP-2b: Prometheus MCP Server ← 無前提,Sprint A 一完成即可 + 實作: apps/api/src/plugins/mcp/providers/prometheus_provider.py + 工具: prometheus_query / prometheus_query_range / prometheus_get_alert_history + 目的: 情報收集前置,告警進來時 AI 立即掌握歷史趨勢 + ↓ +MCP-2a: SSH MCP Server ← 依賴 Sprint A(服務清單確認後,container 名稱/compose 路徑才固定) + 實作: apps/api/src/plugins/mcp/providers/ssh_provider.py + 工具: 診斷群組A(9個只讀工具)+ 操作群組B(6個,需 trust_score>=0.8) + 目的: 補完主機層 70% 告警盲點(HostHighCpuLoad/DockerExited/HarborDown/OllamaDown 等) + 前置: 建立 K8s Secret ssh-mcp-key,設定 SSH_MCP_ENABLED=true + ↓ +告警 label 補充(A 完成後) ← 修改 ops/monitoring/alerts-unified.yml + 加入: mcp_provider / host_type / target_host label + 目的: AI Router 自動路由到正確 MCP,不寫死 if/else + ↓ +[MCP Phase 2 驗收] + → HostHighCpuLoad 告警 → AI 自動呼叫 ssh_get_top_processes → 分析 → 決策 + → DockerContainerExited → AI 呼叫 ssh_docker_restart → K8s watch_rollout 驗證 + +══════════════ SPRINT B ══════════════ + B-1: Ansible Playbook 撰寫 ← B 的基礎,必須先完成 ↓ B-2: ArgoCD on K3s 安裝 ← 依賴 B-1(Host 層先管起來) ↓ -B-3: CD 流程改接 ArgoCD ← 依賴 B-2 +B-3: CD 流程改接 ArgoCD ← 依賴 B-2(移除 kubectl set image) ↓ [Sprint B 全部驗收] ↓ -C-1: Velero 安裝 ← 依賴 B-2(ArgoCD 穩定後) + +══════════════ MCP PHASE 1(Sprint B 完成後)══════════════ + +MCP-1: K8s MCP 強化 ← 依賴 Sprint B(ArgoCD 穩定後 K8s 操作才完整) + 新增工具: k8s_get_pod_logs / k8s_watch_rollout / k8s_get_events / k8s_describe_pod + k8s_get_hpa_status / k8s_get_node_conditions + 同步: NIM nemotron-mini-4b Tool Calling 退場(被 K8s MCP 取代) + 目的: 消除工具幻覺根本問題(不再由 LLM 猜 deployment 名稱,MCP 直接查真實值) + ↓ +[MCP Phase 1 驗收] + → K8s 告警 → MCP 查真實 Pod 名稱 → 精確 rollout restart → 無 "unknown" target + +══════════════ SPRINT C ══════════════ + +C-1: Velero 安裝 ← 依賴 B-2(ArgoCD 穩定後,避免 ArgoCD revert restore) C-2: rsync 備份腳本 ← 獨立,可與 C-1 平行 ↓ C-3: DR SOP 文件 ← 依賴 C-1 + C-2 ↓ -[全部完成] → 變更管理 + 災難備援目標達成 +[Sprint C 全部驗收] + ↓ + +══════════════ MCP PHASE 3(Sprint C 完成後)══════════════ + +MCP-3a: ArgoCD MCP Server ← 依賴 Sprint C(ArgoCD 穩定運作後) + 工具: argocd_get_app_status / argocd_sync_app / argocd_rollback_app / argocd_get_app_history + 目的: ArgoCDSyncFailed 告警 → AI 自動 sync 或 rollback + ↓ +MCP-3b: Sentry MCP Server ← 獨立,可與 MCP-3a 平行 + 工具: sentry_get_recent_errors / sentry_get_error_trend / sentry_get_issue_detail + 目的: 情報收集補完(前端 JS Error 與後端告警關聯分析) + ↓ +[MCP Phase 3 驗收] + → ArgoCDSyncFailed → AI 查 app 狀態 → 自動 rollback → Telegram 通知 + → AWOOOIApiDown + Sentry 同時有 Error → AI 關聯分析 → 更準確根因 + +══════════════ MCP PHASE 4(MCP 1/2/3 全部到位後)══════════════ + +MCP-4a: NemoClaw 降級顧問模式 ← 依賴 MCP Phase 1/2(工具可用性需可測試) + 實作: decision_manager._nemoclaw_advisory_mode() + 觸發: MCP 工具全失敗 OR OpenClaw 信心 < 0.7 + 目的: 無工具純推理 → advisory_note → 人工參考 + +MCP-4b: KM Playbook 三段資料強化 ← 依賴 MCP-2b(Prometheus MCP 提供指標快照) + 實作: learning_service + playbook_service 新增 metrics_before/after 參數 + 目的: Playbook 品質從「症狀→動作」升級到「症狀+情境→動作→效果數字+成功率」 + +MCP-4c: AI 動態規則生成 ← 依賴 MCP-4b(KM 夠豐富後才有意義) + 實作: symptom_pattern 由 AI 動態提煉(取代 hardcoded alertname→action mapping) + 新告警: 相似度 < 0.5 → AI 生成草稿 Playbook → 人工審核後生效 + ↓ + +══════════════ ADR-071 第二批(MCP Phase 2b 完成後)══════════════ + +ADR-071-I: TYPE-2 指標快照卡片 ← 依賴 MCP Phase 2b(Prometheus MCP) + metrics_before/after 寫入 incidents 表 + 自動修復卡片顯示指標前後對比(CPU 92%→23%) + ↓ +ADR-071-J: KM 三段資料整合 ← 依賴 ADR-071-I + MCP Phase 1(K8s watch_rollout) + PlaybookService.extract_from_incident() 含完整指標 + 驗證資料 + Playbook 品質從「症狀→動作」升級到「症狀+情境→動作→效果數字」 + ↓ +[全部完成] → 變更管理 + 災難備援 + 零寫死判斷全自動 AIOps 閉環 + + 四類型告警通知 + 全生命週期 DB 記錄 + 自動 KM 轉換 ``` --- @@ -1644,3 +1774,1013 @@ awoooi K8s pods(32334/32335)在 Sprint A/B/C 全程不觸碰。 --- *文件版本 v1.3(2026-04-10)— 整合專案工作計畫、文件更新時間表、ADR-069 建立規範* + +--- + +## 十四、全自動 AIOps 閉環架構(ADR-070) + +> **版本**: v1.0 +> **新增日期**: 2026-04-11 +> **前提**: Sprint A/B/C 完成後分階段實施 +> **核心原則**: 零寫死判斷 — 所有規則匹配、決策、修復、通知全部由 AI 動態產生與執行 + +--- + +### 14.1 設計哲學:為什麼不能有寫死的判斷 + +傳統 AIOps 的最大缺陷是 **hardcoded rule engine**: + +``` +傳統做法(有寫死判斷): + if alertname == "HostHighCpuLoad": + action = "restart nginx" ← 永遠是同一個動作,不管真正原因 + elif alertname == "PodCrashLooping": + action = "kubectl rollout restart" ← 不管是 OOM 還是 config 錯誤 + +問題: + - 環境變了,規則沒更新 → 錯誤修復 + - 新的告警種類出現 → 完全沒有規則 + - 同一種告警有十種根因 → 只能處理其中一種 +``` + +**AWOOOI AIOps 的目標**: + +``` +動態做法(AI 判斷): + 告警進來 → AI 實時收集情境 → AI 動態生成匹配規則 + → AI 判斷根因 → AI 選擇動作 → 執行 → AI 驗證效果 + → 結果沉澱 KM → 下次判斷更準 + +沒有任何 if/else 寫死 — 規則是 AI 從歷史學習動態生成的 +``` + +--- + +### 14.2 告警種類完整盤點 + +基於現有 alert rule 檔案(k3s-alerts.yaml、k3s-alerts-supplemental.yaml、database-alerts.yaml、minio-kali-alerts.yaml、nvidia-alerts.yaml、alert-chain-monitor.yaml、alerts-unified.yml)的完整分類: + +#### 類別 A:硬體資源(主機層,Node Exporter) + +| 告警名 | 說明 | MCP 可接 | 自動修復能力 | +|--------|------|---------|------------| +| HostHighCpuLoad | CPU > 85% | SSH MCP (Phase MCP-2) | 查程序→分析→建議 restart | +| HostOutOfMemory | RAM 不足 | SSH MCP | 查程序→kill OOM→建議 | +| HostOutOfDiskSpace | 磁碟空間 | SSH MCP | 查大檔→清 log/image | +| HostDown | 主機宕機 | ❌ 無法自動 | 純告警+人工 | +| NodeHighCPU / NodeHighMemory | K3s Node 資源 | K8s MCP | cordon + 驅逐 Pod | +| NodeMemoryPressure / NodeDiskPressure / NodePIDPressure | K3s 壓力 | K8s MCP | cordon + 驅逐 | +| NodeNotReady / NodeUnschedulable | Node 不健康 | K8s MCP | uncordon 或升級 | + +#### 類別 B:K3s / Kubernetes 應用層 + +| 告警名 | 說明 | MCP 可接 | 自動修復能力 | +|--------|------|---------|------------| +| PodRestartingTooMuch / KubePodCrashLooping | Pod 重啟 | K8s MCP | rollout restart | +| DeploymentReplicasMismatch | 副本數錯 | K8s MCP | scale / rollout | +| PodPendingTooLong / KubePodNotReady | Pod 卡住 | K8s MCP | describe + event 分析 | +| AWOOOIApiDown / AWOOOIWebDown | 應用宕機 | K8s MCP | rollout restart | +| K3sNodeDown / K3sVIPDown | K3s 節點 | K8s MCP | drain/uncordon | +| HPAMaxedOut / HPAScalingDisabled | HPA 失效 | K8s MCP | 調整上限 | +| WorkerUnavailable | Worker 無副本 | K8s MCP | scale up | +| ContainerOOMKilled | OOM 殺容器 | K8s MCP | 分析+建議調高 limit | +| DaemonSetMissingPods / StatefulSetReplicasMismatch | 工作負載 | K8s MCP | rollout restart | +| CronJobLastRunFailed | CronJob 失敗 | K8s MCP | 手動觸發 | +| ArgoCDSyncFailed | ArgoCD 同步失敗 | ArgoCD MCP (Sprint B後) | sync / rollback | +| VeleroBackupFailed / VeleroBackupMissing | 備份失敗 | K8s MCP | 觸發重新備份 | +| TLSCertExpiringIn30Days/7Days/Expired | TLS 憑證 | SSH MCP | certbot renew | +| TLSProbeFailure | TLS 探針失敗 | SSH MCP | 分析 nginx conf | + +#### 類別 C:資料庫(PostgreSQL + Redis) + +| 告警名 | MCP | 自動修復能力 | +|--------|-----|------------| +| PostgreSQLDown / RedisDown | K8s MCP | rollout restart | +| PostgreSQLConnectionPoolNearLimit/Exhausted | DB MCP (現有) | 斷開閒置連線 | +| PostgreSQLSlowQueries / LongRunningQuery | DB MCP | TERMINATE 長查詢 | +| PostgreSQLLockWaiting / Deadlocks | DB MCP | 終止 blocking session | +| PostgreSQLTableBloat | DB MCP (部分) | 建議 VACUUM | +| RedisMemoryHigh / Critical | DB MCP | 清 TTL 過期 key | +| RedisLatencyHigh / RedisBlockedClients | DB MCP | UNBLOCK client | + +#### 類別 D:外部網站 / Blackbox + +| 告警名 | MCP | 自動修復能力 | +|--------|-----|------------| +| MoWoooWorkDown / TsenyangWebsiteDown | SSH+Docker MCP | docker restart | +| StockWoooWorkDown / BitanWoooWorkDown | SSH+Docker MCP | docker restart | +| ExternalSiteSSLExpiringSoon | SSH MCP | certbot renew | + +#### 類別 E:Docker 容器(主機層) + +| 告警名 | MCP | 自動修復能力 | +|--------|-----|------------| +| DockerContainerUnhealthy | SSH+Docker MCP | docker restart | +| DockerContainerExited | SSH+Docker MCP | docker start | + +#### 類別 F:DevOps 工具服務 + +| 告警名 | MCP | 自動修復能力 | +|--------|-----|------------| +| HarborDown | SSH MCP | docker compose restart | +| GiteaDown | SSH MCP | docker compose restart | +| OpenClawDown | K8s MCP | rollout restart | +| SignOzDown | SSH MCP | docker compose restart | +| SentryDown | SSH MCP | docker compose restart | +| OllamaDown | SSH MCP | systemctl restart ollama | +| AlertmanagerDown | K8s or SSH MCP | 視部署位置 | +| MinIODown / MinioDiskUsageHigh | SSH MCP | docker restart / 清備份 | +| KaliScannerDown | SSH MCP | systemctl restart | + +#### 類別 G:告警鏈路自監控 + +| 告警名 | 處理方式 | +|--------|---------| +| AlertChainBroken_* | 純告警+人工(自身壞掉難自動修)| +| NoAlertsReceived2Hours | 純告警+人工(靜默異常)| +| AutoRepairLowSuccessRate | 觸發 NemoClaw 推理仲裁 | +| PermanentFixRequired | 人工更新 Playbook | + +#### 類別 H:AI 工具層 + +| 告警名 | MCP | 自動修復能力 | +|--------|-----|------------| +| NvidiaCircuitBreakerOpen | AI Router MCP | 切換備援 Provider | +| NvidiaToolCallingHighLatency/HighErrorRate | AI Router MCP | 降級到 Ollama | + +#### 類別 I:資安(目前告警盲點) + +| 類型 | 現況 | 未來規劃 | +|------|------|---------| +| 入侵偵測 / 異常登入 | **無告警規則** | Phase MCP-3 後,112 Kali 整合 | +| 掃描攻擊 / DDoS | **無告警規則** | Nginx access log 分析 | +| 敏感設定漂移 | Config Drift(無資安視角)| Ansible drift detection | + +#### 類別 J:UI/UX / 前端(目前告警盲點) + +| 類型 | 現況 | 未來規劃 | +|------|------|---------| +| 前端 JS Error | Sentry 捕捉,無 Prometheus 橋 | Sentry MCP (Phase MCP-3) | +| 效能退化 (LCP/FID) | **完全沒有** | SignOz OTEL 補充 | +| API 4xx/5xx 趨勢 | SignOz 有指標,告警不完整 | Prometheus MCP 補規則 | + +--- + +### 14.3 MCP Server 完整規劃 + +#### MCP-0:現有框架(已上線) + +``` +apps/api/src/plugins/mcp/providers/ +├── k8s_provider.py ← K8s 操作(kubectl_get/delete/scale/restart) +├── database_provider.py ← PostgreSQL + Redis 操作 +├── signoz_provider.py ← SignOz 查詢 +├── grafana_provider.py ← Grafana 查詢 +├── rag_provider.py ← RAG 知識庫查詢 +└── filesystem_provider.py +``` + +--- + +#### MCP Phase 1:K8s MCP 強化(Sprint B 後) + +**目標**:替換 NIM nemotron-mini-4b Tool Calling,消除工具幻覺根本問題。 + +**新增工具**: + +| 工具名 | 說明 | +|--------|------| +| `k8s_get_pod_logs` | 取得 Pod 最後 N 行 log(取代 log_summary 手動呼叫)| +| `k8s_watch_rollout` | 監控 rollout 狀態直到完成(真正的驗證,非 sleep 猜)| +| `k8s_get_events` | 取得 namespace/resource 的 K8s events | +| `k8s_describe_pod` | 完整 Pod describe(含 Conditions、Volumes、Env)| +| `k8s_get_hpa_status` | 取得 HPA 當前副本數/上限/下限 | +| `k8s_get_node_conditions` | 取得 Node conditions(Ready/MemoryPressure/DiskPressure)| + +**安全守衛強化**: +- 參數白名單驗證(namespace 限 `awoooi-prod`) +- 操作類工具需 `trust_engine.get_score() >= 0.7` +- 所有操作寫 audit_log → PostgreSQL + +--- + +#### MCP Phase 2:SSH MCP Server + Prometheus MCP(Sprint A 完成後即可開始) + +**SSH MCP Server**(補完主機層 70% 盲點) + +```python +# apps/api/src/plugins/mcp/providers/ssh_provider.py +class SSHProvider(MCPToolProvider): + """ + SSH MCP Provider — 主機層操作代理 + Phase MCP-2: 補完 K8s 無法覆蓋的 70% 主機層告警 + 絞殺者開關: SSH_MCP_ENABLED env var + """ +``` + +**群組 A:診斷類(只讀,無需信任度檢查)** + +| 工具名 | 對應告警 | 執行指令 | +|--------|---------|---------| +| `ssh_get_top_processes` | HostHighCpuLoad, HostOutOfMemory | `ps aux --sort=-%cpu \| head 15` | +| `ssh_get_disk_usage` | HostOutOfDiskSpace, MinioDiskUsageHigh | `df -h && du -sh /var/lib/docker` | +| `ssh_get_memory_info` | HostOutOfMemory | `free -h` | +| `ssh_get_container_logs` | DockerContainerExited, HarborDown | `docker logs --tail 50` | +| `ssh_get_container_status` | 所有 DockerContainer* | `docker ps -a --filter name=` | +| `ssh_get_service_status` | OllamaDown, KaliScannerDown | `systemctl status ` | +| `ssh_check_port` | 任何服務宕機確認 | `ss -tlnp \| grep ` | +| `ssh_get_nginx_error_log` | 網站宕機, TLS 問題 | `tail -n 50 /var/log/nginx/error.log` | +| `ssh_get_swap_info` | HostOutOfMemory | `swapon --show && free -h` | + +**群組 B:安全操作類(需 trust_engine score >= 0.8)** + +| 工具名 | 對應告警 | 執行指令 | 風險 | +|--------|---------|---------|------| +| `ssh_docker_restart` | DockerContainerExited, HarborDown | `docker restart ` | 低 | +| `ssh_docker_compose_restart` | SentryDown, SignOzDown, GiteaDown | `cd && docker compose restart ` | 中 | +| `ssh_systemctl_restart` | OllamaDown, KaliScannerDown | `systemctl restart ` | 低 | +| `ssh_clear_docker_logs` | HostOutOfDiskSpace(log 佔用)| `truncate -s 0 ` | 低 | +| `ssh_renew_ssl` | TLSCertExpiringIn7Days | `certbot renew --cert-name ` | 低 | +| `ssh_reload_nginx` | TLSProbeFailure, conf 更新後 | `nginx -t && systemctl reload nginx` | 低 | + +**永遠禁止執行清單(硬編碼守衛)**: + +```python +FORBIDDEN_PATTERNS = [ + r"rm\s+-rf", # 遞歸刪除 + r"/etc/passwd", # 系統帳號 + r"authorized_keys", # SSH key + r"sudoers", # 權限設定 + r"\$\(", # 命令替換 + r"`", # 反引號執行 + r"\|.*rm", # pipe 到刪除 + r">\s*/etc/", # 重定向到系統目錄 +] +``` + +**SSH 連線安全架構**: + +``` +連線方式: asyncssh(純 Python,不需系統 ssh binary) +認證方式: Private Key(從 K8s Secret 掛載) + Secret 名稱: ssh-mcp-key + 掛載路徑: /run/secrets/ssh_mcp_key +允許主機: SSH_MCP_ALLOWED_HOSTS = ["192.168.0.188", "192.168.0.110", "192.168.0.111"] +連線池: 每 host 保持 1 個長連線,idle 5 分鐘後關閉 +超時: 診斷類 10s,操作類 60s +``` + +**四層安全守衛**(缺一不可): + +``` +守衛 1: tool_name 在白名單? → 不在: 直接拒絕 +守衛 2: host 在 ALLOWED_HOSTS? → 不在: 直接拒絕 +守衛 3: 參數正則驗證(FORBIDDEN_PATTERNS)→ 命中: 直接拒絕 +守衛 4: 群組 B 工具 → trust_score >= 0.8 → 不足: 建議人工執行 +``` + +**Prometheus MCP Server** + +| 工具名 | 說明 | 提供給 KM 的情境 | +|--------|------|----------------| +| `prometheus_query` | 即時指標查詢 | CPU/RAM/Error Rate 當前值 | +| `prometheus_query_range` | 歷史趨勢(1h/6h/24h)| 「今天第幾次高 CPU?趨勢是上升還是偶發?」| +| `prometheus_get_alert_history` | 同一告警過去 7 天 | 「這個告警每天都觸發 → 可能是正常排程行為」| +| `prometheus_get_firing_rules` | 當前觸發中的規則 | 告警是否有相關聯的其他告警同時觸發 | + +--- + +#### MCP Phase 3:ArgoCD MCP + Sentry MCP(Sprint C 後) + +**ArgoCD MCP Server** + +| 工具名 | 說明 | +|--------|------| +| `argocd_get_app_status` | 取得應用同步狀態(Synced/OutOfSync/Degraded)| +| `argocd_sync_app` | 觸發應用同步 | +| `argocd_rollback_app` | 回滾到指定 revision | +| `argocd_get_app_history` | 取得部署歷史 | + +**Sentry MCP Server** + +| 工具名 | 說明 | +|--------|------| +| `sentry_get_recent_errors` | 取得服務近 1 小時的錯誤列表 | +| `sentry_get_error_trend` | 取得錯誤率趨勢(是否在上升)| +| `sentry_get_issue_detail` | 取得特定錯誤的 stack trace | + +**告警 label 補充**: + +```yaml +# 主機層告警需加 host_type label,讓 AI 知道應該用哪個 MCP +# 修改 ops/monitoring/alerts-unified.yml +- alert: HostHighCpuLoad + labels: + host_type: "bare_metal" # ← 新增 + mcp_provider: "ssh_host" # ← 新增,告訴 AI Router 用哪個 MCP + target_host: "{{ $labels.instance | split(':') | first }}" # ← 新增 +``` + +--- + +#### MCP Phase 4:NemoClaw 整合 + KM 強化 + +**NemoClaw 降級顧問模式**: + +```python +# decision_manager.py 中,MCP 工具全失敗時的 fallback + +async def _nemoclaw_advisory_mode(incident: Incident, rag_results: list) -> str: + """ + MCP 工具失敗時,NemoClaw 提供純推理建議 + 不執行任何操作,只輸出 advisory_note 供人工參考 + """ + context = f""" + 告警: {incident.signals[0].labels.get('alertname')} + 歷史相似案例(RAG): {rag_results} + MCP 工具狀態: 全部不可用 + 請用 SRE 推理給出診斷和建議(不執行,僅建議) + """ + result = await nemoclaw.generate(context) + # 寫入 incident.advisory_note → Telegram 顯示 + # 追蹤 NemoClaw 建議 vs 最終人工操作 → 評估準確率 + return result +``` + +**KM Playbook 品質強化**(MCP 帶三段資料): + +```python +# learning_service.py 中 PlaybookService.extract_from_incident() 新增參數 + +await playbook_service.extract_from_incident( + incident=incident, + context_bundle=context_bundle, # MCP 情報層收集的即時情境 + execution_result=execution_result, # 執行結果 + metrics_before=metrics_before, # Prometheus MCP 執行前快照 + metrics_after=metrics_after, # Prometheus MCP 執行後快照(5分鐘後) + k8s_state_after=k8s_state_after, # K8s MCP 執行後狀態 +) + +# Playbook 品質因此從: +# "症狀: CPU>85% → 動作: restart" +# 升級為: +# "症狀: CPU>85% + top 程序是 awoooi-worker + 同時有 Sentry OOM 告警 +# → 動作: kubectl rollout restart deployment/awoooi-worker +# → 效果: CPU 從 92% 降到 23% in 45s(Prometheus 驗證) +# → 成功率: 8/10" +``` + +--- + +### 14.4 全自動 AIOps 閉環完整流程 + +``` +╔══════════════════════════════════════════════════════════════╗ +║ 全自動 AIOps 閉環(零寫死判斷) ║ +╚══════════════════════════════════════════════════════════════╝ + +Prometheus / Node Exporter / Blackbox / Sentry / SignOz + ↓ 指標觸發 + Alertmanager(YAML 規則) + ↓ webhook POST /api/v1/alerts/webhook + incident_service(建立 Incident) + ↓ +┌──────────── [情報收集層 — MCP 感知] ────────────────────────────┐ +│ 依據告警 labels.mcp_provider 自動路由到對應 MCP: │ +│ │ +│ host_type=bare_metal → SSH MCP: │ +│ ssh_get_top_processes(host) → 哪個程序吃 CPU? │ +│ ssh_get_container_status(name) → 容器真實狀態 │ +│ │ +│ host_type=k8s → K8s MCP: │ +│ k8s_get_pod_logs(pod, namespace) → 最近 log │ +│ k8s_describe_pod(pod) → 事件時間軸 │ +│ k8s_get_node_conditions() → Node 健康度 │ +│ │ +│ 永遠執行 → Prometheus MCP: │ +│ prometheus_query_range(metric, 1h) → 趨勢 │ +│ prometheus_get_alert_history(alertname, 7d) → 頻率 │ +│ │ +│ 如有 Sentry 相關 → Sentry MCP: │ +│ sentry_get_recent_errors(service) → 同時有 JS Error? │ +└──────────────────────────┬─────────────────────────────────────┘ + ↓ ContextBundle(即時情境包) +┌──────────── [KM 歷史智慧查詢層] ────────────────────────────────┐ +│ RAG 查詢(nomic-embed 768d): │ +│ query = alertname + top_process + error_pattern │ +│ → 找最相關 runbook / ADR / 過去 incident │ +│ │ +│ Playbook 語意匹配(pgvector): │ +│ symptom_pattern = ContextBundle 提煉 │ +│ → 相似度 >= 0.85 → 直接使用 Playbook │ +│ → 相似度 0.7-0.85 → 作為參考,仍走 LLM 分析 │ +│ │ +│ Episodic Memory 召回: │ +│ 找 30 天內 same service + same alert 的歷史事件 │ +│ → 「這是第 3 次,前兩次都是 Docker OOM,restart 成功」 │ +│ │ +│ GraphRAG 影響範圍: │ +│ blast_radius(affected_service) → 誰會連帶受影響? │ +└──────────────────────────┬─────────────────────────────────────┘ + ↓ KM 結果包 +┌──────────── [OpenClaw AI 決策層] ───────────────────────────────┐ +│ 輸入: ContextBundle(即時)+ KM 結果包(歷史) │ +│ │ +│ OpenClaw 判斷: │ +│ 1. 根因是什麼?(從情境包 + 歷史推斷) │ +│ 2. 動作是什麼?(從 KM Playbook 或 LLM 生成) │ +│ 3. 目標是什麼?(從 K8s MCP / SSH MCP 確認真實名稱) │ +│ 4. 風險是什麼?(blast_radius + 操作類型) │ +│ 5. 信心是多少?(0.0-1.0) │ +│ │ +│ 信心 < 0.7 → 觸發 NemoClaw second opinion: │ +│ NemoClaw 獨立分析同一份資料 → 產生備選方案 │ +│ Router 對比兩個 AI 的建議 → 選信心較高者 or 升級人工審核 │ +└──────────────────────────┬─────────────────────────────────────┘ + ↓ DecisionToken(action + target + confidence) +┌──────────── [決策守衛層] ───────────────────────────────────────┐ +│ 守衛 1: target 不是 placeholder / unknown / alertname │ +│ 守衛 2: action 不含未解析 {xxx} / │ +│ 守衛 3: 根據 risk_level 決定是否需人工審核 │ +│ 守衛 4: 信心 < auto_approve 閾值 → 人工審核 │ +└──────────────────────────┬─────────────────────────────────────┘ + ↓ + ┌───────────┴───────────┐ + 自動執行(信心高) 人工審核(Telegram 卡片) + ↓ ↓ 批准/拒絕 +┌──────────── [MCP 執行層] ───────────────────────────────────────┐ +│ K8s 操作 → K8s MCP: kubectl_restart / scale / delete │ +│ 主機操作 → SSH MCP: docker restart / systemctl restart │ +│ GitOps → ArgoCD MCP: sync / rollback(Sprint B 後) │ +│ DB 操作 → DB MCP: terminate_query / unblock_client │ +└──────────────────────────┬─────────────────────────────────────┘ + ↓ 執行完成 +┌──────────── [MCP 驗證層] ───────────────────────────────────────┐ +│ K8s MCP: watch_rollout_status(deploy, 120s) → 真正的等待確認 │ +│ Prometheus MCP: query(error_rate, after=now+5m) → 指標恢復? │ +│ SSH MCP: get_container_status(name) → 容器真的 Up? │ +└──────────────────────────┬─────────────────────────────────────┘ + ↓ 驗證結果(success + metrics_snapshot) +┌──────────── [Telegram 通知層] ──────────────────────────────────┐ +│ 成功: 通知修復完成 + 修復耗時 + 指標恢復數字 │ +│ 失敗: 通知失敗原因 + NemoClaw advisory_note + 建議人工步驟 │ +│ 追加: NemoClaw Log 摘要(Phase 31,deepseek-r1 分析 pod log) │ +└──────────────────────────┬─────────────────────────────────────┘ + ↓ 結果資料 +┌──────────── [KM 學習層] ────────────────────────────────────────┐ +│ LearningService.record_feedback( │ +│ execution_result, │ +│ metrics_before, metrics_after, ← Prometheus MCP 採集 │ +│ k8s_state_after, ← K8s MCP 採集 │ +│ ) │ +│ │ +│ PlaybookService.extract_from_incident( │ +│ 帶三段資料: 執行前情境 + 執行動作 + 執行後驗證 │ +│ ) │ +│ │ +│ NemoClaw 準確率追蹤: │ +│ advisory_note vs 最終人工操作 → accuracy_score │ +│ 準確率 > 80% → 升格 advisory 為「低信心 Playbook」 │ +└─────────────────────────────────────────────────────────────────┘ + ↓ + 下一次同類告警:信心更高、動作更精準 + (KM 越來越聰明,不需要人工寫規則) +``` + +--- + +### 14.5 AI 動態規則生成(取代 hardcoded rule engine) + +**核心:symptom_pattern 由 AI 自動生成,不寫死** + +```python +# auto_repair_service.py 中 + +async def _build_symptom_pattern_from_context( + incident: Incident, + context_bundle: ContextBundle, # MCP 收集的即時情境 +) -> SymptomPattern: + """ + 不寫死任何 alertname→action 的對應 + 讓 AI 從即時情境 + 歷史案例中,動態提煉 SymptomPattern + """ + # AI 提煉:「這次的症狀特徵是什麼?」 + symptom_prompt = f""" + 告警資訊: {incident.signals[0].labels} + 即時情境: {context_bundle.summary} + 請提煉症狀關鍵字,用於 Playbook 語意匹配(不超過 50 字) + """ + symptom_text = await openclaw.extract(symptom_prompt) + + return SymptomPattern( + alert_type=incident.signals[0].labels.get("alertname"), + symptom_description=symptom_text, # AI 動態生成 + affected_resource=context_bundle.confirmed_target, # MCP 確認的真實名稱 + severity=incident.severity, + ) +``` + +**AI 自學習新告警種類**: + +```python +# 當 Playbook 相似度 < 0.5(完全沒有歷史案例)時: +if max_similarity < 0.5: + # AI 生成全新 Playbook 草稿 + draft = await openclaw.generate_playbook_draft( + incident=incident, + context_bundle=context_bundle, + ) + # 狀態 = DRAFT,人工審核後才生效 + await playbook_service.create_draft(draft, requires_approval=True) + # Telegram 通知:「發現新的告警模式,已生成草稿 Playbook,請審核」 +``` + +--- + +### 14.6 MCP Phase 實施順序與依賴關係 + +``` +[Sprint A 完成後,立即可以開始] + MCP Phase 2a: SSH MCP Server + 前提: Sprint A 清理完畢 → 知道哪些 container 名稱、哪些 compose 路徑 + 優先級: P0(補完 70% 主機層盲點) + 工作量: 1 個新 Provider 檔案 + registry 一行 + K8s Secret + + MCP Phase 2b: Prometheus MCP Server + 前提: 無(Prometheus 現在就在) + 優先級: P0(情報收集最重要) + 工作量: 1 個新 Provider 檔案 + +[Sprint B 完成後] + MCP Phase 1 強化: K8s MCP 新增工具 + 前提: ArgoCD 到位後,K8s 操作才完整 + 優先級: P1 + 工作量: 在現有 k8s_provider.py 新增 4 個工具 + + K8s YAML alert label 補充: + 告警 YAML 加 mcp_provider / host_type / target_host label + 讓 AI Router 自動決定用哪個 MCP + +[Sprint C 完成後] + MCP Phase 3: ArgoCD MCP + Sentry MCP + 前提: ArgoCD 穩定運作(Sprint B/C 後) + 優先級: P2 + + MCP Phase 4: NemoClaw 整合 + KM Playbook 三段資料 + 前提: MCP Phase 1/2/3 全部到位 + 優先級: P3 + +[衝突檢查] + ✅ MCP Phase 2 可在 Sprint B 之前開始(不依賴 ArgoCD) + ✅ 告警 label 補充在 MCP Phase 3 前做(準備工作) + ❌ ArgoCD MCP 不能在 Sprint B 之前做(ArgoCD 還沒裝) + ❌ NIM 退場不能在 K8s MCP Phase 1 強化之前做(會失去 Tool Calling 能力) +``` + +--- + +### 14.7 與 Sprint A/B/C 的整合執行順序(全局視圖) + +``` +立即可做(獨立): + Sprint A-0 Sentry 修復 + Swap + SENTRY_DSN + D1 models.json 集中化(各 Service 模型名稱硬編碼問題) + B2 UI 前端拓撲骨架(React Flow 元件,資料源等 ArgoCD 後接) + +Sprint A 主線: + A-1 ~ A-9 按 §七 執行順序 + +[Sprint A 全驗收後] + MCP Phase 2a: SSH MCP Server ← 立即開始,不等 B + MCP Phase 2b: Prometheus MCP ← 立即開始,不等 B + +Sprint B: + B-1 Ansible IaC + B-2 ArgoCD GitOps + 修改 cd.yaml(移除 kubectl set image) + B-3 keepalived HA + +[Sprint B 全驗收後] + MCP Phase 1: K8s MCP 新增工具 + NIM 退場 + 告警 label 補充 + +Sprint C: + C-1 Velero + C-2 rsync + C-3 DR 演練 + +[Sprint C 全驗收後] + MCP Phase 3: ArgoCD MCP + Sentry MCP + MCP Phase 4: NemoClaw 整合 + KM 三段資料 + B2 資料源: 接真實 ArgoCD API + +[全部完成] → 零寫死判斷的全自動 AIOps 閉環 +``` + +--- + +### 14.8 NemoTron(deepseek-r1:14b)完整角色清單 + +NemoTron 不退場,在 MCP 架構後升格為**推理仲裁引擎**: + +| 場景 | 觸發條件 | 做什麼 | 現況 | +|------|---------|--------|------| +| Log 異常摘要 | 告警後 auto_repair 觸發 | 摘要 pod logs | ✅ Phase 31 已上線 | +| Chat 戰術參謀 | `@nemo` 或雙 AI 對話 | 補充 OpenClaw 視角 | ✅ 已上線 | +| 降級顧問模式 | MCP 工具全部失敗 | 純推理建議(不執行)→ advisory_note | 🔲 MCP Phase 4 | +| 複雜推理仲裁 | OpenClaw 信心 < 0.7 | second opinion,對比兩 AI 建議 | 🔲 MCP Phase 4 | +| 新 Playbook 草稿審核 | 相似度 < 0.5 新告警 | 審核 AI 生成的草稿是否合理 | 🔲 MCP Phase 4 | +| NemoClaw 準確率追蹤 | 每次 advisory_note 後 | 對比建議 vs 最終操作 → 學習 | 🔲 MCP Phase 4 | + +--- + +--- + +### 14.9 告警通知四類型設計(ADR-071) + +> 解決「不管什麼事都發核彈按鈕」的告警疲勞問題。 +> 原則:通知格式由告警性質決定,不由人工設定。 + +#### 14.9.1 四種通知類型 + +| 類型 | 名稱 | 使用者認知狀態 | 觸發條件 | 按鈕 | +|------|------|-------------|---------|------| +| **TYPE-1** | 純資訊通知 | 知道了(FYI)| severity=info + success/completed | ❌ 無按鈕 | +| **TYPE-2** | 已自動修復 | 做得很棒(Acknowledge)| auto_executed=True + COMPLETED | [查看詳情] | +| **TYPE-3** | 需人工審核 | 我來決定(Action Required)| 預設,可操作告警 | 按 alert_category 動態組合 | +| **TYPE-4** | AI 無法判斷 | 我來查修(Investigation Needed)| 信心<0.5 / MCP失敗 / 新型告警 | [忽略] [升級] [手動修復後記錄] | +| **TYPE-4D** | Config Drift 專屬 | 我來查修(Drift)| alertname=ConfigDrift | [查看Diff] [採納變更] [回滾] [忽略] | + +#### 14.9.2 分類器邏輯(零寫死) + +```python +def classify_notification(incident, decision_token, auto_executed) -> NotificationType: + + labels = incident.signals[0].labels if incident.signals else {} + alertname = labels.get("alertname", "") + label_severity = labels.get("severity", "") + + # TYPE-4D:Config Drift 專屬(優先判斷) + if alertname == "ConfigDrift": + return TYPE_4_DRIFT + + # TYPE-1:純資訊(severity=info + 成功類告警) + # 注意:同一 alertname 若 severity 變 critical → 不走 TYPE-1 + if label_severity == "info" and any(kw in incident.title.lower() + for kw in ["success", "完成", "completed"]): + return TYPE_1 + if alertname.startswith(("Backup.", "VeleroBackup")) and label_severity == "info": + return TYPE_1 + if alertname in ("AlertChainHealthy", "AutoRepairHighSuccessRate"): + return TYPE_1 + + # TYPE-2:已自動修復完成 + if auto_executed and decision_token.state == DecisionState.COMPLETED: + return TYPE_2 + + # TYPE-4:AI 無法判斷 + if (decision_token.confidence < 0.5 + or decision_token.state == DecisionState.ERROR + or decision_token.mcp_all_failed): + return TYPE_4 + + # TYPE-3:預設(需人工審核) + return TYPE_3 +``` + +#### 14.9.3 TYPE-3 按鈕依 alert_category 動態組合 + +``` +alert_category(由告警 label 或 AI 分類決定) + +k8s_workload → [重啟] [擴容] [縮容] [回滾] [查看詳情] [忽略] +database → [終止慢查詢] [清連線池] [查看詳情] [忽略] +host_resource → [查程序] [重啟服務] [清 Log] [查看詳情] [忽略] +network → [重載 Nginx] [查 Port] [查看詳情] [忽略] +devops_tool → [重啟服務] [查 Log] [查看詳情] [忽略] +ai_system → [切換 Provider] [查看詳情] [忽略] +ssl_cert → [更新憑證] [查看詳情] [忽略] +``` + +#### 14.9.4 狀態機守衛(State Machine Guardrails) + +callback 進來時強制校驗 Incident 當下狀態: + +```python +async def handle_callback(callback_data, incident_id, user, ...): + + incident = await incident_repo.get(incident_id) + + # 守衛:RESOLVED/CLOSED → 拒絕執行,更新卡片 + if incident.status in (IncidentStatus.RESOLVED, IncidentStatus.CLOSED): + await telegram.edit_message( + message_id, text=f"✅ 此事件已於 {incident.resolved_at} 由 @{resolved_by} 解決" + ) + await telegram.remove_buttons(message_id) + return {"blocked": True, "reason": "already_resolved"} + + # 守衛:EXECUTING/REPAIRING → 防止重複執行 + if incident.status == IncidentStatus.MITIGATING: + await telegram.answer_callback("⏳ 正在修復中,請稍候...") + return {"blocked": True, "reason": "already_executing"} + + # 正常流程 + ... +``` + +#### 14.9.5 TYPE-4 手動修復記錄(直通 LearningService) + +``` +使用者點擊 [手動修復後記錄] + ↓ +Bot 回覆:「請輸入您的修復步驟(可多行),輸入完畢傳送 /done」 + ↓ +使用者輸入:「ssh 192.168.0.110,docker restart sentry-snuba,等 2 分鐘確認」 + ↓ +LearningService.record_manual_fix( + incident=incident, + manual_steps=steps, + user=username, +) + ↓ +PlaybookService.extract_from_manual_fix() + → 建立 DRAFT Playbook(status=draft,需人工審核) + → KnowledgeEntryRecord 建立對應條目(source=human,status=draft) + ↓ +Telegram 通知:「已建立草稿 Playbook #PB-xxx,請至 AWOOOI 審核後生效」 +``` + +#### 14.9.6 Config Drift Diff 長度處理 + +```python +diff_text = drift_result.diff_summary + +if len(diff_text) <= 500: + # 直接在 Telegram code block 回覆 + await telegram.reply(f"
{diff_text}
") +else: + # 回傳 AWOOOI Web 連結 + url = f"https://aiops.wooo.work/incidents/{incident_id}/drift-diff" + await telegram.reply(f"差異過大({len(diff_text)} 字),請至 {url} 查看完整 Diff") +``` + +--- + +### 14.10 告警全生命週期資料庫記錄與 KM 轉換管道 + +> 原則:每一筆告警從進入到結束,全程寫入資料庫,結束後自動轉換成 KM 知識。 + +#### 14.10.1 現有資料庫表(已存在,無需新建) + +| 表名 | 用途 | 覆蓋範圍 | +|------|------|---------| +| `alert_operation_log` | Event Sourcing,每個生命週期事件一筆,不可變 | ✅ 已有 | +| `incidents` | Episodic Memory,完整事件記錄 + AI 決策鏈 | ✅ 已有,有 `vectorized` 欄位 | +| `approval_records` | 審核記錄(人工動作) | ✅ 已有 | +| `auto_repair_executions` | 自動修復執行記錄 | ✅ 已有 | +| `knowledge_entries` | KM 條目(incident_case / runbook / best_practice / postmortem)| ✅ 已有 | +| `audit_logs` | 操作稽核 | ✅ 已有 | + +#### 14.10.2 需要新增的欄位(DB Migration) + +在現有表上補充欄位,不新建表: + +**`incidents` 表新增欄位**: + +```sql +-- 通知類型記錄(TYPE-1/2/3/4/4D) +ALTER TABLE incidents ADD COLUMN notification_type VARCHAR(10); + +-- 告警類別(k8s_workload / database / host_resource / ...) +ALTER TABLE incidents ADD COLUMN alert_category VARCHAR(50); + +-- MCP 情報收集快照(執行前) +ALTER TABLE incidents ADD COLUMN context_bundle JSONB; + +-- 指標快照(執行前後,Prometheus MCP 採集) +ALTER TABLE incidents ADD COLUMN metrics_before JSONB; +ALTER TABLE incidents ADD COLUMN metrics_after JSONB; + +-- 執行驗證結果(K8s MCP watch_rollout 結果) +ALTER TABLE incidents ADD COLUMN verification_result JSONB; + +-- 手動修復步驟(TYPE-4 使用者輸入) +ALTER TABLE incidents ADD COLUMN manual_fix_steps TEXT; +ALTER TABLE incidents ADD COLUMN manual_fix_by VARCHAR(100); +``` + +**`alert_operation_log` 表新增 event_type 值**(PgEnum 需 migration): + +```sql +-- 新增 TYPE 分類相關事件 +ALTER TYPE alert_event_type ADD VALUE IF NOT EXISTS 'NOTIFICATION_CLASSIFIED'; +ALTER TYPE alert_event_type ADD VALUE IF NOT EXISTS 'MANUAL_FIX_RECORDED'; +ALTER TYPE alert_event_type ADD VALUE IF NOT EXISTS 'KM_CONVERTED'; +ALTER TYPE alert_event_type ADD VALUE IF NOT EXISTS 'PLAYBOOK_DRAFT_CREATED'; +ALTER TYPE alert_event_type ADD VALUE IF NOT EXISTS 'STATE_GUARD_BLOCKED'; +``` + +#### 14.10.3 告警→KM 自動轉換管道 + +每個 Incident 解決後,自動觸發 KM 轉換流程: + +``` +Incident 狀態變為 RESOLVED / CLOSED + ↓ +KMConversionService.convert(incident_id) ← 新增 Service + ↓ + ├── 判斷轉換等級: + │ 成功自動修復(TYPE-2)→ 高品質,直接轉 KM(status=approved) + │ 人工審核後執行(TYPE-3)→ 中品質,轉 KM(status=review) + │ 手動修復記錄(TYPE-4)→ 草稿,轉 KM(status=draft) + │ 純資訊(TYPE-1)→ 不轉 KM + │ + ├── 建立 KnowledgeEntryRecord: + │ entry_type = "incident_case" + │ title = f"{alertname} @ {service} — {resolution_summary}" + │ content = 結構化文字(見下方格式) + │ category = alert_category(k8s_workload / database / ...) + │ tags = [alertname, service, action_taken, severity] + │ source = "ai_extracted"(TYPE-2)or "human"(TYPE-4) + │ related_incident_id = incident_id + │ symptoms_hash = SymptomPattern.compute_hash() + │ + ├── 建立 Playbook(若執行成功且 similarity < 0.85): + │ PlaybookService.extract_from_incident( + │ incident, + │ context_bundle, ← MCP 情報快照 + │ metrics_before, ← Prometheus 快照 + │ metrics_after, ← 執行後指標 + │ verification_result, ← K8s watch_rollout 結果 + │ ) + │ + ├── 向量化(embedding): + │ EmbeddingService.embed(knowledge_entry) + │ → pgvector 存入(nomic-embed-text 768d) + │ → incidents.vectorized = True + │ + └── 寫入 AlertOperationLog: + event_type = "KM_CONVERTED" + context = {km_entry_id, playbook_id, quality_level} +``` + +**KnowledgeEntry content 標準格式**: + +```markdown +## 症狀 +- 告警: {alertname} +- 服務: {affected_services} +- 嚴重度: {severity} +- 觸發時間: {created_at} +- 即時情境: {context_bundle.summary} ← MCP 收集 + +## 根因分析 +{decision_chain.root_cause} + +## 執行動作 +- 類型: {action_type} +- 目標: {target_resource} +- 指令: {action_command} +- 執行者: {executed_by}(AI / @username) + +## 效果驗證 +- 執行前: CPU={metrics_before.cpu}% / Error Rate={metrics_before.error_rate}% +- 執行後: CPU={metrics_after.cpu}% / Error Rate={metrics_after.error_rate}% +- 恢復耗時: {resolution_time}s +- 驗證方式: {verification_result.method} + +## 學習標記 +- 成功率歷史: {playbook.success_rate} ({playbook.execution_count} 次) +- 信心分數: {confidence} +- 下次建議: {next_action_hint} +``` + +#### 14.10.4 KM 轉換觸發時機 + +``` +觸發點 1:Incident 狀態變 RESOLVED(自動觸發,非同步) +觸發點 2:使用者點擊 [手動修復後記錄] 輸入步驟後 +觸發點 3:每日 03:00 cron:掃描 vectorized=False 且 RESOLVED 的 incidents,補轉換 +觸發點 4:Playbook 相似度 < 0.5 新告警:生成草稿,人工審核後觸發 +``` + +#### 14.10.5 KM 品質分級 + +| 來源 | KM 狀態 | RAG 查詢權重 | 說明 | +|------|---------|------------|------| +| AI 自動修復成功 + Prometheus 驗證 | approved | 1.0 | 最高品質,有指標佐證 | +| 人工審核後執行成功 | review | 0.8 | 中等品質,待審核 | +| 手動修復記錄 | draft | 0.5 | 草稿,需人工確認準確性 | +| NemoClaw advisory(準確率>80%)| review | 0.7 | AI 推理成功案例 | +| Playbook 執行失敗案例 | approved | 1.0 | 失敗案例同樣重要(Anti-Pattern)| + +--- + +### 14.11 整合後工作項目(含 ADR-071 順序) + +ADR-071 的工作分兩批:**不依賴 MCP 的先做,依賴 MCP 的後做**。 + +``` +══════════ 第一批:Sprint A 完成後立即可做(不依賴 MCP)══════════ + +ADR-071-A: DB Migration + 內容: incidents 表新增 7 個欄位 + alert_operation_log 新增 5 個 event_type + 前提: 無(現有 DB 直接 ALTER) + 注意: PgEnum ADD VALUE 需獨立 migration,不能在同一 transaction 內使用新值 + +ADR-071-A0: alert_category 寫入點(新增,ADR-071-B 的前提) + 背景: 2026-04-11 線上問題 — Backup.harbor info / ConfigDrift 告警進入修復流程, + 產生「❌ 自動修復失敗: kubectl scale deployment unknown」 + 根因: decision_manager 對所有告警一視同仁,分類邏輯太晚(只在 Telegram 推送前) + affected_services 為空時 fallback "unknown",規則引擎生出無效指令 + 內容: 在 Incident 建立或 decision_manager.analyze() 入口, + 從 alertname / severity / labels 推導 alert_category 與 notification_type, + 立即寫入 incidents.alert_category + incidents.notification_type + 分類對照表(基於告警盤點): + TYPE-1(純資訊,不修復): + alertname 含 Backup / Heartbeat / KubeDeploymentReplicasMismatch(info) + severity = info / none + TYPE-4D(Config Drift,走 send_drift_card): + alertname = ConfigurationDrift / KubeConfigDrift + TYPE-3(需審核 or 自動修復): + severity = warning / critical,排除上述類別 + 前提: ADR-071-A(欄位已存在才能寫入) + 驗收: Backup.harbor info 到 decision_manager → alert_category="backup", + notification_type="TYPE-1",不進入 auto_approve 流程 + +ADR-071-B: 類別守衛(前移至 decision_manager,不再只是 Telegram 路由器) + 【重要修正】原設計:telegram_gateway.send_approval_card() 呼叫前加分類判斷 + 新設計:decision_manager step 5(自動執行判斷)之前加類別守衛 + 內容: decision_manager._analyze() 中,auto_approve 評估之前: + - TYPE-1 → 跳過修復,直接呼叫 send_info_notification(),結束 + - TYPE-4D → 跳過修復,直接呼叫 send_drift_card(),結束 + - TYPE-3 → 繼續走 auto_approve → 人工審核 or 自動執行 + 前提: ADR-071-A0(incidents 已有 alert_category) + 衝突解除: 原 ADR-071-B 的 classify_notification() 函數保留, + 但其呼叫點從 telegram_gateway 移到 decision_manager + +ADR-071-D: 狀態機守衛 + 內容: handle_callback() 前加 incident.status 檢查 + 前提: 無(現有 incidents 表有 status) + 驗收: 點擊已 RESOLVED 的卡片按鈕 → 回覆「已解決」並移除按鈕 + 注意: 已在 2026-04-11 session 實作,需驗收測試 + +ADR-071-C: TYPE-1 純資訊卡片 + 內容: 新的 send_info_notification() 函數,無按鈕格式 + 前提: ADR-071-B(守衛確保走到這裡的都是 TYPE-1) + 驗收: Backup.harbor info 告警 → 只收到純文字,無修復按鈕 + +ADR-071-E: TYPE-3 按鈕動態組合 + 內容: _build_inline_keyboard() 依 alert_category 動態選按鈕 + 前提: ADR-071-A0(incidents.alert_category 有值,send_approval_card 拿到) + 驗收: database 類告警 → 不出現 [重啟] [擴容] 按鈕 + +ADR-071-F: Config Drift 專屬卡片(TYPE-4D) + 內容: 新的 send_drift_card() 函數 + Diff 長度截斷處理 + 前提: ADR-071-B(守衛確保 TYPE-4D 走到這裡) + 驗收: ConfigDrift 告警 → 只有 [查看Diff] [採納] [回滾] [忽略],無其他按鈕 + +ADR-071-G: KMConversionService + 內容: 新增 km_conversion_service.py + Incident RESOLVED 後自動觸發 KM 轉換 + KnowledgeEntryRecord + Playbook 建立 + 向量化 + 前提: ADR-071-A + 驗收: Incident RESOLVED → knowledge_entries 自動新增一筆 → vectorized=True + +ADR-071-H: TYPE-4 手動修復記錄 + 內容: handle_callback() 新增 log_manual_fix 動作 + Bot 對話收集步驟 → LearningService.record_manual_fix() + 前提: ADR-071-G(KM 轉換管道已通) + 驗收: 點擊 [手動修復後記錄] → Bot 對話 → 草稿 Playbook 建立 + +══════════ 第二批:MCP Phase 2 完成後(依賴 Prometheus MCP)══════════ + +ADR-071-I: TYPE-2 已自動修復卡片 + 指標快照 + 內容: send_auto_repaired_notification() 含 metrics_before/after + Prometheus MCP 採集執行前後指標寫入 incidents 表 + 前提: MCP Phase 2b(Prometheus MCP) + 驗收: 自動修復完成 → 卡片顯示「CPU 從 92% 降到 23%」 + +ADR-071-J: KM 三段資料整合 + 內容: PlaybookService.extract_from_incident() 新增 metrics/verification 參數 + LearningService 記錄完整三段資料 + 前提: ADR-071-I + MCP Phase 1(K8s watch_rollout) + 驗收: Playbook content 包含指標前後對比數字 + +══════════ 最終工作順序(含 A0 修正)══════════ + +第一批執行序:D → A → A0 → B(前移版)→ C → E → F → G → H +第二批執行序:I → J(等 MCP Phase 2b/1 完成) + +══════════ 衝突檢查(更新版)══════════ + +✅ ADR-071-D 完全獨立,已實作,需驗收 +✅ ADR-071-A0 是 B/C/E/F 的真正前提,必須補在 A 之後 +⚠️ ADR-071-B 位置修正:從 telegram_gateway 前移至 decision_manager 入口 + 原程式碼的 classify_notification() 函數不需刪除,只需移動呼叫點 +✅ ADR-071-G(KM 轉換)不依賴 MCP,只用現有 LearningService + EmbeddingService +❌ ADR-071-I 必須等 Prometheus MCP(MCP Phase 2b)才能有指標快照 +❌ ADR-071-J 必須等 K8s MCP watch_rollout(MCP Phase 1)才能有驗證資料 +✅ TYPE-1 分類和 Config Drift 卡片完全不依賴 MCP,現在就能做 +``` + +--- + +*文件版本 v2.2(2026-04-11)— 新增 ADR-071-A0(alert_category 寫入點)+ 修正 ADR-071-B 位置(前移至 decision_manager)+ 工作順序重排 D→A→A0→B→C→E→F→G→H,修復線上 "deployment unknown" 根因*