docs(adr): ADR-073 補充 ADR-071 整合工作序 + ADR-074 監控補全 Sprint

新增:
- Sprint ADR-073-B 補充:DB Migration + 檢傷分類站 + KMConversionService(ADR-071-A/A0/B/C/G/H)
- Sprint ADR-074:飛輪健康度Exporter + 主機間網路 + DNS + Gitea CD + 備份還原測試等9項監控缺口
- 參考指向完整規格書 2026-04-12-aiops-complete-flywheel-repair-design.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
OG T
2026-04-12 13:21:27 +08:00
parent 77771c16b1
commit f2b427d87c

View File

@@ -0,0 +1,288 @@
# ADR-073: AIOps 飛輪完整盤點 — 問題根因 + 長期解決方案
**狀態**: Accepted — 等待實作批准
**日期**: 2026-04-12 (台北時間)
**作者**: Claude Sonnet 4.6 + 首席架構師
**觸發**: 資料庫審計顯示飛輪從未真正運轉
---
## 一、飛輪設計圖(截圖定案版)
### 正常模式(自主運行)
```
監控觀察 → 接入去重 → 環境診斷 → 推理匹配 → 執行燒橇 → 學習固化
↑ ↓
└─────────────────── 飛輪閉環 ──────────────────────────────┘
```
**六個節點定義**
| 節點 | 功能 | 對應程式碼 |
|------|------|-----------|
| 監控觀察 | 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
```
**人工介入只發生在兩種情況**
1. `risk_level = critical` 或 DESTRUCTIVE_PATTERNS 命中 → TYPE-3 卡片
2. `confidence < 0.5` 或 MCP 全失敗 → TYPE-4 卡片,人工確認後轉 Playbook
---
## 二、資料庫實際數據2026-04-12 統計)
### Incidents132 筆)
| 欄位 | 數值 | 正常應有 |
|------|------|---------|
| severity P3 | 10882% | P3 應是少數 |
| severity P2 | 1814% | |
| severity P0 | 64% | |
| status INVESTIGATING | 8766% | 應該接近 0 |
| status RESOLVED | 4534% | |
| alertname | **全部 NULL** | 應有 alertname |
| alert_category | **全部 NULL** | 應有分類 |
| notification_type | **全部 NULL** | 應記錄卡片類型 |
| vectorized | **全部 False** | 應 True已向量化進 KM|
| outcome | **全部 null** | 應記錄修復結果 |
### Approvals380 筆)
| status | 數量 | 說明 |
|--------|------|------|
| EXECUTION_FAILED | 21155% | 超過一半執行失敗 |
| APPROVED | 13235% | 已核准,未執行或執行中 |
| REJECTED | 308% | 人工拒絕 |
| EXPIRED | 5 | 超時未處理 |
| **EXECUTION_SUCCESS** | **20.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 Entries699 筆)
- 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**`signals` JSONB 裡 alertname = NULLLLM 拿到的是空告警名稱
- **問題 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 全為 NULLKM 記錄的案例沒有分類,無法被有效查詢
- **問題 D**重複發生的告警postgres-primary-0 被重啟 30 次)沒有任何學習,每次重新走全流程
---
## 四、與設計定案的落差對照表
| 設計定案要求 | 實際狀況 | 落差等級 |
|------------|---------|---------|
| 告警 → 自動修復閉環 | Playbooks=0100% 人工審核 | 🔴 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-Cdebounce 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 >= 10evaluate_auto_repair 至少有匹配
### P0-E690 筆 INCIDENT_CASE 向量化
**問題**vectorized = FalseRAG 永遠找不到歷史案例
**解法**:一次性腳本,批次向量化所有 `vectorized=False` 的 knowledge_entries
**驗收**vectorized = TrueRAG 查詢返回相關案例
### P0-FDocker/Host 層告警走 SSH MCP 路徑
**問題**DockerContainerUnhealthy → K8s 路徑 → deployment 不存在 → 失敗
**解法**`_auto_execute()` 判斷 alertname prefixDocker*/Host*)→ 走 SSH MCP `ssh_docker_restart`;不走 K8s 路徑
**驗收**DockerContainerUnhealthy 觸發 → SSH MCP 執行 → 成功
### P1-Aaction 解析修復93 筆「未知操作|」)
**問題**LLM 回傳含 `|` 的 action解析後為空
**解法**`_push_decision_to_telegram()` 的 action 佔位符替換邏輯,加 `|` 分隔符清理
**驗收**:新的 approval_records action 不出現「未知操作|」
### P1-BNO_ACTION → TYPE-1無按鈕
**問題**HostBackupFailed 仍有六個按鈕
**解法**`classify_notification()` 加判斷:`suggested_action == "NO_ACTION"` → TYPE-1
**驗收**HostBackupFailed Telegram 卡片無操作按鈕
### P1-Coutcome / alert_category / notification_type 寫入
**問題**:三個欄位全 NULLKM 無法分類
**解法**
- `_auto_execute()` 完成後寫 `incident.outcome`
- `webhooks.py` 建 Incident 時寫 `alert_category`(從 YAML 規則取)
- `_push_decision_to_telegram()` 發完寫 `notification_type`
### P1-Drisk_level YAML 優先覆蓋 LLM
**問題**LLM 習慣性回傳 highYAML 規則的 risk 沒有覆蓋
**解法**`_dual_engine_analyze()` 中,若 YAML 規則有 risk 值,優先用 YAML 的,不採用 LLM 的
**驗收**HostHighCpuLoadYAML 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 | 1 小時 |
| 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 | KMConversionServiceRESOLVED→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 筆/小時 |