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(