ADR-064: 補 I1 整合記錄(get_incident_type 三層降級、rule.id ≠ incident_type 設計決策) ADR-067: 補 D1 集中化完成記錄(9 purpose keys 對應表) Skill 02: 補 get_incident_type 使用規範 + Ollama D1 模型中央化禁令 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4.4 KiB
ADR-064: Alert Rule Engine — YAML 驅動規則匹配 + AI 自動學習
狀態: 已批准
日期: 2026-04-09
作者: ogt + Claude Sonnet 4.6
審查: 首席架構師(2026-04-09)
背景
openclaw.py 中的 _generate_mock_response 用硬編碼 if/elif 實作規則匹配,每次新增告警類型都要改 Python 代碼並重新部署。隨著監控目標增加,此模式不可持續。
決策
D1: 規則外化為 YAML(apps/api/alert_rules.yaml)
規則引擎從 YAML 檔載入,Pod 重啟即生效,不需要改代碼。
規則結構:
rules:
- id: docker_container_unhealthy
priority: 10
match:
alertname: [DockerContainerUnhealthy]
message: [unhealthy]
response:
kubectl_command: "ssh {host} 'docker inspect {container}...'"
suggested_action: RESTART_DEPLOYMENT
risk: medium
responsibility: INFRA
Priority 體系:
| 範圍 | 用途 |
|---|---|
| 1–499 | 手寫規則(高優先,不被 AI 覆蓋) |
| 500–890 | AI 自動生成規則 |
| 999 | generic_fallback 通用兜底 |
D2: AI 自動規則學習機制
當告警命中 generic_fallback 時,觸發 auto_generate_rule():
- 呼叫 Ollama (deepseek-r1:14b) 生成 YAML 規則片段
- Ollama 失敗 → Gemini 2.0 Flash 備援
- 驗證格式(id/match/response/kubectl_command 必填)
textwrap.dedent()正規化縮排(防 LLM 輸出前置空格)- append 到
alert_rules.yaml lru_cache.cache_clear()— 同 Pod 立即生效
D3: 模組邊界
alert_rule_engine.py= Service 層,只讀 YAML,不直接存取 Redis/DBauto_generate_rule()採用 DI 參數注入(ollama_url,model,gemini_api_key),不 import settings 全域單例- asyncio 觸發在上層 async
_call_with_fallback()執行,不在 sync_generate_mock_response()中操作 event loop
D4: 匹配邏輯
優先順序:alertname 完全匹配 > alert_type 部分匹配 > message 關鍵字
generic_fallback(alertname: ["*"])在 _matches() 中永遠回傳 False,由 match_rule() 的第二輪迴圈單獨選取,防止其 alert_type/message 關鍵字意外命中。
已知限制
L1: 多 Pod 環境下規則可能重複生成
_generating set 是進程記憶體級去重,多 Pod 各自維護。同一告警可能在不同 Pod 同時觸發生成,產生重複規則 append。
緩解: _rule_id_exists() 提供二次去重,但有 lru_cache 的時間窗口 race condition。
計劃: 若未來 Pod 數 > 2,需 Redis 分散式鎖。目前 prod 為 2 Pod,可接受。
L2: lru_cache 跨 Pod 不同步
新規則寫入後,只有寫入的 Pod 清除了 cache,其他 Pod 需重啟才能載入新規則。這是已知行為,下次告警觸發時仍會走 generic_fallback,但不會再次生成(_rule_id_exists 讀 YAML 直接確認)。
測試策略
auto_generate_rule() 採 DI,可在不啟動 FastAPI 的情況下單獨測試:
await auto_generate_rule(
alert_context={"labels": {"alertname": "TestAlert"}},
ollama_url="http://mock",
model="test-model",
)
I1 整合記錄 (2026-04-11, commit d77b2ad)
get_incident_type(alertname) 已整合,實作三層降級:
YAML rule.incident_type(Layer 1)
→ ALERTNAME_TO_TYPE 靜態 dict(Layer 2, src/constants/alert_types.py)
→ "custom"(Layer 3 兜底)
關鍵決策:
- YAML
rule.id不等於incident_type(命名空間不同,禁止混用) - YAML 無
incident_type欄位時一律 fall through,不用 rule.id 代替 webhooks.py已改用get_incident_type()取代靜態 dict
M3 常量遷移:ALERTNAME_TO_TYPE(56 筆)從 webhooks.py 內聯遷移至 src/constants/alert_types.py
相關檔案
apps/api/alert_rules.yaml— 規則定義apps/api/src/services/alert_rule_engine.py— 規則引擎 +get_incident_type()apps/api/src/constants/alert_types.py— ALERTNAME_TO_TYPE 靜態 dict(Layer 2 fallback)apps/api/src/api/v1/webhooks.py— 呼叫get_incident_type()apps/api/tests/test_get_incident_type.py— 11 個整合測試apps/api/src/services/openclaw.py—_generate_mock_response+_call_with_fallback整合點apps/api/Dockerfile— COPY alert_rules.yaml
參考
- ADR-006: AI 模型路由配置
- ADR-052: Phase 24 AIRouter
feedback_lewooogo_modular_enforcement.md: 積木化 5 問