Phase S 技術債修復 (首席架構師審查 82→完整): - S-01: generate_alert_fingerprint 移至 AlertAnalyzer.generate_fingerprint() staticmethod - S-04: 移除 Pydantic v2 deprecated json_encoders (直接用原生 datetime 序列化) Sentry MCP 整合 (Phase 23): - ADR-048: Sentry→OpenClaw AI Triage 架構決策 - sentry_webhook_service.py: parse/analyze/create_incident/build_message Service 層 - config.py: SENTRY_WEBHOOK_SECRET (Fail-Closed HMAC-SHA256) Playwright MCP 整合 (短期): - smoke.spec.ts: 5 頁面 E2E smoke test (home/dashboard/incidents/approvals/terminal) - cd.yaml: E2E Smoke Test 步驟 + Telegram 🎭 Smoke 狀態通知 長期規劃 ADR: - ADR-049: Figma Code Connect 設計系統同步 - ADR-050: Telegram 互動式 Incident 2.0 (6鍵 Inline Keyboard) - ADR-051: Context7 依賴升級顧問 (Next.js 14→15, FastAPI 0.115→0.128) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
177 lines
6.4 KiB
Markdown
177 lines
6.4 KiB
Markdown
# ADR-050: Telegram 互動式 Incident 管理 2.0
|
||
|
||
| 項目 | 內容 |
|
||
|------|------|
|
||
| **狀態** | 📋 已批准,待實作 |
|
||
| **日期** | 2026-04-01 |
|
||
| **決策者** | 首席架構師 + 統帥 |
|
||
| **觸發** | MCP 整合長期計畫 |
|
||
|
||
## 背景
|
||
|
||
目前 AWOOOI Telegram 告警訊息提供三個 Inline Keyboard 按鈕:
|
||
|
||
```
|
||
[✅ 批准 (Y)] [❌ 拒絕 (n)] [🔕 靜默]
|
||
```
|
||
|
||
統帥在 Telegram 收到告警時,除了批准/拒絕外,還需要切換到 Web UI 或 CLI 才能:
|
||
- 查看 Incident 的完整詳情(metrics、stack trace、診斷結果)
|
||
- 重新觸發 OpenClaw 分析(當第一次分析結果可疑時)
|
||
- 查詢歷史相似 Incident 的處置結果
|
||
|
||
這增加了認知負擔,也拖慢了 MTTR(Mean Time To Respond)。
|
||
|
||
## 問題
|
||
|
||
| 操作 | 現況 | 痛點 |
|
||
|------|------|------|
|
||
| 查看詳情 | 須切換至 Web UI | 手機操作不便,流程中斷 |
|
||
| 重新診斷 | 須 CLI 觸發 | 無法在 Telegram 中完成閉環 |
|
||
| 歷史對比 | 須查詢資料庫 | 完全手動,耗時 |
|
||
|
||
## 決策
|
||
|
||
擴充 Telegram Inline Keyboard,加入第二行按鈕:
|
||
|
||
```
|
||
[✅ 批准] [❌ 拒絕] [🔕 靜默]
|
||
[📋 詳情] [🔄 重診] [📊 歷史]
|
||
```
|
||
|
||
在後端新增 `TelegramCallbackService`,處理新按鈕的回調邏輯,並使用 `edit_message` 在原訊息中就地更新(不發送新訊息)。
|
||
|
||
## 架構
|
||
|
||
### 回調處理流程
|
||
|
||
```
|
||
Telegram Inline Button Click
|
||
→ Bot API callback_query
|
||
→ AWOOOI API POST /telegram/callback
|
||
→ TelegramCallbackRouter (apps/api/src/routers/telegram_callback.py)
|
||
→ TelegramCallbackService (apps/api/src/services/telegram_callback_service.py)
|
||
→ 根據 action 分派:
|
||
"detail" → IncidentRepository.get_by_id()
|
||
→ 格式化 Markdown 回傳
|
||
"reanalyze" → IncidentService.trigger_reanalysis()
|
||
→ OpenClaw 重分析排程
|
||
"history" → IncidentRepository.find_similar()
|
||
→ 格式化歷史對比表格
|
||
→ Telegram Bot API edit_message_text()
|
||
→ 原訊息就地更新(保留按鈕,附加展開內容)
|
||
```
|
||
|
||
### Callback Data 格式
|
||
|
||
```
|
||
{action}:{incident_id}
|
||
|
||
範例:
|
||
detail:INC-2026-0401-001
|
||
reanalyze:INC-2026-0401-001
|
||
history:INC-2026-0401-001
|
||
```
|
||
|
||
### 新 Inline Keyboard 結構
|
||
|
||
```python
|
||
# src/services/telegram_notification_service.py
|
||
InlineKeyboardMarkup(inline_keyboard=[
|
||
[
|
||
InlineKeyboardButton(text="✅ 批准", callback_data=f"approve:{incident_id}"),
|
||
InlineKeyboardButton(text="❌ 拒絕", callback_data=f"reject:{incident_id}"),
|
||
InlineKeyboardButton(text="🔕 靜默", callback_data=f"silence:{incident_id}"),
|
||
],
|
||
[
|
||
InlineKeyboardButton(text="📋 詳情", callback_data=f"detail:{incident_id}"),
|
||
InlineKeyboardButton(text="🔄 重診", callback_data=f"reanalyze:{incident_id}"),
|
||
InlineKeyboardButton(text="📊 歷史", callback_data=f"history:{incident_id}"),
|
||
],
|
||
])
|
||
```
|
||
|
||
### 詳情回覆格式
|
||
|
||
```
|
||
📋 Incident 詳情:INC-2026-0401-001
|
||
━━━━━━━━━━━━━━━━━━
|
||
🏷 類型:CPU_SPIKE
|
||
🖥 主機:k3s-node-1 (192.168.0.125)
|
||
⏱ 時間:2026-04-01 14:32 +08
|
||
📊 觸發值:CPU 94.3% (閾值 90%)
|
||
🤖 OpenClaw 信心:0.87
|
||
|
||
診斷摘要:
|
||
過去 30 分鐘 CPU 呈上升趨勢,
|
||
與 incident INC-2026-0318-007 模式相符。
|
||
建議:重啟 signal_worker pod。
|
||
```
|
||
|
||
### 歷史對比格式
|
||
|
||
```
|
||
📊 相似 Incident 歷史(近 30 天)
|
||
━━━━━━━━━━━━━━━━━━
|
||
| 日期 | 類型 | 處置 | 結果 |
|
||
|-------|-----------|-------|------|
|
||
| 03-18 | CPU_SPIKE | 批准 | ✅ |
|
||
| 03-10 | CPU_SPIKE | 批准 | ✅ |
|
||
| 02-28 | CPU_SPIKE | 靜默 | ⚠️ |
|
||
|
||
相似度最高:INC-2026-0318-007 (0.94)
|
||
```
|
||
|
||
## 實作計畫
|
||
|
||
### P1:骨架建立(Sprint 1)
|
||
|
||
- [ ] 新增 `/telegram/callback` endpoint(`src/routers/telegram_callback.py`)
|
||
- [ ] 建立 `TelegramCallbackService` 骨架(含 action dispatch)
|
||
- [ ] 更新 `telegram_notification_service.py` 的 Inline Keyboard 結構
|
||
- [ ] Webhook 設定確認(Bot API setWebhook 指向新 endpoint)
|
||
|
||
**交付條件**:點擊新按鈕不報錯,回傳「功能開發中」佔位訊息
|
||
|
||
### P2:詳情查詢 + 重診觸發(Sprint 2)
|
||
|
||
- [ ] `detail` action:呼叫 `IncidentRepository.get_by_id()`,格式化後 `edit_message`
|
||
- [ ] `reanalyze` action:呼叫 `IncidentService.trigger_reanalysis()`,回傳「重診已排程」
|
||
- [ ] 防止重複觸發:重診進行中時,按鈕顯示「⏳ 診斷中」並 disable
|
||
|
||
### P3:歷史對比分析(Sprint 3)
|
||
|
||
- [ ] `history` action:`IncidentRepository.find_similar(incident_id, top_k=5)`
|
||
- [ ] 相似度演算法:基於 type + host + time_window 的向量搜尋(若已啟用 pgvector)
|
||
- [ ] 格式化歷史表格,含處置結果與信心分數
|
||
|
||
## 技術約束
|
||
|
||
1. **leWOOOgo 積木化**:`TelegramCallbackService` 必須透過 `IncidentRepository` interface 存取資料,禁止 Router 層直接查 DB 或 Redis
|
||
2. **edit_message 限制**:Telegram Bot API 限制 edit_message 只能在 48 小時內有效,超時需降級為發送新訊息
|
||
3. **Callback Query 答覆**:所有 callback_query 必須在 5 秒內呼叫 `answerCallbackQuery()`,否則 Telegram 顯示 loading 轉圈
|
||
4. **去重保護**:同一 Incident 的 reanalyze 在 10 分鐘 TTL 內只觸發一次(參考 feedback_telegram_dedup.md)
|
||
|
||
## 安全考量
|
||
|
||
- Callback data 中的 `incident_id` 需驗證存在且屬於合法狀態
|
||
- Callback query 的 `from.id` 需與 allowlist 核對(使用現有 Telegram access 控制)
|
||
- 禁止在 callback 中執行不可逆操作(如刪除 Incident)
|
||
|
||
## 影響
|
||
|
||
| 面向 | 影響 |
|
||
|------|------|
|
||
| **MTTR** | 預計減少 40%(統帥不需切換視窗) |
|
||
| **API** | 新增 1 個 endpoint,無現有 endpoint 變更 |
|
||
| **DB** | 新增 `find_similar` query,需確認索引 |
|
||
| **Telegram Bot** | 需重新設定 Webhook(一次性操作) |
|
||
|
||
## 相關文件
|
||
|
||
- [ADR-035-telegram-alert-chain-enforcement.md](ADR-035-telegram-alert-chain-enforcement.md)
|
||
- [ADR-045-telegram-gateway-consolidation.md](ADR-045-telegram-gateway-consolidation.md)
|
||
- [feedback_telegram_dedup.md](~/.claude/projects/-Users-ogt-awoooi/memory/feedback_telegram_dedup.md)
|
||
- [feedback_telegram_alert_format.md](~/.claude/projects/-Users-ogt-awoooi/memory/feedback_telegram_alert_format.md)
|
||
- [feedback_lewooogo_modular_enforcement.md](~/.claude/projects/-Users-ogt-awoooi/memory/feedback_lewooogo_modular_enforcement.md)
|