diff --git a/apps/api/src/services/openclaw.py b/apps/api/src/services/openclaw.py index e3d752e60..ba619d434 100644 --- a/apps/api/src/services/openclaw.py +++ b/apps/api/src/services/openclaw.py @@ -255,6 +255,82 @@ class OpenClawService: } # ========================================================================= + # ========================================================================= + # OpenClaw (Nemo) 委派仲裁 — 架構鐵律 + # 2026-04-01 ogt: AWOOOI API 不直接打 LLM,委派給 OpenClaw (192.168.0.188:8089) + # ========================================================================= + + async def _call_openclaw_analyze( + self, + incident_id: str, + severity: str, + signals: list[dict], + affected_services: list[str], + expert_context: dict | None = None, + ) -> dict | None: + """ + 委派 Incident RCA 給 OpenClaw (Nemo) API + + Returns: + proposal_dict if success, None if failed (fallback to direct LLM) + """ + try: + client = await self._get_client() + payload = { + "incident_id": incident_id, + "severity": severity, + "signals": signals[:5], + "affected_services": affected_services, + "expert_context": expert_context, + } + resp = await client.post( + f"{settings.OPENCLAW_URL}/api/v1/analyze/incident", + json=payload, + timeout=httpx.Timeout(130.0, connect=5.0), + ) + resp.raise_for_status() + data = resp.json() + + # 驗證必要欄位 + if not data.get("action_title") or not data.get("risk_level"): + logger.warning("openclaw_analyze_invalid_response", incident_id=incident_id) + return None + + logger.info( + "openclaw_analyze_success", + incident_id=incident_id, + confidence=data.get("confidence", 0), + risk_level=data.get("risk_level"), + ) + + # 轉換為 AWOOOI API proposal dict 格式 + return { + "action": data.get("action_title", "AI 分析"), + "description": data.get("description", ""), + "kubectl_command": data.get("kubectl_command") or "", + "target_resource": data.get("target_resource", "unknown"), + "namespace": data.get("namespace", "awoooi-prod"), + "risk_level": data.get("risk_level", "medium"), + "reasoning": data.get("reasoning", ""), + "confidence": data.get("confidence", 0.8), + "primary_responsibility": data.get("primary_responsibility", "INFRA"), + "optimization_suggestions": data.get("optimization_suggestions", []), + "signoz_correlation": data.get("signoz_correlation", ""), + "from_cache": False, + "provider": "openclaw_nemo", + "ai_tokens": 0, + "ai_cost": 0.0, + } + + except Exception as e: + logger.warning( + "openclaw_analyze_failed", + incident_id=incident_id, + error=str(e), + reason="fallback to direct LLM", + ) + return None + # AI Provider Implementations - Enhanced with Structured Output # ========================================================================= @@ -1312,6 +1388,14 @@ Focus on: signoz_available=signoz_metrics is not None, ) + # 2026-04-01 ogt: 架構鐵律 — OpenClaw (Nemo) 是 AI 大腦,優先委派仲裁 + # AWOOOI K8s Pod 不直接打 Ollama/NVIDIA,避免並發逾時 + openclaw_result = await self._call_openclaw_analyze( + incident_id, severity, signals, affected_services, expert_context + ) + if openclaw_result is not None: + return openclaw_result, "openclaw_nemo", True + # 使用快取呼叫 LLM alert_context = { "incident_id": incident_id, diff --git a/k8s/awoooi-prod/04-configmap.yaml b/k8s/awoooi-prod/04-configmap.yaml index 47e2558f3..14b32a3fb 100644 --- a/k8s/awoooi-prod/04-configmap.yaml +++ b/k8s/awoooi-prod/04-configmap.yaml @@ -16,7 +16,7 @@ data: # 服務端點 (非機密) OLLAMA_URL: "http://192.168.0.188:11434" - OPENCLAW_URL: "http://192.168.0.188:8089" + OPENCLAW_URL: "http://192.168.0.188:8088" KALI_SCANNER_URL: "http://192.168.0.112:8080" SIGNOZ_URL: "http://192.168.0.188:3301" LANGFUSE_URL: "http://192.168.0.110:3100"