feat(agents): route high risk through controlled automation
Some checks failed
Code Review / ai-code-review (push) Successful in 13s
CD Pipeline / tests (push) Failing after 1m8s
CD Pipeline / build-and-deploy (push) Has been skipped
CD Pipeline / post-deploy-checks (push) Has been skipped
Ansible / Reboot Recovery Contract / validate (push) Has been cancelled
Some checks failed
Code Review / ai-code-review (push) Successful in 13s
CD Pipeline / tests (push) Failing after 1m8s
CD Pipeline / build-and-deploy (push) Has been skipped
CD Pipeline / post-deploy-checks (push) Has been skipped
Ansible / Reboot Recovery Contract / validate (push) Has been cancelled
This commit is contained in:
@@ -7,7 +7,7 @@ Phase 18: Failure Auto-Repair Loop (2026-03-31 統帥批准)
|
||||
- 監聽 AuditLog 失敗事件
|
||||
- AI 分析失敗原因
|
||||
- 評估風險等級
|
||||
- 執行自動修復 (LOW 風險) 或請求人工授權 (MEDIUM/CRITICAL)
|
||||
- LOW / MEDIUM / HIGH 由 AI Agent 受控自動修復;CRITICAL / break-glass 才硬封鎖
|
||||
|
||||
設計原則:
|
||||
- 實作 IFailureWatcher Protocol
|
||||
@@ -53,9 +53,13 @@ RISK_LEVELS = {
|
||||
"operations": ["restart_pod", "restart_deployment", "clear_cache"],
|
||||
},
|
||||
"MEDIUM": {
|
||||
"auto_repair": False,
|
||||
"auto_repair": True,
|
||||
"operations": ["scale_deployment", "rollback", "update_config"],
|
||||
},
|
||||
"HIGH": {
|
||||
"auto_repair": True,
|
||||
"operations": ["controlled_rollback", "controlled_config_repair", "controlled_service_repair"],
|
||||
},
|
||||
"CRITICAL": {
|
||||
"auto_repair": False,
|
||||
"operations": ["delete_pvc", "drop_database", "network_policy"],
|
||||
@@ -108,8 +112,8 @@ class FailureWatcherService(IFailureWatcher):
|
||||
1. 收到失敗事件 (from Redis Stream or direct call)
|
||||
2. AI 分析失敗原因 (OpenClaw)
|
||||
3. 評估風險等級
|
||||
4. LOW → 自動修復 → 揭露通知
|
||||
5. MEDIUM/CRITICAL → Telegram + 前端等待授權
|
||||
4. LOW / MEDIUM / HIGH → 受控自動修復 → 揭露通知
|
||||
5. CRITICAL / hard blocker → Telegram + 前端 break-glass blocked 狀態
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
@@ -183,11 +187,11 @@ class FailureWatcherService(IFailureWatcher):
|
||||
target_resource=target_resource,
|
||||
reason=global_reason,
|
||||
)
|
||||
# 強制升級為 CRITICAL,必須人工授權
|
||||
# 全域熔斷代表 repair storm 風險,仍維持 break-glass blocked。
|
||||
risk_level = "CRITICAL"
|
||||
result["risk_level"] = "CRITICAL"
|
||||
result["next_action"] = "blocked_by_global_cooldown"
|
||||
await self._request_human_approval(
|
||||
await self._queue_ai_repair_followup(
|
||||
audit_log_id=audit_log_id,
|
||||
analysis=analysis,
|
||||
reason=global_reason,
|
||||
@@ -201,7 +205,7 @@ class FailureWatcherService(IFailureWatcher):
|
||||
)
|
||||
|
||||
if not can_auto_repair:
|
||||
# 超過冷卻期限制,升級為 MEDIUM
|
||||
# 超過單資源冷卻期限制,改由 AI 受控重試佇列處理,不再要求人工接手。
|
||||
logger.info(
|
||||
"repair_cooldown_escalate",
|
||||
audit_log_id=audit_log_id,
|
||||
@@ -211,7 +215,7 @@ class FailureWatcherService(IFailureWatcher):
|
||||
risk_level = "MEDIUM"
|
||||
result["risk_level"] = "MEDIUM"
|
||||
|
||||
if risk_level == "LOW" and RISK_LEVELS["LOW"]["auto_repair"]:
|
||||
if risk_level != "CRITICAL" and RISK_LEVELS.get(risk_level, {}).get("auto_repair"):
|
||||
# 自動修復 (Phase 18.3: 傳入完整 failure_data)
|
||||
success, repair_result = await self.execute_auto_repair(
|
||||
audit_log_id=audit_log_id,
|
||||
@@ -242,22 +246,22 @@ class FailureWatcherService(IFailureWatcher):
|
||||
auto=True,
|
||||
)
|
||||
else:
|
||||
# 升級為 MEDIUM,請求人工授權
|
||||
# 失敗後排入 AI controlled retry / rollback,不再直接轉人工。
|
||||
result["risk_level"] = "MEDIUM"
|
||||
await self._request_human_approval(
|
||||
await self._queue_ai_repair_followup(
|
||||
audit_log_id=audit_log_id,
|
||||
analysis=analysis,
|
||||
reason="自動修復失敗,需人工介入",
|
||||
reason="AI 受控自動修復失敗,已排入 rollback / transport / PlayBook 修復重試",
|
||||
)
|
||||
result["next_action"] = "await_approval"
|
||||
result["next_action"] = "ai_retry_queued"
|
||||
else:
|
||||
# MEDIUM/CRITICAL: 請求人工授權
|
||||
await self._request_human_approval(
|
||||
# CRITICAL: break-glass / hard blocker,不執行寫入。
|
||||
await self._queue_ai_repair_followup(
|
||||
audit_log_id=audit_log_id,
|
||||
analysis=analysis,
|
||||
reason=f"風險等級 {risk_level},需人工審核",
|
||||
reason=f"風險等級 {risk_level} 命中 break-glass guardrail,AI 已封鎖寫入並保留證據",
|
||||
)
|
||||
result["next_action"] = "await_approval"
|
||||
result["next_action"] = "break_glass_blocked"
|
||||
|
||||
logger.info(
|
||||
"failure_watcher_processed",
|
||||
@@ -505,9 +509,9 @@ class FailureWatcherService(IFailureWatcher):
|
||||
"NETWORK_ERROR": "檢查網路連線,驗證 DNS 解析",
|
||||
"PERMISSION_DENIED": "檢查 RBAC 權限配置",
|
||||
"RESOURCE_ERROR": "增加資源配額或清理資源",
|
||||
"UNKNOWN": "需人工分析日誌",
|
||||
"UNKNOWN": "AI 深度診斷:補抓 timeline / log / metric / PlayBook 相似案例",
|
||||
}
|
||||
return suggestions.get(classification, "需人工分析")
|
||||
return suggestions.get(classification, "AI 深度診斷")
|
||||
|
||||
async def _check_repair_cooldown(
|
||||
self,
|
||||
@@ -709,7 +713,7 @@ class FailureWatcherService(IFailureWatcher):
|
||||
error=str(e),
|
||||
)
|
||||
|
||||
async def _request_human_approval(
|
||||
async def _queue_ai_repair_followup(
|
||||
self,
|
||||
audit_log_id: str,
|
||||
analysis: dict,
|
||||
@@ -720,10 +724,11 @@ class FailureWatcherService(IFailureWatcher):
|
||||
# 推送到 Redis (前端 WebSocket 訂閱)
|
||||
redis = get_redis()
|
||||
repair_request = {
|
||||
"type": "repair_request",
|
||||
"type": "ai_controlled_repair_followup",
|
||||
"audit_log_id": audit_log_id,
|
||||
"analysis": analysis,
|
||||
"reason": reason,
|
||||
"controlled_retry": True,
|
||||
"created_at": datetime.now(UTC).isoformat(),
|
||||
}
|
||||
await redis.publish(
|
||||
@@ -736,18 +741,18 @@ class FailureWatcherService(IFailureWatcher):
|
||||
|
||||
tg = get_telegram_gateway()
|
||||
message = (
|
||||
f"🔧 <b>修復請求</b>\n\n"
|
||||
f"🤖 <b>AI 受控修復後續</b>\n\n"
|
||||
f"├ 📋 AuditLog: <code>{audit_log_id[:8]}...</code>\n"
|
||||
f"├ 📊 分類: {analysis.get('classification', 'UNKNOWN')}\n"
|
||||
f"├ ⚠️ 風險: {analysis.get('risk_level', 'MEDIUM')}\n"
|
||||
f"├ 🔍 原因: {analysis.get('root_cause', reason)}\n"
|
||||
f"└ 💡 建議: {analysis.get('suggested_repair', '需人工分析')}\n\n"
|
||||
f"請在 Dashboard 授權或使用 /repair {audit_log_id[:8]}"
|
||||
f"└ 💡 下一步: {analysis.get('suggested_repair', 'AI 深度診斷')}\n\n"
|
||||
f"狀態:<code>ai_retry_or_break_glass_recorded</code>"
|
||||
)
|
||||
await tg.send_alert_notification(message)
|
||||
|
||||
logger.info(
|
||||
"repair_request_sent",
|
||||
"ai_controlled_repair_followup_sent",
|
||||
audit_log_id=audit_log_id,
|
||||
risk_level=analysis.get("risk_level"),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user