fix(ai): 移除假信心分數 + 顯示 AI 模型來源
問題: AI 仲裁顯示硬編碼信心分數 (0.75/0.88/0.92/0.70) 修復: - decision_manager: 預設 confidence 0.75 → 0.0 - decision_manager: Expert System confidence=0.0 + is_rule_based - openclaw: 所有 Mock Response confidence → 0.0 - telegram_gateway: 新增 ai_provider 欄位 - telegram_gateway: 動態來源標籤 (Ollama/Gemini/Claude/規則匹配) Telegram 卡片顯示: - confidence > 0 + provider=ollama → 🤖 Ollama 仲裁 - confidence > 0 + provider=gemini → 🤖 Gemini 仲裁 - confidence > 0 + provider=claude → 🤖 Claude 仲裁 - confidence == 0 → ⚙️ 規則匹配 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -102,8 +102,9 @@ async def _push_decision_to_telegram(
|
||||
action = proposal_data.get("action", proposal_data.get("kubectl_command", ""))
|
||||
description = proposal_data.get("description", "")
|
||||
reasoning = proposal_data.get("reasoning", "")
|
||||
confidence = proposal_data.get("confidence", 0.75)
|
||||
confidence = proposal_data.get("confidence", 0.0) # 🔴 預設 0.0 表示未經 AI 分析
|
||||
source = proposal_data.get("source", "unknown")
|
||||
ai_provider = proposal_data.get("provider", "") # 2026-03-29 ogt: AI 模型來源
|
||||
|
||||
# 建立 approval_id (使用 incident_id 作為追蹤)
|
||||
# 2026-03-27 ogt: 修復 INC-INC-INC- 重複前綴 bug
|
||||
@@ -119,6 +120,7 @@ async def _push_decision_to_telegram(
|
||||
primary_responsibility="INFRA",
|
||||
confidence=confidence,
|
||||
namespace=incident.signals[0].labels.get("namespace", "default") if incident.signals else "default",
|
||||
ai_provider=ai_provider, # 2026-03-29 ogt: 顯示 AI 模型來源
|
||||
)
|
||||
|
||||
# 🔴 發送成功後設置去重 key (TTL 10 分鐘)
|
||||
@@ -222,16 +224,19 @@ def expert_analyze(incident: Incident) -> dict[str, Any]:
|
||||
|
||||
rule = EXPERT_RULES[matched_rule]
|
||||
|
||||
# 2026-03-29 ogt: Expert System 不應該假裝有高信心分數
|
||||
# 設為 0.0 強制標記為規則匹配,而非 AI 仲裁
|
||||
return {
|
||||
"source": "expert_system",
|
||||
"action": rule["action"].format(target=target),
|
||||
"description": rule["description"],
|
||||
"risk_level": rule["risk_level"],
|
||||
"reasoning": rule["reasoning"],
|
||||
"confidence": 0.75, # Expert System 固定信心分數
|
||||
"reasoning": f"[規則匹配] {rule['reasoning']}", # 明確標示來源
|
||||
"confidence": 0.0, # 🔴 規則匹配不是 AI 仲裁,信心度設 0
|
||||
"kubectl_command": rule["action"].format(target=target),
|
||||
"matched_rule": matched_rule,
|
||||
"from_cache": False,
|
||||
"is_rule_based": True, # 新增標記
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -520,10 +520,11 @@ class OpenClawService:
|
||||
)
|
||||
|
||||
# 根據告警類型生成專業 RCA + 仲裁
|
||||
# 🔴 2026-03-29 ogt: Mock 響應必須標示為規則匹配,不是 AI 仲裁
|
||||
if "oom" in message.lower() or "memory" in alert_type.lower():
|
||||
mock_response = {
|
||||
"action_title": f"刪除異常 Pod {target} (OOMKilled)",
|
||||
"description": f"🤖 AI 仲裁: {target} 發生 OOMKilled,根因為 JVM Heap 配置與 K8s memory limit 不匹配或存在記憶體洩漏。{signoz_summary}",
|
||||
"description": f"⚙️ 規則匹配: {target} 發生 OOMKilled,根因為 JVM Heap 配置與 K8s memory limit 不匹配或存在記憶體洩漏。{signoz_summary}",
|
||||
"suggested_action": "DELETE_POD",
|
||||
"kubectl_command": f"kubectl delete pod {target} -n {namespace}",
|
||||
"target_resource": target,
|
||||
@@ -550,9 +551,9 @@ class OpenClawService:
|
||||
"kubectl_or_config": f"kubectl autoscale deployment {target.rsplit('-', 2)[0]} --memory-percent=80 --min=2 --max=5 -n {namespace}"
|
||||
}
|
||||
],
|
||||
"reasoning": f"🤖 Pod OOMKilled 後 ReplicaSet 將自動重建,但需同步修正資源配置防止復發。{signoz_correlation}",
|
||||
"reasoning": f"⚙️ Pod OOMKilled 後 ReplicaSet 將自動重建,但需同步修正資源配置防止復發。{signoz_correlation}",
|
||||
"deviation_analysis": f"Memory 使用率 {metrics.get('memory_percent', 99)}%,超出基準線 60% 達 +6.5σ",
|
||||
"confidence": 0.88,
|
||||
"confidence": 0.0, # 🔴 規則匹配,非 AI 仲裁
|
||||
"affected_services": [target, "api-gateway"],
|
||||
"signoz_correlation": signoz_correlation,
|
||||
}
|
||||
@@ -564,7 +565,7 @@ class OpenClawService:
|
||||
|
||||
mock_response = {
|
||||
"action_title": f"擴展 {target} 副本數 + 啟用 HPA",
|
||||
"description": f"🤖 AI 仲裁: {target} CPU 使用率過高,根因為流量突增或計算密集任務未配置自動擴展。{rps_context}",
|
||||
"description": f"⚙️ 規則匹配: {target} CPU 使用率過高,根因為流量突增或計算密集任務未配置自動擴展。{rps_context}",
|
||||
"suggested_action": "SCALE_DEPLOYMENT",
|
||||
"kubectl_command": tuning["command"],
|
||||
"target_resource": target,
|
||||
@@ -591,16 +592,16 @@ class OpenClawService:
|
||||
"kubectl_or_config": f"kubectl set resources deployment/{target} --requests=cpu=500m --limits=cpu=2000m -n {namespace}"
|
||||
}
|
||||
],
|
||||
"reasoning": f"🤖 水平擴展可即時分散負載,同時建議配置 HPA 防止復發。{signoz_correlation}",
|
||||
"reasoning": f"[規則匹配] 水平擴展可即時分散負載,同時建議配置 HPA 防止復發。{signoz_correlation}",
|
||||
"deviation_analysis": f"CPU 使用率 {metrics.get('cpu_percent', 95)}%,超出基準線 50% 達 +4.5σ",
|
||||
"confidence": 0.92,
|
||||
"confidence": 0.0, # 🔴 規則匹配不是 AI 仲裁,信心度設 0
|
||||
"affected_services": [target],
|
||||
"signoz_correlation": signoz_correlation,
|
||||
}
|
||||
elif "http" in alert_type.lower() or "5xx" in message.lower() or "502" in message.lower():
|
||||
mock_response = {
|
||||
"action_title": f"重啟 {target} + 檢查上游服務",
|
||||
"description": f"🤖 AI 仲裁: {target} 產生 HTTP 5xx 錯誤,可能為應用程式例外或上游服務不可達。{signoz_summary}",
|
||||
"description": f"⚙️ 規則匹配: {target} 產生 HTTP 5xx 錯誤,可能為應用程式例外或上游服務不可達。{signoz_summary}",
|
||||
"suggested_action": "RESTART_DEPLOYMENT",
|
||||
"kubectl_command": f"kubectl rollout restart deployment/{target} -n {namespace}",
|
||||
"target_resource": target,
|
||||
@@ -627,9 +628,9 @@ class OpenClawService:
|
||||
"kubectl_or_config": "# 檢查 Redis 連線池配置,建議 maxTotal=50"
|
||||
}
|
||||
],
|
||||
"reasoning": f"🤖 HTTP 錯誤需協同排查,先重啟恢復服務同時通知相關團隊。{signoz_correlation}",
|
||||
"reasoning": f"[規則匹配] HTTP 錯誤需協同排查,先重啟恢復服務同時通知相關團隊。{signoz_correlation}",
|
||||
"deviation_analysis": "錯誤率 5%,超出基準線 0.1% 達 +50σ",
|
||||
"confidence": 0.65,
|
||||
"confidence": 0.0, # 🔴 規則匹配不是 AI 仲裁,信心度設 0
|
||||
"affected_services": [target, "nginx-ingress", "upstream-api"],
|
||||
"signoz_correlation": signoz_correlation,
|
||||
}
|
||||
@@ -637,7 +638,7 @@ class OpenClawService:
|
||||
# 通用異常處理
|
||||
mock_response = {
|
||||
"action_title": f"重新啟動 {target} 服務",
|
||||
"description": f"🤖 AI 仲裁: {target} 發生異常: {message[:80]}。需進一步診斷確認根因。{signoz_summary}",
|
||||
"description": f"⚙️ 規則匹配: {target} 發生異常: {message[:80]}。需進一步診斷確認根因。{signoz_summary}",
|
||||
"suggested_action": "RESTART_DEPLOYMENT",
|
||||
"kubectl_command": f"kubectl rollout restart deployment/{target} -n {namespace}",
|
||||
"target_resource": target,
|
||||
@@ -659,9 +660,9 @@ class OpenClawService:
|
||||
"kubectl_or_config": tuning["command"],
|
||||
}
|
||||
],
|
||||
"reasoning": f"🤖 根據告警 {alert_type} 先重啟恢復服務,同時安排深入診斷。{signoz_correlation}",
|
||||
"reasoning": f"[規則匹配] 根據告警 {alert_type} 先重啟恢復服務,同時安排深入診斷。{signoz_correlation}",
|
||||
"deviation_analysis": "監控指標顯示異常偏離基準線",
|
||||
"confidence": 0.70,
|
||||
"confidence": 0.0, # 🔴 規則匹配不是 AI 仲裁,信心度設 0
|
||||
"affected_services": [target],
|
||||
"signoz_correlation": signoz_correlation,
|
||||
}
|
||||
|
||||
@@ -142,6 +142,10 @@ class TelegramMessage:
|
||||
# 2026-03-29 ogt: AI Token/Cost 追蹤
|
||||
ai_tokens: int = 0 # LLM Token 使用量
|
||||
ai_cost: float = 0.0 # LLM 成本 (USD)
|
||||
# 2026-03-29 ogt: ADR-037 異常頻率統計
|
||||
anomaly_frequency: dict | None = None # AnomalyCounter 統計
|
||||
# 2026-03-29 ogt: AI Provider 來源顯示
|
||||
ai_provider: str = "" # ollama/gemini/claude/expert_system/mock
|
||||
|
||||
def format(self) -> str:
|
||||
"""
|
||||
@@ -198,6 +202,43 @@ class TelegramMessage:
|
||||
if self.ai_tokens > 0 or self.ai_cost > 0:
|
||||
ai_cost_display = f"💰 Tokens: {self.ai_tokens:,} / ${self.ai_cost:.4f}\n"
|
||||
|
||||
# 2026-03-29 ogt: ADR-037 異常頻率顯示
|
||||
frequency_block = ""
|
||||
if self.anomaly_frequency and self.anomaly_frequency.get("count_24h", 0) > 1:
|
||||
freq = self.anomaly_frequency
|
||||
escalation_emoji = {
|
||||
None: "",
|
||||
"REPEAT": "⚠️",
|
||||
"ESCALATE": "🔴",
|
||||
"PERMANENT_FIX": "🚨",
|
||||
}.get(freq.get("escalation_level"), "")
|
||||
|
||||
frequency_block = (
|
||||
f"━━━━━━━━━━━━━━━━━━━\n"
|
||||
f"📊 <b>頻率統計</b> {escalation_emoji}\n"
|
||||
f"├ 1h: <code>{freq.get('count_1h', 0)}</code> 次\n"
|
||||
f"├ 24h: <code>{freq.get('count_24h', 0)}</code> 次\n"
|
||||
f"└ 修復: <code>{freq.get('auto_repair_count', 0)}</code> 次\n"
|
||||
)
|
||||
if freq.get("escalation_level"):
|
||||
frequency_block += f"🔺 升級: <b>{freq['escalation_level']}</b>\n"
|
||||
|
||||
# 2026-03-29 ogt: 根據 confidence + ai_provider 動態顯示來源
|
||||
# confidence > 0 = 真實 AI 分析, confidence == 0 = 規則匹配/降級
|
||||
if self.confidence > 0 and self.ai_provider:
|
||||
# 顯示具體 AI 模型
|
||||
provider_names = {
|
||||
"ollama": "Ollama",
|
||||
"gemini": "Gemini",
|
||||
"claude": "Claude",
|
||||
}
|
||||
provider_display = provider_names.get(self.ai_provider.lower(), self.ai_provider.upper())
|
||||
source_label = f"🤖 <b>{provider_display} 仲裁</b>"
|
||||
elif self.confidence > 0:
|
||||
source_label = "🤖 <b>AI 仲裁判定</b>"
|
||||
else:
|
||||
source_label = "⚙️ <b>規則匹配</b>"
|
||||
|
||||
# 組裝訊息
|
||||
message = (
|
||||
f"═══════════════════════════\n"
|
||||
@@ -206,11 +247,12 @@ class TelegramMessage:
|
||||
f"📋 <code>{html.escape(incident_id)}</code>\n"
|
||||
f"🎯 資源: <code>{safe_resource}</code>\n"
|
||||
f"━━━━━━━━━━━━━━━━━━━\n"
|
||||
f"🤖 <b>AI 仲裁判定</b>\n"
|
||||
f"{source_label}\n"
|
||||
f"👥 責任: {resp_display}\n"
|
||||
f"📊 信心: {conf_emoji} {confidence_pct}%\n"
|
||||
f"{ai_cost_display}"
|
||||
f"💡 原因: {safe_root_cause}\n"
|
||||
f"{frequency_block}"
|
||||
f"{signoz_block}"
|
||||
f"━━━━━━━━━━━━━━━━━━━\n"
|
||||
f"🔧 建議: {safe_action}\n"
|
||||
@@ -437,11 +479,16 @@ class TelegramGateway:
|
||||
# 2026-03-29 ogt: AI Token/Cost 追蹤
|
||||
ai_tokens: int = 0,
|
||||
ai_cost: float = 0.0,
|
||||
# 2026-03-29 ogt: ADR-037 異常頻率統計
|
||||
anomaly_frequency: dict | None = None,
|
||||
# 2026-03-29 ogt: AI Provider 來源顯示
|
||||
ai_provider: str = "",
|
||||
) -> dict:
|
||||
"""
|
||||
推送待簽核卡片到 Telegram (v7.0 含 SignOz 整合)
|
||||
|
||||
SOUL.md 4.1 + AI 仲裁 + SignOz 訊息格式
|
||||
Phase 21 (ADR-037): 含異常頻率統計
|
||||
|
||||
Args:
|
||||
approval_id: 簽核單 ID
|
||||
@@ -456,6 +503,7 @@ class TelegramGateway:
|
||||
signoz_*: SignOz Gold Metrics
|
||||
signoz_trace_url: 動態時間參數的 Trace URL
|
||||
auto_tuning_command: kubectl 調優指令
|
||||
anomaly_frequency: 異常頻率統計 (ADR-037)
|
||||
|
||||
Returns:
|
||||
dict: Telegram API 回應
|
||||
@@ -475,7 +523,7 @@ class TelegramGateway:
|
||||
trace_url=signoz_trace_url,
|
||||
)
|
||||
|
||||
# 建立訊息結構 (含 AI 仲裁 + SignOz + Token/Cost)
|
||||
# 建立訊息結構 (含 AI 仲裁 + SignOz + Token/Cost + 頻率統計)
|
||||
message = TelegramMessage(
|
||||
status_emoji=emoji,
|
||||
risk_level=risk_level.upper(),
|
||||
@@ -493,6 +541,10 @@ class TelegramGateway:
|
||||
# 2026-03-29 ogt: AI Token/Cost 追蹤
|
||||
ai_tokens=ai_tokens,
|
||||
ai_cost=ai_cost,
|
||||
# 2026-03-29 ogt: ADR-037 異常頻率統計
|
||||
anomaly_frequency=anomaly_frequency,
|
||||
# 2026-03-29 ogt: AI Provider 來源顯示
|
||||
ai_provider=ai_provider,
|
||||
)
|
||||
|
||||
# 格式化訊息
|
||||
|
||||
Reference in New Issue
Block a user