diff --git a/apps/api/src/services/decision_manager.py b/apps/api/src/services/decision_manager.py index e05e7705..469c4c3a 100644 --- a/apps/api/src/services/decision_manager.py +++ b/apps/api/src/services/decision_manager.py @@ -548,17 +548,56 @@ class DecisionManager: _push_decision_to_telegram(incident, token.proposal_data) ) + async def _query_kb_context(self, incident: Incident) -> str: + """ + KB Phase 2: 語意搜尋相關 KB 條目,組裝為 LLM context 字串 + 2026-04-04 Claude Code: KB RAG 整合 + + 失敗時靜默降級,不影響主分析流程 + """ + try: + from src.services.knowledge_service import get_knowledge_service + query_parts = list(incident.affected_services) + if incident.signals: + query_parts.insert(0, getattr(incident.signals[0], "alert_name", "")) + query = " ".join(filter(None, query_parts)) + + svc = get_knowledge_service() + results = await svc.semantic_search(query, limit=3, threshold=0.4) + if not results: + return "" + + lines = ["## 📚 Knowledge Base 相關條目 (KB RAG)"] + for entry, score in results: + lines.append( + f"\n### [{entry.entry_type}] {entry.title} (similarity={score:.2f})" + ) + lines.append(entry.content[:500]) + if len(entry.content) > 500: + lines.append("... (truncated)") + + logger.info( + "kb_rag_context_injected", + incident_id=incident.incident_id, + kb_hits=len(results), + ) + return "\n".join(lines) + + except Exception as e: + logger.warning("kb_rag_failed", incident_id=incident.incident_id, error=str(e)) + return "" + async def _dual_engine_analyze( self, incident: Incident, ) -> dict[str, Any]: """ - 三軌決策分析 (Phase 7.5 升級) + 三軌決策分析 (Phase 7.5 升級 + KB Phase 2 RAG 整合) 策略: 1. 先檢查 Playbook 是否有高度匹配 (similarity >= 85%) 2. Playbook 命中則直接使用 (最快、經驗驗證) - 3. 否則 LLM + Expert System 雙軌 + 3. 否則 LLM + Expert System 雙軌 + KB RAG context 注入 優先順序: Playbook > LLM > Expert System """ @@ -570,16 +609,29 @@ class DecisionManager: # Expert System 同步執行 (立即可用) expert_result = expert_analyze(incident) + # KB Phase 2: 語意搜尋相關知識條目 (失敗時靜默降級) + # 2026-04-04 Claude Code: KB RAG 整合,提升 LLM 決策品質 + kb_context = await self._query_kb_context(incident) + # LLM 非同步執行 (Phase 22: OpenClaw + Nemotron 協作) # 2026-03-31 Claude Code: 使用 _with_tools 方法啟用雙軌協作 try: signals_dict = [s.model_dump() for s in incident.signals] + # 將 KB context 注入 expert_context 傳給 LLM + llm_expert_context: dict[str, Any] = {**expert_result} if expert_result else {} + if kb_context: + existing = str(llm_expert_context.get("diagnosis_context", "")) + llm_expert_context["diagnosis_context"] = ( + f"{kb_context}\n\n{existing}" if existing else kb_context + ) + llm_result, provider, success = await self._openclaw.generate_incident_proposal_with_tools( incident_id=incident.incident_id, severity=incident.severity.value, signals=signals_dict, affected_services=incident.affected_services, + expert_context=llm_expert_context if llm_expert_context else None, ) if success and llm_result: @@ -587,6 +639,7 @@ class DecisionManager: "dual_engine_llm_win", incident_id=incident.incident_id, provider=provider, + kb_rag=bool(kb_context), ) return { **llm_result,