diff --git a/apps/api/src/api/v1/webhooks.py b/apps/api/src/api/v1/webhooks.py index 0b919ab2..15511dea 100644 --- a/apps/api/src/api/v1/webhooks.py +++ b/apps/api/src/api/v1/webhooks.py @@ -22,6 +22,7 @@ Endpoints: 8. 前端戰情室即時顯示聚合次數 """ +import asyncio import hashlib import hmac import uuid @@ -314,6 +315,8 @@ async def _push_to_telegram_background( alert_category: str = "", # 2026-04-20 ogt: ADR-092 tg_sent Redis 標記(防收斂靜默) fingerprint: str = "", + # P2.4 中間態清理 2026-04-24 ogt + Claude Sonnet 4.6 + placeholder_message_id: int | None = None, ) -> None: """ 背景任務: 推送待簽核卡片到 Telegram (v7.0 含 SignOz 整合) @@ -423,6 +426,10 @@ async def _push_to_telegram_background( if fingerprint: await get_approval_service().mark_telegram_confirmed(fingerprint) + # P2.4 中間態清理 2026-04-24 ogt + Claude Sonnet 4.6 + if placeholder_message_id: + await gateway.delete_message(placeholder_message_id) + # 2026-04-08 Claude Code: 記錄 Telegram 推送事件 try: from src.repositories.alert_operation_log_repository import get_alert_operation_log_repository @@ -848,6 +855,22 @@ async def receive_alert( "labels": alert.labels or {}, } + # P2.4 中間態推播 2026-04-24 ogt + Claude Sonnet 4.6 + # LLM 分析前先送佔位卡,讓使用者知道系統已收到告警並正在處理 + _placeholder_msg_id: int | None = None + try: + _tg_gw = get_telegram_gateway() + _placeholder_msg_id = await asyncio.wait_for( + _tg_gw.send_analyzing_placeholder( + alert_type=alert.alert_type or "unknown", + resource_name=alert.target_resource or "", + severity=alert.severity or "medium", + ), + timeout=3.0, # 最多等 3s,不阻塞主流程 + ) + except Exception: + pass # 佔位卡失敗不影響主流程 + # 呼叫 OpenClaw LLM 分析 (v7.0 含 SignOz 整合) # 2026-03-29 ogt: 加入 Token/Cost 追蹤 openclaw = get_openclaw() @@ -1010,6 +1033,8 @@ async def receive_alert( "ssl_expiry": "ssl_cert", }.get(alert.alert_type, "general"), fingerprint=fingerprint, + # P2.4 中間態清理 2026-04-24 ogt + Claude Sonnet 4.6 + placeholder_message_id=_placeholder_msg_id, ) return AlertResponse( diff --git a/apps/api/src/services/telegram_gateway.py b/apps/api/src/services/telegram_gateway.py index f2a2c776..c9ee7d2d 100644 --- a/apps/api/src/services/telegram_gateway.py +++ b/apps/api/src/services/telegram_gateway.py @@ -1525,6 +1525,62 @@ class TelegramGateway: return {"inline_keyboard": buttons} + async def send_analyzing_placeholder( + self, + alert_type: str, + resource_name: str, + severity: str = "medium", + ) -> int | None: + """ + P2.4 中間態推播 2026-04-24 ogt + Claude Sonnet 4.6 + 在 LLM 分析開始前送出佔位卡,讓使用者知道系統正在處理。 + 分析完成後用 delete_message() 刪除,再由 send_approval_card 補上完整卡。 + Returns: Telegram message_id 或 None(Bot 未設定 / API 失敗) + """ + if not self.bot_token: + return None + emoji = {"critical": "🔴", "medium": "🟡", "low": "🟢"}.get(severity.lower(), "⚠️") + text = ( + f"{emoji} 告警收到,AI 正在分析中...\n\n" + f"資源: {html.escape(resource_name or 'unknown')}\n" + f"類型: {html.escape(alert_type or 'unknown')}\n\n" + f"預計 10-30 秒完成,請稍候..." + ) + try: + result = await self._send_request("sendMessage", { + "chat_id": self.chat_id, + "text": text, + "parse_mode": "HTML", + "disable_web_page_preview": True, + }) + msg_id: int | None = None + result_val = result.get("result") + if isinstance(result_val, dict): + msg_id = result_val.get("message_id") + logger.info("analyzing_placeholder_sent", message_id=msg_id, resource=resource_name) + return msg_id + except Exception as e: + logger.warning("analyzing_placeholder_failed", error=str(e)) + return None + + async def delete_message(self, message_id: int) -> bool: + """ + P2.4 中間態清理 2026-04-24 ogt + Claude Sonnet 4.6 + 刪除佔位卡(分析完成、完整卡已發出後呼叫)。 + """ + if not self.bot_token or not message_id: + return False + try: + await self._send_request("deleteMessage", { + "chat_id": self.chat_id, + "message_id": message_id, + }) + logger.info("placeholder_deleted", message_id=message_id) + return True + except Exception as e: + logger.warning("placeholder_delete_failed", message_id=message_id, error=str(e)) + return False + async def send_approval_card( self, approval_id: str, diff --git a/docs/LOGBOOK.md b/docs/LOGBOOK.md index de0d6921..3b01b69c 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -6,6 +6,26 @@ --- +## 📍 2026-04-24 — ADR-092 P0+P1+P2.1 全修(commit 7f4088b / 04ff225 / bb5f16f) + +### P2.1 修復(commit bb5f16f) +- **consensus_engine.py**: 四 ExpertAgent confidence=0.0 → 加權投票 total=0 → 永遠 NO_ACTION;改為依訊號強度 0.45~0.80 +- **consensus_engine.py**: `_normalize_action` 加「重新啟動」別名 → 正確歸 RESTART;移除未使用 _target +- **prompts.py**: 新增 Evidence-First Protocol + Skepticism Rules(要求 LLM 引用 `` 才能高 confidence) +- **openclaw.py**: `analyze_alert` 提取 `diagnosis_context` → `` 注入 full_prompt +- 驗證:CrashLoop 測試 consensus score 從 0.0 → 0.744 + +### 🔴 手動 DB Migration 待執行 +```bash +psql $DATABASE_URL -f apps/api/migrations/adr092_p1_learning_chain_fix.sql +``` + +### 剩餘 P2 工作 +- P2.4: Telegram 中間態推播(editMessageText 佔位卡) +- P2.6: trust_drift_detector 接入 ai_slo_watchdog_job + +--- + ## 📍 2026-04-24 — 12 Agent 全景審計 + P0-P2 全面並行修復 ### 需求