From f421e652d303a0729d6f1a6733382ab44b8d138b Mon Sep 17 00:00:00 2001 From: OG T Date: Fri, 17 Apr 2026 14:42:24 +0800 Subject: [PATCH] =?UTF-8?q?fix(telegram):=20BUG-C=20TYPE-3=20=E6=8E=92?= =?UTF-8?q?=E7=89=88=E6=B8=85=E6=B4=97=20+=20=E6=89=B9=E5=87=86/=E6=8B=92?= =?UTF-8?q?=E7=B5=95=E6=B0=B8=E9=81=A0=E7=BD=AE=E9=A0=82=EF=BC=88ADR-075?= =?UTF-8?q?=20UI=20=E7=AC=AC=E4=B8=89=E6=B3=A2=E4=BF=AE=E5=BE=A9=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Checkpoint 1 — decision_manager.py TYPE-3 root_cause 清洗: - 舊: root_cause=_smt(reasoning, 500) → debate_summary 全文(診斷/方案/審查/質疑)全部傾倒到 AI 診斷欄 - 新: _parse_debate_summary 只取 diagnosis 欄位 + _smt 截斷 300 字 - 移除 _requires_human 變數(已無用途) Checkpoint 2 — telegram_gateway.py _build_inline_keyboard 按鈕順序重構: - 舊: K8s 類別按鈕置頂,批准/拒絕受 requires_human_approval 控制 → 死卡 - 新: [✅ 批准][❌ 拒絕] 永遠第一行,K8s/DB/Host 操作按鈕置後 - 移除 requires_human_approval 參數(邏輯已簡化為無條件置頂) 修改範圍: decision_manager.py else 路由段 + _build_inline_keyboard + send_approval_card 簽名, telegram_gateway.py 模板/訊息格式零改動。 Co-Authored-By: Claude Sonnet 4.6 --- apps/api/src/services/decision_manager.py | 12 +++++----- apps/api/src/services/telegram_gateway.py | 28 +++++++++-------------- 2 files changed, 17 insertions(+), 23 deletions(-) diff --git a/apps/api/src/services/decision_manager.py b/apps/api/src/services/decision_manager.py index 5d725667..729c6ca2 100644 --- a/apps/api/src/services/decision_manager.py +++ b/apps/api/src/services/decision_manager.py @@ -398,15 +398,16 @@ async def _push_decision_to_telegram( ) else: # TYPE-2 / TYPE-3 / TYPE-4 都走 send_approval_card(按鈕組合由 alert_category 決定) - # 2026-04-17 ogt + Claude Sonnet 4.6: 傳入 requires_human_approval + 智能截斷 - # 根因:① requires_human_approval=True 時動態按鈕卡缺少 [批准][拒絕] → 死卡 - # ② [:500] 在括號中間切斷 → 幽靈截斷「質疑:無(通」 - _requires_human = bool(proposal_data.get("requires_human_review", False)) + # 2026-04-17 ogt + Claude Sonnet 4.6 (BUG-C): TYPE-3 root_cause 清洗 + # 舊:root_cause=_smt(reasoning, 500) → debate_summary 全文傾倒到 AI 診斷欄位 + # 新:_parse_debate_summary 只取 diagnosis;方案/審查/質疑不在此卡顯示 + _parsed_card = _parse_debate_summary(reasoning) + _card_root_cause = _smt(_parsed_card.get("diagnosis") or description, 300) tg_result = await gateway.send_approval_card( approval_id=approval_id, risk_level=risk_level, resource_name=target[:50], - root_cause=_smt(reasoning, 500) if reasoning else _smt(description, 500), + root_cause=_card_root_cause, suggested_action=action[:120] if action else (description[:120] if description else "待分析"), estimated_downtime="5-15 min", primary_responsibility="INFRA", @@ -424,7 +425,6 @@ async def _push_decision_to_telegram( alert_category=_alert_category, notification_type=_notification_type, playbook_name=_playbook_name, - requires_human_approval=_requires_human, ) # 2026-04-09 Claude Sonnet 4.6: 存 message_id → 後續狀態更新在原訊息延續 diff --git a/apps/api/src/services/telegram_gateway.py b/apps/api/src/services/telegram_gateway.py index 039a12c4..788c65db 100644 --- a/apps/api/src/services/telegram_gateway.py +++ b/apps/api/src/services/telegram_gateway.py @@ -1422,9 +1422,6 @@ class TelegramGateway: # ADR-071-E: TYPE-3 動態按鈕 (2026-04-11 Claude Sonnet 4.6) alert_category: str = "", notification_type: str = "", - # 2026-04-17 ogt + Claude Sonnet 4.6: requires_human_approval 強制附加批准/拒絕行 - # 根因:有動態按鈕時走 category 路徑,approve/reject 被漏掉 → 卡片成「死卡」 - requires_human_approval: bool = False, ) -> dict: """ 建立 Inline Keyboard @@ -1481,21 +1478,21 @@ class TelegramGateway: _dynamic_buttons = _build_category_buttons_for(alert_category) if alert_category else [] if is_type3 and _dynamic_buttons: - # TYPE-3 動態操作按鈕:第一行為類別專屬操作 + # TYPE-3 動態按鈕:批准/拒絕永遠置頂第一行 + # 2026-04-17 ogt + Claude Sonnet 4.6 (BUG-C): 強制置頂批准/拒絕 + # 舊:批准/拒絕列在最後且受 requires_human_approval 控制 → K8s 按鈕蓋台 → 死卡 + # 新:[批准][拒絕] 永遠第一行,K8s 類別按鈕置後,SRE 第一眼就看到審核扳機 + rows: list[list[dict]] = [[ + {"text": "✅ 批准", "callback_data": approve_nonce}, + {"text": "❌ 拒絕", "callback_data": reject_nonce}, + ]] + # K8s/DB/Host 等類別操作按鈕(每行最多 3 個)置於第二列以後 category_btns = [ {"text": text, "callback_data": cb_data} for text, cb_data in _dynamic_buttons ] - # 每行最多 3 個,超過換行 - rows = [category_btns[i:i+3] for i in range(0, len(category_btns), 3)] - # 2026-04-17 ogt + Claude Sonnet 4.6: requires_human_approval → 必須附加批准/拒絕行 - # 根因:有動態按鈕時舊邏輯只有 [詳情][忽略],SRE 找不到審核扳機 → 死卡 - if requires_human_approval: - rows.append([ - {"text": "✅ 批准", "callback_data": approve_nonce}, - {"text": "❌ 拒絕", "callback_data": reject_nonce}, - ]) - # 通用操作:[查看詳情] [忽略] + rows += [category_btns[i:i+3] for i in range(0, len(category_btns), 3)] + # 通用操作:[詳情] [忽略] rows.append([ {"text": "📋 詳情", "callback_data": f"detail:{incident_id}"}, {"text": "🔕 忽略", "callback_data": silence_nonce}, @@ -1571,8 +1568,6 @@ class TelegramGateway: notification_type: str = "", # 2026-04-16 ogt + Claude Sonnet 4.6: 修復鏈路顯示 (ADR-076) playbook_name: str = "", - # 2026-04-17 ogt + Claude Sonnet 4.6: 強制在動態按鈕卡上加批准/拒絕行 - requires_human_approval: bool = False, ) -> dict: """ 推送待簽核卡片到 Telegram (v7.0 含 SignOz 整合) @@ -1664,7 +1659,6 @@ class TelegramGateway: incident_id=incident_id, alert_category=alert_category, notification_type=notification_type, - requires_human_approval=requires_human_approval, ) # 發送訊息