統帥要求「提出完整的解決方案和詳細的實施步驟」→ 本 plan 回覆。 內容涵蓋: - 28 按鈕完整 action → MCP tool 對應表(3 類:查/寫/secops) - 6 個 Sprint 工作分解(5.0 規格 → 5.1 dispatch → 5.2 查類 → 5.3 寫類 → 5.4 secops → 5.5 E2E) - 架構設計決策(callback_dispatcher registry pattern) - 依賴與風險矩陣 - 5 個 E2E 驗收案例 - Rollout 策略(查類先上線,觀察 24h 再上寫類) 估時: 3-5 天(總計 5.5 工作日) Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
11 KiB
11 KiB
Phase 5 實施計畫:Telegram 分類按鈕完整化
建立: 2026-04-14 台北深夜 Claude Sonnet 4.6(首席架構師) 狀態: 🟡 待統帥批准啟動 相關: ADR-079(下架 + 計畫), feedback_no_ghost_buttons.md(鐵律) 估時: 3-5 天(6 個 Sprint)
0. 戰略目標
把已下架的 28 個分類按鈕完整實作為可用功能,讓 Telegram 卡片從「資訊展示板」升級為「一鍵 SRE 動作平台」。
成功條件:
- 統帥在 Telegram 點任何分類按鈕 → 在原卡片下方 reply 執行結果(成功/失敗/log摘要)
- 寫類按鈕(有副作用)有 nonce + audit log
- 查類按鈕(無副作用)秒回
- E2E 測試覆蓋 click → action → MCP → result reply 完整鏈
1. Action → MCP 對應表(完整規格)
1.1 查類按鈕(無副作用,優先實作)
| # | 分類 | Action | callback_data 格式 | MCP Tool | 參數 | 回覆 |
|---|---|---|---|---|---|---|
| 1 | host_resource | check_process | check_process:{incident_id} |
ssh_get_top_processes |
host=from labels.instance | Top 10 by CPU |
| 2 | host_resource | check_log | check_log:{incident_id} |
ssh_get_nginx_error_log / ssh_get_container_logs |
service=from signals | 最後 50 行 |
| 3 | network | check_port | check_port:{incident_id} |
ssh_check_port |
host, port | Port 狀態 |
| 4 | devops_tool | check_log | check_log:{incident_id} |
ssh_get_container_logs |
container_name | 最後 50 行 |
| 5 | storage | check_minio_log | check_minio_log:{incident_id} |
ssh_get_container_logs |
container=minio | Log |
| 6 | external_site | check_health | check_health:{incident_id} |
prometheus_query |
probe_success{instance=...} |
Probe 結果 |
| 7 | external_site | check_log | check_log:{incident_id} |
ssh_get_nginx_error_log |
host=188 | Log |
| 8 | business | open_signoz | open_signoz:{incident_id} |
(URL 返回) | SignOz link | 返回 deeplink URL |
| 9 | flywheel_health | open_flywheel | open_flywheel:{incident_id} |
(URL 返回) | Flywheel dashboard | 返回 URL |
| 10 | database | check_slow_query | check_slow_query:{incident_id} |
database_list_slow_queries (需新增) |
namespace | Slow query 列表 |
查類總計 10 個按鈕 | 估時 1 天
1.2 寫類按鈕(有副作用,需 nonce + audit)
| # | 分類 | Action | callback_data(4-part nonce) | MCP Tool | 風險 |
|---|---|---|---|---|---|
| 11 | kubernetes | restart | restart:{id}:{ts}:{rand} |
kubectl_restart |
MEDIUM |
| 12 | kubernetes | scale_up | scale_up:{id}:{ts}:{rand} |
kubectl_scale replicas+1 |
MEDIUM |
| 13 | kubernetes | scale_down | scale_down:{id}:{ts}:{rand} |
kubectl_scale replicas-1(禁 0) |
MEDIUM |
| 14 | kubernetes | rollback | rollback:{id}:{ts}:{rand} |
kubectl_rollout_undo(需新增) |
HIGH |
| 15 | host_resource | restart_service | restart_service:{id}:{ts}:{rand} |
ssh_systemctl_restart |
HIGH |
| 16 | host_resource | clear_log | clear_log:{id}:{ts}:{rand} |
ssh_clear_docker_logs |
LOW |
| 17 | devops_tool | restart_service | restart_service:{id}:{ts}:{rand} |
ssh_docker_restart |
HIGH |
| 18 | storage | restart_minio | restart_service:{id}:{ts}:{rand} |
ssh_docker_restart container=minio |
HIGH |
| 19 | network | reload_nginx | reload_nginx:{id}:{ts}:{rand} |
ssh_reload_nginx |
LOW |
| 20 | ssl_cert | renew_cert | renew_cert:{id}:{ts}:{rand} |
ssh_renew_ssl |
MEDIUM |
| 21 | database | kill_slow_query | kill_slow_query:{id}:{ts}:{rand} |
kubectl_exec psql terminate |
HIGH |
| 22 | database | clear_conn_pool | clear_conn_pool:{id}:{ts}:{rand} |
kubectl_restart (間接) |
HIGH |
| 23 | business | pause_1h | pause_1h:{id}:{ts}:{rand} |
Alertmanager silence API | LOW |
| 24 | flywheel_health | trigger_diagnose | flywheel_diagnose:{id}:{ts}:{rand} |
內部 API call | LOW |
寫類總計 14 個按鈕 | 估時 2 天
1.3 資安按鈕(最高風險,需 Multi-Sig)
| # | 分類 | Action | callback_data | MCP Tool | 風險 |
|---|---|---|---|---|---|
| 25 | secops | isolate | secops_isolate:{id}:{ts}:{rand} |
kubectl_exec NetworkPolicy apply |
CRITICAL |
| 26 | secops | block_ip | secops_block_ip:{id}:{ts}:{rand} |
ssh_systemctl_restart iptables add |
CRITICAL |
| 27 | secops | evict | secops_evict:{id}:{ts}:{rand} |
kubectl_delete pod |
CRITICAL |
| 28 | secops | authorize | secops_authorize:{id}:{ts}:{rand} |
內部授權 API | HIGH |
資安總計 4 個按鈕 | 估時 0.5 天
2. Sprint 工作分解
Sprint 5.0:規格與設計(0.5 天)
- 建立
callback_action_spec.yaml— 每 action 的規格(alert_category / MCP tool / 參數模板 / 風險 / 所需 label) - 更新
_security.parse_callback_data()— 支援 action namespaced format - 在
telegram_gateway.py建_CATEGORY_ACTIONSregistry(action → spec)
Sprint 5.1:Dispatch 框架(0.5 天)
- 新增
apps/api/src/services/callback_dispatcher.pyasync def dispatch_action(action: str, incident_id: str, user: User) -> ActionResult- 從 registry 查 spec → 建 MCP call → 執行 → format reply
- 在
telegram_gateway._handle_callback_query加 fallback 分支 →callback_dispatcher.dispatch_action() - Reply 格式:
✅ {action_label}\n<code>{mcp_output[:500]}</code>reply_to 原卡片
Sprint 5.2:查類按鈕實作(1 天)
按 1.1 表格實作 10 個查類按鈕。每按鈕:
- callback_data 用 2-part info 格式(無副作用無需 nonce)
- dispatcher 呼叫對應 MCP tool
- reply_to 原卡片顯示結果摘要
- 單元測試:action spec 註冊 + dispatch 呼叫正確 MCP
- E2E 測試:發告警 → 點按鈕 → reply 正確
Sprint 5.3:寫類按鈕實作(2 天)
按 1.2 表格實作 14 個寫類按鈕。每按鈕:
- callback_data 用 4-part nonce 格式
- nonce 驗證通過
- dispatcher 執行前 log
audit_action_start - MCP 執行
- 執行後 log
audit_action_complete+ reply 原卡片 - 失敗 reply 「❌ 執行失敗: {error}」
- rollback 動作需 Multi-Sig(見 Sprint 5.4)
Sprint 5.4:資安按鈕(Multi-Sig)(0.5 天)
4 個 secops 按鈕都標為 CRITICAL:
- 啟用 Guardrail BLOCK 層(已有
sprint5r_guardrail) - 2 人簽核才執行(current_signatures >= 2)
- audit log 需記錄兩個簽核者
- 失敗通知發 SRE 群組 + 統帥 DM
Sprint 5.5:E2E 測試與上線(1 天)
- 每按鈕至少 1 個 E2E 測試(click simulation → dispatcher → MCP mock OR real → reply)
- 端到端驗證:真實告警觸發 → 統帥 Telegram 點按鈕 → 看到 reply
- Rollout 策略:先上查類(Sprint 5.2)觀察 24h,確認零 bug 再上寫類(5.3)
- 更新 ADR-079 狀態為 Completed
3. 架構設計決策
3.1 為何用 callback_dispatcher 而非每按鈕寫 elif
原因:
- 28 個按鈕寫 28 個 elif → 900 行代碼,違反 leWOOOgo 積木化
- Registry pattern + spec 分離 → 新增按鈕只需 yaml 一行
檔案結構:
apps/api/src/services/
├── callback_dispatcher.py # dispatcher 主邏輯
├── callback_action_spec.yaml # action → MCP spec 註冊
└── callback_action_registry.py # spec loader (lru_cache)
3.2 為何不直接重新啟用 _CATEGORY_BUTTONS
原因:
- 當前下架狀態 = 零風險 baseline
- Phase 5 完成才重新啟用(按 category 逐批)
- 避免「啟用→爆炸→再下架」振盪
步驟:
- Phase 5.2 完成 → 啟用查類按鈕(只 check_*)
- 觀察 24h
- Phase 5.3 完成 → 啟用寫類按鈕
- Phase 5.4 完成 → 啟用 secops 按鈕
3.3 reply_to 訊息策略
所有 action reply 必須 reply_to_message_id=原告警卡 msg_id:
- 用戶可以點 reply 看到對應的告警上下文
- Redis
tg_msg:{incident_id}已有此資料(approval_execution 用同一套)
4. 依賴與風險
4.1 必須先修的技術債
_security.parse_callback_data()目前只認 4-part nonce 和 2-part info,需擴充為「註冊表驗證」- Redis
tg_msg:{incident_id}寫入時機確認(approval_execution 寫,其他路徑也要寫) callback_dispatcher要整合 Langfuse trace(每 action 可追蹤)
4.2 新增 MCP 能力
kubectl_rollout_undo(Sprint 5.3 rollback 按鈕需要)database_list_slow_queries(Sprint 5.2 database check 需要)- Alertmanager
/api/v2/silencesintegration(Sprint 5.3 pause_1h 需要)
4.3 風險
| 風險 | 機率 | 影響 | 緩解 |
|---|---|---|---|
| 寫類按鈕被誤點 | 中 | 非預期修復 | nonce 4-part + 寫明「確認執行」 UI |
| Multi-Sig 未達 2 人 | 中 | secops 動作卡住 | 超時 15 min 降級 TYPE-4 人工 |
| MCP 呼叫超時 | 中 | 用戶看不到 reply | 每 dispatcher call 包 30s timeout |
| 大量同時點按鈕 | 低 | Ollama/K8s 負載 | Redis lock dispatch:{incident_id}:{action} 60s |
5. 驗收標準
5.1 單元測試覆蓋率
- callback_action_spec loader 100%
- callback_dispatcher 主路徑 + 錯誤路徑 > 80%
- 每按鈕 action → MCP mapping 正確性測試
5.2 E2E 驗收
- E2E-1 (查類):發 HostHighCpuLoad 告警 → 點「🔍 查程序」→ 1 秒內 reply「Top 10 processes」
- E2E-2 (寫類):發 KubePodCrashLooping → 點「🔄 重啟」→ nonce 驗證 → 執行 kubectl rollout restart → reply「✅ 執行成功」
- E2E-3 (secops):發 UnauthorizedSSH → 點「🚫 隔離」→ 等第二簽核 → apply NetworkPolicy → reply「✅ 已隔離」
- E2E-4 (failure):寫類按鈕執行失敗 → reply「❌ 執行失敗: {error}」+ fallback TYPE-4 人工審核
- E2E-5 (nonce replay):舊 callback_data 重複使用 → parser reject → reply「⚠️ Callback 已過期」
5.3 效能指標
- 查類按鈕 P95 < 2s
- 寫類按鈕 P95 < 10s(含 MCP call + reply)
- 所有 dispatcher call 100% 記錄 audit log
6. 文件更新
Phase 5 完成後需更新:
ADR-079狀態改為CompletedADR-075更新:分類按鈕從_CATEGORY_BUTTONSdict 改引用 registryLOGBOOK.md記錄 Sprint 5.0-5.5 完成docs/operations/ON-CALL-HANDBOOK.md新增「如何用 Telegram 按鈕自助排障」章節docs/reference/ALERT-TAXONOMY-CATALOG.md每分類加「可用按鈕」欄位
7. 起始點(等批准後第 1 步)
Kick-off 檢查(0.5 小時):
# 1. 確認 MCP provider 全綠
kubectl exec -n awoooi-prod deploy/awoooi-api -- python -c "
from src.plugins.mcp.registry import list_providers
for p in list_providers():
print(f'{p.name}: {len(p.tools)} tools')
"
# 預期: k8s=10, ssh=15, prometheus=3, signoz=5, database=4, sentry=3
# 2. 確認 Redis tg_msg:* 有資料
ssh 192.168.0.188 'redis-cli -p 6380 -n 10 KEYS "tg_msg:*" | head'
# 3. 確認 Guardrail BLOCK 層啟用(Sprint 5.4 依賴)
grep "GUARDRAIL_MODE" apps/api/src/core/config.py
批准後第 1 個 commit 範圍:callback_action_spec.yaml + callback_dispatcher.py 骨架 + 單元測試。
Awaiting 統帥批准 → 啟動 Sprint 5.0