feat(api): Phase 7.5-7.6 Playbook 整合決策與自動萃取
Phase 7.5: DecisionManager 三軌決策 - 新增 Playbook 優先匹配 (similarity >= 85%) - 三軌決策順序: Playbook > LLM > Expert System - 整合 PlaybookService 推薦引擎 Phase 7.6: 自動萃取機制 - approval_execution.py 成功執行後觸發萃取 - 條件: RESOLVED/CLOSED + effectiveness >= 4 - 滿分 (5) 自動核准 Playbook 測試: - 13 個 Playbook 單元測試全部通過 - 修復 Incident 模型欄位對應 (reasoning_steps) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -30,10 +30,15 @@ import structlog
|
||||
from src.core.config import settings
|
||||
from src.core.redis_client import get_redis
|
||||
from src.models.incident import Incident
|
||||
from src.models.playbook import SymptomPattern
|
||||
from src.services.openclaw import get_openclaw
|
||||
from src.services.playbook_service import get_playbook_service
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
# Phase 7.5: Playbook 優先閾值
|
||||
PLAYBOOK_SIMILARITY_THRESHOLD = 0.85 # 相似度 >= 85% 直接使用 Playbook
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Telegram 推送 (Phase 6.5: 決策就緒通知)
|
||||
@@ -394,13 +399,20 @@ class DecisionManager:
|
||||
incident: Incident,
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
雙軌決策分析
|
||||
三軌決策分析 (Phase 7.5 升級)
|
||||
|
||||
策略:
|
||||
- 同時啟動 LLM 和 Expert System
|
||||
- LLM 成功則用 LLM (更智能)
|
||||
- LLM 失敗則用 Expert System (保底)
|
||||
1. 先檢查 Playbook 是否有高度匹配 (similarity >= 85%)
|
||||
2. Playbook 命中則直接使用 (最快、經驗驗證)
|
||||
3. 否則 LLM + Expert System 雙軌
|
||||
|
||||
優先順序: Playbook > LLM > Expert System
|
||||
"""
|
||||
# Phase 7.5: 先嘗試 Playbook 匹配
|
||||
playbook_result = await self._try_playbook_match(incident)
|
||||
if playbook_result:
|
||||
return playbook_result
|
||||
|
||||
# Expert System 同步執行 (立即可用)
|
||||
expert_result = expert_analyze(incident)
|
||||
|
||||
@@ -440,6 +452,108 @@ class DecisionManager:
|
||||
)
|
||||
return expert_result
|
||||
|
||||
async def _try_playbook_match(
|
||||
self,
|
||||
incident: Incident,
|
||||
) -> dict[str, Any] | None:
|
||||
"""
|
||||
Phase 7.5: 嘗試 Playbook 匹配
|
||||
|
||||
條件:
|
||||
- 相似度 >= PLAYBOOK_SIMILARITY_THRESHOLD (85%)
|
||||
- Playbook 狀態為 APPROVED
|
||||
- 成功率 >= 80% (如果有執行紀錄)
|
||||
|
||||
Returns:
|
||||
匹配成功返回 proposal_data,否則 None
|
||||
"""
|
||||
try:
|
||||
playbook_service = get_playbook_service()
|
||||
|
||||
# 建構症狀模式
|
||||
alert_names = [s.alert_name for s in incident.signals] if incident.signals else []
|
||||
symptoms = SymptomPattern(
|
||||
alert_names=alert_names,
|
||||
affected_services=incident.affected_services or [],
|
||||
severity_range=[incident.severity.value] if incident.severity else ["P2"],
|
||||
)
|
||||
|
||||
# 取得推薦 (只取 Top 1)
|
||||
recommendations = await playbook_service.get_recommendations(
|
||||
symptoms=symptoms,
|
||||
top_k=1,
|
||||
)
|
||||
|
||||
if not recommendations:
|
||||
logger.debug(
|
||||
"playbook_no_match",
|
||||
incident_id=incident.incident_id,
|
||||
)
|
||||
return None
|
||||
|
||||
best_match = recommendations[0]
|
||||
playbook = best_match.playbook
|
||||
|
||||
# 檢查相似度閾值
|
||||
if best_match.similarity_score < PLAYBOOK_SIMILARITY_THRESHOLD:
|
||||
logger.debug(
|
||||
"playbook_similarity_below_threshold",
|
||||
incident_id=incident.incident_id,
|
||||
playbook_id=playbook.playbook_id,
|
||||
similarity=best_match.similarity_score,
|
||||
threshold=PLAYBOOK_SIMILARITY_THRESHOLD,
|
||||
)
|
||||
return None
|
||||
|
||||
# 檢查成功率 (如果有執行紀錄)
|
||||
if playbook.total_executions > 0 and playbook.success_rate < 0.8:
|
||||
logger.debug(
|
||||
"playbook_low_success_rate",
|
||||
incident_id=incident.incident_id,
|
||||
playbook_id=playbook.playbook_id,
|
||||
success_rate=playbook.success_rate,
|
||||
)
|
||||
return None
|
||||
|
||||
# Playbook 命中!
|
||||
# 取得第一個修復步驟的指令
|
||||
kubectl_command = ""
|
||||
if playbook.repair_steps:
|
||||
# 將 target 替換為實際服務名稱
|
||||
target = incident.affected_services[0] if incident.affected_services else "unknown"
|
||||
kubectl_command = playbook.repair_steps[0].command.format(target=target)
|
||||
|
||||
logger.info(
|
||||
"playbook_match_success",
|
||||
incident_id=incident.incident_id,
|
||||
playbook_id=playbook.playbook_id,
|
||||
playbook_name=playbook.name,
|
||||
similarity=best_match.similarity_score,
|
||||
success_rate=playbook.success_rate,
|
||||
)
|
||||
|
||||
return {
|
||||
"source": "playbook",
|
||||
"playbook_id": playbook.playbook_id,
|
||||
"playbook_name": playbook.name,
|
||||
"action": kubectl_command,
|
||||
"kubectl_command": kubectl_command,
|
||||
"description": playbook.description,
|
||||
"risk_level": playbook.repair_steps[0].risk_level.value.lower() if playbook.repair_steps else "medium",
|
||||
"reasoning": f"Playbook 匹配 ({best_match.similarity_score:.0%} 相似度, {playbook.success_rate:.0%} 成功率): {best_match.reason}",
|
||||
"confidence": min(best_match.similarity_score, playbook.success_rate) if playbook.total_executions > 0 else best_match.similarity_score,
|
||||
"matched_symptoms": best_match.matched_symptoms,
|
||||
"from_cache": False,
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(
|
||||
"playbook_match_error",
|
||||
incident_id=incident.incident_id,
|
||||
error=str(e),
|
||||
)
|
||||
return None
|
||||
|
||||
async def _find_existing_token(
|
||||
self,
|
||||
incident_id: str,
|
||||
|
||||
Reference in New Issue
Block a user