# 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` | | `requirements.txt` | 修改 | 加入 `paramiko` | ### PlayBook 動作類型 | action_type | 說明 | |---|---| | `DOCKER_RESTART` | 透過 SSH 跳板 restart 指定容器 | | `SSH_CMD` | 執行白名單內的任意 SSH 指令 | | `ALERT_ONLY` | 僅發 Telegram 告警,人工介入 | | `WAIT_RETRY` | 紀錄後等待排程重試 | ### 安全設計 - SSH 指令白名單:僅允許 `docker restart *`, `docker compose restart *`, `docker start *` - 冷卻機制:同 PlayBook 在 `cooldown_min` 內不重複觸發 - 升級機制:達到 `max_retries` 後 incident.status = `escalated` 並通知人工 ### KM 沉澱格式 每次修復後寫入 `ai_insights`: - `insight_type = "auto_heal_playbook"` - 包含事件、症狀、行動、結果、教訓五要素 - 自動排入 `embedding_retry_queue` 完成 RAG 向量化 --- ## 取捨 **優先使用 paramiko** 而非 subprocess + CLI ssh,原因是在容器內環境控制更精準,且支援跳板機 ProxyJump。若 paramiko 未安裝則自動降級到 CLI ssh(向後相容)。 --- ## 結果 - 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 通知仍正常推播。 ### 新增 DB 表 - `migrations/014_telegram_users.sql` — EventRouter 推播對象(替代 .env 硬編碼) - 種子:`telegram_id=-1003940688311, is_admin=true`