fix(arch-review): 首席架構師審查 S1×3 S2×3 S3×3 全修復 + ADR-064
Some checks failed
CD Pipeline / build-and-deploy (push) Has been cancelled

S1 Critical:
- S1-1: asyncio 觸發移至 _call_with_fallback async 上下文,移除 sync 中的 get_event_loop()
- S1-2: _append_rule_to_yaml 加 textwrap.dedent() 正規化 LLM 輸出縮排
- S1-3: _matches() 對 alertname=["*"] 直接回傳 False,防意外命中

S2 Major:
- S2-1: auto_generate_rule() 改為 DI 參數注入 (ollama_url/model/gemini_api_key),移除 import settings
- S2-4: _generate_mock_response docstring 澄清為規則引擎生產路徑,非假數據
- S2-5: suggested_action .strip() 防空白字串繞過 or

S3 Minor:
- S3-2: priority 上界 min(next, 890)
- S3-3: alertname sanitize re.sub([{}]) 防 format KeyError
- S3-4: model_registry.py 最後修改時間戳更新

文件:
- ADR-064: Alert Rule Engine YAML 驅動 + AI 自動學習
- Skills 02: 告警規則引擎 DI 規範 + asyncio 禁止事項
- Skills 03: _generate_mock_response 語意澄清 + 規則引擎降級流程
- LOGBOOK: 本次 Session 完整記錄

2026-04-09 ogt: 首席架構師審查修正

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
OG T
2026-04-09 10:52:40 +08:00
parent 11fc2860cf
commit 428e66c111
8 changed files with 303 additions and 53 deletions

View File

@@ -572,12 +572,17 @@ class OpenClawService:
signoz_metrics: GoldMetrics | None = None,
) -> str:
"""
Mock LLM 回應生成器 - 規則引擎降級 (v8.0)
規則引擎降級回應 (v8.0) — 生產用途,不是假數據
從 alert_rules.yaml 載入規則,取代硬編碼 if/elif
新增規則只需修改 YAML不需要改代碼重新部署。
從 alert_rules.yaml 載入規則進行匹配AI 分析失敗時的正式降級路徑
命中 generic_fallback 時會回傳 rule_id="generic_fallback"
由上層 async 方法_call_with_fallback觸發 auto_generate_rule() 學習新規則。
Returns:
(json_str, rule_id) tuple
2026-04-09 ogt: 重構為規則引擎,移除 if/elif 硬編碼
2026-04-09 ogt: S2-4 架構師審查 — 修正 Mock 語意混淆,澄清為規則引擎生產路徑
"""
from src.services.alert_rule_engine import match_rule
@@ -640,20 +645,9 @@ class OpenClawService:
is_mock=True,
)
# 2026-04-09 ogt: 命中通用兜底時,背景自動生成專屬規則
if rule_id == "generic_fallback":
from src.services.alert_rule_engine import auto_generate_rule
import asyncio
try:
loop = asyncio.get_event_loop()
if loop.is_running():
loop.create_task(auto_generate_rule(alert_context))
else:
asyncio.run(auto_generate_rule(alert_context))
except Exception as _e:
logger.warning("auto_rule_trigger_failed", error=str(_e))
return json.dumps(mock_response)
# 2026-04-09 ogt: rule_id 回傳給上層 async 方法觸發自動規則生成
# 不在此 sync 方法中呼叫 asyncio避免 event loop 混用問題 (S1-1 架構師審查)
return json.dumps(mock_response), rule_id
# =========================================================================
# LLM Cache Layer (憲法要求: 嚴禁無快取裸奔)
@@ -871,7 +865,20 @@ class OpenClawService:
# Mock Mode: 開發測試用
if settings.MOCK_MODE:
logger.info("mock_mode_enabled", using="mock_llm")
return self._generate_mock_response(alert_context or {}, signoz_metrics), "mock", True, 0, 0.0
_mock_json, _rule_id = self._generate_mock_response(alert_context or {}, signoz_metrics)
if _rule_id == "generic_fallback":
import asyncio
from src.services.alert_rule_engine import auto_generate_rule
try:
asyncio.create_task(auto_generate_rule(
alert_context or {},
ollama_url=settings.OLLAMA_URL,
model=settings.OPENCLAW_DEFAULT_MODEL,
gemini_api_key=getattr(settings, "GEMINI_API_KEY", ""),
))
except Exception as _e:
logger.warning("auto_rule_trigger_failed", error=str(_e))
return _mock_json, "mock", True, 0, 0.0
# Phase 15.1 + 15.3: Langfuse 追蹤整合 + SignOz Deep Linking
with langfuse_trace(
@@ -978,7 +985,20 @@ class OpenClawService:
# 所有 Provider 失敗時fallback 到 Mock (優雅降級)
logger.warning("all_providers_failed_using_mock", fallback="mock_llm")
trace.score(name="provider_success", value=0.0, comment="All providers failed, using mock")
return self._generate_mock_response(alert_context or {}, signoz_metrics), "mock_fallback", True, 0, 0.0
_mock_json, _rule_id = self._generate_mock_response(alert_context or {}, signoz_metrics)
if _rule_id == "generic_fallback":
import asyncio
from src.services.alert_rule_engine import auto_generate_rule
try:
asyncio.create_task(auto_generate_rule(
alert_context or {},
ollama_url=settings.OLLAMA_URL,
model=settings.OPENCLAW_DEFAULT_MODEL,
gemini_api_key=getattr(settings, "GEMINI_API_KEY", ""),
))
except Exception as _e:
logger.warning("auto_rule_trigger_failed", error=str(_e))
return _mock_json, "mock_fallback", True, 0, 0.0
def _get_model_name(self, provider: str) -> str:
"""取得 provider 對應的模型名稱 (從 ModelRegistry)"""