90 KiB
PChome 業績成長自動化作戰系統 — AI 競價情報模組 Single Source of Truth
最後更新: 2026-06-18 (台北時間) 狀態: 🟢 四 AI Agent 自動化閉環已落地;LLM 路由紅線升級為 Ollama-first 三主機級聯;PChome 後台業績匯入韌性已補強;產品定位正名為「PChome 業績成長自動化作戰系統」;外部市場來源正規化層、自動同步、作戰清單與價格參考表優先讀取、CSV 備援預檢、前台操作入口、高可見頁面繁中化守門、比價/作戰 UI 工作台化、GCP embedding 熔斷延後處理、110 proxy rescue 與 direct host health skip 已建立 適用版本: V10.627
零、LLM 路由紅線(2026-05-12)
- 所有 AI Agent、LLM 推理與 embedding 預設必須走 Ollama 三主機級聯:GCP-A
34.87.90.216:11434→ GCP-B34.21.145.224:11434→ 111192.168.0.111:11434。 services/ollama_service.resolve_ollama_host()是主機解析契約;OLLAMA_HOST、HERMES_URL、EMBEDDING_HOST、OLLAMA_API_BASE只接受 GCP-A / GCP-B / 111 或 110 的核准轉發端口。- 188 直連 GCP-A / GCP-B timeout 時,resolver 可先使用同順位 110 proxy rescue:GCP-A direct →
192.168.0.110:11435→ GCP-B direct →192.168.0.110:11436→ 111。proxy rescue 只是同一順位的可用入口,不代表 GCP direct host 已恢復。 OLLAMA_RESOLVE_HOST_HEALTH_SKIP_ENABLED=true時,resolver 會讀最近host_health_probes;若 direct GCP-A/GCP-B 在視窗內已被判定不健康,會直接略過該 direct endpoint,先試同順位 proxy rescue,避免每 120 秒 cache refresh 都等待 direct timeout。此 skip 只套用 direct GCP,不套用 110 proxy。config.OLLAMA_HOST、config.HERMES_URL、config.EMBEDDING_HOST只保留為舊 caller 相容常數;import-time 不得 probe network,也不得因 GCP-A/GCP-B 短暫不可用而 freeze 到 111。需要即時路由時一律呼叫get_ollama_host()、get_hermes_url()、get_embedding_host()或OllamaService。- Gemini 只能作為 Ollama 主路徑失敗後的備援;MCP Grounding、PPT/vision、週/月報、Code Review、EA HITL、複雜 SKU 升級等舊鎖定場景也必須先走 GCP-A → GCP-B → 111。
- 188
192.168.0.188僅是 App / DB / scheduler / Telegram bot 容器宿主與 AutoHeal target,不可作為 Ollama 節點。 - 通用 AI 文案、關鍵字、商品洞察與 Telegram Q&A 第一響應不得 Gemini-first。
- Hermes intent / analyst 路徑不得手刻
/api/generate或只 resolve 單次 host;必須走OllamaService。預設HERMES_ALLOW_111_FALLBACK=false,同一請求只跑 GCP-A → GCP-B;兩台都失敗時回規則引擎或 DB 證據 fallback,不把批量價格分析轉嫁到 111。救急時才可顯式設 true 允許 111 接手。 - NemoTron qwen3 dispatch 的
/api/chattool-calling 路徑也必須同一請求最多嘗試三台 Ollama,第一台失敗要mark_unhealthy()後再試下一台,最後才 fallback NIM。 - PPT vision、PPT 文案 final fallback、MCP 離線 final fallback 等特殊 Ollama 路徑也不得只打單一 host;如需
/api/generate,一律透過OllamaService.generate()。 - Code Review pipeline 也必須 Ollama-first:Hermes scan 與 OpenClaw assessment 都走
OllamaService三主機 retry;Gemini telemetry 只能以code_review_openclaw_gemini出現,表示 Ollama/可選 Claude 備援都失敗後才啟用。 - Code Review Hermes scan 預設不呼叫 LLM,改用 deterministic fast static scan,避免部署後先卡三段 Ollama timeout;需要 LLM 掃描時才以
CODE_REVIEW_HERMES_LLM_SCAN_ENABLED=true啟用本地矩陣。 - Code Review Hermes LLM scan 啟用時才使用本地模型矩陣,且預設只跑 GCP-A
qwen2.5-coder:7b→ GCP-Bgemma3:4b;CODE_REVIEW_ALLOW_111_FALLBACK=true時才允許落到 111,並由OllamaService降級到llama3.2:latest。不啟用 Gemini 備援,本地掃描失敗時只回空 findings 並交由 OpenClaw 本地矩陣續跑。 - Code Review OpenClaw assessment 預設只跑 GCP-A → GCP-B:GCP-A
qwen2.5-coder:7b、GCP-Bgemma3:4b;primary timeout 預設15s、secondary timeout 預設60s,讓 A 掛時快速讓位給 B,且 B 有足夠時間完成審查 prompt。111 是最後救急節點,但部署後重分析預設不打 111;只有CODE_REVIEW_ALLOW_111_FALLBACK=true才允許 111 接手,並降級到llama3.2:latest。Code Review 的 Ollamakeep_alive預設為5m,不得再用24h長駐 runner 壓住 GCP-B/111。GCP-A/GCP-B 都失敗且 Claude/Gemini 未顯式開啟時,必須回 deterministic 本地降級摘要,不呼叫 Gemini、不落 111、不走其他雲端模型。 - Embedding / semantic RAG 背景任務預設只跑 GCP-A → GCP-B:
OpenClawLearningServiceembedding worker 與RAGService查詢 embedding 呼叫OllamaService.generate_embedding(..., allow_111_fallback=False);111 只可作人工明確指定的救急路徑,不承接bge-m3背景批次。OLLAMA_EMBED_TIMEOUT/OLLAMA_EMBED_MAX_TIMEOUT預設30s、OLLAMA_EMBED_KEEP_ALIVE=1m、OLLAMA_EMBED_MAX_CHARS=4000;此上限依 GCP-Bbge-m3實測 6–23s 波動調整,避免慢但成功的 embedding 被 15s cap 誤殺。 allow_111_fallback=False時,若 resolver 因 unhealthy cache 回傳 111,不得直接結束 embedding;必須強制改試尚未嘗試的 GCP-A / GCP-B,避免正式 log 出現tried=[]或只試單台 GCP-B。allow_111_fallback=False且 GCP-A / GCP-B 皆失敗時,背景 embedding 會開啟短暫 GCP failure circuit(預設 60 秒),期間不重複打兩台 GCP、不落 111,避免 worker 與 log 被連續失敗拖慢;GCP 恢復後會自然再試。- 背景 embedding 的 GCP-only 熔斷屬於可降級背景能力,應記錄為明確 WARNING 與 circuit 狀態,不應每次污染 ERROR 通道;真正允許三主機 fallback 的同步 embedding 全失敗仍保留 ERROR。
- OpenClaw embedding worker 遇到 GCP-only failure circuit 時,必須把已 claim 任務退回
pending並延後處理,不得扣attempts、不得把同批任務刷成failed;熔斷期間也不得繼續 claim 新任務。 - Scheduler host health probe 不只看
/api/tags;GCP-A / GCP-B 節點必須再通過bge-m3/api/embed實作探針,才算 healthy。探針 timeout 預設 30s,111 預設不納入這個背景 embedding 探針,避免監測任務把 fallback Mac 載入bge-m3。 - 背景 embedding 會讀取最近
host_health_probesruntime 結果;若 GCP-A / GCP-B 在OLLAMA_EMBED_HOST_HEALTH_SKIP_WINDOW_MINUTES=20視窗內已被標為 unhealthy,OllamaService.generate_embedding(..., allow_111_fallback=False)會先跳過該節點並開啟短暫 GCP circuit,不再等待 30 秒 timeout,也仍不落 111。此功能由OLLAMA_EMBED_HOST_HEALTH_SKIP_ENABLED=true控制,DB 讀取失敗時 fail-open 回到原本網路 retry。 - BGE-M3 一致性檢查是監測任務,不是 fallback 壓測;預設只比對 GCP-A / GCP-B。111 Mac fallback 只有
EMBED_CONSISTENCY_INCLUDE_111=true時才納入,避免每週背景檢查把bge-m3載入 111。 - OpenClaw Telegram Q&A 主路徑也不得綁單一 host:
_call_qwen3_qa()必須透過OllamaService跑 GCP-A → GCP-B → 111,並把實際落點寫入ai_calls.provider。 - OpenClaw Telegram 圖片商品辨識也必須 Ollama-first:
_identify_product_name_with_ollama_vision()透過OllamaService嘗試 GCP-A → GCP-B → 111;Gemini 只允許以openclaw_bot_image_geminicaller 作為失敗後備援。 - OpenClaw 週報、月報、Meta analysis、日報洞察、Telegram PPT 分析與 MCP fallback 也必須 Ollama-first;Gemini caller 只能帶
_gemini_fallback或明確 fallback caller 語意,且不得先於 Ollama/NIM 被呼叫。OpenClaw strategy 的 Ollamakeep_alive預設為5m,避免報告型任務把 GCP-B/111 runner 長駐 24h。 - OpenClaw 週報、月報、Meta analysis、日報洞察與每日報告的 Gemini/NIM 備援 caller 必須登錄在 caller registry、AI 觀測台 agent group 與 Telegram 狀態統計,避免 fallback 用量被歸類為未知或漏算。
ai_calls.provider='ollama_other'只允許作為 unresolved/unknown Ollama telemetry bucket,例如全 host 失敗、尚未選定實際 GCP-A/GCP-B/111 host 或舊 caller 未帶 host;不得把ollama_other當成實際路由目標或新增非核准 Ollama host。- GCP-B 若缺 caller 指定的 coder/large 模型,
OllamaService必須先在 GCP-B 改用OLLAMA_SECONDARY_MODEL_FALLBACK(預設gemma3:4b),不可因 model 404 把整台 GCP-B 標成 unhealthy 後直接推到 111;真正 timeout / HTTP 5xx 才標 host unhealthy。 - Gemini API 出站有第二道 kill switch:
GEMINI_FALLBACK_ENABLED預設為false。即使GEMINI_API_KEY存在,通用 AI fallback、OpenClaw 報告/QA/PPT/圖片、MCP Grounding 與 Code Review L3 都不得呼叫 Gemini;只有操作員明確設為true時,Gemini 才能作緊急備援。 docker-compose.yml的momo-app、scheduler、telegram-bot必須明確設定GEMINI_API_HARD_DISABLED=${GEMINI_API_HARD_DISABLED:-true}與GEMINI_FALLBACK_ENABLED=${GEMINI_FALLBACK_ENABLED:-false};.env可保留GEMINI_API_KEY,但不得因 key 存在就讓核心容器產生 Gemini 付費出站。- OpenClaw 日/週/月/Meta 等敘事報告屬長任務,Ollama 只能走 GCP-A → GCP-B;不得使用 111 final fallback 承接長文生成。GCP 兩台都不可用時,應走既有 Gemini hard-disabled guard 後的 NIM / deterministic degraded path,避免 111 被非即時分析壓高負載。
- Gemini 不可被任何狀態面板或 router 推薦為主提供者:
AIProviderService._get_recommended_provider()不得回傳gemini,只能顯示為 fallback 狀態;llm_model_router的ea_engine若收到gemini-*default 必須改回hermes3:latest,需要深推理時才升本地deepseek-r1:14b。 - ElephantAlpha prompt / agent registry 不得再把 OpenClaw 描述為 Gemini 主模型;OpenClaw 是
qwen2.5-coder:7b/qwen3:14bOllama-first 策略師,Gemini 僅能在 guard 顯式解鎖後作 emergency fallback。 - 111
192.168.0.111只是最後一道 Mac fallback,不承接 7B+、vision、long-context 模型長駐;OllamaService.generate()落到 111 時會將qwen3、deepseek-r1、hermes3、qwen2.5*、gemma3、llava、minicpm-v與 7B+ 模型依OLLAMA_111_MODEL_DOWNGRADE_PATTERNS降級到OLLAMA_111_MODEL_FALLBACK=llama3.2:latest,並以OLLAMA_111_KEEP_ALIVE=5m、OLLAMA_111_MAX_TIMEOUT=20、OLLAMA_111_NUM_CTX=4096、OLLAMA_111_NUM_PREDICT=512封頂。OpenClaw 報告型路徑的業務 keep-alive 預設5m;Code Review 以CODE_REVIEW_ALLOW_111_FALLBACK=false、Hermes 以HERMES_ALLOW_111_FALLBACK=false預設跳過 111,避免 16GB RAM 主機與 GCP-B 被長駐 runner、長輸出與 24h keep-alive 壓到高 load。 - Scheduler 每 15 分鐘執行
run_ollama_111_usage_guard_check(),只讀ai_calls統計最近視窗的 GCP-A / GCP-B / 111 呼叫量;預設 60 分鐘內 Ollama 呼叫至少 20 次、111 至少 3 次且占比 >= 5% 才推 Telegram。這是觀測護欄,不改路由、不寫 DB、不自動重啟服務。 OllamaService對 111 final fallback 有 circuit breaker:預設最近 60 分鐘 Ollama 呼叫至少 20 次、111 至少 5 次且占比 >= 5% 時,短暫跳過 111(OLLAMA_111_CIRCUIT_CACHE_SEC=60),避免 111 在已偏高時繼續承接長任務;DB 觀測失敗時 fail-open,不讓主要 GCP-A/GCP-B 路由被觀測層中斷。- 111 的 LAN 入口必須經
scripts/ops/ollama111_allow_proxy.pyallowlist proxy:真實 Ollama 綁127.0.0.1:11434,proxy 綁192.168.0.111:11434,預設只允許 111 本機與 188 生產宿主;110 / 121 / 其他 LAN client 不能直接打 111,避免跨專案 CI 或 VM 繞過 momo-pro router 載入 7B+ runner。111 上以scripts/ops/install_ollama111_allow_proxy.sh安裝 user LaunchAgent,安裝器會把 proxy script 複製到~/.local/share/momo-pro-system/ollama111_allow_proxy.py,讓 LaunchAgent 不依賴 iCloud repo 掛載路徑,並讓 proxy 與OLLAMA_HOST=127.0.0.1:11434在登入/重啟後自動恢復。拒絕日誌以OLLAMA111_PROXY_REJECT_LOG_DEDUP_SEC=60去重,避免 121 這類旁路探測刷爆 111 磁碟日誌。 - ElephantAlpha 的
price_drop_alert/market_opportunityTelegram HITL 告警必須把同款證據獨立呈現,至少包含match_type、price_basis、alert_tier與match_score;沒有高信心同款與總價可比證據時,不得把 PChome/MOMO 價差寫成可直接跟價建議。
零之零、產品定位正名(2026-06-15)
- 本專案的營運定位正名為「PChome 業績成長自動化作戰系統」。
- 主要目標是提升 PChome 銷售業績;MOMO 是目前已接入的外部價格參考來源,不再把 PChome 視為附屬競品語意。
- 使用者可見 UI、Telegram 與報表文案必須白話、可行動,優先使用「商品對應」「可直接比價」「待補對應」「放大價格優勢」「檢查售價與活動」等營運語言,避免把
identity_v2、match_score、candidate queue等工程詞直接丟給使用者。 services/pchome_revenue_growth_service.py是第一版只讀作戰清單:讀 PChome 後台業績與已驗證 MOMO 外部價格參考,輸出/api/ai/pchome-growth/opportunities。此服務不呼叫 LLM、不抓外站、不寫 DB。- 2026-06-15 只讀盤點確認:
daily_sales_snapshot."商品ID"與competitor_prices.competitor_product_id在正式資料中直接重疊為 0。因此第一版作戰清單不得硬接兩邊 ID;若沒有可驗證對應,只能輸出「先補商品對應」任務。 - 蝦皮與酷澎暫停接入,不進作戰清單、不發告警;後續只可透過 official API / provider API / manual CSV 進
external_offers類正規化層,並清楚標示資料品質。 - V10.607 新增
external_market_sources/external_offers正規化層與/api/ai/pchome-growth/source-contract只讀 API。MOMO 先以既有比價快取橋接進來源狀態;蝦皮與酷澎只保留 official API、provider API、manual CSV contract,預設暫停且不進告警。 - V10.608 新增
/api/ai/pchome-growth/external-offers/csv-dry-run與 AI 情報頁「外部報價預檢」。CSV 預檢只讀、不寫 DB;逐列回報「可使用」「需人工確認」「不能使用」,並支援中文表頭,避免格式小錯造成整批匯入失敗。 - V10.609 明確把外部報價主路徑改為自動化:
run_external_offer_sync_task每 4 小時將已確認同款的既有比價快取同步進external_offers。CSV 只保留為 API / crawler / provider 失敗時的備援預檢入口,不是日常營運主流程。 - V10.610 起
/api/ai/pchome-growth/opportunities優先讀取external_offers的自動同步資料;只有新資料層缺資料時才 fallback 舊competitor_prices。API stats 會回傳資料來源計數,方便確認作戰清單是否已走新資料層。 - V10.611 起
/ai_intelligence是營運使用者主入口;V10.617 已將舊「今日作戰入口 / 外部報價預檢」改為「今日重點總覽 / 備援資料檢查」,主流程不得再把人工 CSV 放在前段。 - V10.612 起
/api/ai/icaim/dashboard的「MOMO 外部價格參考」表格也優先讀external_offers,缺資料才 fallbackcompetitor_prices;價差與風險改採 PChome 視角,正數代表 PChome 比 MOMO 外部參考價高。 - V10.613 起高可見前台頁面必須以繁體中文呈現:程式碼審查、AI 自動化健康檢查、PPT 產線與商品看板操作標籤不得使用英文工程標題或簡體字;測試需防止頁面文案退回英文。
- V10.614 起部署監控、基礎設施生命線與 PPT 產線狀態也納入繁中守門:前台不得顯示
Dashboard、Pipeline、Runtime等工程詞,動態階段需轉成「測試 / 建置 / 部署」。 - V10.615 起 AI 智慧推薦頁必須把 Ollama 顯示為「Ollama 主路徑」,Gemini 只能顯示為「Gemini 備援」且手動選項停用;使用者可見錯誤與搜尋流程不得出現
Web Search、Token:、半形英文冒號等工程文案。 - V10.616 起主商品看板
/的統計與補強區塊也納入繁中守門:不得顯示ACTIVE、PICK COUNT、AVG CONFIDENCE、EVIDENCE GAP、PCHOME MATCH BACKFILL等工程標籤;畫面需使用「有效商品」「挑品數」「平均信心」「待補證據」「PChome 比價補強」等白話營運文案。 - V10.617 起
/ai_intelligence必須採「先給下一步」的作戰導向 UI:首屏需先回答「今天先做什麼」,再呈現商品處理進度、外部價格來源與操作捷徑;今日處理清單需用表格呈現優先級、建議動作、商品、近 7 天業績、比價結果、資料可信度與下一步;MOMO 外部價格參考需顯示價格風險分佈,且表格需以 PChome 價格優先,明確顯示「PChome 貴 / PChome 便宜」與可信度,不得只用大段文字說明使用方式。 - V10.618 起
/price_comparison也必須採「先給下一步」的比價決策 UI:首屏需顯示目前卡在哪一步、PChome / MOMO 資料準備狀態與下一個按鈕;比價結果需先呈現「需檢查價格 / 可主推曝光 / 價格接近」分佈,再用表格列出每筆商品的下一步,不得只呈現 Step 流程或原始價差表。 - V10.619 起 MOMO 比價候選來源新增「PChome 商品導向搜尋」:當比價 API 已有 PChome 商品但缺 MOMO 清單時,必須用每筆 PChome 商品名稱產生精準搜尋詞反查 MOMO,保留品牌、品名、容量與組合線索;新版 MOMO 搜尋頁需解析 Next.js
goodsInfoListpayload。此路徑只擴大候選池,不放寬同款 matcher 門檻。 - V10.620 起
unit_comparable不再一律丟人工確認:若build_unit_price_comparison()可產生明確容量/數量、MOMO 單位價、PChome 單位價與差距百分比,候選需標為「自動單位價比較」並回傳auto_compare_type=unit_price。此類候選可自動呈現價格壓力,但不得混入舊總價同款比價表,也不得直接寫入正式價差或自動改價;無法產生單位證據時才維持「需人工確認」。 - V10.621 起
/price_comparison的「自動找 MOMO 候選」會把可直接總價比價與自動單位價候選同步到external_offers,ingestion_method='targeted_momo_search',人工確認候選不得寫入。external_offers.raw_payload_json.price_basis='unit_price'時,作戰清單必須使用unit_price_comparison的 MOMO / PChome 單位價與unit_gap_pct判斷價格壓力;不得把 MOMO 組合總價與 PChome 單品總價直接相減。此同步只影響外部價格參考與作戰清單,不寫competitor_prices,也不自動改價。 - V10.622 起任何
external_offers自動同步成功寫入後,必須呼叫mark_pchome_growth_cache_stale()寫入共享 cache epoch;/api/ai/pchome-growth/opportunities讀快取前必須比對get_pchome_growth_cache_epoch()。這是跨 Gunicorn worker 的可見性保護,避免自動候選已進外部價格參考,但 AI 情報頁仍回 120 秒舊作戰清單。 - V10.623 起
/price_comparison與/ai_intelligence不得只靠大段文字說明流程:比價頁第一屏必須有主 KPI、目前卡點、四步流程與結果決策摘要;作戰頁第一屏必須有今日任務、可立即處理、待補比價與最新業績日。所有狀態都要由實際 API/前端狀態驅動,讓使用者一眼知道下一步要按哪個動作。 - V10.638 起 PChome 導向 MOMO 補抓會把「找到但不能自動比價」的候選以
match_status='needs_review'、data_quality_status='needs_review'保存到external_offers;這些候選不得進價格壓力判斷,也不得發告警,但/api/ai/pchome-growth/opportunities可回傳待確認候選數,讓 UI 顯示「已有候選待確認」而不是只顯示無法比價。 - V10.639 起待確認候選排序必須容忍缺少單位數量;沒有
momo_total_quantity/competitor_total_quantity時仍可保存為needs_review,不得中斷 PChome 導向 MOMO 回填。 - V10.640 起
/ai_intelligence必須提供 MOMO 待確認候選操作佇列;使用者可直接確認同款或排除候選。確認後external_offers會轉為verified/verified並進入作戰清單,排除後轉為rejected/rejected,兩者都必須清掉 PChome 成長作戰清單快取。 - V10.641 起
/ai_intelligence的摘要數字不可只是靜態文字;第一屏 KPI、商品處理進度、待確認數字都必須可點擊並導向對應明細。今日清單若已有 MOMO 待確認候選,下一步必須顯示「確認候選」並跳到候選面板,不得再只顯示「補齊比價」。 - V10.642 起
/ai_intelligence的摘要卡與商品處理數字不可只跳到大區塊;點擊後必須開啟商品明細面板,列出商品名稱、分類、近 7 天業績、業績變化、MOMO 比價狀態與下一步按鈕。明細需至少支援全部、價格壓力、價格優勢、待確認、缺比價與有外部價切換;外部價格風險分佈也必須能一鍵篩選下方表格。 - V10.643 起
/ai_intelligence的商品明細上方必須提供「商品策略分流」視覺摘要,至少包含價格壓力、價格優勢、待確認、缺比價四類;每一類需顯示件數、近 7 天業績與比例條,且可點擊切換明細。舊 KPI 卡也不得是靜態數字,需可導向全部商品、可處理商品、高風險比價或處理紀錄。 - V10.644 起
/ai_intelligence的商品明細列不得只用句子描述比價;每列必須顯示 PChome 價格、MOMO 參考價、差距、可信度四格價格證據,並保留下一步按鈕。單位價候選需顯示單位價與單位,候選待確認或缺資料則以「待補 / 候選待確認」呈現,不得捏造價格。 - V10.645 起
/ai_intelligence的商品明細分流切換後,必須顯示「這類商品怎麼處理」的行動摘要,包含件數、近 7 天業績、平均可信度、最大價差、代表商品與主按鈕;使用者不得只能看到商品列表而不知道下一步。 - V10.646 起
/ai_intelligence的商品明細必須提供搜尋與排序;搜尋至少涵蓋商品、分類、商品編號與 MOMO 候選資訊,排序至少支援優先級、近 7 天業績、價差、下滑幅度與可信度。搜尋/排序後的行動摘要與明細列表必須使用同一批結果。 - V10.647 起
/ai_intelligence的商品明細每一筆都必須能打開單品作戰詳情,詳情需顯示商品、建議動作、近 7 天業績、業績變化、PChome/MOMO 價格證據、價差、可信度、判斷原因與下一步操作;不得只讓使用者看一排文字後自行猜測。 - V10.648 起
/ai_intelligence的商品明細上方必須提供分類策略看板,把商品依分類彙總成可點擊的數據條列;每列至少顯示分類、近 7 天業績、商品數、價格壓力、價格優勢、缺比價、待確認與建議下一步。點擊分類後必須切到該分類商品明細。 - V10.649 起
/ai_intelligence必須提供銷售策略建議看板,把商品分成價格防守、主推曝光、組合/單位價、資料補齊等營運路徑;每張策略卡需顯示件數、近 7 天業績、代表商品與可點擊下一步,點擊後必須切到對應商品明細。 - V10.650 起
/ai_intelligence必須提供「今日策略動作」清單,從作戰商品中挑出前 5 件具體行動;每列需顯示處理順序、動作、商品、近 7 天業績、原因與可點擊的詳情/處理入口,避免使用者只看到分類與策略後仍不知道下一步要做哪一件商品。 - V10.651 起從「今日策略動作」或其他非明細列入口打開單品作戰詳情時,商品明細列表中的對應商品仍必須標示為目前選取;使用者需能看出詳情與明細列的關聯。
- V10.652 起正式首頁
/必須顯示「PChome 業績成長自動化作戰系統」,舊商品看板僅保留在/dashboard或/product-dashboard;「今日策略動作」必須放在首屏任務摘要後方,不能只藏在商品明細區;每列必須直接顯示價格證據,至少包含 PChome、MOMO、差距與可信度四格。候選待確認或缺資料時需以待確認/待補呈現,不得要求使用者先打開詳情才知道判斷依據。 - V10.654 起全站側邊欄第一個主入口必須命名為「業績成長指揮台」;舊商品看板只能以「舊商品看板」保留在
/dashboard,比價工作台必須直連/dashboard?filter=pchome_review...,不得再使用/query 轉址,避免正式首頁與舊頁混淆。 - V10.655 起正式首頁
/必須以 HTTP 200 原地渲染「業績成長指揮台」,不得 302 跳轉到/ai_intelligence;側邊欄第一個主入口必須直連/。/ai_intelligence只作為相容入口保留,不得成為主導流路由。 - V10.656 起正式首頁首屏必須是「PChome 業績成長系統」專業儀表板,而非大段說明型頁首;第一屏需直接呈現近 7 天業績、比價可用率、下滑商品、待補比價、最大分類、下滑商品 TOP 5、PChome/MOMO 價格狀態圓環與處理狀態,且全部使用
/api/ai/pchome-growth/opportunities真資料渲染。 - V10.658 起全站頁面必須回到「提升 PChome 業績」同一產品主線;共用 shell 需提供短流程骨架:評估、分析、建議、解法、治理。頁首、hero、任務卡與提示區不得依賴大段文字讓使用者理解狀況;首屏應以短指標、狀態分流、下一步動作與可執行入口呈現專業評估、分析、建議與解決方案。
- V10.659 起主要入口頁的首屏文案必須改為流程職責語言,而不是功能說明書。當日業績負責找出下滑與價差壓力,匯入負責資料新鮮度,AI 建議負責把證據轉成銷售建議,比價負責同款與價差決策,缺貨負責避免主推商品斷貨,月結分析負責判斷成長、毛利與品類結構。
零之一、12 Agent 決策信封(2026-05-24)
- 12 角色分工不作為 12 個常駐模型;在產品層統一收斂成
decision_envelope,由 Hermes / NemoTron / OpenClaw / ElephantAlpha 與人工審核、PPT QA、競品 review queue 共用。 decision_envelope必須至少能表達:decision_type、severity、evidence[]、recommended_action、expected_impact、confidence、guardrails、trace。guardrails.can_auto_execute=false是預設;價格調整、正式比價覆寫、PPT 發送與修復執行都必須遵守 HITL 或既有 service guard,不得因 Agent 信心高就繞過 matcher / feeder / review service。- 證據不足時不得輸出空泛效益預測;必須標記
data_quality=missing|partial|stale,並把建議行動降級成human_review、needs_research或silence_alert。 - Telegram
triaged_alert()已支援渲染decision_envelope,讓告警固定呈現嚴重度、證據、建議行動、預期影響、信心度與追蹤 ID;後續觀測台與 PPT 也應共用同一份欄位語意。 - NemoTron
price_alert/human_review派發會把同款證據、價差、七日銷量變化、營收流失、HITL 邊界與資料品質寫入同一份decision_envelope,並同步放入 EventRouter event 與 KM metadata;12 Agent 後續只能沿用此信封補充分析,不得繞過 matcher / feeder / review service 直接改價或覆寫比價資料。 - EventRouter / Telegram 的 HITL callback 必須優先使用
decision_envelope.decision_id作為事件追蹤 ID;若上游未帶event.id,triaged_alert()仍會用decision_id產生momo:eig:*callback,避免價格決策審核落成unknown。所有momo:eig:*callback 必須以 UTF-8 byte-safe 截斷,確保callback_data不超過 Telegram 64-byte 限制。 - 競品比價相關的 Agent 建議只能讀
competitor_match_attempts/ review queue /competitor_prices的既有證據;不得直接寫competitor_prices或覆蓋_should_upsert_competitor_price()的保護規則。 - 已帶
decision_envelope的價格/覆核事件必須由 EventRouter 直接渲染證據模板,不再進 L1/L2 AI 重新摘要;Telegram 決策信封需顯示標的 SKU、商品名稱、PChome 候選、evidence、guardrails 與 HITL 動作,避免已有實證的比價告警被二次生成文字稀釋或造成額外模型成本。 - PChome 覆核隊列本身也必須輸出
decision_envelope:fetch_competitor_review_queue()、fetch_competitor_review_queue_page()與/api/pchome-review/queue的每筆候選需帶相同的subject、evidence、recommended_action、expected_impact與guardrails,供 Dashboard、Agent、Telegram 與 PPT 共用;任何下游不得另寫一套比價狀態翻譯或繞過 HITL guardrails。 - Dashboard 覆核卡與
/api/export/excel/pchome-review也必須顯示/匯出decision_envelope的等級、資料品質、建議代碼、HITL、trace 與can_auto_execute=false邊界;操作員離開系統畫面或下載 Excel 後,仍要看得到「不可自動寫正式價差」的 guardrails。 - OpenClaw 週報/日報/月報與 competitor PPT 不得再各自重算或翻譯 PChome 覆核狀態;必須透過
competitor_intel_repository.summarize_review_decision_envelopes()讀取同一份decision_envelope摘要,並在 prompt / data_summary / KPI slide 保留 HITL 與can_auto_execute=false邊界。 - Webcrumbs / Shared UI host data 也必須透過
summarize_review_decision_envelopes()輸出reviewDecisionBrief,並在 metadata 保留 review queue、HITL、auto-execute-blocked、decision_support_rate、catalog_comparable_count與 catalog review lane counts;不得另寫一套 PChome 覆核摘要或在前端 runtime 重新推論價格行動。 - ElephantAlpha 的
resource_optimization與低信心ea_escalation也必須輸出decision_envelope:資源壓力信封只能使用action_plans、CPU 實測、hygiene 結果與 insight/action trace,不得加入 LLM 預測效益;triaged_alert()對ea_escalation亦需渲染信封並以decision_id作為 callback 追蹤 ID。
一、四 AI Agent 路由架構
SQL漏斗(~300筆)
↓
[Hermes 3 8B] — 分析師 (Ollama 三主機級聯, 零成本)
模型: hermes3:latest @ GCP-A → GCP-B → 111
任務: 競價威脅分類 → TOP 20 HIGH/MED/LOW
↓
[NemoTron / qwen3] — 派發器
主路徑: qwen3:14b @ GCP-A/GCP-B;落到 111 時自動降級 llama3.2
備援: NVIDIA NIM meta/llama-3.1-8b-instruct
任務: Tool Calling → Telegram 告警 / DB 寫入
↓
[OpenClaw] — 策略師 (Ollama-first;Gemini 僅備援 / 鎖定場景)
任務: 週策略報告、洞察報告、L3 HITL 建議
↓
[ElephantAlpha] — 編排者 (L3 Orchestrator)
任務: 跨 Agent orchestration、HITL、AutoHeal bridge、受控 log scan
1.1 PChome 挑品 Agent(2026-05-01)
services/ai_product_pick_agent.py 新增 PChome 銷售用挑品 Agent:
- 只讀真實資料表:
products、price_records、competitor_prices、competitor_price_history,若daily_sales_snapshot可用則納入近 7 天銷售額、數量、毛利或成本推算毛利率。 - 將 PChome 比 MOMO 有價格優勢、比對信心足夠、且有歷史快照或銷售動能的品項寫入
ai_price_recommendations。信心度不以固定倍率灌高,而是由商機分數與證據完整度共同決定,證據包含 PChome match score、歷史快照、銷售/毛利、PChome 商品 ID/名稱、抓取時間與促銷/評價/庫存標籤。每次重算只保留最新 50 品為status='pending',未進榜舊品標為superseded,避免統計與清單超量。 - 寫入策略使用
strategy='product_pick',保留在既有 AI 決策表,不新增假頁面或暫存 JSON。 - 後台入口:
POST /api/ai/product-picks/generate,/ai_intelligence可手動產生清單。 - 配對來源仍以 PChome crawler 真實搜尋結果為準;無競品資料時不生成挑品。
- 比對覆蓋率補強入口:
POST /api/ai/pchome-match/backfill,優先補抓仍無有效 PChome 配對的高價 ACTIVE 商品,完成後自動重算 AI 挑品清單。 - 過期價格刷新入口:
POST /api/ai/pchome-match/refresh-stale,只針對已建立identity_v2但expires_at過期的 PChome product_id 執行run_expired_identity_refresh();不得跑 fresh search recovery,不得呼叫 LLM,完成後重算 AI 挑品並清除 Dashboard / competitor intel cache。 - 過期 identity 搜尋救援入口:
POST /api/ai/pchome-match/recover-stale預設必須關閉主操作入口,僅保留只讀 preview;正式 smoke 顯示小批次成功率不足且耗時偏高時,不得在 Dashboard 顯示日常操作按鈕。若需操作員手動執行,必須先明確設定PCHOME_STALE_RECOVERY_ENABLED=true,再對已過期identity_v2先走既有 PChome product_id refresh;只有舊 ID 查無商品或重評低於門檻時,才允許受控 fresh search recovery。救援隊列必須先排除 variant、catalog、commercial condition、count、bundle、unit-price 與任選 / 多款 / 香味 / 色號 / 即期 / 融燭燈 / 香氛蠟燭 /+/xN/*N/ 具名香味或膚感版本等高風險名稱訊號。這條路徑可抓 PChome,但不得呼叫 LLM;正式寫入仍必須通過 matcher、hard veto、auto price write safety 與 overwrite protection。 - 補抓狀態入口:
GET /api/ai/pchome-match/backfill/status除背景任務狀態外,必須回傳 read-only coverage snapshot:active_with_price/valid_matches/match_rate/fresh_matches/fresh_match_rate/decision_ready_matches/decision_ready_rate/stale_matches/pending/actionable_review_count,供 Dashboard 顯示目前該刷新過期價格或補抓未搜尋商品;此端點不寫 DB、不呼叫 LLM、不抓外站。match_rate是身份覆蓋率,fresh_match_rate是已配對 identity 內的新鮮比例,decision_ready_rate才是可直接進入決策、圖表與簡報的 ACTIVE 商品比價覆蓋率。 - 排程閉環:
run_pchome_match_backfill_task每日 10:30 執行,補抓 PChome 待比對商品、寫入歷史價格,再重算strategy='product_pick'清單。 - PChome / MOMO 競價摘要出口
services/competitor_intel_repository.py使用 30 分鐘共享快取(COMPETITOR_INTEL_CACHE_TTL_SECONDS可調),避免/growth_analysis、/daily_sales、PPT/AI 報表每次請求重跑昂貴覆蓋率與價差趨勢查詢;run_competitor_price_feeder_task與 PChome backfill 完成後會主動清除快取。快取只包摘要輸出,不改 matcher 的高信心門檻與 identity_v2 準確性規則。 - 商品看板第一屏:
/的 V2 看板直接以products、price_records、competitor_prices、competitor_match_attempts、competitor_match_reviews、ai_price_recommendations顯示比對覆蓋率、PChome 優勢、MOMO 威脅、AI 挑品、待比對優先清單與 PChome 覆核隊列;filter=ai_picks可查看 50 品 AI 挑品列表,filter=pchome_review可直接查看需人工處理的比價覆核 SKU,並以 DB 分頁支援 search/category/status 後的完整隊列,不得只截前 50 筆。覆核狀態篩選必須至少包含全部、需單位價、已排除、低信心、價格過期、找不到同款與人工閉環,讓人工可依 matcher 診斷類型分批處理。列內顯示候選 PChome 商品、候選價、match score、單位價換算摘要、人工動作與 matcher 診斷原因標籤(品牌不符、商品線不符、容量差異、組合差異、需單位價、價差極端等),不得只顯示籠統「待比對」。/api/export/excel/pchome-review必須匯出同一套覆核隊列、人工處置、候選 PChome、單位價比較與原始診斷,讓人工覆核、簡報與後續 AI 分析共用同一份證據。/api/pchome-review/<sku>/decision是人工閉環入口:accept_identity才可把候選寫入competitor_prices與competitor_price_history並打上manual_review/manual_accept/identity_v2;reject_identity、unit_price_required與needs_research只寫competitor_match_reviews並追加 manual attempt,不得把不同販售組合或否決候選灌入正式價差。PChome feeder 後續搜尋同一候選時必須讀取competitor_match_reviews:已否決候選寫manual_rejected並跳過正式寫入,且必須繼續評估下一個候選,不能讓已否決候選長期阻塞同 SKU;已標記單位價候選寫manual_unit_price_required;已要求補搜尋候選寫manual_needs_research並停留在覆核隊列;已採用候選可保守補到最低門檻並保留manual_review/manual_accept標籤。搜尋候選池只有強同款分數達0.90才可提前停止,避免 0.76 灰區候選卡掉後續更精準搜尋詞。人工reject_identity、unit_price_required、needs_research若命中當前正式候選,必須將同候選competitor_prices過期,不得繼續顯示正式總價差。商品列表必須將manual_rejected、manual_unit_price_required、manual_needs_research顯示為明確人工閉環狀態,不可回落成籠統「待比對」。fetch_competitor_coverage()必須輸出人工採用、人工否決、人工單位價與採用率,daily/growth/PPT 共用 payload 必須顯示人工閉環成效,避免只呈現待審數。商品看板深度快取同時寫入data/dashboard_full_cache.pkl,供多個 Gunicorn worker 共用,避免部署後各 worker 重複重建 7,000+ 商品統計造成開頁變慢;所有資料異動與 AI 挑品重算都透過clear_dashboard_cache()同步清除記憶體與共享快取,手動重算 API 會立即預熱商品看板快取,避免第一位使用者承擔重建成本。 - PChome re-score 回收線:
rescore_accepted_current只能表示最新版 matcher 判定「值得人工覆核身份」,不可直接寫入正式competitor_prices;no_match、price_basis=none、alert_tier=suppress、variant_selection_review不得進入此隊列。fetch_competitor_coverage()必須輸出rescore_accepted_count,Dashboard、daily/growth 與 OpenClaw 競品摘要都要把「重算待人工覆核」獨立呈現,避免和一般低信心/單位價覆核混在一起。 - PChome 低信心操作分流:Dashboard 與 read-only
/api/pchome-review/queue必須把近門檻可救、證據不足、低信心舊候選拆成recoverable_low_score、true_low_confidence、legacy_low_score三個可篩選桶;廣義low_score僅作 repository/export 相容查詢,不可在 UI 中冒充單一操作分流。 - PChome coverage 的
attempt_status/rescore_accepted_count/actionable_review_count口徑必須與 review queue 對齊:統計「沒有新鮮有效 identity」的商品,而不是只統計「完全沒有 identity」的商品;已過期但可重算採用的 stale identity 仍應出現在待審數字中,避免 API 與 Dashboard 漏報。 run_retryable_candidate_revalidation()的自動回刷主戰場仍限low_score/refresh_low_score/recoverable_low_score;true_low_confidence只有在已補 focused exact 規則的窄範圍品線、舊分數 >= 0.95、comparison_mode='exact_identity'、含strong_exact_spec_match且不含 commercial / variant / count / bundle / refill 等阻擋理由時,才可進入重評,不得全面打開人工審核池。rescore_accepted_current只允許命中具名 focused exact 品線、舊分數 >= 0.76、且仍無 hard veto / 阻擋理由時進窄門回刷;最後仍由最新版 matcher 判定是否可寫正式價差,像不同指甲油型號 / 色號必須 hard veto。- 高分
true_low_confidence的自動救回只能用具名 focused exact 線逐批擴充;同品牌、同品線、同規格/同組合的花美水 Relax、St.Clare 私密呼呼、BIOPEUTIC 果酸、台塑生醫嬰兒沐浴洗髮、Elizabeth Arden 八小時護唇膏與理膚寶水全面修復潤唇膏可走 total-price,色號、香味、款式、即期品與 catalog selection 仍維持 review / veto。 true_low_confidencefocused exact 線必須同步接入run_retryable_candidate_revalidation()的 SQL 窄門,讓舊候選可被批次回收;該窄門只允許具名品線豁免variant_selection_review,其他 hard veto / 型別、款式、香味、件數、組合、refill、commercial condition 阻擋仍不得回刷。- 任選 catalog focused exact 只允許雙方都明確是同品線任選賣場且規格一致的窄範圍案例,例如 FLORTTE 眼線液筆 0.5ml、露得清護手霜 56g 無香/有香、Kanebo ALLIE 持采亮化 UV 防曬水凝乳 60g;若有
commercial_condition_gap(即期品、短效、航空版等狀態差異),focused bypass 不得移除variant_selection_review,不得自動寫正式價差。 - O.P.I 指彩救回只允許同品牌、同
類光繚/如膠似漆指甲油或指彩線,且共享ISL...精準型號 token 的案例自動走 total-price;不同型號/色號仍維持人工或 veto。此規則可接入true_low_confidencerevalidation 窄門,但不得變成「同品線即通過」。 - 其他正式覆核池 focused exact 線只能針對「已在正式頁面反覆出現且有硬規格」的窄範圍族群,例如 The Ordinary 咖啡因 EGCG、Natures Care 綿羊油同入數、TOMOON 指甲剪同尺寸、HH 雙 200ml 組、SEBAMED 200ml x2、YES 9cm 剪刀;同尺寸、同入數、同組合或單側漏規格必須可由 matcher 明確判斷,不能只因同品牌同品線通過。
/api/ai/pchome-match/backfill/status必須把近門檻重評池與過期 identity 救援池以只讀revalidation_preview/stale_recovery_preview曝光給操作員;預覽只復用正式候選 SQL 並受 limit / 60 秒快取限制,不啟動 PChome 搜尋、不呼叫 LLM、不寫competitor_match_attempts/competitor_prices。重評 preview 必須先從最新competitor_match_attempts縮小候選,再用JOIN LATERAL取單一最新 MOMO 價;救援 preview 必須從過期competitor_prices小集合出發並用JOIN LATERAL取最新 MOMO 價,兩者都不得掃全量price_records;Dashboard 只能顯示「可救援」觀測值,不得在未開啟PCHOME_STALE_RECOVERY_ENABLED時提供 recover-stale 執行按鈕;其中review_gated_count僅代表窄門true_low_confidenceexact 候選,不得被解讀為全量人工池可自動回刷。- PChome re-score audit 預設必須先取每個 SKU 的最新
competitor_match_attempts狀態,再套用 status / reason 篩選;舊低信心歷史候選只能透過--include-historical-candidates明確進入考古掃描,避免已入隊、已否決或已修正 SKU 被舊紀錄重新推回報表。 - production re-score
--apply-accepted僅可追加rescore_accepted_currentattempt 給人工覆核;執行後需清除 Dashboard / competitor intel cache,且必須抽查competitor_prices/competitor_price_history未新增正式價差。 - production re-score 若曾把
variant_selection_review追加成rescore_accepted_current,必須用audit_competitor_match_attempt_rescore.py --retract-variant-accepted追加最新true_low_confidence退回列;此路徑只寫competitor_match_attempts,不得刪歷史紀錄,也不得寫competitor_prices/competitor_price_history。 - PChome matcher replay 必須先守住假陽性:
EX8等型號不可被誤解析成x8入數;香氛固體凝膠 / 空氣芳香劑若一側為泛稱、一側含明確香味或 No. 款式,必須走aroma_scent_variant_conflictveto,不得因同品牌同重量直接寫正式價差。 - PChome matcher 對「同規格同數量」的多件組可以安全回收,但必須同時滿足:商品型別完全對齊、品牌同線、規格與數量對齊、沒有 variant / count / bundle / commercial / unit-price / price-ratio 阻擋理由,才可打
safe_multi_component_exact_total_price並進exact / total_price / price_alert_exact;混合組、香味款、色號款、catalog 任選仍需留在identity_review或 veto。護唇品 focused total-price 僅允許已明確建規則的 DHC 純欖 1.5g、FRUDIA 蜂蜜藍莓 10g、SEBAMED 嬰兒護唇膏 4.8g x2、理膚寶水滋養修護潤唇膏 4.7ml,不得把所有 lip/cosmetic catalog 一次放行。 - PChome feeder 正式寫入必須再套一層價格資料閘門:只有
match_type='exact'、price_basis='total_price'、alert_tier='price_alert_exact'且無variant_selection_review的結果可以自動寫入competitor_prices;manual_review/identity_review只能留在覆核隊列或人工採用流程,不得由 retryable replay 或 known identity refresh 自動升成正式價差。Rescore audit 若遇到variant_selection_review,也不得產生accepted_current。
| 角色 | 模型 | 主機 | 成本 | 每日限額 |
|---|---|---|---|---|
| Hermes 分析師 | hermes3:latest / bge-m3 | GCP-A → GCP-B → 111 Ollama | 零 | 無限 |
| NemoTron 派發器 | qwen3:14b;111 fallback 降級 llama3.2;NIM fallback | GCP-A → GCP-B → 111;NVIDIA NIM 備援 | Ollama 零;NIM 配額內免費 | NIM 80 |
| OpenClaw 策略師 | qwen2.5-coder:7b / qwen3:14b;111 fallback 降級 llama3.2 | Ollama-first;Gemini emergency fallback only | Ollama 零;Gemini 預設封鎖 | — |
| ElephantAlpha 編排者 | ElephantAlpha | 依部署環境 | 受控 | HITL / 任務制 |
一之一、AI 自動化閉環實況(2026-04-29)
事件 / 排程失敗 / code review finding
→ EventRouter 分流、去重、降級
→ Hermes L1 摘要或 NemoTron L2 tool calling
→ L2 SAFE_ACTIONS / AutoHeal / OpenClaw memory
→ Telegram 通知,失敗則 file queue,成功後 replay
→ ai_insights + embedding_retry_queue
→ OpenClaw / ElephantAlpha 後續策略與 HITL
硬性邊界:
- EventRouter 是告警與 L2 safe action 的入口。
- AutoHeal 是自癒副作用入口。
momo-db/momo-postgres不可被 AI 自動 restart / stop / recreate。- raw
ai_insightsinsert 必須接enqueue_insight_embedding()或可被 backfill。 - ElephantAlpha 只做編排與 bridge,不可繞過 ADR-011 / ADR-012 / ADR-013。
- ElephantAlpha / NemoTron 不可直接執行商品價格調整;
execute_price_adjustment、adjust_price等動作必須攔截並寫入human_review,等待人工核准。
可觀測性:
/metrics匯出momo_ai_event_router_dispatch_total。/metrics匯出momo_ai_event_router_latency_ms_count/sum/max。/metrics匯出momo_ai_event_router_safe_action_total。/metrics匯出momo_ai_event_router_replay_total。/metrics匯出momo_ai_autoheal_action_total與momo_ai_autoheal_duration_ms_count/sum/max。/metrics在尚無事件時仍輸出momo_ai_*zero-baseline series,讓 Prometheus/Grafana 重啟後可立即看到 metric names。/ai_automation_smoke提供登入後 AI 自動化健康檢查頁。/api/ai-automation/smoke提供 read-only JSON 狀態,不做外部網路呼叫。- 健康檢查 API 會將最近檢查結果保存到 JSONL,頁面顯示最近狀態趨勢。
- 健康檢查歷史支援 JSONL 匯出、清理與每日「正常 / 注意 / 嚴重」摘要。
- 健康檢查每日摘要支援手動 Telegram 推播,並由
momo-scheduler每日 09:10 呼叫run_ai_smoke_daily_summary_task()。 - Grafana provisioning 新增
docker/grafana/provisioning/dashboards/json/ai-automation-overview.json,觀測 EventRouter dispatch/latency、safe action、Telegram replay 與 AutoHeal action/duration。 - Active monitoring stack 使用
monitoring/prometheus.yml的momo-appjob scrapemomo-pro-system:80/metrics;Prometheus container 需加入momo-network。 - Active Blackbox HTTP targets 必須探測
/health(188 stack 目前https://mo.wooo.work/health與http://momo-pro-system:80/health;110 gateway stack 目前https://mo.wooo.work/health),不可探測 Dashboard 首頁/,避免監控流量觸發重型 DB 查詢。 /metrics對realtime_sales_monthly只用 rawSELECT COUNT(*)取得總筆數,避免 ORM schema drift 讓 Prometheus scrape 產生 warning。momo-app必須 bind mount./gunicorn.conf.py:/app/gunicorn.conf.py:ro,讓 CD sync/rebuild 後的 Gunicorn runtime 設定與 repo 保持一致。- Gunicorn runtime 預設
worker_class = gthread、GUNICORN_THREADS=4、preload_app = False;此組合讓 HUP 熱重載可用,也避免 Dashboard 長查詢完全阻塞/health。 - CD rebuild 模式必須先 build image 成功,再短暫 stop/rm/recreate 三應用容器,避免 no-cache build 造成長時間 502。
- ElephantAlpha 使用 NVIDIA NIM hosted API;production 預設模型為
nvidia/llama-3.3-nemotron-super-49b-v1.5,ELEPHANT_ALPHA_FALLBACK_MODELS需保留至少一個可呼叫備援;403/404、408/409/425/429、5xx、timeout 與 connection error 必須嘗試下一個模型。 - ElephantAlpha L3 HITL 只允許發送有實證、可審核、可行動的升級告警;價格類 trigger 無 Hermes 具體威脅時,只記錄 suppressed escalation telemetry 與 cooldown,不寫 pending
human_review,不發 Telegram 空告警。 - ElephantAlpha 價格類 trigger 的 HITL / 決策 prefetch 必須先使用觸發 SQL 與
competitor_prices/price_records的 DB 實證生成 SKU、MOMO / PChome 價差與建議 action lines;完整 Hermes LLM prefetch 預設關閉(ELEPHANT_ALPHA_HERMES_LLM_PREFETCH_ENABLED=false),避免 5s timeout 後落入無實證摘要或雲端備援。若無 DB 實證,只記錄 suppressed telemetry / cooldown,不發 Telegram 空告警。 - ElephantAlpha
price_drop_alert/market_opportunitytrigger 不得對整張price_records做全表最新價聚合;必須先篩最近有效identity_v2PChome 候選,再用 per-SKUJOIN LATERAL讀最新 MOMO 價格,並把match_score、tags、match_diagnostic_json帶入 evidence。 - ElephantAlpha 協調器收到非純 JSON、fenced JSON 或混文字 JSON 時,必須先做容錯抽取;仍無法解析時,只能使用 DB/Hermes 實證生成保守 HITL fallback。fallback 不得放入 OpenClaw
generate_*類舊策略步驟,也不得暗示已自動調價。 - V10.624 起 ElephantAlpha 價格類 trigger 即使信心度達自主門檻,也只能發送 HITL 價格覆核通知;必須跳過 orchestrator
execution_plan內的 Hermes/NemoTron/OpenClaw 長任務 step。這是價格決策護欄:避免 60 秒 execution timeout 卡住 scheduler,也避免把價格策略誤描述為已自動執行。 - ElephantAlpha 執行器若遇到舊版 OpenClaw strategy 類步驟(含
generate_market_strategy/generate_dynamic_pricing_strategy/generate_resource_optimization_strategy),只能記錄為 advisory skipped,不得觸發 circuit breaker,也不得轉成實際排程、外部呼叫或價格行動。 resource_optimization不再交給 LLM 生成「預期效益 / 已執行」敘事,顯示名稱統一為「資源壓力治理」。此 trigger 必須先由程式量測action_plansbacklog、P1/P2 數、pending_review、逾時項目與 CPU load;只有 CPU 達門檻、P1/P2 積壓或逾時積壓才發 Telegram「資源壓力告警」。單純 queue 大但 CPU 正常只記錄 telemetry,不派發 Hermes/NemoTron、不宣稱 48 小時效益;Telegram 段落使用「系統處置紀錄」而非泛稱「已執行」,避免暗示 AI 已完成未經驗證的外部動作。resource_optimization的 Telegram 必須包含decision_envelope區塊,標明source_agent=elephant_alpha、資料品質、量測證據、can_auto_execute=false與 deterministic trace;此路徑不呼叫 Gemini、不呼叫 Hermes/NemoTron,也不得把 queue backlog 翻譯成主機資源耗盡。resource_optimization會先執行ActionPlanHygieneService清理過期噪音:只關閉超過 72 小時的code_review_fix/openclaw_recommendation類 advisory action_plans,以及 NemoTrondirect_response/reply_simple舊聊天回覆計畫;將狀態改為auto_disabled或rejected並寫入metadata_json.hygiene_history。不刪資料,也不碰 NemoTron human_review / pricing / tool action 類業務行動。momo-scheduler每 6 小時固定執行run_action_plan_hygiene_task(),讓過期 advisory action_plans 的關閉不再依賴resource_optimization告警觸發;排程失敗會經 EventRouter 發送action_plan_hygiene_failure。action_plans產生端必須防重:Code Review 同一檔案已有 activecode_review_fix時不重建;OpenClaw recommendation 會寫入文字 fingerprint 並跳過同一建議;AIOrchestrator 不再把 NemoTrondirect_response/reply_simple聊天回覆存成 action plan,真正需工具、審核或執行的 NemoTron action 才能進 queue。- OpenClaw/Hermes embedding 優先呼叫 Ollama
/api/embed,只在舊節點不支援時 fallback/api/embeddings;timeout 由EMBEDDING_TIMEOUT/OLLAMA_EMBED_TIMEOUT控制,並受OLLAMA_EMBED_MAX_TIMEOUT封頂。背景 worker / RAG 查詢不得落 111,除非 caller 顯式允許allow_111_fallback=True。 - PPT 自動產線由
momo-scheduler依節奏執行run_ppt_auto_generation_task(schedule_kind):每日 20:30 產日報、週一 20:40 產週報/市場情報、每月 1 日 20:50 產月報與管理型簡報、季初 21:00 產季報、半年初 21:10 產半年報、年初 21:20 產年報,再交給 22:00ppt_vision_audit做視覺審核;每次嘗試會寫入ppt_generation_runs,/observability/ppt_audit_history以精準參數檢查目標版本是否已產生,並可用/observability/ppt_audit/generate_missing手動補齊缺漏,總開關為PPT_AUTO_GENERATION_ENABLED。PPT vision 需PPT_VISION_ENABLED=true與容器內 LibreOffice;/observability/ppt_audit_file/<filename>會把 PPTX 轉成 PDF 快取供站內線上預覽,原始 PPTX 仍保留下載。QA 失敗項目的「重跑」必須從檔名推回原 report_type,並只失效相同report_type + parameters的 activeppt_reportscache,避免拿到舊 PPT 或誤重跑 daily。
二、真實資料庫 Schema(已校對確認)
2.1 products 表(SQLAlchemy ORM,SQLite/PostgreSQL 通用)
| 欄位 | 型別 | 說明 |
|---|---|---|
id |
Integer PK | 主鍵 |
i_code |
String(50) UNIQUE | MOMO 商品代碼(爬蟲來源,即商品 SKU) |
name |
String(255) | 商品名稱 |
url |
String(500) | MOMO 商品頁 URL |
image_url |
Text | 商品圖片 URL |
category |
String(100) | 分類名稱(直接欄位) |
status |
String(20) | 預設 'ACTIVE' |
created_at |
DateTime | 建立時間 |
updated_at |
DateTime | 更新時間 |
category_id |
Integer FK → categories.id | 分類關聯(可選) |
重要:
i_code= MOMO 網站上的商品 ID(例如I132467614)
2.2 price_records 表
| 欄位 | 型別 | 說明 |
|---|---|---|
id |
Integer PK | 主鍵 |
product_id |
Integer FK → products.id | 商品關聯 |
price |
Float | MOMO 自家售價(爬蟲抓取) |
timestamp |
DateTime indexed | 抓取時間戳 |
⚠️ 架構限制:
price_records只存 MOMO 自家售價,無source欄位,無競品(PChome)價格。 PChome 比價資料必須由外部爬蟲即時抓取,以pchome_prices: dict形式注入HermesAnalystService.run()。
2.3 daily_sales_snapshot 表(PChome 後台業績匯出,動態表)
重要: 此表由
import_service.py使用df.to_sql()動態建立。 欄位名稱完全繼承自 PChome 後台匯出的業績 Excel 原始欄位,加上程式碼追加的snapshot_date。 V10.605 起,匯入器會掃描所有 worksheet 與前 15 列表頭,優先選擇含「日期 / 商品名稱 / 總業績或銷售金額」的明細工作表;格式或日期真的不合格的檔案會移到 Google Drive匯入失敗,避免每 30 分鐘重複告警。
已確認的關鍵欄位(PChome 後台報表欄位名稱)
| 欄位 | 型別 | 說明 | 備注 |
|---|---|---|---|
snapshot_date |
Date | 資料所屬日期(程式追加) | 由 import_service.py 從「日期」欄位解析 |
商品ID |
VARCHAR | PChome 後台 / 訂單目錄商品識別碼 | ⚠️ 不可假設等於 products.i_code |
商品名稱 |
TEXT | 商品名稱 | |
總業績 / 銷售金額 |
NUMERIC | 銷售業績金額 | 匯入器以欄位群組模糊比對,兩者皆可 |
數量 |
NUMERIC | 銷售數量 | |
總成本 |
NUMERIC | 成本 | |
廠商名稱 |
VARCHAR | 廠商名稱 | |
商品館 / 館別 |
VARCHAR | 分類 |
欄位自動偵測邏輯(find_col)
系統使用 keyword 模糊比對,不要求欄位名完全固定:
SKU/商品ID = find_col(['商品ID', 'Product ID', 'ID', 'i_code', 'Item Code'])
商品名稱 = find_col(['商品名稱', '品名', 'Name', 'Product'])
銷售金額 = find_col(['銷售金額', '業績', '金額', 'Amount', 'Sales', 'Total'])
成本 = find_col(['成本', 'Cost', '進價', '總成本'])
數量 = find_col(['銷售數量', '銷量', '數量', 'Qty', 'Quantity'])
日期 = find_col(['日期', '訂單日期', '交易日期', 'Date'])
分類 = find_col(['商品館', '館別', '分類', 'Category'])
2.4 competitor_prices 表(Migration 004 — 已建立)
競品價格快取表,由 competitor_price_feeder.py Worker 寫入,AI Pipeline LEFT JOIN 消費。
| 欄位 | 型別 | 說明 |
|---|---|---|
id |
SERIAL PK | 主鍵 |
sku |
VARCHAR(50) | MOMO 商品代碼(= products.i_code) |
source |
VARCHAR(30) | 競品來源:'pchome'(預留 shopee 等) |
price |
NUMERIC(10,2) | 競品售價 |
original_price |
NUMERIC(10,2) | 競品原價 |
discount_pct |
INTEGER | 折扣 %(NULL=未折扣) |
competitor_product_id |
VARCHAR(100) | PChome 商品 ID |
competitor_product_name |
TEXT | PChome 商品名稱(核對用) |
match_score |
NUMERIC(4,3) | 商品身份比對分數(0~1),< 0.76 不寫入正式快取 |
tags |
JSONB | 語意標籤,如 ["on_sale","discount_20pct"] |
crawled_at |
TIMESTAMP | 爬取時間 |
expires_at |
TIMESTAMP | TTL = crawled_at + 48h(可由 PCHOME_FEEDER_TTL_HOURS 調整),過期後 Hermes 忽略;UI 身份覆蓋率不因價格 TTL 過期歸零 |
UNIQUE: (sku, source) — 同一 SKU+來源只有一筆,ON CONFLICT UPDATE
語意標籤字典:
| 標籤 | 觸發條件 |
|---|---|
on_sale |
PChome is_on_sale = True |
discount_10pct |
折扣 10-19% |
discount_20pct |
折扣 20-29% |
discount_30pct |
折扣 ≥ 30% |
low_stock |
庫存 < 10 |
high_rating |
評分 ≥ 4.5 |
2.5 ai_price_recommendations 表(Migration 003 — 已建立)
此表需執行 migrations/003_ai_price_recommendations.sql 才能完整寫入 DB:
CREATE TABLE IF NOT EXISTS ai_price_recommendations (
id SERIAL PRIMARY KEY,
sku VARCHAR(50) UNIQUE NOT NULL,
name TEXT NOT NULL,
reason TEXT,
status VARCHAR(20) DEFAULT 'pending', -- pending / approved / rejected
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
現狀:
nemoton_dispatcher_service.py的_exec_add_to_recommendation()在 engine 注入且表存在時才寫入,否則只發 Telegram 通知,不會 crash。
三、SQL 漏斗設計(PChome 業績 ID 邊界)
daily_sales_snapshot 來自 PChome 後台業績匯出,商品ID 與 products.i_code 不保證同一套 ID。任何跨表分析必須先經過可驗證的 mapping / identity contract,不能只用字串相等把 PChome 後台銷售資料與 MOMO 商品主檔硬接。
歷史上曾以如下漏斗概念描述近 7 天銷售額下滑,但此 SQL 只能在 ID 已確認同源時使用:
WITH latest_momo_price AS (
-- 從爬蟲商品庫取最新 MOMO 售價
SELECT
p.i_code AS sku, -- MOMO 商品代碼
p.name,
p.category,
pr.price AS momo_price,
ROW_NUMBER() OVER (PARTITION BY p.id ORDER BY pr.timestamp DESC) AS rn
FROM products p
JOIN price_records pr ON pr.product_id = p.id
WHERE p.status = 'ACTIVE'
),
recent_sales AS (
-- 從每日業績快照計算近7天 vs 前7天銷售額
SELECT
"商品ID" AS sku, -- ⚠️ 實際欄位名為「商品ID」(非「商品編號」)
SUM(CASE WHEN snapshot_date >= CURRENT_DATE - 7
THEN COALESCE("銷售金額"::numeric, 0) ELSE 0 END) AS sales_7d_curr,
SUM(CASE WHEN snapshot_date >= CURRENT_DATE - 14
AND snapshot_date < CURRENT_DATE - 7
THEN COALESCE("銷售金額"::numeric, 0) ELSE 0 END) AS sales_7d_prev
FROM daily_sales_snapshot
GROUP BY "商品ID"
)
SELECT lmp.sku, lmp.name, lmp.category, lmp.momo_price,
rs.sales_7d_curr, rs.sales_7d_prev
FROM latest_momo_price lmp
JOIN recent_sales rs ON rs.sku = lmp.sku
WHERE lmp.rn = 1
AND rs.sales_7d_prev > 0
AND (rs.sales_7d_curr - rs.sales_7d_prev) / rs.sales_7d_prev < -0.10
ORDER BY (rs.sales_7d_curr - rs.sales_7d_prev) / rs.sales_7d_prev ASC
LIMIT 300
ID 邊界:
products.i_code是 MOMO 商品主檔 / 價格爬蟲 SKU。daily_sales_snapshot."商品ID"是 PChome 後台業績匯出中的商品識別碼。- 兩者不可被文件、Agent 或報表預設視為相同。需要合併分析時,必須先建立可審核 mapping 或沿用已驗證的 PChome identity / 商品名稱證據。
四、競品價格補給線架構(已實裝)
生產者-消費者解耦設計
[competitor_price_feeder.py Worker] ←← 每 4 小時獨立運行
↓ 搜尋 PChome(search_products)
↓ 商品身份比對(marketplace_product_matcher.py)
↓ 提取語意標籤
↓ UPSERT competitor_prices(預設 TTL 48h)
↓
[HermesAnalystService.fetch_candidates()] ←← AI Pipeline 消費端
↓ LEFT JOIN competitor_prices(零網路等待)
↓ 有效期內(expires_at > NOW())+ match_score ≥ 0.76 + tags 含 identity_v2 才 JOIN
↓ pchome_price + competitor_tags 一起傳給 Hermes
關鍵設計決策
| 決策 | 選擇 | 原因 |
|---|---|---|
| 解耦方式 | DB 表快取(非 Redis) | PostgreSQL 已是核心,無需額外依賴;支援 JOIN |
| TTL | 6 小時 | 與 AI Pipeline 排程週期對齊 |
| 比對算法 | 品牌 + 核心 token + 容量/重量/包數 + 品類 + 價格 sanity check | 由 marketplace_product_matcher.py 統一供 feeder、legacy crawler、AI/PPT 鏈路使用 |
| 最低比對門檻 | 0.76 | 核心比價寧可待審,不允許低信心錯配影響 AI 決策 |
| 已有不同 PChome 商品覆蓋門檻 | 0.84 | 新候選與既有正式配對不同時,除非超高信心,否則寫入 needs_review attempt 不覆蓋 |
| 單位價可比模式 | unit_comparable |
同核心商品但買送/套組/件數不同時,不寫正式總價差;只寫入 attempt,並以單位價證據供 Dashboard / PPT / AI 報表與人工覆核 |
| Browse.sh 診斷 | optional wrapper | 只用於 selector / XHR / network trace 探勘;不得取代正式 crawler,也不得直接把輸出寫成正式競品價格 |
| 語意標籤 | JSONB 陣列 | 傳給 Hermes 提升情境感知品質 |
競品比對邏輯(competitor_price_feeder.py)
MOMO 商品名稱
→ marketplace_product_matcher.build_search_terms()
→ PChomeCrawler.search_products(keyword, limit=12)
→ marketplace_product_matcher.score_marketplace_match()
→ 品牌衝突 / 容量衝突 / 包數衝突 hard veto
→ 同核心但買送/套組/件數不同標記 unit_comparable,不進正式總價差
→ 同款高信心 score ≥ 0.76 才進 competitor_prices
→ 低信心、規格衝突、既有配對衝突寫入 competitor_match_attempts
fetch_candidates() v2 漏斗(已更新)
LEFT JOIN competitor_prices cp
ON cp.sku = lmp.sku
AND cp.source = 'pchome'
AND cp.expires_at > NOW()
AND cp.match_score >= 0.76
AND COALESCE(cp.tags, '[]'::jsonb) ? 'identity_v2'
→ 無競品資料的商品仍回傳,pchome_price=NULL,_batch_analyze 自動跳過
下游消費規範(2026-05-19 更新)
- Dashboard、AI pick、Hermes、Excel export、daily/growth 圖表與 competitor PPT 必須以
competitor_prices + competitor_price_history + competitor_match_attempts為短期唯一生產真相源,且只消費identity_v2matcher 驗證過的配對;舊版僅靠match_score的快取不可直接進入決策或簡報。 pchome_matches與 livepchome_batch()僅保留 legacy compatibility,不得作為新簡報或 AI 決策主來源。services/competitor_intel_repository.py是下游頁面、圖表、簡報的共用查詢出口;新增消費端不得各自硬寫不同 match threshold。所有競品報表的價差方向統一為MOMO - PChome:正值代表 MOMO 較貴 / PChome 低價壓力,負值代表 MOMO 價格優勢;daily、growth、OpenClaw、PPT 不得使用反向定義。- competitor PPT 不可只輸出 matched rows 造成覆蓋率假象;
fetch_competitor_comparison_results()必須用LEFT JOIN valid_competitor保留高營收/高價但尚未有效配對的 MOMO 商品,並帶出match_status、candidate_count、best_match_score與match_diagnostic,讓簡報與 AI 文案明確區分「高信心比對」與「待補身份/價格」。 services/competitor_identity_revalidator.py可對既有competitor_priceslegacy row 離線重跑identity_v2:只有新版 matcher 分數>= 0.76且無 hard veto 才補identity_v2/legacy_revalidatedtags;預設不刷新expires_at,避免過期價格進入決策。CompetitorPriceFeeder.run_expired_identity_refresh()會優先刷新已通過identity_v2但 TTL 過期的 PChome row:直接用既有competitor_product_id批次呼叫 PChome 商品 API,再用新版 matcher 重新驗證名稱/規格/價格 sanity,通過後寫回competitor_prices與competitor_price_history。這條路徑提升新鮮價格覆蓋率,但不降低 match threshold,也不讓過期價格直接進入決策;佇列排序必須先處理既有price_basis_total_price/alert_tier_price_alert_exact或 diagnostic 等價欄位的安全價差 row,再處理需要 review 的舊 row。若既有competitor_product_id已查不到或回傳候選低於門檻,expired refresh 只寫refresh_no_result/ 低信心 attempt 並標記fresh_search_recovery_deferred,不得在同一條價格刷新路徑 fresh search 替換正式 identity。fresh search recovery 只保留給 retryable candidate revalidation / unmatched priority 等補抓路徑。- 過期 identity refresh 排序必須優先
price_basis_total_price/alert_tier_price_alert_exact或match_diagnostic_json.price_basis='total_price'/alert_tier='price_alert_exact'的正式價差配對,再依expires_at與 MOMO 價格排序,避免高風險可決策價差長期排在低價或非告警型 stale row 後面。 marketplace_product_matcher.py的擴充只能走「正向證據 + 反向 veto」:品牌一致、商品線/型號訊號強、價格合理且無 hard veto 時才允許strong_product_line_match加分;補充瓶/補充包/refill 與一般正裝不互相配對,分享組/加量組/明星組等組合包不得誤配單品。- 近門檻規則必須成對補「召回 + 防錯配」測試:可召回者需有品牌、商品線、規格或具名 identity anchor,例如 MUJI 精油芬香護手霜、Mustela 慕之幼爽身潤膚乳、Herbacin 小甘菊護手霜;防錯配者需成為 hard veto,例如 M·A·C Macximal 柔霧/緞光唇膏質地、ERBE 指甲清垢棒/指甲緣刨刀功能、Schick 舒芙/舒綺女用除毛刀品線。不得用單一同規格或同品牌放寬全域門檻。
- 套組/買送/件數不同但品牌、核心商品線與單一基礎規格一致時,matcher 必須回傳
comparison_mode='unit_comparable'與unit_comparablereason;Feeder 只能寫入competitor_match_attempts.attempt_status='unit_comparable'或refresh_unit_comparable,不得寫入competitor_prices。Dashboard 與competitor_intel_repository必須用build_unit_price_comparison()產生每 ml / 每 g / 每入單位價證據,讓 PPT / AI 報表可說明「需單位價比較」而不是把總價當同款價差。商品看板在正式配對尚未成立時,仍必須顯示最佳候選 PChome 商品名稱、候選價與「候選價,需單位換算」說明,讓人工覆核可直接看見下一步;daily/growth、PPT 與 OpenClaw 摘要不得自建查詢,需消費fetch_competitor_review_queue()與 coverage 的unit_comparable_count。若任一側含多個不同容量/重量規格,視為多品項套組,不可進unit_comparable。 - PChome feeder 的外部 request timeout 由
PCHOME_FEEDER_TIMEOUT控制,預設 12 秒;排程不得因單一 PChome 搜尋 API timeout 被拖到數分鐘。 - 品牌 alias 屬於正向身份證據,不是門檻放寬;
DR.WU / DR WU / DRWU / 達爾膚這類同品牌中英混寫必須正規化後再進 matcher,避免同規格真同款被誤降成 brandless identity review。 - 近門檻 rescore pilot 必須支援明確 SKU 篩選;
audit_competitor_match_attempt_rescore.py --sku <sku>可只重算指定 SKU,避免為了小批次驗證而掃整批true_low_confidence。 - 商品看板的 PChome 狀態必須把 matcher 診斷原因翻成可行動語意:品牌不符已排除、規格不符已排除、補充包不相容、組合規格不相容、系列不符已排除、需單位價比較、低信心待補強等,不可只顯示籠統「待比對」或「身份否決」。
- PChome 補抓產線與 priority list 若尚未進入搜尋/補抓,必須顯示「PChome 補抓產線」、「尚未搜尋」與「尚未進入 PChome 補抓」,不得使用「待比對」這類會被誤解成已有候選待人工審核的字眼。
- 商品看板、PChome review queue 與
/api/export/excel/pchome-review必須優先讀取match_diagnostic_json.reasons並轉成操作員可讀標籤;文字版error_message只作 legacy fallback。商品列的 PChome 狀態摘要也必須使用同一套專業標籤,避免 overview 顯示「妝效質地不同」但列表仍顯示籠統身份不符。新增 matcher reason 時需同步更新MATCH_DIAGNOSTIC_REASON_LABELS與 dashboard 狀態翻譯,避免 UI 顯示makeup_finish_conflict這類 machine code。PChome 標題缺品牌但有窄範圍 exact identity anchor 的商品,只能透過具名 brandless recovery 進 manual-review identity;多色任選 / 單一色號 gap 必須標記variant_selection_review,並從recoverable_low_score降回true_low_confidence,不得自動批次寫正式價差。 - Dashboard 必須把「待比對」拆成可診斷狀態:
價格過期待刷新、舊版配對待重驗、低分配對待補強、已排除、需單位價比較、找不到同款、抓取異常、尚未搜尋。硬性不相容候選應顯示為已排除/不相容,不得讓使用者誤以為每筆都需要人工待審。
執行方式
# 手動觸發一輪抓取
python3 services/competitor_price_feeder.py
# 預覽 legacy PChome 快取 identity_v2 重驗證(不寫入)
python3 -m services.competitor_identity_revalidator --limit 500
# 寫入安全通過的 identity_v2 tag;不刷新過期價格
python3 -m services.competitor_identity_revalidator --limit 500 --apply
# 未來整合為 K3s CronJob(每 4 小時)
# k8s/jobs/competitor-price-feeder-cronjob.yaml
五、Telegram 語意化訊息規範(ChatOps 標準)
5.1 核心原則
- 語意化排版 (Semantic Formatting) — Emoji 作為視覺標籤,0.5 秒判斷嚴重性
- 倒金字塔結構 — 結論先行 → 核心數據 → AI 洞察 → 建議行動 → 運算足跡
- 收斂行動呼籲 (Call to Action) — 每則訊息只有一個明確的 👉 建議行動
- 底部運算足跡 — FinOps + Observability,用分隔線隔開主訊息
- EA HITL 專業 brief —
ea_escalation必須分成決策狀態、背景摘要、風險摘要、TOP 待審 SKU 與建議處置;價格類行動不得用長 bullet 串接,必須拆出 MOMO/PChome 價格、價差、人工處置與 PChome ID。 - 價格類決策信封專業 brief —
price_alert、pchome_match_review、competitor_price_review等含 PChome / 價格證據的decision_envelope,EventRouter 必須直送 evidence template,不得進 L1/L2 重摘要;Telegram 內容必須拆成「標的、價格證據、比對證據、人工下一步」,並從信封讀取momo_price、competitor_price、candidate_gap_pct、match_score、unit_price_insight與existing_match_conflict。 - Shared UI 信封摘要共用 — Webcrumbs host data 與其他共用 UI runtime 必須讀
reviewDecisionBrief/decision_envelope的結構化證據,不在瀏覽器端重組比價判斷;這些 payload 只能讀 DB 既有覆核資料,不呼叫 LLM、不抓外站、不寫資料。
5.2 語意化 Emoji 字典
| 類別 | Emoji | 語意 |
|---|---|---|
| 身份識別 | ⚡ NemoTron 派發器 |
Dispatcher 身份 |
| 身份識別 | 🔍 Hermes 3 8B |
Analyst 身份(僅出現在足跡) |
| 風險級別 | 🚨 |
高危險,立即行動 |
| 風險級別 | ⚠️ |
中風險,人工覆核 |
| 風險級別 | 💡 |
低風險,策略建議 |
| 例行報告 | 📊 |
核心數據區塊標頭 |
| 業務屬性 | 💰 |
價格/毛利 |
| 業務屬性 | 📦 |
庫存/銷量 |
| 業務屬性 | 🏆 |
競品情報 |
| AI 洞察 | 🧠 |
AI 分析結果 |
| 運算足跡 | ⚙️ |
FinOps 底部區塊 |
5.3 三大類訊息模板(標準格式)
類別一:緊急告警(trigger_price_alert 觸發)
🚨 [⚡ NemoTron 派發器] 競價高危險預警
⚠️ 核心問題:[A003 舒特膚 AD 乳液] 價格大幅落後競品,訂單流失中!
📊 關鍵數據:
• 我方價格:$1,200
• 競品價格:$980 (價差 22.4%)
• 銷量變化:近七天銷量 -35.0%
🧠 AI 洞察 (信心度 85%):
價差已突破 20% 警戒線,且伴隨實質銷量下滑,高度判定為競品大力促銷攔截。
👉 建議行動:建議立即降價至 $1,000 迎戰,或發放 $200 專屬折價券
─────────────────────
⚙️ 運算足跡:
• 🔍 分析: Hermes 3 8B (GCP-A/GCP-B/111 Ollama) | 耗時: 34.2s | Tokens: 512 | $0 成本
• ⚡ 決策: NemoTron NIM | 185 Tokens | $0 (配額內 2/80)
類別二:人工覆核(flag_for_human_review 觸發)
⚠️ [⚡ NemoTron 派發器] 異常波動需人工覆核
🔍 待查商品:[A001 玻尿酸面膜10片裝]
📊 矛盾數據:
• 價格狀態:無明顯價差 (與競品齊平)
• 異常現象:過去 3 天銷量突然掛零 (平日日均 15 件)
• 庫存狀態:目前庫存充足 (500+ 件)
🧠 AI 洞察 (信心度 45%):
數據出現矛盾訊號,AI 信心不足以自主決策,需人工走查確認。
👉 建議行動:請營運人員立即進行人工走查。
─────────────────────
⚙️ 運算足跡:
• 🔍 分析: Hermes 3 8B (GCP-A/GCP-B/111 Ollama) | 耗時: 34.2s | Tokens: 512 | $0 成本
• ⚡ 決策: NemoTron NIM | 185 Tokens | $0 (配額內 2/80)
類別三:策略執行通知(add_to_recommendation 觸發)
💡 [⚡ NemoTron 派發器] 潛力商品自動佈署
🏆 推薦品項:[A009 美白化妝水150ml] 已自動加入「首頁推薦區塊」
📊 決策依據:
我方價格低於市場 20%,近7天銷量回升,具備流量轉換潛力
🧠 AI 洞察 (信心度 82%):
具備價格競爭優勢,NemoTron 主動提升曝光量以最大化業績。
👉 執行狀態:✅ 系統已自動寫入 ai_price_recommendations 推薦表
─────────────────────
⚙️ 運算足跡:
• 🔍 分析: Hermes 3 8B (GCP-A/GCP-B/111 Ollama) | 耗時: 34.2s | Tokens: 512 | $0 成本
• ⚡ 決策: NemoTron NIM | 185 Tokens | $0 (配額內 2/80)
類別四:Gemini 備援 / 鎖定場景推理週報
... (前文省略) ...
─────────────────────
⚙️ 運算足跡:
• 🔍 彙整: Hermes 3 8B (GCP-A/GCP-B/111 Ollama) | 耗時: 12s | $0 成本
• 🧠 備援/鎖定場景: Gemini 2.5 Flash | 8,420 Tokens | 費用: 約 $0.003 USD
5.4 運算足跡資料來源
| 模型 | API Response 欄位 | 說明 |
|---|---|---|
| Hermes (Ollama) | eval_count, total_duration |
生成 tokens 數 + 推理耗時 (ns→s) |
| NemoTron (NIM) | usage.total_tokens (OpenAI 格式) |
prompt + completion tokens 合計 |
| Gemini | usageMetadata.totalTokenCount |
乘費率算 USD |
程式碼位置:
nemoton_dispatcher_service.py→_build_footprint_block(hermes_stats, nim_stats)
5.5 Inline Keyboard 按鈕(Level 2 互動,待實裝)
當收到類別一緊急告警時,訊息底部附帶互動按鈕(Telegram Bot API inline_keyboard):
[ ✅ 批准降價 ] [ ❌ 拒絕並忽略 ] [ 🔗 查看報表 ]
✅ 批准降價→ 呼叫 MOMO PRO 後台 API 改價 + 決策寫入知識庫❌ 拒絕並忽略→ 決策寫入知識庫(訓練未來在此品類保守點)🔗 查看報表→ 跳轉至 MOMO PRO 該商品數據分析頁
現狀: 尚未實裝,Inline Keyboard 需搭配 Telegram Webhook + callback_query handler
六、Telegram 告警架構
告警群組
- 群組: 小龍蝦 (業務情報專用,非 SRE 維運)
- Chat ID:
-1003940688311 - Bot:
<TELEGRAM_BOT_TOKEN>
單 Bot 多身份策略(One Bot, Multiple Headers)
| 模組 | Telegram 標頭 |
|---|---|
| Hermes 分析師 | [Hermes 分析師] |
| NemoTron 派發器 | [NemoTron 派發器] |
| Gemini 備援 | [Gemini 備援](僅 Ollama 失敗或 ADR-028 鎖定場景) |
三種告警類型
| Tool | 觸發條件 | Telegram 格式 |
|---|---|---|
trigger_price_alert |
HIGH 風險 (gap>15% + 銷量跌>20%) | 🔴/🟡 競價威脅告警 |
add_to_recommendation |
我方價格低於競品且銷量正成長 | ⭐ 推薦商品候選 |
flag_for_human_review |
信心 < 0.6 或情況複雜 | ⚠️ 需要人工審核 |
六、已驗證的服務參數
Hermes 分析師
| 參數 | 值 |
|---|---|
| 模型 | hermes3:latest |
| Ollama URL | GCP-A http://34.87.90.216:11434 → GCP-B http://34.21.145.224:11434 → 111 http://192.168.0.111:11434 |
| Timeout | 120s |
| Temperature | 0.1 |
| 實測推理時間 | 19.3s(3筆,實彈 2026-04-17) |
| TOP_N | 20(每次最多輸出威脅數) |
NemoTron 派發器
| 參數 | 值 |
|---|---|
| 模型 | meta/llama-3.1-8b-instruct |
| NIM API URL | https://integrate.api.nvidia.com/v1 |
| Timeout | 60s |
| 每日配額上限 | 80(留 20 給 AWOOOI) |
| 配額耗盡 fallback | 直接派發 HIGH 風險告警,跳過 NIM |
| 實測 token 消耗 | 1206 tokens/輪(實彈 2026-04-17) |
七、已知技術債與待辦
| 優先 | 項目 | 說明 |
|---|---|---|
| ✅ | Migration 003 + 004 | competitor_prices + ai_price_recommendations 已在 188 momo_analytics 執行 |
| ✅ | 188 生產容器實彈驗證 | Hermes + NIM + PostgreSQL + Telegram 全通 (2026-04-17) |
| ✅ | momo-app port 5001→5002 | docker-registry 佔用 5001,docker-compose.yml 改為 127.0.0.1:5002:80,已 Up healthy |
| ✅ | momo-app 雙網路連線 | 同時連 momo-network + momo-pro_default(後者含 momo-db alias momo-postgres) |
| P1 | PChome Feeder CronJob | competitor_price_feeder.py 每 4 小時排程 (Scheduler 整合) |
| P1 | 告警去重 TTL | 同一 SKU 短期內重複告警未防範 |
| ✅ | daily_sales_snapshot 欄位防禦 |
V10.605 已支援多 worksheet / 表頭列掃描 / 格式失敗隔離 |
| P1 | PChome 後台業績匯出前半段自動化 | 現有 importer 已自動吃 Google Drive;仍需接 PChome 後台排程匯出、Email 附件或受控 browser 下載 |
| P2 | Scheduler 整合 | 每6小時自動觸發 Hermes→NIM→Telegram 管線 |
| P2 | Gemini 備援治理 | 僅保留 ADR-028 鎖定場景與 Ollama 失敗備援,新增 caller 必須走 ADR |
八、部署拓撲(2026-04-17 確認)
實體機器對應
| 服務 | 主機 | 容器名 | 說明 |
|---|---|---|---|
| PostgreSQL | 192.168.0.188 | momo-db |
pgvector/pgvector:pg14,含所有 AI 相關表 |
| momo-app | 192.168.0.188 | momo-pro-system |
Up healthy,port 5002:80(5001 被 docker-registry 佔用,已改 5002) |
| momo-scheduler | 192.168.0.188 | momo-scheduler |
常駐排程容器 |
| Ollama Primary | 34.87.90.216 | Ollama 原生 | GCP-A,AI/LLM/embedding 主路徑 |
| Ollama Secondary | 34.21.145.224 | Ollama 原生 | GCP-B,同等備援 |
| Ollama Fallback | 192.168.0.111 | Ollama 原生 | 最後一道本地防線 |
| E2E 驗證容器 | 192.168.0.188 | momo-e2e-test |
臨時容器,含新服務模組 |
188 /home/ollama/momo-pro/.env 正確設定
TELEGRAM_BOT_TOKEN=<TELEGRAM_BOT_TOKEN> # ← 唯一正確 token
TELEGRAM_CHAT_IDS=["-1003940688311"] # 小龍蝦群組
NVIDIA_API_KEY=<NVIDIA_API_KEY>
USE_POSTGRESQL=true
POSTGRES_HOST=momo-db
# POSTGRES_DB / USER / PASSWORD 使用 docker-compose.yml 預設值
⚠️ Split-brain 陷阱:188 容器環境曾存在舊 bot token
8569720657(不同 bot,不在小龍蝦群組), 已於 2026-04-17 深夜修正為正確 token8610496165。
九、校對歷程
| 日期 | 問題 | 修正 |
|---|---|---|
| 2026-04-17 | fetch_candidates() SQL 使用 "商品編號" |
修正為 "商品ID";2026-06-15 追認此欄位來自 PChome 後台業績匯出,不可預設等於 MOMO products.i_code |
| 2026-04-17 | Hermes gap_pct 由 LLM 計算 → 誤差大 | 改為 Python 預算 (momo-pchome)/pchome*100 |
| 2026-04-17 | 推理時間 52s | 預算 gap_pct 後降至 19.3s (3筆) |
| 2026-04-17 | model_footprint DB 欄位寫入 {} |
分離 footprint_text(Telegram 顯示)與 footprint_data(DB JSON) |
| 2026-04-17 | SQLite 語法 NOW() / datetime('now') / ::jsonb |
全部改為 CURRENT_TIMESTAMP / Python 計算(跨 DB 相容) |
| 2026-04-17 | 本機 SQLite 測試通過但 188 未同步任何檔案 | rsync 推送 6 核心檔案 + 全站 dry-run 對帳 + migrations 跑通 |
| 2026-04-17 | 188 容器無 volume mount,docker cp 臨時解 |
重建 image(COPY . . bake 進新代碼);port 5001 衝突記錄為技術債 |
| 2026-04-17 | 188 .env Telegram token 不正確(split-brain) | 修正為 8610496165,188→Telegram message_id=282 確認 |
| 2026-04-17 | NIM Tool Calling E2E | 真實 NVIDIA_API_KEY 驗證:dispatched=3, errors=[] |
| 2026-05-20 | PChome 商品身份比對仍可能因單一規格重疊誤放行 | V10.312 起 matcher 解析 mg/mcg 劑量、件組套組、多規格集合與同數字不同單位;劑量/容量/重量/件數/品類衝突會硬否決或導向單位價覆核,避免錯配污染 Dashboard、daily/growth、PPT 與 AI 競價分析 |
| 2026-05-20 | 正確 PChome 候選常因只掃第一頁或搜尋詞丟失品牌/規格而未進入 matcher | V10.314 起搜尋 API 依 limit 掃多頁、對暫時性錯誤有限重試;feeder 預設 5 組搜尋詞、20 候選、2 頁,並保留括號/方括號內品牌與規格,提升覆核隊列與正式比價的候選品質 |
| 2026-05-20 | 指定日期競品簡報可能混用目前 competitor_prices 快取價 |
V10.315 起 fetch_competitor_comparison_results() 有 start/end date 時改用 competitor_price_history 期間快照,MOMO 價格取報表結束日前最新價;即時報表才使用目前有效 competitor_prices |
| 2026-05-20 | PChome 覆蓋率分子可能被非活躍或無 MOMO 現價 SKU 膨脹 | V10.317 起 fetch_competitor_coverage() 的 valid_matches 改為 active MOMO latest price 與有效 PChome identity_v2 價格交集,確保 daily/growth/PPT/AI 看到的比價資料品質不被舊快取列高估 |
| 2026-05-20 | EA HITL 告警可能把非 SKU 診斷誤排成待審 SKU,或在缺少 DB/Hermes 實證時打擾人工 | V10.318 起 ea_escalation 僅對含 SKU/價格比較的 actions 使用競價卡片;非 SKU 診斷改為「待確認事項」。價格類低信心事件若無 DB/Hermes 實證,測試鎖定只 suppress、不寫 human_review、不發 Telegram |
| 2026-05-21 | ElephantAlpha NIM/LLM 回應偶爾不是純 JSON,會觸發 json.loads() 失敗並落入舊式空泛策略 fallback |
V10.383 起協調器容忍 fenced/混文字 JSON;無法解析時改用 DB/Hermes 實證 fallback,且 fallback 不再包含 OpenClaw generate_* 舊步驟或自動調價暗示 |
| 2026-05-20 | Telegram HTML parse mode 不支援 <br>,可能導致告警或報告送出 400 |
V10.321 起 Telegram template 發送前會把 <br> / <br/> / <BR /> 轉為換行;保留其他 HTML 標籤,非 HTML parse mode 不改寫 |
| 2026-05-20 | 部分舊 Telegram 入口繞過中央 sanitizer,且 RAG awaiting review 使用錯誤 chat_id= 參數會讓人工審核推播失敗 |
V10.322 起 Bot API price decision 走 send_telegram_with_result();price_decision() 補 report_url 相容並 escape 動態欄位;RAG awaiting review 改用 chat_ids=[...] 呼叫 _send_telegram_raw() |
| 2026-06-25 | UI/UX 不可只修首頁,導覽主入口必須同一套 PChome 業績提升語言 | V10.662 起作戰、分析、營運、AI 助手主入口與廠商缺貨子工具都使用短句對齊「評估、分析、建議、解法、治理」流程;首頁今日行動卡維持 980px 上限與高對比主按鈕,禁止回到全寬長文說明。 |
| 2026-06-25 | 首頁今日行動 CTA 不可被全域 Bootstrap guard 蓋成透明或低對比 | V10.663 起 #commandTaskButton.growth-command-alert-action 使用精準 selector 與 background-color hard override,正式 smoke 必須量測按鈕背景與卡片寬度。 |
| 2026-06-25 | 系統、舊入口與觀測台頁首不可用長篇工程說明取代決策用途 | V10.664 起舊入口、系統管理與 AI 觀測台頁首統一改為短句,聚焦資料新鮮度、成本、品質、RAG 與自癒如何支援 PChome 業績判斷。 |
| 2026-06-25 | 說明、空狀態與登入頁不可停在教學口吻,必須提示下一個業績動作 | V10.665 起 AI 助手說明、業績空狀態、缺貨舊首頁、登入頁與 PPT 視覺 QA 空狀態改為行動導向短句,避免使用者在說明文字中迷路。 |
| 2026-06-25 | PPT 視覺 QA 頁首不可依 runtime 條件退回描述型長句 | V10.666 起 PPT 視覺 QA 主頁首固定顯示「先確認簡報可預覽、可審核,再把問題交給修復流程」,避免 runtime 已就緒時失去行動導向。 |
| 2026-06-25 | 邊角工具頁不可只描述功能,必須說明如何支援 PChome 業績判斷 | V10.667 起 AI 生成歷史、PChome 爬蟲、趨勢資料、用戶管理、外部工具狀態與市場情報停用頁都改為短句,聚焦資料新鮮度、文案回收、權限治理與正式比價流程。 |
| 2026-06-25 | 低頻治理頁與舊入口不可出現英文工程狀態或教學句 | V10.668 起 legacy bridge、維護/權限、匯入、設定、通知模板、缺貨管理、登入歷史、系統日誌與 AI 健康檢查頁統一改為繁中短句,聚焦資料可靠、權限守門、供貨風險與部署治理如何支援業績流程。 |
| 2026-06-25 | 匯入頁不可把資料表流程當成使用者主訊息 | V10.669 起雲端匯入與系統匯入完成訊息改說明業績資料新鮮度與更新筆數,不再用「下載→匯入資料庫→刪除」或資料表名稱作為前台重點。 |
| 2026-06-25 | 可見操作頁不可把權杖、DB、Agent、Pipeline 當成主語 | V10.670 起 AI 助手、日報、銷售分析、缺貨、部署監控與觀測台頁面進一步改用「用量、產出紀錄、AI 分工、部署流程、知識命中」等營運可讀語言。 |
| 2026-06-25 | Google Drive 自動匯入不可在正式排程開瀏覽器 | V10.671 起背景匯入缺少 config/google_token.json 時 fail-closed 並提示一次性授權檔轉換;正式 scheduler 不再嘗試 run_local_server(),且 token refresh 必須能寫回共用 config/ 掛載,避免主機重啟後再次出現 could not locate runnable browser 或授權檔遺失。 |
| 2026-06-25 | 待確認候選必須能一眼比對雙平台賣場 | V10.672 起 MOMO 待確認候選回傳 PChome/MOMO 兩個賣場連結與白話檢核點,前台改成雙欄比對並提供「同時開兩個賣場」,不再顯示 variant_selection_review 等工程 matcher tag。 |
| 2026-06-25 | 待確認候選 API 不可回傳 raw matcher code | V10.673 起 match_reasons 相容欄位也改回白話理由,避免前台或檢視 payload 時再次看到 variant_selection_review、focused_exact_identity_* 等工程代碼。 |
| 2026-06-25 | 共用成長流程列手機版不可溢出畫面 | V10.674 起全站 momo-growth-rail 在手機寬度改為換行呈現,避免「評估 / 分析 / 建議 / 解法 / 治理」流程 chip 超出視覺邊界。 |
| 2026-06-25 | 匯入、缺貨、設定與通知模板頁不可外露 SQL / 資料表 / 模板代碼 | V10.675 起匯入 job API 對前台回傳白話處置訊息,保留 raw error 於 DB/log;自動匯入失敗通知不再顯示 psycopg2、daily_sales_snapshot、snapshot_date 等內部字串;缺貨首頁、系統設定與通知模板列表改用營運語言,並補上逾時匯入任務重置與取消 API。 |
| 2026-06-25 | 觀測台入口與通知預覽不可用工程主語干擾營運判讀 | V10.676 起觀測台導覽統一使用「AI 分工矩陣」,通知模板列表會把 K8s/Pod/資料庫/CI Pipeline 等內部詞轉成服務健康、資料連線與部署流程;主機健康事件與自癒劇本改顯示任務/問題/處置提醒,不直接露 unknown_task、scheduler_task_failure、CODE_FIX 等 raw code。 |
| 2026-06-25 | 部署監控不得用退役正式域名判定失敗 | V10.677 起 CI/CD 狀態 API 與 active blackbox 監控預設以 PUBLIC_URL / PROD_BASE_URL 對齊現行正式入口 https://mo.wooo.work/health,不再把 momo.wooo.work timeout 判成正式部署失敗;Webcrumbs loader fallback 也改為資訊級降級訊號,避免健康頁與 log 產生假紅燈。 |
| 2026-06-25 | 匯入任務公開摘要不得回傳資料表或本機檔案定位 | V10.678 起 /api/import_jobs / /api/import_job/<id> 的 import_summary 只回傳營運摘要、日期範圍、匯入筆數與同步狀態,不再外露 table_name、synced_to、daily_sales_snapshot、realtime_sales_monthly、Google Drive file id 或本機暫存路徑。 |