feat(ea-hitl): ADR-021 EA 升級審核 pre-fetch + 競價告警金額影響量化
根治 2026-05-02 統帥反映的三層 EA escalation 訊息空泛問題: 1. _escalate_to_human 對 price_drop_alert / market_opportunity / threat_escalation 三類觸發,送 Telegram 前先 await Hermes 取具體 SKU 清單覆蓋 plan 元流程文字(5s 短超時,失敗 fallback 原 plan) 2. NemoTron 競價告警新增 _compute_business_impact helper: 過去 7 日營收流失(gap_pct>0 才算)+ 跟進競品建議價, dispatch 主路徑 / 防線二 / Hermes rule fallback 三條全部 Python 獨裁注入,告警含「📉 NT$ X」「🎯 NT$ Y」具體金額 3. 補實 telegram_bot_service.handle_callback 的 momo:eig: prefix handler,HITL「🛑 忽略此事件」按鈕首次有對應 audit 寫入 Critic 審查通過(5 項必修全綠): - Critical-1: user_label HTML escape 防 Telegram username XSS - High-1: pre-fetch 改 asyncio.wait_for(5s) 防阻塞 escalation - High-2: 全部行缺金額時 return None 觸發 plan fallback - Medium-2: 空 event_id callback 拒絕避免 audit 污染 - Medium-3: gap_pct≤0 時 revenue_loss_7d 強制歸 0 不誤導降價 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
124
docs/adr/ADR-021-ea-hitl-prefetch-and-alert-impact.md
Normal file
124
docs/adr/ADR-021-ea-hitl-prefetch-and-alert-impact.md
Normal file
@@ -0,0 +1,124 @@
|
||||
# ADR-021: EA HITL Pre-fetch + 競價告警必填金額影響量化
|
||||
|
||||
- **Status**: Accepted
|
||||
- **Date**: 2026-05-03
|
||||
- **Deciders**: 統帥
|
||||
- **Related**: 補強 ADR-012(Agent Action Ladder L3 HITL)對 escalation 訊息內容的要求;不取代任何既有 ADR
|
||||
- **Affects**: `services/elephant_alpha_autonomous_engine.py`、`services/nemoton_dispatcher_service.py`、`services/hermes_analyst_service.py`、`services/telegram_bot_service.py`
|
||||
|
||||
## Context
|
||||
|
||||
2026-05-02 統帥收到一條 EA 升級審核 Telegram 告警(commit `b5a2b094` 時段),內容為:
|
||||
|
||||
> 自主決策信心度 0.83 低於門檻,需人工批准
|
||||
> AI 摘要:競爭對手價格溢價 >5% 且有庫存充足…
|
||||
> 步驟 1:[OpenClaw] 基於市區拉貨區市場特性,生成包含動態定價模型與競爭對手分析的策略建議
|
||||
> 步驟 2:[Hermes] 識別具體競爭商品並量化價格差異(目標確認至少 20 個高價溢價 SKU)
|
||||
> 步驟 3:[NemoTron] 將生成的定價策略與競爭分析結果同步至人工審核管道
|
||||
|
||||
統帥指出此告警**毫無決策價值**,原因:
|
||||
|
||||
1. 沒有具體 SKU、沒有金額影響、沒有可批准/駁回的具體動作
|
||||
2. 三步驟「建議行動」實為「申請開始分析」的元流程描述(OpenClaw 生成策略 → Hermes 識別 SKU → NemoTron 同步),人類審核者沒有判斷依據
|
||||
3. HITL 攔截點放錯位置:應在「具體動作出爐後、執行前」攔,而非「分析計畫產出但尚未跑」
|
||||
|
||||
### 程式碼層證據
|
||||
|
||||
**根因 A — `_escalate_to_human` 的 ai_actions 來源是 plan 階段**:[`services/elephant_alpha_autonomous_engine.py:528-557`](services/elephant_alpha_autonomous_engine.py#L528-L557) 的 `_execute_autonomous_decision` 在 `elephant_orchestrator.analyze_and_coordinate` 產出 `StrategicDecision` 後,**信心度不足即 escalate**——此時 `decision.execution_plan` 還只是 Gemini 寫的 plan 文字,Hermes/NemoTron 從未實際跑過取得具體 SKU。`_escalate_to_human` 把 plan 的前 3 個 step description 直接灌進 `triaged_alert.ai_actions` 是空泛元流程的根源。
|
||||
|
||||
**根因 B — Hermes/NemoTron 已有具體告警,但缺金額影響量化**:[`services/nemoton_dispatcher_service.py:683-700`](services/nemoton_dispatcher_service.py#L683-L700) 的 `_exec_trigger_price_alert` 已經能輸出「[SKU] 商品|MOMO $X / PChome $Y|價差 ±%|銷量 ±%」,但**缺絕對金額影響**——人類看到「價差 22.4%、銷量 -35%」仍需自己換算「我這週實際少賺多少」才能決策。
|
||||
|
||||
**根因 C — `momo:eig:` callback 按鈕從未實作**:[`services/telegram_templates.py:466-467`](services/telegram_templates.py#L466-L467) 的 `triaged_alert` 鍵盤產出「🛑 忽略此事件」按鈕(callback_data = `momo:eig:{event_id}`),但 [`services/telegram_bot_service.py:512`](services/telegram_bot_service.py#L512) 的 `handle_callback` 只 dispatch `menu:/cmd:/await:` 三個 prefix,**`momo:` prefix 完全沒處理**——統帥點擊忽略按鈕後永遠沒反應,HITL 流程閉環缺一環。
|
||||
|
||||
## Decision
|
||||
|
||||
對 EA L3 HITL 升級審核訊息與 NemoTron 競價告警採取三項根治措施:
|
||||
|
||||
### 規則 1 — EA 升級審核 pre-fetch Hermes 具體威脅清單
|
||||
|
||||
對 `_PRICE_RELATED_TRIGGERS = {price_drop_alert, market_opportunity, threat_escalation}` 三類觸發,`_escalate_to_human` **送 Telegram 前先呼叫 Hermes 取得具體 SKU 清單**,將前 5 筆格式化為:
|
||||
|
||||
```
|
||||
[SKU] 商品名稱|MOMO $X vs PChome $Y (±%)|近 7 日流失 NT$ Z|建議跟進 NT$ W
|
||||
```
|
||||
|
||||
蓋掉原本的 plan 元流程文字。**強制配套限制**:
|
||||
|
||||
- `asyncio.wait_for(timeout=5)` 短超時:Hermes 熱駐留 < 10s,但冷啟動會拖到 30s+,HITL 訊息延遲不可大於 10s
|
||||
- Pre-fetch 失敗(timeout / 0 threats / 全部缺金額)→ fallback 回原 plan 文字,**不中斷 escalation 主流程**
|
||||
- 「全部行皆缺金額」也視同無料 fallback,避免「乾巴巴兩行 MOMO/PChome 比價」比 plan 文字更空泛
|
||||
|
||||
### 規則 2 — NemoTron 告警必填金額影響量化
|
||||
|
||||
新增模組級 helper `_compute_business_impact(threat) -> {revenue_loss_7d, recommended_price}`:
|
||||
|
||||
- **`revenue_loss_7d`** = `max(0, sales_7d_prev_amount - sales_7d_curr_amount)` 且**僅在 `gap_pct > 0` 時計算**
|
||||
- 語意:「我方比競品貴,且過去 7 日銷量金額下滑 → 推估價格因素導致的流失」
|
||||
- `gap_pct ≤ 0`(我方便宜或持平)即使銷量下滑亦歸 0,避免把季節性/商品壽命終結等非價格因素誤標為「流失」誘導降價
|
||||
- **`recommended_price`** = `round(pchome_price)` 當 `gap_pct > 0`;否則 `None`
|
||||
- 語意:「跟進競品的最低調價金額」;統帥可基於此再依毛利策略加溢價
|
||||
|
||||
`_fmt_price_alert` / `_fmt_human_review` 加入「📉 過去 7 日營收流失:NT$ X」「🎯 跟進競品建議價:NT$ Y(毛利策略可再加溢價)」區塊。dispatch() 主路徑、防線二強制覆核、`_hermes_rule_fallback` 三條路徑**全部走 Python 獨裁注入**(同 Bug-1 防線二原則:客觀數字不過 LLM)。
|
||||
|
||||
為支援此計算,`PriceThreat` dataclass 新增 `sales_7d_curr_amount` / `sales_7d_prev_amount` 兩個欄位,由 `_batch_analyze` 從 SQL 結果直接帶出(**不**餵給 LLM 推理,避免 token 爆炸與 LLM 嘗試引用幻覺)。
|
||||
|
||||
### 規則 3 — 補實 `momo:eig:` callback handler
|
||||
|
||||
`telegram_bot_service.py.handle_callback` 在既有 `menu:/cmd:/await:` dispatch 之前加 `momo:eig:` 處理,呼叫 `_handle_event_ignore_callback`:
|
||||
|
||||
1. 解析 `event_id`,**空 id 立即拒絕**(防 audit 污染)
|
||||
2. 寫入 `ai_insights`:`status='ignored', insight_type='human_review', metadata_json` 含 `event_id / decided_by / decided_at`(ADR-012 §③ audit trail)
|
||||
3. 編輯原訊息加「🛑 已忽略 by `<user>` @ `<ts>`」尾註
|
||||
4. **`user_label` / `ts_label` 寫入 HTML 前必須 `html.escape()`**(user-controlled Telegram username 透過 `<a href>/<pre>` 注入超連結與破版的 XSS 防線)
|
||||
5. 任何例外都 best-effort,不阻斷 UI
|
||||
|
||||
### Critic 審查(必修)
|
||||
|
||||
| 編號 | 嚴重度 | 內容 | 修復 |
|
||||
|------|--------|------|------|
|
||||
| Critical-1 | CRITICAL | `user_label` 直接 HTML 拼接 → username 注入 `<a>/<pre>` 破版 | `html.escape()` 雙重 escape |
|
||||
| High-1 | HIGH | Pre-fetch Hermes 同步阻塞 escalation cooldown 視窗(30-60s) | `asyncio.wait_for(timeout=5)` |
|
||||
| High-2 | HIGH | Hermes 有 threats 但全部缺金額時 → 兩行乾巴巴比價反而更空泛 | `any_concrete` 判斷,全缺則 `return None` 觸發 plan fallback |
|
||||
| Medium-2 | MEDIUM | 空 `event_id` callback 寫入 `'unknown'` 污染 audit | prefix 解析後即拒絕 |
|
||||
| Medium-3 | MEDIUM | `gap_pct ≤ 0` 但 `prev > curr` 仍顯示「流失」誤導降價 | `revenue_loss_7d` 條件改為 `if gap_pct > 0` |
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
| 方案 | 為何不選 |
|
||||
|------|---------|
|
||||
| A. 廢掉 `market_opportunity` 觸發類型,全交 NemoTron pipeline | EA 同時負責 `price_drop_alert / threat_escalation / resource_optimization / code_exception` 多類觸發,不可單刪一類;且 EA orchestrator 是 SOT,廢觸發類型需大改 |
|
||||
| B. 把 EA 信心度門檻降到 0.5,讓「market_opportunity」全部自主執行 | 違反 ADR-012 L3 HITL 對「實際修改價格」的安全網;統帥未授權自動調價 |
|
||||
| C. Pre-fetch 改為背景 task,escalation 訊息分兩次發 | 兩次告警分裂語境,使用者體驗差;且 Hermes 結果晚到時 escalation 已 cooldown |
|
||||
| D. 不 pre-fetch,要求人類自己用 `/menu` 查 SKU | 違反「告警必須 actionable」精神,且增加人類認知負荷 |
|
||||
|
||||
## Consequences
|
||||
|
||||
### 正面
|
||||
|
||||
- EA 升級審核 Telegram 內容從元流程描述變為「具體 SKU + 價格 + 金額流失 + 建議調價」,HITL 真正可決策
|
||||
- NemoTron 既有告警再升級,每筆都帶可批准/駁回的金額判斷依據
|
||||
- `momo:eig:` 按鈕首次有對應 handler,HITL 流程閉環完整
|
||||
- pre-fetch 改用 5s 短超時 + fallback,最壞情況退回原 plan 文字,不破壞既有行為
|
||||
|
||||
### 負面 / 風險
|
||||
|
||||
- 每次價格類 escalation 多花 ≤ 5s(Hermes 熱駐留實測 < 10s 但有 timeout),整體告警延遲略增
|
||||
- Hermes 在 5s 內若沒回應,告警內容降級回 plan 文字(仍維持原行為,無新增風險)
|
||||
- `gap_pct ≤ 0` 案例的銷量下滑(非價格因素)將完全不顯示流失金額——若統帥需追蹤「非價格流失」需另開告警類型(待後續 ADR)
|
||||
|
||||
### 監控指標
|
||||
|
||||
- Telegram 「EA 升級審核」訊息含「📉 過去 7 日營收流失」比例(部署後應 → 每筆價格類觸發都有,除非 5s timeout)
|
||||
- `_handle_event_ignore_callback` audit 寫入頻率(觀察人類實際使用 HITL 比例)
|
||||
- Hermes pre-fetch timeout rate(>10% 代表 Ollama 冷啟動嚴重,需檢查 keep_alive)
|
||||
|
||||
## 實作 checklist
|
||||
|
||||
- [x] `services/hermes_analyst_service.py` PriceThreat 新增絕對金額欄位
|
||||
- [x] `services/nemoton_dispatcher_service.py` `_compute_business_impact` helper + 三條 dispatch 路徑注入
|
||||
- [x] `services/elephant_alpha_autonomous_engine.py` `_fetch_hermes_threats_summary` + 5s timeout + fallback
|
||||
- [x] `services/telegram_bot_service.py` `_handle_event_ignore_callback` + HTML escape + 空 id 拒絕
|
||||
- [x] Critic 審查通過(Critical-1 / High-1 / High-2 / Medium-2 / Medium-3 全修)
|
||||
- [x] Smoke test:`_compute_business_impact` 對 gap≤0 / gap=0 / 銷量回升 / bogus type 四案例驗證
|
||||
- [x] `docs/adr/README.md` 索引加 ADR-021
|
||||
- [ ] 部署到 188 + 觀察首日 EA escalation 內容(人工驗證)
|
||||
@@ -42,6 +42,7 @@
|
||||
| [018](ADR-018-four-agent-ai-automation-control-plane.md) | 四 AI Agent 自動化控制面(Hermes/NemoTron/OpenClaw/ElephantAlpha) | Accepted | 2026-04-29 |
|
||||
| [019](ADR-019-telegram-bot-agentic-conversation-layer.md) | Telegram Bot Agentic Conversation Layer(菜單→Agent 決策統一入口) | Accepted | 2026-05-02 |
|
||||
| [020](ADR-020-code-review-full-autoheal.md) | Code Review 全自動修復政策(局部覆寫 ADR-012 HITL) | Accepted | 2026-05-02 |
|
||||
| [021](ADR-021-ea-hitl-prefetch-and-alert-impact.md) | EA HITL Pre-fetch + 競價告警必填金額影響量化 | Accepted | 2026-05-03 |
|
||||
|
||||
## 規範
|
||||
|
||||
|
||||
Reference in New Issue
Block a user