diff --git a/apps/api/src/services/platform_operator_service.py b/apps/api/src/services/platform_operator_service.py index ec73918d..73d75d7a 100644 --- a/apps/api/src/services/platform_operator_service.py +++ b/apps/api/src/services/platform_operator_service.py @@ -200,6 +200,36 @@ def _approval_step_title(tool_name: str, step_seq: int) -> str: return f"Step {step_seq}: {tool_name}" +def _outbound_timeline_title( + channel_type: str, + message_type: str, + content_preview: str | None, +) -> str: + """將 legacy Telegram outbound 分類成 Operator 看得懂的語義標題。""" + channel = channel_type.upper() + preview = content_preview or "" + if "RUNBOOK REVIEW" in preview: + return f"{channel}:Runbook 待人工審核" + if "AI 治理警報" in preview: + return f"{channel}:AI 治理警報" + if "HANDOFF REQUIRED" in preview or "AI 自動修復失敗" in preview: + return f"{channel}:AI 自動修復失敗,已轉人工" + if "AUTO RESOLVED" in preview or "AI 自動修復完成" in preview: + return f"{channel}:AI 自動修復完成" + if "ESCALATION" in preview or "事故升級" in preview: + return f"{channel}:事故升級通知" + if "ACTION REQUIRED" in preview: + return f"{channel}:告警審批卡" + + fallback = { + "approval_request": "人工審批請求", + "error": "錯誤回覆", + "final": "處置結果", + "interim": "漸進式狀態回饋", + }.get(message_type, message_type) + return f"{channel}:{fallback}" + + async def get_run_detail( run_id: str, project_id: str | None = None, @@ -411,10 +441,15 @@ async def get_run_detail( _timeline_item( ts=row.sent_at or row.queued_at, kind="outbound", - title=f"{row.channel_type} 出站:{row.message_type}", + title=_outbound_timeline_title( + row.channel_type, + row.message_type, + row.content_preview, + ), status=row.send_status, summary=row.content_preview or row.send_error, metadata={ + "message_type": row.message_type, "provider_message_id": row.provider_message_id, "triggered_by_state": row.triggered_by_state, }, diff --git a/apps/api/tests/test_awooop_operator_timeline_labels.py b/apps/api/tests/test_awooop_operator_timeline_labels.py new file mode 100644 index 00000000..b07f572a --- /dev/null +++ b/apps/api/tests/test_awooop_operator_timeline_labels.py @@ -0,0 +1,37 @@ +from src.services.platform_operator_service import _outbound_timeline_title + + +def test_outbound_timeline_title_labels_runbook_review() -> None: + title = _outbound_timeline_title( + "telegram", + "approval_request", + "📄 RUNBOOK REVIEW|待審核\nIncident:INC-1", + ) + + assert title == "TELEGRAM:Runbook 待人工審核" + + +def test_outbound_timeline_title_labels_governance_alert() -> None: + title = _outbound_timeline_title( + "telegram", + "final", + "⚠️ *AI 治理警報|知識庫劣化*", + ) + + assert title == "TELEGRAM:AI 治理警報" + + +def test_outbound_timeline_title_labels_auto_repair_handoff() -> None: + title = _outbound_timeline_title( + "telegram", + "error", + "🤖❌ HANDOFF REQUIRED|AI 自動修復失敗,已轉人工", + ) + + assert title == "TELEGRAM:AI 自動修復失敗,已轉人工" + + +def test_outbound_timeline_title_falls_back_to_human_label() -> None: + title = _outbound_timeline_title("telegram", "interim", "正在調用 MCP 工具") + + assert title == "TELEGRAM:漸進式狀態回饋" diff --git a/docs/LOGBOOK.md b/docs/LOGBOOK.md index 09b0a1cd..c39f3339 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -5349,3 +5349,54 @@ Repo / CI: - AwoooP + AI 自動化飛輪整體閉環:約 65%。 判讀:這輪修的是「治理事件資料落地」而不是畫面格式。AI 自動化要能閉環,scanner / governance / AWOOOP 必須先能完整記錄事實;否則前端再漂亮也只是看不到真相的控制台。 + +--- + +## 2026-05-07(台北)— AwoooP Run Timeline 出站訊息語義化 + +**背景**: + +- AwoooP Run Detail 目前能鏡像 Telegram 出站訊息,但 timeline title 仍顯示: + - `telegram 出站:approval_request` + - `telegram 出站:final` + - `telegram 出站:error` +- 對 SRE 來說,這無法快速區分「AI 自動修復完成」、「AI 自動修復失敗轉人工」、「Runbook 待審核」、「AI 治理警報」或「告警審批卡」。 + +**改動**: + +- `platform_operator_service.py` 新增 `_outbound_timeline_title()`。 +- 不改 DB schema,不新增 message_type enum,避免再引入 migration 風險。 +- 保留原始 `message_type` 到 timeline metadata,畫面顯示改為人能判斷的語義標題: + - `TELEGRAM:Runbook 待人工審核` + - `TELEGRAM:AI 治理警報` + - `TELEGRAM:AI 自動修復失敗,已轉人工` + - `TELEGRAM:AI 自動修復完成` + - `TELEGRAM:告警審批卡` + - `TELEGRAM:漸進式狀態回饋` +- 新增 `test_awooop_operator_timeline_labels.py` 回歸測試。 + +**驗證**: + +```text +py_compile: +apps/api/src/services/platform_operator_service.py +apps/api/tests/test_awooop_operator_timeline_labels.py + +ruff: +apps/api/src/services/platform_operator_service.py +apps/api/tests/test_awooop_operator_timeline_labels.py +# All checks passed + +pytest: +DATABASE_URL='postgresql+asyncpg://test:test@127.0.0.1:5432/test' \ + /Users/ogt/awoooi/apps/api/.venv/bin/python -m pytest \ + apps/api/tests/test_awooop_operator_timeline_labels.py \ + apps/api/tests/test_awooop_operator_auth.py \ + apps/api/tests/test_platform_router_order.py -q +# 11 passed +``` + +**進度校準**: + +- Telegram 噪音與可讀性主線:約 89%。 +- AwoooP + AI 自動化飛輪整體閉環:約 66%。