fix(ai): SuggestedAction enum 擴充 + Pydantic fallback 防護
Some checks failed
CD Pipeline / build-and-deploy (push) Successful in 10m48s
Type Sync Check / check-type-sync (push) Failing after 2m52s

根本原因: NemoTron 輸出 "investigate" → Pydantic 只接受 4 個值 → 爆炸
→ openclaw_analysis_parse_failed → analysis_result=None → 全部 fallback 卡片顯示「待分析」

修復:
1. SuggestedAction enum 新增 INVESTIGATE/OBSERVE/APPLY_HPA/TUNE_RESOURCES
   (prompt.py 列了 6 個,enum 只有 4 個,prompt/model 不同步是根源)
2. normalize_suggested_action validator: uppercase + 別名映射 + 未知值 fallback NO_ACTION
   確保任何 LLM 輸出都不會讓 Pydantic 爆炸導致 analysis_result = None

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
OG T
2026-04-17 21:36:28 +08:00
parent 5d715e16ee
commit fe77e6d297

View File

@@ -19,11 +19,19 @@ class SuggestedAction(str, Enum):
AI 建議操作類型
必須與 executor.OperationType 對應
2026-04-17 ogt + Claude Sonnet 4.6: 新增 INVESTIGATE/APPLY_HPA/TUNE_RESOURCES
根本原因: prompts.py 列了 6 個值,但 enum 只有 4 個
→ NemoTron 輸出 "investigate" → Pydantic 爆炸 → analysis_result = None → 全部 fallback
"""
RESTART_DEPLOYMENT = "RESTART_DEPLOYMENT"
DELETE_POD = "DELETE_POD"
SCALE_DEPLOYMENT = "SCALE_DEPLOYMENT"
NO_ACTION = "NO_ACTION" # 無需處理
APPLY_HPA = "APPLY_HPA"
TUNE_RESOURCES = "TUNE_RESOURCES"
INVESTIGATE = "INVESTIGATE" # 調查診斷,不下執行指令
OBSERVE = "OBSERVE" # 觀察等待
NO_ACTION = "NO_ACTION" # 無需處理
class AIRiskLevel(str, Enum):
@@ -86,6 +94,7 @@ class OpenClawDecision(BaseModel):
...,
description="建議執行的操作類型",
)
target_resource: str = Field(
...,
description="目標資源名稱 (e.g., 'harbor', 'grafana')",
@@ -192,9 +201,31 @@ class OpenClawDecision(BaseModel):
@field_validator("suggested_action", mode="before")
@classmethod
def normalize_suggested_action(cls, v):
"""正規化 suggested_action"""
"""
正規化 suggested_action大小寫 + 別名映射 + 未知值 fallback
2026-04-17 ogt + Claude Sonnet 4.6(亞太):
根本原因: NemoTron 輸出 "investigate"(小寫) → Pydantic 拒絕 → analysis_result = None
舊版只做 uppercase未知值仍爆 → 修復為: 先 uppercase再別名映射最後 fallback NO_ACTION
"""
if isinstance(v, str):
return v.upper().replace("-", "_").replace(" ", "_")
normalized = v.upper().replace("-", "_").replace(" ", "_")
# 別名映射 (LLM 可能輸出非正式名稱)
alias_map = {
"DIAGNOSE": "INVESTIGATE",
"DEBUG": "INVESTIGATE",
"MONITOR": "OBSERVE",
"WATCH": "OBSERVE",
"TUNE": "TUNE_RESOURCES",
"HPA": "APPLY_HPA",
}
normalized = alias_map.get(normalized, normalized)
# 未知值 fallback NO_ACTION絕不讓 Pydantic 爆炸導致 analysis_result = None
try:
SuggestedAction(normalized)
return normalized
except ValueError:
return "NO_ACTION"
return v