From 9bfa6fc045fe8deb103d1805ae9b3eac7a289211 Mon Sep 17 00:00:00 2001 From: OG T Date: Thu, 16 Apr 2026 01:27:02 +0800 Subject: [PATCH] =?UTF-8?q?fix(sweeper):=20=E9=99=90=E5=88=B6=E5=8F=AA?= =?UTF-8?q?=E6=8E=83=2048h=20=E5=85=A7=20incident=EF=BC=8C=E9=98=B2?= =?UTF-8?q?=E6=AD=A2=E6=AD=B7=E5=8F=B2=E8=88=8A=E6=A1=88=E6=B4=97=E7=89=88?= =?UTF-8?q?=20Telegram?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 問題: 首次部署 sweeper 時,找到 117 個無 sweeper_done: 標記的舊 incident (最舊 2026-04-09,7 天前) → 觸發全部 LLM 分析 舊 incident 資料格式 → OPENCLAW_NEMO timeout → Expert System 降級 confidence=0.2 "降級" → Telegram 連發相同格式告警洗版 修正: 加入 _MAX_INCIDENT_AGE_HOURS=48 過濾 只處理 48h 內的 INVESTIGATING incident 確保 created_at 時區安全(naive → UTC) 2026-04-16 Claude Sonnet 4.6 Asia/Taipei Co-Authored-By: Claude Sonnet 4.6 --- .../api/src/jobs/incident_analysis_sweeper.py | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/apps/api/src/jobs/incident_analysis_sweeper.py b/apps/api/src/jobs/incident_analysis_sweeper.py index 6bac9998..2730c2bf 100644 --- a/apps/api/src/jobs/incident_analysis_sweeper.py +++ b/apps/api/src/jobs/incident_analysis_sweeper.py @@ -36,6 +36,8 @@ _MAX_BATCH = 5 # 每批最多 5 個 _SEMAPHORE_LIMIT = 3 # 最多 3 個並發 AI 分析 _DONE_MARKER_PREFIX = "sweeper_done:" # 輕量標記:已觸發過分析 _DONE_MARKER_TTL = 3600 # 1 小時 TTL,後續由 get_or_create 去重 +# 2026-04-16 ogt: 只處理 48h 內的 incident,避免首次啟動把所有歷史舊案洗版到 Telegram +_MAX_INCIDENT_AGE_HOURS = 48 async def run_incident_analysis_sweeper() -> None: @@ -81,9 +83,30 @@ async def _sweep_once(sem: asyncio.Semaphore) -> None: if not incidents: return + # 過濾:只處理 48h 內的 incident(避免首次啟動把全部歷史舊案洗版 Telegram) + from datetime import datetime, timezone, timedelta + now_utc = datetime.now(timezone.utc) + cutoff = now_utc - timedelta(hours=_MAX_INCIDENT_AGE_HOURS) + + recent_incidents = [] + for incident in incidents: + created = getattr(incident, "created_at", None) + if created: + # 確保 created_at 有時區資訊 + if created.tzinfo is None: + created = created.replace(tzinfo=timezone.utc) + if created >= cutoff: + recent_incidents.append(incident) + else: + # 沒有 created_at 的舊資料:跳過 + pass + + if not recent_incidents: + return + # 找出尚未觸發過分析的 (用輕量標記,不掃描 decision:DEC-* 全集) unanalyzed = [] - for incident in incidents: + for incident in recent_incidents: done_key = f"{_DONE_MARKER_PREFIX}{incident.incident_id}" if not await redis.exists(done_key): unanalyzed.append(incident)