feat(api): LLM 整合 Expert System 診斷上下文

長期方案實作: Expert 診斷 + LLM 智能分析

變更:
1. decision_manager._dual_engine_analyze():
   - 測試資源跳過 LLM (省錢)
   - 傳遞 Expert 診斷上下文給 LLM
   - LLM 失敗時根據診斷調整回應

2. openclaw.generate_incident_proposal():
   - 新增 expert_context 參數
   - Prompt 包含 Expert 診斷結果
   - 引導 LLM 基於診斷做決策

流程:
Playbook → Expert診斷 → LLM(with context) → 智能建議

這是「先診斷根因,再決定行動」的正確實作

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
OG T
2026-03-26 21:41:26 +08:00
parent 2ef7daccde
commit d148756b67
2 changed files with 74 additions and 11 deletions

View File

@@ -585,32 +585,54 @@ class DecisionManager:
incident: Incident,
) -> dict[str, Any]:
"""
三軌決策分析 (Phase 7.5 升級)
三軌決策分析 (Phase 7.5 升級 + 2026-03-27 智能診斷重構)
策略:
1. 先檢查 Playbook 是否有高度匹配 (similarity >= 85%)
2. Playbook 命中則直接使用 (最快、經驗驗證)
3. 否則 LLM + Expert System 雙軌
3. Expert System 提供初步診斷 (分類 + 診斷指令)
4. LLM 基於診斷上下文提供智能建議
5. LLM 失敗時,根據 Expert 診斷決定是否需人工介入
優先順序: Playbook > LLM > Expert System
優先順序: Playbook > LLM(with Expert context) > Expert System
"""
# Phase 7.5: 先嘗試 Playbook 匹配
playbook_result = await self._try_playbook_match(incident)
if playbook_result:
return playbook_result
# Expert System 同步執行 (立即可用)
# ========== 2026-03-27 重構: 分層智能診斷 ==========
# Step 1: Expert System 提供初步診斷 (永不失敗)
expert_result = expert_analyze(incident)
# LLM 非同步執行
try:
signals_dict = [s.model_dump() for s in incident.signals]
# Step 2: 測試資源直接返回 (不浪費 LLM 呼叫)
if expert_result.get("is_test_resource"):
logger.info(
"dual_engine_test_resource_skip",
incident_id=incident.incident_id,
target=incident.affected_services[0] if incident.affected_services else "unknown",
)
return expert_result
# Step 3: 準備 LLM 上下文 (含 Expert 診斷)
signals_dict = [s.model_dump() for s in incident.signals]
expert_context = {
"initial_diagnosis": expert_result.get("matched_rule"),
"diagnosis_description": expert_result.get("description"),
"suggested_diagnosis_commands": expert_result.get("diagnosis_commands", []),
"expert_confidence": expert_result.get("confidence"),
"requires_human_review": expert_result.get("human_review_required", False),
}
# Step 4: LLM 分析 (帶上 Expert 上下文)
try:
llm_result, provider, success = await self._openclaw.generate_incident_proposal(
incident_id=incident.incident_id,
severity=incident.severity.value,
signals=signals_dict,
affected_services=incident.affected_services,
expert_context=expert_context, # 傳遞 Expert 診斷上下文
)
if success and llm_result:
@@ -618,10 +640,12 @@ class DecisionManager:
"dual_engine_llm_win",
incident_id=incident.incident_id,
provider=provider,
expert_rule=expert_result.get("matched_rule"),
)
return {
**llm_result,
"source": f"llm_{provider}",
"expert_diagnosis": expert_result.get("matched_rule"),
}
except Exception as e:
@@ -629,13 +653,23 @@ class DecisionManager:
"dual_engine_llm_failed",
incident_id=incident.incident_id,
error=str(e),
expert_rule=expert_result.get("matched_rule"),
)
# LLM 失敗,使用 Expert System
# Step 5: LLM 失敗,使用 Expert System 結果
# 但根據診斷結果調整回應
logger.info(
"dual_engine_expert_fallback",
incident_id=incident.incident_id,
expert_rule=expert_result.get("matched_rule"),
human_review=expert_result.get("human_review_required", False),
)
# 如果 Expert 標記需人工介入,降低 confidence
if expert_result.get("human_review_required"):
expert_result["confidence"] = min(expert_result.get("confidence", 0.5), 0.5)
expert_result["description"] += " [LLM 分析失敗,建議人工確認]"
return expert_result
async def _try_playbook_match(

View File

@@ -1068,17 +1068,25 @@ Trace URL: {signoz_trace_url}
severity: str,
signals: list[dict],
affected_services: list[str],
expert_context: dict | None = None,
) -> tuple[dict | None, str, bool]:
"""
為 Incident 生成 LLM-based 修復提案
Phase 6.4: 賦予大腦「生成解決方案」的思考能力
2026-03-27: 整合 Expert System 診斷上下文
Args:
incident_id: Incident ID
severity: 嚴重度 (P0/P1/P2/P3)
signals: 關聯的告警訊號
affected_services: 受影響服務
expert_context: Expert System 初步診斷 (可選)
- initial_diagnosis: 規則匹配結果
- diagnosis_description: 診斷描述
- suggested_diagnosis_commands: 建議診斷指令
- expert_confidence: 信心分數
- requires_human_review: 是否需人工介入
Returns:
(proposal_dict, provider, success)
@@ -1108,11 +1116,31 @@ Trace URL: {signoz_trace_url}
signoz_context = f"""
## 📊 SignOz Real-time Metrics
{signoz_metrics.to_summary()}
"""
# 2026-03-27: 整合 Expert System 診斷上下文
expert_diagnosis_context = ""
if expert_context:
diagnosis_cmds = expert_context.get("suggested_diagnosis_commands", [])
diagnosis_cmds_str = "\n".join([f" - `{cmd}`" for cmd in diagnosis_cmds]) if diagnosis_cmds else " - (無)"
expert_diagnosis_context = f"""
## 🔍 Expert System Initial Diagnosis
- **Matched Rule**: {expert_context.get('initial_diagnosis', 'unknown')}
- **Diagnosis**: {expert_context.get('diagnosis_description', 'N/A')}
- **Confidence**: {expert_context.get('expert_confidence', 0.5):.0%}
- **Requires Human Review**: {'Yes' if expert_context.get('requires_human_review') else 'No'}
- **Suggested Diagnosis Commands**:
{diagnosis_cmds_str}
**IMPORTANT**: The Expert System has provided an initial diagnosis.
Consider this context but apply your own analysis. If Expert says "human review required",
provide diagnostic guidance rather than automated fixes.
"""
proposal_prompt = f"""{OPENCLAW_SYSTEM_PROMPT}
{signoz_context}
{expert_diagnosis_context}
## 🚨 Incident Context
- **Incident ID**: {incident_id}
@@ -1124,13 +1152,14 @@ Trace URL: {signoz_trace_url}
{signal_summary}
## 🎯 Your Task
Based on the above incident and signals, generate a remediation proposal.
Based on the above incident, signals, and Expert System diagnosis, generate a remediation proposal.
You MUST respond with ONLY valid JSON following the schema above.
Focus on:
1. Root cause analysis based on signals and SignOz data
2. Specific kubectl command to remediate
1. Root cause analysis based on signals, SignOz data, and Expert diagnosis
2. Specific kubectl command to remediate (or diagnostic command if root cause unclear)
3. Risk assessment for the proposed action
4. Preventive recommendations
5. If Expert System flagged "human review required", prioritize diagnostic commands over fixes
"""
logger.info(