feat(incident): add trigger_reanalysis() with Redis 10min dedup (ADR-050)
This commit is contained in:
@@ -746,6 +746,89 @@ class IncidentService:
|
||||
)
|
||||
return None
|
||||
|
||||
async def trigger_reanalysis(self, incident_id: str) -> dict:
|
||||
"""
|
||||
觸發 Incident 重診 (ADR-050 P2: reanalyze button)
|
||||
|
||||
去重保護:同一 incident 10 分鐘內只觸發一次。
|
||||
觸發後將 incident status 標記為 analyzing,等待 AI 自動接手。
|
||||
|
||||
Args:
|
||||
incident_id: Incident ID
|
||||
|
||||
Returns:
|
||||
dict: {
|
||||
"triggered": bool,
|
||||
"message": str,
|
||||
"already_analyzing": bool,
|
||||
}
|
||||
|
||||
2026-04-01 Claude Code (ADR-050 P2): reanalyze button handler
|
||||
"""
|
||||
REANALYZE_TTL_SECONDS = 600 # 10 分鐘去重 TTL (ADR-050)
|
||||
dedup_key = f"reanalyze_dedup:{incident_id}"
|
||||
|
||||
try:
|
||||
redis_client = get_redis()
|
||||
|
||||
# 去重檢查 (SETNX: 只有第一次設定會成功)
|
||||
is_new = await redis_client.set(dedup_key, "1", ex=REANALYZE_TTL_SECONDS, nx=True)
|
||||
if not is_new:
|
||||
logger.info(
|
||||
"reanalyze_deduplicated",
|
||||
incident_id=incident_id,
|
||||
reason="Already triggered within 10 minutes",
|
||||
)
|
||||
return {
|
||||
"triggered": False,
|
||||
"message": "重診已在進行中,請 10 分鐘後再試",
|
||||
"already_analyzing": True,
|
||||
}
|
||||
|
||||
# 從 Working Memory 取得 Incident
|
||||
incident = await self.get_from_working_memory(incident_id)
|
||||
if not incident:
|
||||
incident = await self.get_from_episodic_memory(incident_id)
|
||||
|
||||
if not incident:
|
||||
# 刪除剛設定的去重 key(讓下次能重試)
|
||||
await redis_client.delete(dedup_key)
|
||||
logger.warning("reanalyze_incident_not_found", incident_id=incident_id)
|
||||
return {
|
||||
"triggered": False,
|
||||
"message": f"找不到事件 {incident_id}",
|
||||
"already_analyzing": False,
|
||||
}
|
||||
|
||||
# 標記 status 為 analyzing(讓 AI 引擎接手)
|
||||
# 使用延遲 import 避免循環依賴(同 create_incident_from_signal 模式)
|
||||
from src.models.incident import IncidentStatus
|
||||
# 使用 INVESTIGATING 若 ANALYZING 不存在
|
||||
analyzing_status = getattr(IncidentStatus, "ANALYZING", None) or getattr(IncidentStatus, "INVESTIGATING", None)
|
||||
if analyzing_status:
|
||||
incident.status = analyzing_status
|
||||
await self.save_to_working_memory(incident)
|
||||
|
||||
logger.info(
|
||||
"reanalyze_triggered",
|
||||
incident_id=incident_id,
|
||||
severity=incident.severity.value,
|
||||
)
|
||||
|
||||
return {
|
||||
"triggered": True,
|
||||
"message": "重診已排程,AI 正在分析中",
|
||||
"already_analyzing": False,
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.exception("reanalyze_failed", incident_id=incident_id, error=str(e))
|
||||
return {
|
||||
"triggered": False,
|
||||
"message": f"重診觸發失敗: {str(e)[:80]}",
|
||||
"already_analyzing": False,
|
||||
}
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Singleton
|
||||
|
||||
Reference in New Issue
Block a user