fix(ea): Hermes-first short-circuit + 消除 EA escalation LLM 幻覺訊息
Some checks are pending
CD Pipeline / deploy (push) Has started running
Some checks are pending
CD Pipeline / deploy (push) Has started running
統帥 2026-05-03 23:30 反饋:「EA escalation 訊息空洞、浪費 Gemini API」
根因:
1. 燒錢:每個 trigger 先跑 Gemini orchestration,再 prefetch Hermes
Hermes 0 threats 時 → Gemini 已燒,訊息卻空泛幻覺
2. 空泛:fallback 路徑灌 OpenClaw plan 文字 + decision.reasoning
全是「312 SKU / 23% / 14 項任務」LLM 自由發揮,無 DB 鉤住
修補:
A. _execute_autonomous_decision 加 Hermes-first short-circuit
- 價格類 trigger 先跑 Hermes (5s timeout, 免費 Ollama)
- 0 threats → 直接 return,不燒 Gemini orchestration
- 預期 >70% trigger 在此階段攔截,月省 ~3-5M Gemini tokens
- prefetch 結果存入 trigger.conditions 給 orchestrator 用
(避免 orchestrator 又自己編 SKU 數字)
- log_ai_call 記 short_circuit=true 供 token report 統計
B. _escalate_to_human fallback 路徑改極簡訊息
- concrete=Hermes 實證 vs concrete=None 兩條路徑徹底分離
- 有實證 → 完整訊息(含 SKU 流失金額)
- 無實證 → 極簡訊息「⚠️ Hermes 即時數據不可用」+ SQL 查詢指引
不再灌 OpenClaw plan 文字 / decision.reasoning(幻覺源頭)
Operation Ollama-First v5.0 / Phase 6.5 hotfix / ADR-021 遵循收尾
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -534,6 +534,49 @@ class ElephantAlphaAutonomousEngine:
|
||||
return default
|
||||
|
||||
async def _execute_autonomous_decision(self, trigger: AutonomousTrigger) -> None:
|
||||
# ─── Operation Ollama-First v5.0 修補(2026-05-03)───
|
||||
# 統帥反饋:每個 EA trigger 都先跑 Gemini orchestrate(燒錢)才 prefetch Hermes,
|
||||
# 結果 Hermes 0 threats 時送出空泛幻覺訊息 + Gemini 帳單照付。
|
||||
# 修法:價格類 trigger Hermes-first short-circuit
|
||||
# 1. 先跑 Hermes(5s timeout,免費 Ollama)
|
||||
# 2. 0 threats → 直接 return(**不燒 Gemini**)
|
||||
# 3. 有 threats → 才走原 Gemini orchestration
|
||||
# 預期:>70% 的 trigger 在 Hermes 階段就被攔截,月省 Gemini ~3-5M tokens
|
||||
if trigger.trigger_type in _PRICE_RELATED_TRIGGERS:
|
||||
try:
|
||||
hermes_threats = await self._fetch_hermes_threats_summary(top_n=5)
|
||||
except Exception as e:
|
||||
self._log.warning("Hermes pre-flight check 失敗 (non-blocking): %s", e)
|
||||
hermes_threats = None
|
||||
|
||||
if not hermes_threats:
|
||||
# 0 threats 或 prefetch timeout → 直接 drop trigger,不燒 Gemini
|
||||
self._log.info(
|
||||
"EA short-circuit: trigger=%s 無 Hermes 實證 threats,"
|
||||
"跳過 Gemini orchestration 省成本",
|
||||
trigger.trigger_type
|
||||
)
|
||||
# 紀錄 short-circuit 事件供 token report 統計(不影響主流程)
|
||||
try:
|
||||
from services.ai_call_logger import log_ai_call
|
||||
with log_ai_call(
|
||||
caller='ea_engine',
|
||||
provider='gcp_ollama', # 只跑了 Hermes,沒燒 Gemini
|
||||
model='hermes3:latest',
|
||||
meta={'short_circuit': True, 'trigger': trigger.trigger_type,
|
||||
'reason': 'no_hermes_threats'},
|
||||
) as ctx:
|
||||
ctx.set_tokens(input=0, output=0) # Hermes 已自己記
|
||||
ctx.status = 'cache_only' # 不算 ok 也不算 error
|
||||
except Exception:
|
||||
pass # logger 失敗不影響主流程
|
||||
return
|
||||
|
||||
# Hermes 有實證 threats → 把它存進 trigger.conditions 給 orchestrator 用
|
||||
# 避免 orchestrator 又自己編 SKU 數字
|
||||
trigger.conditions = trigger.conditions or {}
|
||||
trigger.conditions['_prefetched_hermes_threats'] = hermes_threats
|
||||
|
||||
context = await self._build_trigger_context(trigger)
|
||||
try:
|
||||
decision = await self._run_with_timeout(
|
||||
@@ -881,32 +924,62 @@ class ElephantAlphaAutonomousEngine:
|
||||
self._log.warning("Pre-fetch threats raised (non-blocking): %s", e)
|
||||
concrete_actions = None
|
||||
|
||||
# ─── Operation Ollama-First v5.0 修補:消除空泛幻覺訊息 ───
|
||||
# 統帥反饋(2026-05-03):fallback 路徑帶 OpenClaw Gemini plan 文字 +
|
||||
# decision.reasoning 全是「312 SKU / 23% / 14 項任務」幻覺數字,無 DB 鉤住,
|
||||
# 嚴重誤導決策。修法:concrete=Hermes 實證 vs concrete=None 兩條路徑徹底分離。
|
||||
# - 有實證 → 完整訊息(含 SKU 流失金額)
|
||||
# - 無實證 → 極簡訊息「Hermes 即時數據不可用」+ 不再灌 LLM 幻覺
|
||||
|
||||
from services.telegram_templates import triaged_alert, _send_telegram_raw
|
||||
|
||||
if concrete_actions:
|
||||
# 有實證數據路徑:保留完整訊息
|
||||
ai_actions_payload = concrete_actions
|
||||
ai_summary_text = (decision.reasoning or "")[:300]
|
||||
ai_cause_text = (
|
||||
f"觸發類型:{_zh_trigger(trigger.trigger_type)} | "
|
||||
f"信心度:{decision.confidence:.2f} | "
|
||||
f"參與模組:{', '.join(_AGENT_LABEL.get(a.lower(), a) for a in decision.agents_required)}"
|
||||
)
|
||||
else:
|
||||
# 無實證數據路徑:極簡訊息,明確標註無數據
|
||||
self._log.warning(
|
||||
"EA escalation 落入 no-concrete-data fallback (trigger=%s);"
|
||||
"送極簡訊息避免 LLM 幻覺數字誤導統帥",
|
||||
trigger.trigger_type
|
||||
)
|
||||
ai_actions_payload = [
|
||||
f"步驟 {s.get('step', i+1)}:{_zh_step(s)}"
|
||||
for i, s in enumerate(decision.execution_plan[:3])
|
||||
] or ["無具體執行計畫"]
|
||||
"⚠️ Hermes 即時威脅清單不可用(5s timeout 或無 SKU 命中)",
|
||||
"📋 建議:手動下 SQL 查詢過去 24h competitor_price_history 確認狀況",
|
||||
"🔧 或:SSH 188 跑 docker exec momo-pro-system python -c "
|
||||
"'from services.hermes_analyst_service import HermesAnalystService;"
|
||||
" print(HermesAnalystService().run().threats[:5])'",
|
||||
]
|
||||
ai_summary_text = (
|
||||
f"⚠️ 本訊息為**無實證**告警:Hermes pre-fetch 失敗,"
|
||||
f"以下原始決策內容含 LLM 自由發揮數字(非 DB 數據),請審慎參考。"
|
||||
)
|
||||
ai_cause_text = (
|
||||
f"觸發類型:{_zh_trigger(trigger.trigger_type)} | "
|
||||
f"信心度:{decision.confidence:.2f} | "
|
||||
f"⚠️ 無 Hermes SKU 數據(不顯示 LLM 幻覺 plan 文字)"
|
||||
)
|
||||
|
||||
try:
|
||||
from services.telegram_templates import triaged_alert, _send_telegram_raw
|
||||
msg, keyboard = triaged_alert(
|
||||
base_event={
|
||||
"event_type": "ea_escalation",
|
||||
"title": f"🐘 EA 升級審核 · {_zh_trigger(trigger.trigger_type)}",
|
||||
"summary": (
|
||||
f"自主決策信心度 {decision.confidence:.2f} 低於門檻,需人工批准"
|
||||
+ ("" if concrete_actions else "(⚠️ 無實證數據)")
|
||||
),
|
||||
"id": f"ea_review_{int(datetime.now().timestamp())}",
|
||||
},
|
||||
tier_label="🐘 Elephant Alpha · L3 HITL",
|
||||
ai_summary=(decision.reasoning or "")[:300],
|
||||
ai_cause=(
|
||||
f"觸發類型:{_zh_trigger(trigger.trigger_type)} | "
|
||||
f"信心度:{decision.confidence:.2f} | "
|
||||
f"參與模組:{', '.join(_AGENT_LABEL.get(a.lower(), a) for a in decision.agents_required)}"
|
||||
),
|
||||
ai_summary=ai_summary_text,
|
||||
ai_cause=ai_cause_text,
|
||||
ai_actions=ai_actions_payload,
|
||||
)
|
||||
await self._run_with_timeout(_send_telegram_raw, msg, timeout=10, reply_markup=keyboard)
|
||||
|
||||
Reference in New Issue
Block a user