# ADR-013: AIOps 自動修復閉環架構 **狀態**: Accepted **日期**: 2026-04-19 **提案者**: Antigravity --- ## 背景與問題 EwoooC 系統已有 L1 Hermes 告警派發,但告警只能「通知」,無法「自癒」。 當 `psycopg2.OperationalError: could not translate host name "momo-postgres"` 這類明確的基礎設施問題發生時,仍需人工 SSH 登入修復,缺乏自動化閉環。 --- ## 決策 建立三層 AIOps 閉環架構: ``` Exception → Incident(DB) → PlayBook 匹配 → Auto-Heal 執行 → HealLog(DB) → KM 沉澱(ai_insights) → Telegram 通知 ``` ### 新增元件 | 元件 | 類型 | 說明 | |------|------|------| | `database/autoheal_models.py` | Model | Incident / Playbook / HealLog 三張表 | | `migrations/013_autoheal.sql` | Migration | 建表 + 種子 PlayBook 植入 | | `services/auto_heal_service.py` | Service | 核心引擎(分類、匹配、執行、沉澱) | | `database/manager.py` | 修改 | 加入 `_init_autoheal_tables()` | | `scheduler.py` | 修改 | 三個核心任務植入 `handle_exception` | | `utils/ssh_helper.py` | Helper | 共用 CLI `ssh` 執行層,供 AutoHeal / AiderHeal 使用 | ### PlayBook 動作類型 | action_type | 說明 | |---|---| | `DOCKER_RESTART` | 透過 SSH 跳板 restart 指定容器 | | `SSH_CMD` | 執行白名單內的任意 SSH 指令 | | `ALERT_ONLY` | 僅發 Telegram 告警,人工介入 | | `WAIT_RETRY` | 紀錄後等待排程重試 | ### 安全設計 - SSH 指令白名單:2026-04-29 起僅允許明確列入 allowlist 的 read-only 診斷指令;高副作用容器操作需走受控 PlayBook 與保護清單 - `momo-db` / `momo-postgres` 永遠是 protected resource,不得被自動 restart / stop / recreate - DB / DNS 類離線 playbook 若無法安全修復,降級為 `ALERT_ONLY` 或 `WAIT_RETRY` - 冷卻機制:同 PlayBook 在 `cooldown_min` 內不重複觸發 - 升級機制:達到 `max_retries` 後 incident.status = `escalated` 並通知人工 ### KM 沉澱格式 每次修復後寫入 `ai_insights`: - `insight_type = "auto_heal_playbook"` - 包含事件、症狀、行動、結果、教訓五要素 - 自動排入 `embedding_retry_queue` 完成 RAG 向量化 --- ## 取捨 2026-05-13 實作修訂:現行程式碼已標準化到 `utils/ssh_helper.py`,以 CLI `ssh` + `ProxyJump` 參數組裝執行,AutoHeal 與 AiderHeal 共用同一層;因此 `paramiko` 不再是 runtime 依賴。若未來要重新引入 Paramiko,必須同時補回 helper 實作、requirements 與回歸測試。 --- ## 結果 - P1/P2 等級的 DB_UNREACHABLE / DNS_FAIL 類問題可在 30 秒內完成自動修復 - 所有修復知識自動沉澱至 RAG KM,提升未來 AI 的判斷品質 - 覆蓋任務:`run_auto_import_task` / `run_icaim_analysis_task` / `run_weekly_strategy_task` --- ## 部署後記(2026-04-19 實測) ### 踩坑修正(共 5 個 hotfix commit) | 問題 | 原因 | 修正 | |---|---|---| | `UndefinedTable: playbooks` | 建表順序 `[Incident, Playbook, HealLog]` FK 衝突 | 改為 `[Playbook, Incident, HealLog]` | | `DetachedInstanceError` HealLog/Incident | `commit()` expire_on_commit 後 lazy-load 失敗 | `session.refresh(obj); session.expunge(obj)` | | `TypeError: NoneType:.0f` | fallback HealLog 缺 `duration_ms` | except 分支補 `duration_ms=duration_ms` | | compose=True 雙重呼叫 bug | `_execute_playbook` 先呼叫 compose,馬上覆寫為 docker restart | 刪除 use_compose 分支 | | `No authentication methods available` | paramiko 找不到 SSH key | key 複製至 `/app/config/autoheal_id_ed25519`(rw mount),不需重建容器 | ### SSH 認證鏈設定 ``` momo-scheduler → id_ed25519 → 110 (wooo) → tunnel → 188 (ollama) → docker restart ``` `ollama@188` 的 `id_ed25519.pub` 已加入 `authorized_keys`(第 11 行)。 ### 實測結果 ``` result=success duration=3110ms # DNS_FAIL → docker restart momo-db 成功 ``` heal_log 在 restart momo-db 後因 DB 瞬斷無法寫入 DB(id=7~9 遺失)屬設計邊界,Telegram 通知仍正常推播。 ### 2026-04-29 安全修訂 上述 `DNS_FAIL → docker restart momo-db` 屬早期實測紀錄,現在不再允許作為自動修復策略。最新規則: - `momo-db` / `momo-postgres` 是資料層 protected resource。 - AutoHeal 寫 DB 失敗時不可讓自癒流程 crash,需保留 Telegram / log / fallback 記錄。 - `CODE_FIX`、resource action 與 ElephantAlpha orchestration 必須橋接 AutoHeal,不得直接執行危險副作用。 - raw `ai_insights` 寫入後必須 enqueue embedding,讓自癒知識進入 RAG。 ### 新增 DB 表 - `migrations/014_telegram_users.sql` — EventRouter 推播對象(替代 .env 硬編碼) - 種子:`telegram_id=-1003940688311, is_admin=true`