Files
awoooi/docs/superpowers/plans/2026-04-07-sprint4-disposition-tracking.md
OG T e82d3802c5 docs: Sprint 4 告警處置統計系統 — 完整計畫文件 + LOGBOOK 更新
Sprint 4 計畫包含 6 Phase / 19 工作項:
- Phase A: 資料層 (IncidentFrequencyStats + Redis 計數器)
- Phase B: 寫入層 (4 觸發點: auto_repair/cold_start/human/manual)
- Phase C: API 端點 (/stats/disposition)
- Phase D: Telegram 告警卡片統計
- Phase E: 前端 (/reports 儀表板 + 首頁 + auto-repair + neural-command)
- Phase F: 週報 + 文件

首席架構師審查: 100% Fully Approved
衝突檢查: 所有依賴正確,DAG 無環

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-04-07 11:37:21 +08:00

10 KiB
Raw Blame History

Sprint 4 — 告警處置統計 + 自動修復閉環

建立: 2026-04-07 (台北時區) 建立者: Claude Code (統帥批准) 狀態: 架構審查 100% 通過,首席架構師 Fully Approved 依賴: Sprint 3 SSH_COMMAND (91/100 通過) + 首次信任機制 (53b2dae)


目標

將 AWOOOI 從「監控工具」升級為「可觀測性決策平台」。 核心指標:每個告警必須追蹤「怎麼被處理的」— 自動修復 / 人工審核 / 手動處理 / 冷啟動信任。


前置條件Sprint 3 遺留)

# 項目 狀態 說明
S3-DEPLOY 推送 Sprint 3 + 首次信任到 Gitea 待執行 git push gitea main
S3-E2E E2E 驗證首次信任自動修復 待部署後 告警 → 首次信任 → 自動修復成功

Phase A: 資料層 (Model + Redis 計數器)

A1. IncidentFrequencyStats 新增處置計數欄位

檔案: apps/api/src/models/incident.py 改動: IncidentFrequencyStats class 新增 4 欄位

# 新增欄位 (向後兼容 default=0)
human_approved_count: int = Field(default=0, ge=0, description="人工按批准後執行次數")
manual_resolved_count: int = Field(default=0, ge=0, description="無系統修復紀錄但 resolved 次數")
cold_start_trust_count: int = Field(default=0, ge=0, description="首次信任自動放行次數")
total_resolution_count: int = Field(default=0, ge=0, description="總處置次數")

依賴: 無 風險: 低 — Pydantic model 加 default=0完全向後兼容


A2. AnomalyCounter 新增 record_disposition()

檔案: apps/api/src/services/anomaly_counter.py 改動: 新增 Redis HASH 計數器

PREFIX_DISPOSITION = "anomaly:disposition:"  # 新 Redis key 前綴

async def record_disposition(
    self,
    anomaly_key: str,
    disposition_type: str,  # "auto_repair" | "human_approved" | "manual_resolved" | "cold_start_trust"
) -> None:
    """記錄處置類型(原子 HINCRBY"""
    key = f"{self.PREFIX_DISPOSITION}{anomaly_key}"
    await self.redis.hincrby(key, disposition_type, 1)
    await self.redis.hincrby(key, "total", 1)
    await self.redis.expire(key, self.TTL_SECONDS)

依賴: 無 風險: 低 — 新增 Redis key不影響現有結構


A3. AnomalyCounter get_frequency_stats() 擴充

檔案: apps/api/src/services/anomaly_counter.py 改動: 修改 get_frequency() 或新增 get_disposition_stats()

async def get_disposition_stats(self, anomaly_key: str) -> dict:
    """回傳處置分佈統計"""
    key = f"{self.PREFIX_DISPOSITION}{anomaly_key}"
    raw = await self.redis.hgetall(key)
    return {
        "auto_repair": int(raw.get(b"auto_repair", 0)),
        "human_approved": int(raw.get(b"human_approved", 0)),
        "manual_resolved": int(raw.get(b"manual_resolved", 0)),
        "cold_start_trust": int(raw.get(b"cold_start_trust", 0)),
        "total": int(raw.get(b"total", 0)),
    }

依賴: A2 風險: 低


Phase B: 寫入層(觸發點接線)

B1. 自動修復成功 → auto_repair_count++

檔案: apps/api/src/services/auto_repair_service.py 觸發點: execute_auto_repair() 成功後 (L378 附近) 改動:

# 成功後記錄處置類型
symptoms = self._extract_symptoms(incident)
anomaly_key = self.hash_signature_from_symptoms(symptoms)
counter = get_anomaly_counter()
await counter.record_disposition(anomaly_key, "auto_repair")

依賴: A2 注意: 需取得 anomaly_key — 從 incident.signals 提取 signature → hash


B2. 首次信任自動修復 → cold_start_trust_count++

檔案: apps/api/src/services/auto_repair_service.py 觸發點: evaluate_auto_repair() 冷啟動通過後 改動:

  • execute_auto_repair() 新增參數 is_cold_start: bool = False
  • 成功時:
    if is_cold_start:
        await counter.record_disposition(anomaly_key, "cold_start_trust")
    else:
        await counter.record_disposition(anomaly_key, "auto_repair")
    

依賴: A2, B1 注意: 冷啟動也算自動修復,但要獨立計數以追蹤信任機制效果


B3. 人工按「批准」執行 → human_approved_count++

檔案: apps/api/src/services/approval_execution.py 觸發點: execute_approved_action() 成功後 改動:

# 執行成功後記錄處置類型
# 需從 approval → incident_id → incident → anomaly_signature → hash
anomaly_key = await self._get_anomaly_key_from_approval(approval)
if anomaly_key:
    counter = get_anomaly_counter()
    await counter.record_disposition(anomaly_key, "human_approved")

依賴: A2 注意: approval_execution 是 Tier 3 紅區邊緣,需小心


B4. 手動處理推斷 → manual_resolved_count++

檔案: apps/api/src/services/incident_service.py 觸發點: Incident 狀態變為 resolved改動:

# 推斷邏輯(首席架構師建議)
if new_status == IncidentStatus.RESOLVED:
    has_auto_repair = await self._check_has_successful_auto_repair(incident)
    has_human_approval = await self._check_has_executed_approval(incident)
    if not has_auto_repair and not has_human_approval:
        counter = get_anomaly_counter()
        anomaly_key = self._get_anomaly_key(incident)
        await counter.record_disposition(anomaly_key, "manual_resolved")

依賴: A2 邊界條件: 外部修復SRE 手動 SSH系統無法感知 → 用排除法推斷


Phase C: 讀取層 (API 端點)

C1. 新增 /api/v1/stats/disposition 端點

檔案: apps/api/src/api/v1/stats.py 改動: 新增 GET endpoint 回傳格式:

{
  "total": 120,
  "auto_repair": 84,
  "human_approved": 24,
  "manual_resolved": 6,
  "cold_start_trust": 6,
  "auto_rate": 0.70,
  "human_rate": 0.20,
  "by_anomaly_type": { ... }
}

依賴: A3


C2. 現有端點擴充

檔案: apps/api/src/api/v1/auto_repair.py, apps/api/src/api/v1/stats.py 改動:

  • /api/v1/auto-repair/statsdisposition_summary
  • /api/v1/auto-repair/history 每筆加 disposition_type

依賴: A3, C1


Phase D: Telegram 告警呈現

D1. 告警卡片加統計行

檔案: apps/api/src/services/telegram_gateway.py 位置: anomaly_frequency 區塊 (L233-250) 新格式:

📊 頻率統計 [升級emoji]
├ 1h: 3 次 | 24h: 12 次
├ 🤖 自動: 8 | 👤 審核: 3 | 🔧 手動: 1
└ 自動化率: 67%

依賴: A3 (get_disposition_stats)


D2. 歷史按鈕強化

檔案: apps/api/src/services/telegram_gateway.py 位置: _send_incident_history() (L2508) 改動: 新增處置分佈明細區塊

依賴: A3, D1


Phase E: 前端頁面呈現

E1. /reports 頁面 — 完整處置統計儀表板(主戰場)

檔案: apps/web/src/app/[locale]/reports/page.tsx 內容:

  • 頂部 KPI異常總計 / 自動化率 / 介入率
  • 四大計數卡片:自動修復 / 人工審核 / 手動處理 / 冷啟動信任
  • 處理分佈橫條圖(百分比堆疊,按異常類型)
  • 時間範圍篩選7天/30天
  • 異常類型下拉篩選

依賴: C1


E2. 首頁 Metrics Strip 加摘要

檔案: apps/web/src/app/[locale]/page.tsx 改動: 新增「自動化率」指標,點擊跳轉 /reports

依賴: C1


E3. /auto-repair 頁面加處置概況

檔案: apps/web/src/app/[locale]/auto-repair/page.tsx 改動: AutoRepairStats interface 擴充 + 處置方式小卡

依賴: C2


E4. /neural-command stats tab 加處置統計

檔案: apps/web/src/components/neural-command/NeuralStats.tsx 改動: 在統計區加處置分佈

依賴: C1


E5. i18n 翻譯

檔案: messages/zh-TW.json, messages/en.json 新增 key: disposition.autoRepair, disposition.humanApproved, disposition.manualResolved, disposition.coldStartTrust, disposition.autoRate 等

依賴: E1


Phase F: 週報 + 文件更新

F1. 週報增加處置分佈

檔案: apps/api/src/services/weekly_report_service.py 改動: WeeklyReportMessage 新增 auto_repair_count, human_approved_count, automation_rate

依賴: A3


F2. Memory / LOGBOOK / ADR 更新

  • 更新 docs/LOGBOOK.md
  • 新增 feedback_disposition_tracking.md
  • 更新 MEMORY.md 索引

實施順序 (依賴鏈 DAG)

S3-DEPLOY ─→ S3-E2E
                 │
Phase A: ───────────────────────
A1 (Model) ──→ A2 (Redis Write) ──→ A3 (Redis Read)
                 │                      │
Phase B: ───────────────────────        │
B1 (auto_repair) ┐                     │
B2 (cold_start)  ├─ 依賴 A2            │
B3 (human_appr)  │                     │
B4 (manual_res)  ┘                     │
                                        │
Phase C: ───────────────────────        │
C1 (stats/disposition) ←────────────────┘
C2 (擴充現有端點) ← C1
     │
Phase D: ───────────────────────
D1 (Telegram 卡片) ← A3
D2 (歷史強化) ← D1
     │
Phase E: ───────────────────────
E1 (/reports 儀表板) ← C1
E2 (首頁 Strip) ← C1
E3 (/auto-repair) ← C2
E4 (/neural-command) ← C1
E5 (i18n) ← E1
     │
Phase F: ───────────────────────
F1 (週報) ← A3
F2 (文件) ← 全部完成

衝突檢查

檢查項 結果 說明
與 Sprint 3 SSH_COMMAND 衝突 無衝突 Sprint 3 已完成Sprint 4 建立在其上
與 Phase 24 AI Router 衝突 無衝突 不同 Service 層,互不依賴
與 Telegram 格式衝突 無衝突 擴充 anomaly_frequency 區塊,不動現有格式
與 anomaly_counter 衝突 ⚠️ 需注意 新增 Redis key 前綴,不修改現有 key
與 auto_repair_service 衝突 ⚠️ 需注意 首次信任剛加入B1/B2 在同一方法操作
與 incident model 衝突 無衝突 新增 Field(default=0),向後兼容
工作順序邏輯 正確 DAG 無環A→B→C→D/E→F 線性依賴
前端 i18n 衝突 無衝突 新增 key不修改現有