feat(telegram): ADR-019 Phase 3 - feature-flagged agent dispatch for cmd:X
All checks were successful
CD Pipeline / deploy (push) Successful in 2m47s

ADR-019 Phase 3:在 handle_cmd 入口插入 agent dispatch hook,將白名單 cmd 翻成
NL question 交 OpenClaw agent 處理。Agent 自動透過 Phase 2 的 check_data_freshness
tool probe 資料缺口,缺資料時主動詢問用戶,避免靜默產出空白結果。

新增:
- 環境變數 OPENCLAW_AGENT_DISPATCH (預設 0)、OPENCLAW_AGENT_DISPATCH_CMDS (逗號分隔)
- _CMD_TO_NL 翻譯字典:sales / top / vendor 三 cmd 起步
- _agent_dispatch_cmd() helper:feature flag + 白名單 + agent 呼叫 + 失敗 fallback

設計考量:
- 預設 OFF,零 prod regression 風險
- Agent 失敗自動回原 handler,不卡用戶
- 灰度路徑:先在 staging 開 OPENCLAW_AGENT_DISPATCH=1 + CMDS=sales 觀察一週
- 21 cmd 不需要全部翻譯,只翻譯有「資料缺口可能性」或「參數需確認」的

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
OoO
2026-05-02 13:00:02 +08:00
parent b3348ae77d
commit 38f4033eb0

View File

@@ -119,6 +119,20 @@ _ALLOW_PRIVATE_WITHOUT_WHITELIST = (
in {'1', 'true', 'yes', 'on'}
)
# ADR-019 Phase 3: Feature-flagged agent dispatch
# 預設 OFF啟用步驟
# export OPENCLAW_AGENT_DISPATCH=1
# export OPENCLAW_AGENT_DISPATCH_CMDS=sales,top,vendor (逗號分隔白名單)
# 啟用後白名單內 cmd 改走 OpenClaw NL agentagent 自決查資料/詢問用戶/答覆
_OPENCLAW_AGENT_DISPATCH_ENABLED = (
os.getenv('OPENCLAW_AGENT_DISPATCH', '0').strip().lower()
in {'1', 'true', 'yes', 'on'}
)
_AGENT_DISPATCH_CMDS = {
c.strip() for c in os.getenv('OPENCLAW_AGENT_DISPATCH_CMDS', '').split(',')
if c.strip()
}
# ── fail-closed 統一授權檢查 ───────────────────────────────────
# 規則(任一滿足即通過,否則一律拒絕):
# 1. group/supergroup 且 chat_id == ALLOWED_GROUP
@@ -4530,10 +4544,46 @@ def openclaw_answer(question: str):
# ── 指令處理 ──────────────────────────────────────────────────
_CMD_TO_NL = {
'sales': lambda a: f"請查 {a or '今日'} 的業績數字(包含營收、訂單數、毛利率)",
'top': lambda a: f"請列出 {a or '今日'} 的 TOP10 熱銷商品",
'vendor': lambda a: f"請列出 {a or '今日'} 的 TOP10 熱銷廠商",
}
def _agent_dispatch_cmd(cmd, arg, chat_id, reply_to) -> bool:
"""ADR-019 Phase 3: Feature-flagged. 將白名單 cmd 翻成 NL question 交 agent 處理。
Agent 自動 probe 資料新鮮度(透過 Phase 2 的 check_data_freshness tool缺資料時
主動詢問用戶。回 True 表示已交 agent 處理handle_cmd 不再走原 dispatch。
回 False 表示維持原行為(含 feature flag 關閉、cmd 不在白名單、agent 失敗等)。
"""
if not _OPENCLAW_AGENT_DISPATCH_ENABLED:
return False
if cmd not in _AGENT_DISPATCH_CMDS:
return False
if cmd not in _CMD_TO_NL:
return False # 翻譯規則尚未建立 → 安全降級
nl_question = _CMD_TO_NL[cmd](arg)
try:
sys_log.info(f"[AgentDispatch] cmd:{cmd}:{arg or ''} → NL: {nl_question}")
txt, kb = openclaw_answer(nl_question)
send_message(chat_id, txt, reply_to, keyboard=kb)
return True
except Exception as e:
sys_log.error(f"[AgentDispatch] cmd:{cmd} agent failed, fallback to direct handler: {e}")
return False
def handle_cmd(cmd, arg, chat_id, reply_to):
ld = latest_date() or datetime.now(TAIPEI_TZ).strftime('%Y/%m/%d')
target = normalize_date(arg) if arg else ld
# ADR-019 Phase 3: agent dispatch hookfeature flag 預設 OFF
if _agent_dispatch_cmd(cmd, arg, chat_id, reply_to):
return
def _send_mcp_text_result(title: str, data, empty_message: str) -> bool:
"""相容新版 MCP 文字回傳;已處理則回 True舊 dict 格式則回 False。"""
if isinstance(data, str):