fix(code-review): P0-1 action fallback 語意修正 + P1-2 reason enum + P2-2 secops 清洗
All checks were successful
CD Pipeline / build-and-deploy (push) Successful in 10m7s
All checks were successful
CD Pipeline / build-and-deploy (push) Successful in 10m7s
Code Review 發現 (2026-04-17 首席架構師審查):
P0-1 auto_approve.py 條件 1d 語意修正:
- 原:用 `action` 變數(已 fallback = action or kubectl_command)做 kubectl 判斷
→ action="" + kubectl_command="kubectl get pods" → action="kubectl get pods" → 1d 通過
→ _kubectl_cmd 與 action 同值(重複判斷同一來源),掩蓋 action 本身是自然語言的情況
- 修:改用 proposal_data.get("action", "") 原始值(_raw_action)
→ 直接檢查 action 欄位本身,邏輯語意明確
P1-2 auto_approve.py NO_EXECUTABLE_ACTION 新增:
- 新增 AutoApproveReason.NO_EXECUTABLE_ACTION enum 值
- 條件 1d 改用此 reason(原 NO_PLAYBOOK 語意為「無匹配 Playbook」,不適用此場景)
- 避免污染 KM 飛輪學習資料的根因分類(ADR-068)
P2-2 decision_manager.py secops 分支:
- threat_behavior 改用 _parse_debate_summary → 取 diagnosis 欄位
- 與 BUG-A/BUG-C 修復一致,不再傾倒完整 debate_summary 前 150 字
ADR-082: Phase 2 多 Agent 協作
2026-04-17 ogt + Claude Sonnet 4.6(亞太): Code Review 後修正
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -50,6 +50,7 @@ class AutoApproveReason(str, Enum):
|
||||
CRITICAL_OPERATION = "critical_operation" # 關鍵操作
|
||||
LOW_TRUST = "low_trust" # 信任不足
|
||||
NO_PLAYBOOK = "no_playbook" # 無匹配 Playbook
|
||||
NO_EXECUTABLE_ACTION = "no_executable_action" # action 為自然語言,無法執行
|
||||
LOW_SUCCESS_RATE = "low_success_rate" # Playbook 成功率不足
|
||||
INSUFFICIENT_HISTORY = "insufficient_history" # 執行歷史不足
|
||||
|
||||
@@ -294,13 +295,17 @@ class AutoApprovePolicy:
|
||||
# 根因:Solver 經 OpenClaw Nemo 路徑輸出「重啟 Crash Looping Pod」等自然語言
|
||||
# action 非空 → 條件 1c 通過 → auto_approved=True
|
||||
# 但 kubectl_command 為空 → 實際無法執行 → incident 卡在 investigating
|
||||
# 修復:action 必須含 kubectl 關鍵字才可自動執行,否則降級人工審核
|
||||
# 修復:直接讀 proposal_data["action"] 原始值(非 fallback 後的 action 變數)
|
||||
# 避免「action 空 → fallback 成 kubectl_command → action 含 kubectl → 誤放行」
|
||||
# Code Review 2026-04-17: P0-1 修正 action fallback 語意混淆
|
||||
# P1-2 改用 NO_EXECUTABLE_ACTION(避免污染 KM 飛輪學習資料)
|
||||
_raw_action = proposal_data.get("action", "") or ""
|
||||
_kubectl_cmd = proposal_data.get("kubectl_command", "") or ""
|
||||
_has_kubectl = "kubectl" in action.lower() or "kubectl" in _kubectl_cmd.lower()
|
||||
_has_kubectl = "kubectl" in _raw_action.lower() or "kubectl" in _kubectl_cmd.lower()
|
||||
if not _has_kubectl:
|
||||
return self._reject(
|
||||
reason=AutoApproveReason.NO_PLAYBOOK,
|
||||
detail=f"Action '{action[:60]}' is natural language — no kubectl command, requires human review",
|
||||
reason=AutoApproveReason.NO_EXECUTABLE_ACTION,
|
||||
detail=f"Action '{_raw_action[:60] or _kubectl_cmd[:60]}' is natural language — no kubectl command, requires human review",
|
||||
risk_level=risk_level,
|
||||
trust_score=trust_score,
|
||||
confidence=confidence,
|
||||
|
||||
@@ -381,7 +381,7 @@ async def _push_decision_to_telegram(
|
||||
alertname=_alertname,
|
||||
threat_level=_threat_level,
|
||||
resource=target[:60],
|
||||
threat_behavior=reasoning[:150] if reasoning else description[:150],
|
||||
threat_behavior=_smt(_parse_debate_summary(reasoning).get("diagnosis") or reasoning, 150) if reasoning else description[:150],
|
||||
)
|
||||
elif _alert_category == "business":
|
||||
# TYPE-6B:業務/FinOps 資訊告警 — 發到 SRE 群組(無審核按鈕)(ADR-075 Step-5)
|
||||
|
||||
142
docs/LOGBOOK.md
142
docs/LOGBOOK.md
@@ -6,6 +6,148 @@
|
||||
|
||||
---
|
||||
|
||||
## 📍 2026-04-17 晚 — P1+P2 安全熱修 + 第一次授權執行歷史里程碑 🏁
|
||||
|
||||
### 第一次 [✅批准] 歷史時刻
|
||||
|
||||
- **INC-20260417-43E98A**:混沌注入 `KubePodCrashLooping` → TYPE-3 卡片呈現符合預期
|
||||
- [✅批准][❌拒絕] 置頂 ✅,AI 診斷只顯示診斷摘要 ✅,action 為 `kubectl get pods -n awoooi-prod` ✅
|
||||
- 統帥按下 [✅批准] → `approval_execution.py` 接收 → 原卡片下方 reply「✅ 執行成功/失敗」
|
||||
- KM 沉澱:`_write_execution_result_to_km()` 自動寫入 `INCIDENT_CASE`(含 alertname/category/action)
|
||||
|
||||
### P1 安全熱修 — Commit `93205ce`
|
||||
|
||||
| 問題 | 根因 | 修復 |
|
||||
|------|------|------|
|
||||
| 自然語言 action 通過 auto_approve | 條件 1c 只判斷 action 是否為空,未驗證格式 | 新增條件 1d:action 必須含 `kubectl` 關鍵字,否則 `NO_PLAYBOOK` 拒絕,降人工審核 |
|
||||
| Solver Nemo 格式路徑輸出自然語言 | `action_title` 不含 kubectl 仍被轉為 CandidateAction | `_extract_candidates()`:`action_title` 不含 kubectl → `return []` → 觸發 `_degraded_plan` |
|
||||
| 降級動作為 `"restart_pod"` 等自然語言 | `_default_action_for_category` 返回非 kubectl 字串 | 改為真實 `kubectl get/top/exec` 調查指令(唯讀,無副作用) |
|
||||
|
||||
### 架構現況(2026-04-17 晚)
|
||||
|
||||
| 層級 | 狀態 | 說明 |
|
||||
|------|------|------|
|
||||
| L1 監控/告警 | ✅ 生產運行 | Prometheus + Alertmanager |
|
||||
| L2 AI 診斷 | ✅ 生產運行 | 5-Agent Debate,confidence/blast_radius 計算 |
|
||||
| L3 條件自動執行 | ✅ 首次驗證 | kubectl 格式 + blast_radius 評分 → 人工或自動 |
|
||||
| L4 自動放行(高信任) | ⚠️ 架構就緒 | trust_score 邏輯存在;min_trust_score=0(pod重啟會歸零)|
|
||||
| L5 自主學習飛輪 | ⚠️ 架構就緒 | `_write_execution_result_to_km` 寫入,未 7 天驗證 |
|
||||
|
||||
### 已驗證事實修正
|
||||
|
||||
- **卡片不會 in-place edit**:執行結果以 `reply_to_message_id` 送新訊息到原告警下方
|
||||
- **KM 沉澱是真的**:`approval_execution.py:676` `create_entry` 確實執行
|
||||
- **AI 仲裁 20%** = Solver 走降級路徑,`confidence=0.2` 是設計值(降級動作應給低分)
|
||||
|
||||
---
|
||||
|
||||
## 📍 2026-04-17 下午三 — 混沌演習 + Telegram UI 第三波修復(BUG-C)🎯
|
||||
|
||||
### 混沌演習結果(Alertmanager API 注入法)
|
||||
|
||||
- 注入:`KubePodCrashLooping` → `INC-20260417-C6D1D6` 建立 ✅
|
||||
- AI Debate 完成(confidence=0.9, risk=low)✅
|
||||
- 揭露新 BUG:TYPE-3 root_cause 仍含 debate_summary 全文 + K8s 按鈕蓋台 approve/reject
|
||||
|
||||
### 修復 Commit `f421e65`
|
||||
|
||||
| 問題 | 根因 | 修復 |
|
||||
|------|------|------|
|
||||
| TYPE-3 卡片 AI 診斷欄顯示完整 debate_summary | `root_cause=_smt(reasoning, 500)` 未解析 | `_parse_debate_summary` 只取 `diagnosis` + `_smt 300` |
|
||||
| K8s 動態按鈕蓋台,看不到批准/拒絕 | `requires_human_approval` 條件未滿足時跳過 approve/reject | `_build_inline_keyboard` 重構:[✅批准][❌拒絕] 永遠第一行,K8s 按鈕置後 |
|
||||
|
||||
**副作用清理**:移除 `requires_human_approval` 參數(`_build_inline_keyboard` + `send_approval_card` + 呼叫端),邏輯簡化為無條件置頂。
|
||||
|
||||
---
|
||||
|
||||
## 📍 2026-04-17 下午二 — Telegram UI 第二波修復(BUG-A + BUG-B)📊
|
||||
|
||||
### 系統盤點(System Audit)
|
||||
|
||||
完成 6 TYPE 全分類盤點:TYPE-1/2/3/4/4D/8M
|
||||
|
||||
- TYPE-2/3/4:✅ TelegramMessage 結構化模板,正常
|
||||
- TYPE-8M:✅ 已修復(第一波 6baa2e9)
|
||||
- TYPE-1:⚠️ BUG-A — `message=reasoning[:200]` 傾倒完整 debate_summary
|
||||
- TYPE-4D:⚠️ BUG-B — `diff_summary=description[:500]` 傾倒 AI 輸出的 JSON 原文
|
||||
|
||||
### 修復 Commit `418d735`
|
||||
|
||||
| 問題 | 根因 | 修復 |
|
||||
|------|------|------|
|
||||
| TYPE-1 純資訊通知顯示 "診斷...;方案...;安全審查..." 全文 | `reasoning[:200]` 未解析 debate_summary | `_parse_debate_summary(reasoning)` 只取 `diagnosis` + `_smt` 截斷 200 字 |
|
||||
| TYPE-4D Config Drift 顯示 `{"action_title":"...","description":"..."}` | `description[:500]` 傳入未解析的 LLM JSON | JSON Catcher:`json.loads` 成功 → 格式化「📝建議操作/📖說明/⏪回滾方案」;失敗 → 平滑降級純文字 |
|
||||
|
||||
**修改範圍**:僅 `decision_manager.py` 路由準備段(+23行/-2行),`telegram_gateway.py` 模板層零改動。
|
||||
|
||||
---
|
||||
|
||||
## 📍 2026-04-17 下午 — Telegram UI 三連修(顧問戰報分析)🎯
|
||||
|
||||
### 顧問診斷兩張截圖
|
||||
|
||||
**截圖一(好消息)**:Solver 成功輸出 `kubectl delete pod awoooi-api -n awoooi-prod`(blast_radius=25),
|
||||
Trust Score 未達 0.8 門檻 → 系統正確降級為 ACTION REQUIRED — Trust Engine 正常運作。
|
||||
|
||||
**截圖二(真問題)**:TYPE-8M 卡片三欄重複 + 幽靈截斷 + 死卡(無批准/拒絕按鈕)
|
||||
|
||||
### 修復 Commit `6baa2e9`
|
||||
|
||||
| 問題 | 根因 | 修復 |
|
||||
|------|------|------|
|
||||
| 批准/拒絕按鈕消失(死卡) | `_build_inline_keyboard` 有動態按鈕時跳過 approve/reject | 新增 `requires_human_approval` 參數,True 時強制插入批准/拒絕行 |
|
||||
| TYPE-8M 三欄重複渲染 | `diagnosis`/`system_impact`/`probable_cause` 全取 `reasoning[:100]` | 新增 `_parse_debate_summary()` 拆分各組件 |
|
||||
| 幽靈截斷「質疑:無(通」 | 粗暴 `[:N]` 在括號中間切斷 | 新增 `_smart_truncate()` 在句子邊界截斷 |
|
||||
|
||||
驗證:`verify_telegram_ui.py` 全部通過,Run 927 部署中(13:58 台北)。
|
||||
|
||||
---
|
||||
|
||||
## 📍 2026-04-17 — Phase 5 燃料修復 + 生產 Bug 三連修 🔧
|
||||
|
||||
### 背景
|
||||
顧問(統帥)從 Telegram 截圖診斷出 4 個生產問題:
|
||||
CI/CD 失敗、API 短暫離線、drift 研判原因空白、Telegram 截斷幽靈復發
|
||||
|
||||
### 根本原因 + 修復 Commits
|
||||
|
||||
| Commit | 問題 | 根因 | 狀態 |
|
||||
|--------|------|------|------|
|
||||
| `e0bfcc7` | Phase 5 blast_radius fill rate = 0% | Solver prompt 範例為 `restart_service:xxx` 自訂格式 → LLM 輸出自然語言 → auto_approve Cond 1c 拒絕 → blast_radius_calculator 從未被呼叫 | ✅ 已部署 `0ab92c2` |
|
||||
| `5dae610` | CD pipeline rebase 衝突 | `git rebase` 無 `-X theirs` → kustomization.yaml 衝突未解 → push rejected | ✅ 已部署 `0ab92c2` |
|
||||
| `58d9c06` | drift_narrator 研判原因空白 | `_generate_narrative()` 直接呼叫 `192.168.0.111:11434`(dead Ollama)→ httpx 拋 exception → 整個 narrate_and_notify() 跳出 → DB 從未寫入 | ✅ 已部署 `0ab92c2` |
|
||||
| `0ab92c2` | Telegram 截斷幽靈 "質疑:無(通" | `root_cause=reasoning[:300]` 裁切在 300 字 | ✅ 已部署 |
|
||||
|
||||
### 修復技術摘要
|
||||
|
||||
**Solver prompt 修復(e0bfcc7)**:
|
||||
- 舊:`action 範例 = "restart_service:awoooi-api"` → LLM 模仿輸出自然語言
|
||||
- 新:明確要求 kubectl 命令 + 正確範例 `kubectl rollout restart deployment/awoooi-api -n awoooi-prod`
|
||||
- 影響:auto_approve Cond 1c 恢復,_auto_execute() 路徑打通,blast_radius_calculator 開始運作
|
||||
|
||||
**drift_narrator 修復(58d9c06)**:
|
||||
- 舊:`httpx.AsyncClient → POST 192.168.0.111:11434/api/generate` (Dead IP)
|
||||
- 新:`get_openclaw().call(prompt)` — 走 AI Router,自動 fallback
|
||||
- 與 drift_interpreter.py 同樣修法(d952435)
|
||||
|
||||
### 生產驗證(2026-04-17 13:38 台北)
|
||||
|
||||
| 指標 | 狀態 |
|
||||
|------|------|
|
||||
| Run 926 部署 | ✅ success,image `0ab92c20...` |
|
||||
| API 在線 | ✅ HTTP 200 |
|
||||
| Solver kubectl 格式 | ⏳ 等下一個告警觸發 |
|
||||
| blast_radius_score 記錄 | ⏳ 等新 incident |
|
||||
| drift_narrator 研判原因 | ⏳ 等 14:00 cronjob 觸發 |
|
||||
| Telegram 截斷修復 | ⏳ 等長 reasoning 的 incident |
|
||||
|
||||
### GitOps Token 修復(本 Session 早期)
|
||||
- Gitea Issue `write:issue` scope 缺失 → 403
|
||||
- 修復:docker exec gitea → generate-access-token → patch K8s Secret
|
||||
- Phase 5 GitOps PR 功能:`AIOPS_P5_GITOPS_PR=false`(configmap,可按需啟用)
|
||||
|
||||
---
|
||||
|
||||
## 📍 2026-04-16 — E2E 全節點驗證 + 生產 bug 連環修復
|
||||
|
||||
### 問題背景
|
||||
|
||||
Reference in New Issue
Block a user