fix(telegram): BUG-A TYPE-1 + BUG-B TYPE-4D 資料前處理(ADR-075 UI 第二波修復)
All checks were successful
CD Pipeline / build-and-deploy (push) Successful in 10m25s

BUG-A (TYPE-1 純資訊通知):
- 舊: message=reasoning[:200] → debate_summary 全文傾倒(診斷/方案/審查/質疑一起出現)
- 新: _parse_debate_summary(reasoning) 只取 diagnosis 欄位 + _smt 截斷 200 字

BUG-B (TYPE-4D Config Drift):
- 舊: diff_summary=description[:500] → LLM 輸出的 JSON 原文直接顯示在 <pre> 區塊
- 新: JSON Catcher — json.loads(description) 成功則格式化「📝建議操作/📖說明/回滾方案」
       失敗 (JSONDecodeError/TypeError/AttributeError) → 平滑降級為純文字截斷

僅修改 decision_manager.py 路由準備段,telegram_gateway.py 模板層零改動。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
OG T
2026-04-17 14:14:04 +08:00
parent f677b72114
commit 418d73540b

View File

@@ -21,6 +21,7 @@ Decision Manager - Phase 6.5 非同步決策狀態機
"""
import asyncio
import json
from datetime import UTC, datetime
from enum import Enum
from typing import Any, Protocol, runtime_checkable
@@ -312,24 +313,44 @@ async def _push_decision_to_telegram(
if _notif_type == NotificationType.TYPE_1:
# 純資訊通知 — 無按鈕
# 2026-04-12 ogt: Incident 沒有 title 欄位,用 alertname
# 2026-04-17 ogt + Claude Sonnet 4.6: BUG-A 修復 — 只取 diagnosis 欄位
# 舊message=reasoning[:200] → 整條 debate_summary 生文字傾倒
# 新:解析 debate_summary只取「診斷...」部分給 SRE 看
_info_title = (
incident.signals[0].labels.get("alertname", "") or
incident.signals[0].alert_name
) if incident.signals else "告警通知"
_parsed_info = _parse_debate_summary(reasoning)
_info_msg = _smt(_parsed_info.get("diagnosis") or description, 200)
tg_result = await gateway.send_info_notification(
incident_id=incident.incident_id,
title=_info_title or "告警通知",
message=reasoning[:200] if reasoning else description[:200],
message=_info_msg,
alertname=incident.signals[0].labels.get("alertname", "") if incident.signals else "",
severity="info",
)
elif _notif_type == NotificationType.TYPE_4_DRIFT:
# Config Drift 專屬卡片
# 2026-04-17 ogt + Claude Sonnet 4.6: BUG-B JSON Catcher
# LLM 可能輸出 JSON 結構({"action_title":"...","description":"...","rollback":"..."}
# 解析成功 → 格式化可讀文字;失敗 → 平滑降級為原始截斷字串(不可拋出 Exception
try:
_drift_data = json.loads(description)
_parts: list[str] = []
if _drift_data.get("action_title"):
_parts.append(f"📝 建議操作:{_drift_data['action_title']}")
if _drift_data.get("description"):
_parts.append(f"📖 說明:{_smt(_drift_data['description'], 200)}")
if _drift_data.get("rollback"):
_parts.append(f"⏪ 回滾方案:{_smt(_drift_data['rollback'], 100)}")
_diff_text = "\n".join(_parts) if _parts else description[:500]
except (json.JSONDecodeError, TypeError, AttributeError):
_diff_text = description[:500]
tg_result = await gateway.send_drift_card(
incident_id=incident.incident_id,
approval_id=approval_id,
resource_name=target[:50],
diff_summary=description[:500],
diff_summary=_diff_text,
)
elif _notif_type == NotificationType.TYPE_8M or _alert_category in ("alertchain_health", "flywheel_health"):
# TYPE-8M飛輪/告警鏈路健康異常,發到個人 DM不發群組