From 4fc1f49dca3fb098efa97ad7b9b284ec56594d54 Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 21 Apr 2026 22:26:07 +0800 Subject: [PATCH] =?UTF-8?q?fix(pipeline):=20=E4=B8=89=E6=96=B7=E9=BB=9E?= =?UTF-8?q?=E4=BF=AE=E5=BE=A9=20=E2=80=94=20SLO=E5=85=AC=E5=BC=8F+NO=5FACT?= =?UTF-8?q?ION=E5=A0=86=E7=A9=8D+=E5=B9=BB=E8=A6=BA=E9=99=8D=E7=B4=9A?= =?UTF-8?q?=E9=A2=A8=E9=9A=AA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit D1 flywheel_stats_service: execution_count 欄位不存在 → 改讀 success_count+failure_count;消除飛輪執行成功率永遠 0.0% 假象 D2 openclaw._validate_deployment_inventory: 幻覺 deployment 降級後 原 HIGH/CRITICAL risk 未清零 → 加 result.risk_level = AIRiskLevel.LOW D3 webhooks.py (兩處 alert path): NO_ACTION/INVESTIGATE/OBSERVE 三類 非破壞性動作強制 risk_level = LOW,跳過 Telegram 批准直接 auto-approve → approval_execution.py 的 NO_ACTION handler 立即標 EXECUTION_SUCCESS Root cause 鏈:BUTTON_DATA_INVALID 修復後 TG 按鈕可發,但 NO_ACTION 積壓的 35 筆 PENDING 是因 HIGH risk 無法走 auto-approve 路徑導致。 Co-Authored-By: Claude Sonnet 4.6 --- apps/api/src/api/v1/webhooks.py | 22 +++++++++++++++++++ .../src/services/flywheel_stats_service.py | 4 ++-- apps/api/src/services/openclaw.py | 7 ++++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/apps/api/src/api/v1/webhooks.py b/apps/api/src/api/v1/webhooks.py index e1beccce..0b919ab2 100644 --- a/apps/api/src/api/v1/webhooks.py +++ b/apps/api/src/api/v1/webhooks.py @@ -871,6 +871,18 @@ async def receive_alert( } risk_level = risk_mapping.get(analysis_result.risk_level.value, RiskLevel.MEDIUM) + # 2026-04-21 ogt: NO_ACTION/INVESTIGATE/OBSERVE 強制 LOW risk,避免卡 PENDING + # 根因:_validate_deployment_inventory 降級後若原 risk 為 HIGH/CRITICAL, + # 會等 Telegram 批准,但無任何可執行動作 → 永久積壓 PENDING + _non_destructive_actions = {"NO_ACTION", "INVESTIGATE", "OBSERVE"} + _sa_val = ( + analysis_result.suggested_action.value + if hasattr(analysis_result.suggested_action, "value") + else str(analysis_result.suggested_action) + ) + if _sa_val in _non_destructive_actions: + risk_level = RiskLevel.LOW + impact_mapping = { "NONE": DataImpact.NONE, "READ_ONLY": DataImpact.READ_ONLY, @@ -1100,6 +1112,16 @@ async def _process_new_alert_background( } risk_level = risk_mapping.get(analysis_result.risk_level.value, RiskLevel.MEDIUM) + # 2026-04-21 ogt: NO_ACTION/INVESTIGATE/OBSERVE 強制 LOW risk,避免卡 PENDING + _non_destructive_actions = {"NO_ACTION", "INVESTIGATE", "OBSERVE"} + _sa_val = ( + analysis_result.suggested_action.value + if hasattr(analysis_result.suggested_action, "value") + else str(analysis_result.suggested_action) + ) + if _sa_val in _non_destructive_actions: + risk_level = RiskLevel.LOW + blast = analysis_result.blast_radius impact_mapping = { "NONE": DataImpact.NONE, diff --git a/apps/api/src/services/flywheel_stats_service.py b/apps/api/src/services/flywheel_stats_service.py index b7c8e702..ff8a29b0 100644 --- a/apps/api/src/services/flywheel_stats_service.py +++ b/apps/api/src/services/flywheel_stats_service.py @@ -204,9 +204,9 @@ class FlywheelStatsService: status = pb.get("status", "") if status == "approved": count += 1 - exec_count = pb.get("execution_count", 0) or 0 success_count = pb.get("success_count", 0) or 0 - total_exec += exec_count + failure_count = pb.get("failure_count", 0) or 0 + total_exec += success_count + failure_count total_success += success_count except (json.JSONDecodeError, KeyError): continue diff --git a/apps/api/src/services/openclaw.py b/apps/api/src/services/openclaw.py index c81730a8..98898626 100644 --- a/apps/api/src/services/openclaw.py +++ b/apps/api/src/services/openclaw.py @@ -32,6 +32,7 @@ from src.core.config import settings from src.core.prompts import NEMOTRON_SYSTEM_PROMPT, OPENCLAW_SYSTEM_PROMPT from src.core.redis_client import get_redis from src.models.ai import ( + AIRiskLevel, OpenClawDecision, SuggestedAction, ) @@ -1214,6 +1215,12 @@ class OpenClawService: result.confidence = 0.0 except Exception: pass + # 2026-04-21 ogt: 降級後風險強制 LOW,避免 NO_ACTION 因原 HIGH/CRITICAL risk + # 留在 PENDING 等 Telegram 批准(已無執行內容,等待毫無意義) + try: + result.risk_level = AIRiskLevel.LOW + except Exception: + pass def _parse_analysis_result(self, raw_response: str) -> OpenClawDecision | None: """