From eda0cfd0340d32efa7a0da963865c52c1749e482 Mon Sep 17 00:00:00 2001 From: OG T Date: Sun, 12 Apr 2026 20:20:41 +0800 Subject: [PATCH] =?UTF-8?q?fix(adr075):=20drift=20=E9=80=9A=E7=9F=A5?= =?UTF-8?q?=E6=94=B9=E7=94=A8=20send=5Fdrift=5Fcard=EF=BC=8C=E8=A3=9C?= =?UTF-8?q?=E9=BD=8A=E6=89=80=E6=9C=89=E5=91=BC=E5=8F=AB=E9=BB=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - drift.py: 移除死碼 send_text(),改由 narrate_and_notify() 統一發卡片 - drift_narrator_service: _send_telegram() 改呼 send_drift_card() 帶四顆按鈕 - webhooks.py /alerts 路徑: 補傳 alert_category 啟用動態按鈕 Co-Authored-By: Claude Sonnet 4.6 --- apps/api/src/api/v1/drift.py | 31 ++---------------- apps/api/src/api/v1/webhooks.py | 10 ++++++ .../src/services/drift_narrator_service.py | 32 +++++++++++-------- 3 files changed, 30 insertions(+), 43 deletions(-) diff --git a/apps/api/src/api/v1/drift.py b/apps/api/src/api/v1/drift.py index 5cc0e598..71b0d9f5 100644 --- a/apps/api/src/api/v1/drift.py +++ b/apps/api/src/api/v1/drift.py @@ -158,40 +158,13 @@ async def _analyze_and_notify(report: DriftReport) -> None: _logger = _structlog.get_logger(__name__) try: interpreter = get_drift_interpreter() - analyzer = get_drift_analyzer() interpretation = await interpreter.analyze(report) repo = get_drift_repository() await repo.update_interpretation(report.report_id, interpretation) - diff_summary = analyzer.format_diff_summary(report) - intent_label = { - "emergency_hotfix": "🚨 緊急 Hotfix", - "human_error": "⚠️ 人為誤操作", - "automated_change": "🤖 系統自動變更", - "unknown": "❓ 意圖不明", - }.get(interpretation.intent.value, "❓ 意圖不明") - - try: - from src.services.telegram_gateway import get_telegram_gateway - tg = get_telegram_gateway() - await tg.send_text( - f"🔍 Config Drift 偵測\n" - f"Namespace: {report.namespace}\n" - f"嚴重度: HIGH×{report.high_count} MEDIUM×{report.medium_count}\n\n" - f"意圖分析: {intent_label}\n" - f"{interpretation.explanation}\n" - f"信心: {interpretation.confidence:.0%}\n\n" - f"漂移詳情:\n{diff_summary}\n\n" - f"Report ID: {report.report_id}\n" - f"POST /api/v1/drift/reports/{report.report_id}/rollback — 覆蓋回 Git\n" - f"(adopt 端點暫停開放,待 ADR-057 實作後啟用)" - ) - except Exception as e: - _logger.warning("drift_telegram_failed", error=str(e)) - - # Phase 30 (2026-04-10 Claude Code ADR-067): AI 人話摘要推送 - # 在技術格式訊息之後,額外推送 qwen2.5:7b-instruct 生成的繁中摘要 + # ADR-075: drift_narrator_service 負責發送 TYPE-4D 卡片(含按鈕) + # 舊的 send_text() 已移除,改由 narrate_and_notify() 統一處理 try: from src.services.drift_narrator_service import get_drift_narrator_service narrator = get_drift_narrator_service() diff --git a/apps/api/src/api/v1/webhooks.py b/apps/api/src/api/v1/webhooks.py index 06eb8e19..c77fc20f 100644 --- a/apps/api/src/api/v1/webhooks.py +++ b/apps/api/src/api/v1/webhooks.py @@ -976,6 +976,16 @@ async def receive_alert( # 2026-04-08 ogt: 補傳 incident_id 以啟用詳情/重診/歷史按鈕 incident_id="", # /alerts 路徑尚無 incident,detail/reanalyze/history 按鈕不顯示 # /alerts 路徑沒有 notification_type(非 Alertmanager 路徑),不需 TYPE-4D routing + # ADR-075: alert_type → category 對應,啟用動態按鈕 + alert_category={ + "k8s_node_failure": "kubernetes", + "k8s_pod_crash": "kubernetes", + "db_connection_timeout": "database", + "high_cpu": "host_resource", + "high_memory": "host_resource", + "disk_full": "storage", + "ssl_expiry": "ssl_cert", + }.get(alert.alert_type, "general"), ) return AlertResponse( diff --git a/apps/api/src/services/drift_narrator_service.py b/apps/api/src/services/drift_narrator_service.py index 65327703..de6d0ab8 100644 --- a/apps/api/src/services/drift_narrator_service.py +++ b/apps/api/src/services/drift_narrator_service.py @@ -17,7 +17,6 @@ Drift Narrator Service - Phase 30 from __future__ import annotations -import html from typing import TYPE_CHECKING import httpx @@ -224,25 +223,30 @@ class DriftNarratorService: ) async def _send_telegram(self, report: "DriftReport", narrative: str) -> None: - """推送 Telegram 人話摘要卡""" + """ + 推送 TYPE-4D Config Drift 卡片(ADR-075) + + 使用 send_drift_card() 取代舊 send_notification(),呈現結構化格式與操作按鈕。 + diff_summary = AI 研判(narrative) + 漂移詳情(前 8 筆) + approval_id / incident_id 均使用 report_id(無需建立 ApprovalRequest) + """ from src.services.telegram_gateway import get_telegram_gateway - severity_icon = "🔴" if report.high_count > 0 else "🟡" - msg = ( - f"{severity_icon} K8s 配置漂移\n" - f"Namespace: {html.escape(report.namespace)}\n" - f"HIGH: {report.high_count} | MEDIUM: {report.medium_count}\n" - f"\n" - f"🤖 AI 研判\n" - f"{html.escape(narrative)}\n" - f"\n" - f"📋 Report: {html.escape(report.report_id)}\n" - f"qwen2.5:7b-instruct | 免費本地推理" + diff_summary = ( + f"🤖 AI 研判\n{narrative}\n\n" + f"漂移明細(HIGH: {report.high_count} | MEDIUM: {report.medium_count})\n" + f"{self._format_drift_summary(report)}" ) try: tg = get_telegram_gateway() - await tg.send_notification(msg[:4096]) + await tg.send_drift_card( + incident_id=report.report_id, + approval_id=report.report_id, + resource_name=report.namespace, + diff_summary=diff_summary[:500], + detected_at="", + ) except Exception as e: logger.warning("drift_narrator_telegram_error", error=str(e))