diff --git a/apps/api/src/services/ai_providers/nemotron.py b/apps/api/src/services/ai_providers/nemotron.py index b3744b5a..013db91a 100644 --- a/apps/api/src/services/ai_providers/nemotron.py +++ b/apps/api/src/services/ai_providers/nemotron.py @@ -171,9 +171,21 @@ class NemotronProvider: timeout = getattr(settings, "NEMOTRON_TIMEOUT_SECONDS", 45) nvidia = self._get_nvidia() + # 2026-04-05 Claude Code: I2 修正 — 補上 system prompt (NIM 標準 message format) + # NIM API 要求 system role 先定義模型角色,否則 tool calling 品質下降 result = await asyncio.wait_for( nvidia.tool_call( - messages=[{"role": "user", "content": tool_prompt}], + messages=[ + { + "role": "system", + "content": ( + "你是 Kubernetes 操作專家。根據 AI 診斷結果," + "使用提供的工具生成精確的 kubectl 操作指令。" + "優先選擇最安全、最小影響範圍的操作。" + ), + }, + {"role": "user", "content": tool_prompt}, + ], tools=_K8S_TOOLS, ), timeout=timeout, diff --git a/apps/api/src/services/drift_detector.py b/apps/api/src/services/drift_detector.py index c873ecc1..39c622ef 100644 --- a/apps/api/src/services/drift_detector.py +++ b/apps/api/src/services/drift_detector.py @@ -296,22 +296,34 @@ class DriftDetector: return diffs + @staticmethod + def _pattern_matches(pattern: str, field_path: str) -> bool: + """ + 匹配 field_path 是否符合 pattern。 + + 支援兩種萬用字元: + - [*] → 任意索引 (e.g. containers[*] 匹配 containers[0], containers[1]) + - * → 任意字串段 + + 2026-04-05 Claude Code: I4 修正 — 舊邏輯直接 strip [*] 導致 + containers[*].image 無法匹配 containers[0].image (首席架構師 Review I4) + """ + import re as _re + + # 將 pattern 轉為正則:[*] → \[\d+\],* → [^.]+ + regex = _re.escape(pattern) + regex = regex.replace(r"\[\*\]", r"\[\d+\]") + regex = regex.replace(r"\*", r"[^.]+") + # 允許 pattern 是前綴(field_path 可能更深,. 或 [ 或字串結尾均可) + return bool(_re.match(f"^{regex}(\\.|\\[|$)", field_path)) + def _is_allowlisted(self, field_path: str) -> bool: """判斷欄位是否在白名單(靜默記錄不告警)""" - for pattern in self._allowlist: - # 簡單前綴匹配(*替換為粗略包含) - clean_pattern = pattern.replace("[*]", "") - if field_path.startswith(clean_pattern.replace("*", "")): - return True - return False + return any(self._pattern_matches(p, field_path) for p in self._allowlist) def _is_critical(self, field_path: str) -> bool: """判斷欄位是否為關鍵欄位(HIGH 等級)""" - for pattern in self._critical_fields: - clean_pattern = pattern.replace("[*]", "") - if clean_pattern.replace("*", "") in field_path: - return True - return False + return any(self._pattern_matches(p, field_path) for p in self._critical_fields) # =============================================================================