fix(aiops): 修復 sensors=0/0 根因 — MCPToolRegistry 從未在 startup 初始化
Some checks failed
CD Pipeline / build-and-deploy (push) Failing after 1m44s

三個問題同時修復:

1. main.py: 補上 init_mcp_tool_registry() 呼叫
   - ADR-081 Phase 1 建立了 MCPToolRegistry 但從未在 lifespan startup 被呼叫
   - 導致 PreDecisionInvestigator sensors=0/0,evidence_summary 永遠空白
   - 空白 evidence → Diagnostician 永遠 ABSTAIN

2. signal_producer.py: str(dict) → json.dumps()
   - labels/annotations 用 Python str() 序列化,寫入 Redis 後無法反序列化

3. brain/incident_engine.py: 新增 _parse_dict_field() helper
   - 從 Redis 讀回的 labels/annotations 可能是 JSON 字串
   - isinstance(..., dict) 防禦不足,需先 json.loads()

2026-04-16 ogt + Claude Sonnet 4.6(亞太): 飛輪感官修復

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
OG T
2026-04-16 15:35:19 +08:00
parent d294caf830
commit 588b0d745b
3 changed files with 30 additions and 4 deletions

View File

@@ -230,6 +230,15 @@ async def lifespan(_app: FastAPI) -> AsyncGenerator[None, None]:
register_all_providers()
logger.info("mcp_providers_registered")
# ADR-081 Phase 1: MCPToolRegistry 初始化PreDecisionInvestigator 感官工具)
# 2026-04-16 ogt + Claude Sonnet 4.6: 修復 sensors=0/0 根因 — init 從未在 startup 被呼叫
try:
from src.services.mcp_tool_registry import init_mcp_tool_registry
await init_mcp_tool_registry()
logger.info("mcp_tool_registry_initialized")
except Exception as e:
logger.warning("mcp_tool_registry_init_failed", error=str(e))
# Phase 6.5: Telegram 心跳監控(每 30 分鐘發送到 SRE 戰情室群組)
# 2026-04-16 ogt + Claude Sonnet 4.6: 恢復 — 使用者確認必須繼續在 SRE 戰情室發送
# 上次停用原因forwarded_to_separate_group有誤群組就是 SRE_GROUP_CHAT_ID

View File

@@ -12,6 +12,7 @@ Signal Producer Service - Phase 17 P0 Router 層違規修復
- Router -> Service -> Redis
"""
import json
from dataclasses import dataclass
from typing import Any
@@ -74,8 +75,8 @@ class SignalProducerService:
"namespace": signal.namespace,
"target": signal.target,
"message": signal.message,
"labels": str(signal.labels or {}),
"annotations": str(signal.annotations or {}),
"labels": json.dumps(signal.labels or {}),
"annotations": json.dumps(signal.annotations or {}),
"received_at": now_taipei().isoformat(),
}

View File

@@ -30,6 +30,22 @@ from lewooogo_brain.interfaces.incident_processor import (
)
def _parse_dict_field(value: Any) -> dict:
"""將 Redis 讀回的欄位(可能是 JSON 字串或 dict轉為 dict。
signal_producer 以 json.dumps() 寫入;舊資料可能是 str(dict) repr一律 fallback 空 dict。
2026-04-16 ogt + Claude Sonnet 4.6: 修復 labels/annotations 永遠為 {} 的根因
"""
if isinstance(value, dict):
return value
if isinstance(value, str) and value:
try:
result = json.loads(value)
return result if isinstance(result, dict) else {}
except (json.JSONDecodeError, ValueError):
return {}
return {}
# =============================================================================
# Memory Provider Protocol (依賴注入用)
# =============================================================================
@@ -237,8 +253,8 @@ class IncidentEngine(IIncidentProcessor):
severity=severity,
source=data.get("source", "unknown"),
fired_at=datetime.now(timezone.utc),
labels=data.get("labels", {}) if isinstance(data.get("labels"), dict) else {},
annotations=data.get("annotations", {}) if isinstance(data.get("annotations"), dict) else {},
labels=_parse_dict_field(data.get("labels")),
annotations=_parse_dict_field(data.get("annotations")),
)
def _compute_fingerprint(self, data: dict[str, Any]) -> str: