554 lines
26 KiB
Markdown
554 lines
26 KiB
Markdown
# MOMO PRO — AI 競價情報模組 Single Source of Truth
|
||
|
||
> **最後更新**: 2026-05-01 (台北時間)
|
||
> **狀態**: 🟢 四 AI Agent 自動化閉環已落地 — EventRouter / AutoHeal / OpenClaw Memory / ElephantAlpha bridge / Prometheus metrics / Smoke Dashboard / Smoke Trend Management / Telegram Summary / Grafana provisioning / Prometheus scrape / CD Gunicorn 掛載具測試覆蓋
|
||
> **適用版本**: V10.22 Legacy 5888 入口清理版
|
||
|
||
---
|
||
|
||
## 一、四 AI Agent 路由架構
|
||
|
||
```
|
||
SQL漏斗(~300筆)
|
||
↓
|
||
[Hermes 3 8B] — 分析師 (本地 Ollama, 零成本)
|
||
模型: hermes3:latest @ 192.168.0.111:11434
|
||
任務: 競價威脅分類 → TOP 20 HIGH/MED/LOW
|
||
↓
|
||
[NemoTron NIM] — 派發器 (雲端, 免費配額)
|
||
模型: meta/llama-3.1-8b-instruct @ NVIDIA NIM
|
||
任務: Tool Calling → Telegram 告警 / DB 寫入
|
||
↓
|
||
[OpenClaw / 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 挑品清單。
|
||
- 排程閉環:`run_pchome_match_backfill_task` 每日 10:30 執行,補抓 PChome 待比對商品、寫入歷史價格,再重算 `strategy='product_pick'` 清單。
|
||
- 商品看板第一屏:`/` 的 V2 看板直接以 `products`、`price_records`、`competitor_prices`、`ai_price_recommendations` 顯示比對覆蓋率、PChome 優勢、MOMO 威脅、AI 挑品與待比對優先清單;`filter=ai_picks` 可查看 50 品 AI 挑品列表,並在列表上方顯示平均信心、平均價差、最大價差與估算總價差空間,列表列內顯示 AI 排名與建議理由,且可透過 `/api/export/excel/ai-picks` 匯出 50 品 Excel 操作清單。商品看板深度快取同時寫入 `data/dashboard_full_cache.pkl`,供多個 Gunicorn worker 共用,避免部署後各 worker 重複重建 7,000+ 商品統計造成開頁變慢;所有資料異動與 AI 挑品重算都透過 `clear_dashboard_cache()` 同步清除記憶體與共享快取,手動重算 API 會立即預熱商品看板快取,避免第一位使用者承擔重建成本。
|
||
|
||
| 角色 | 模型 | 主機 | 成本 | 每日限額 |
|
||
|------|------|------|------|---------|
|
||
| Hermes 分析師 | hermes3:latest / embedding model | 192.168.0.111:11434 或 188 Ollama | 零 | 無限 |
|
||
| NemoTron 派發器 | meta/llama-3.1-8b-instruct | NVIDIA NIM | 免費 80/天 | 80 |
|
||
| OpenClaw 策略師 | Gemini | 雲端 | 需審批 | — |
|
||
| ElephantAlpha 編排者 | ElephantAlpha | 依部署環境 | 受控 | HITL / 任務制 |
|
||
|
||
---
|
||
|
||
## 一之一、AI 自動化閉環實況(2026-04-29)
|
||
|
||
```text
|
||
事件 / 排程失敗 / 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_insights` insert 必須接 `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` 提供登入後 smoke dashboard。
|
||
- `/api/ai-automation/smoke` 提供 read-only JSON 狀態,不做外部網路呼叫。
|
||
- Smoke API 會將最近快檢結果保存到 JSONL,dashboard 顯示最近狀態趨勢。
|
||
- Smoke history 支援 JSONL 匯出、清理與每日 OK / Warning / Critical 摘要。
|
||
- Smoke 每日摘要支援手動 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-app` job scrape `momo-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` 只用 raw `SELECT 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 必須嘗試下一個模型。
|
||
- OpenClaw/Hermes embedding 優先呼叫 Ollama `/api/embed`,只在舊節點不支援時 fallback `/api/embeddings`;timeout 由 `EMBEDDING_TIMEOUT` / `OLLAMA_EMBED_TIMEOUT` 控制。
|
||
|
||
---
|
||
|
||
## 二、真實資料庫 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` 表(動態表,從 Excel 匯入)
|
||
|
||
> **重要**: 此表由 `import_service.py` 使用 `df.to_sql()` 動態建立。
|
||
> 欄位名稱**完全繼承自匯入的 MOMO Excel 報表原始欄位**,加上程式碼追加的 `snapshot_date`。
|
||
|
||
#### 已確認的關鍵欄位(實際 MOMO 報表欄位名稱)
|
||
|
||
| 欄位 | 型別 | 說明 | 備注 |
|
||
|------|------|------|------|
|
||
| `snapshot_date` | Date | 資料所屬日期(程式追加) | 由 `import_service.py` 從「日期」欄位解析 |
|
||
| `商品ID` | VARCHAR | **商品識別碼**(= `products.i_code`) | ⚠️ 非 `商品編號`!|
|
||
| `商品名稱` | TEXT | 商品名稱 | |
|
||
| `銷售金額` | NUMERIC | 銷售業績金額 | 系統以 find_col 模糊比對,優先 `銷售金額` |
|
||
| `數量` | NUMERIC | 銷售數量 | |
|
||
| `總成本` | NUMERIC | 成本 | |
|
||
| `廠商名稱` | VARCHAR | 廠商名稱 | |
|
||
| `商品館` / `館別` | VARCHAR | 分類 | |
|
||
|
||
#### 欄位自動偵測邏輯(`find_col`)
|
||
|
||
系統使用 keyword 模糊比對,**不要求欄位名完全固定**:
|
||
|
||
```python
|
||
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.45 不寫入 |
|
||
| `tags` | JSONB | 語意標籤,如 `["on_sale","discount_20pct"]` |
|
||
| `crawled_at` | TIMESTAMP | 爬取時間 |
|
||
| `expires_at` | TIMESTAMP | TTL = crawled_at + 6h,過期後 Hermes 忽略 |
|
||
|
||
**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:
|
||
|
||
```sql
|
||
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 漏斗設計(已修正欄位名稱)
|
||
|
||
`hermes_analyst_service.py` → `fetch_candidates()` 的核心 SQL:
|
||
|
||
```sql
|
||
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
|
||
```
|
||
|
||
**漏斗效果**: 226萬筆 price_records → ~300 筆(近7天銷量跌幅 > 10% 的活躍商品)
|
||
|
||
**JOIN 邏輯**:
|
||
- `products.i_code` ↔ `daily_sales_snapshot."商品ID"` — 均為 MOMO 商品代碼,格式相同
|
||
|
||
---
|
||
|
||
## 四、競品價格補給線架構(已實裝)
|
||
|
||
### 生產者-消費者解耦設計
|
||
|
||
```
|
||
[competitor_price_feeder.py Worker] ←← 每 4 小時獨立運行
|
||
↓ 搜尋 PChome(search_products)
|
||
↓ 模糊比對(price_comparison.py)
|
||
↓ 提取語意標籤
|
||
↓ UPSERT competitor_prices(TTL 6h)
|
||
↓
|
||
[HermesAnalystService.fetch_candidates()] ←← AI Pipeline 消費端
|
||
↓ LEFT JOIN competitor_prices(零網路等待)
|
||
↓ 有效期內(expires_at > NOW())+ match_score ≥ 0.45 才 JOIN
|
||
↓ pchome_price + competitor_tags 一起傳給 Hermes
|
||
```
|
||
|
||
### 關鍵設計決策
|
||
|
||
| 決策 | 選擇 | 原因 |
|
||
|------|------|------|
|
||
| 解耦方式 | DB 表快取(非 Redis) | PostgreSQL 已是核心,無需額外依賴;支援 JOIN |
|
||
| TTL | 6 小時 | 與 AI Pipeline 排程週期對齊 |
|
||
| 比對算法 | 品牌(0.4) + 規格(0.3) + 關鍵字(0.3) | 依賴現有 `price_comparison.py` |
|
||
| 最低比對門檻 | 0.45 | 低於此分數不寫入,避免張冠李戴影響 AI 決策 |
|
||
| 語意標籤 | JSONB 陣列 | 傳給 Hermes 提升情境感知品質 |
|
||
|
||
### 競品比對邏輯(`competitor_price_feeder.py`)
|
||
|
||
```
|
||
MOMO 商品名稱[:20字]
|
||
→ PChomeCrawler.search_products(keyword, limit=10)
|
||
→ _find_best_match(momo_name, results)
|
||
→ ProductNameParser(品牌 + 規格 + 關鍵字)
|
||
→ _structural_similarity() → score
|
||
→ score ≥ 0.45 → _upsert_competitor_price()
|
||
```
|
||
|
||
### `fetch_candidates()` v2 漏斗(已更新)
|
||
|
||
```sql
|
||
LEFT JOIN competitor_prices cp
|
||
ON cp.sku = lmp.sku
|
||
AND cp.source = 'pchome'
|
||
AND cp.expires_at > NOW()
|
||
AND cp.match_score >= 0.45
|
||
```
|
||
→ 無競品資料的商品仍回傳,`pchome_price=NULL`,`_batch_analyze` 自動跳過
|
||
|
||
### 執行方式
|
||
|
||
```bash
|
||
# 手動觸發一輪抓取
|
||
python3 services/competitor_price_feeder.py
|
||
|
||
# 未來整合為 K3s CronJob(每 4 小時)
|
||
# k8s/jobs/competitor-price-feeder-cronjob.yaml
|
||
```
|
||
|
||
---
|
||
|
||
## 五、Telegram 語意化訊息規範(ChatOps 標準)
|
||
|
||
### 5.1 核心原則
|
||
|
||
1. **語意化排版 (Semantic Formatting)** — Emoji 作為視覺標籤,0.5 秒判斷嚴重性
|
||
2. **倒金字塔結構** — 結論先行 → 核心數據 → AI 洞察 → 建議行動 → 運算足跡
|
||
3. **收斂行動呼籲 (Call to Action)** — 每則訊息只有一個明確的 👉 建議行動
|
||
4. **底部運算足跡** — FinOps + Observability,用分隔線隔開主訊息
|
||
|
||
### 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 (本地 111) | 耗時: 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 (本地 111) | 耗時: 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 (本地 111) | 耗時: 34.2s | Tokens: 512 | $0 成本
|
||
• ⚡ 決策: NemoTron NIM | 185 Tokens | $0 (配額內 2/80)
|
||
```
|
||
|
||
#### 類別四(未來):Gemini 雲端推理週報
|
||
|
||
```
|
||
... (前文省略) ...
|
||
─────────────────────
|
||
⚙️ 運算足跡:
|
||
• 🔍 彙整: Hermes 3 8B (本地 111) | 耗時: 12s | $0 成本
|
||
• 🧠 推理: Gemini 1.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: `8610496165:AAFOlcWV4oRUSC2TI-fYux7JV97fjNzsYR8`
|
||
|
||
### 單 Bot 多身份策略(One Bot, Multiple Headers)
|
||
| 模組 | Telegram 標頭 |
|
||
|------|--------------|
|
||
| Hermes 分析師 | `[Hermes 分析師]` |
|
||
| NemoTron 派發器 | `[NemoTron 派發器]` |
|
||
| Gemini 策略師 | `[Gemini 策略師]` (未來) |
|
||
|
||
### 三種告警類型
|
||
| Tool | 觸發條件 | Telegram 格式 |
|
||
|------|---------|--------------|
|
||
| `trigger_price_alert` | HIGH 風險 (gap>15% + 銷量跌>20%) | 🔴/🟡 競價威脅告警 |
|
||
| `add_to_recommendation` | 我方價格低於競品且銷量正成長 | ⭐ 推薦商品候選 |
|
||
| `flag_for_human_review` | 信心 < 0.6 或情況複雜 | ⚠️ 需要人工審核 |
|
||
|
||
---
|
||
|
||
## 六、已驗證的服務參數
|
||
|
||
### Hermes 分析師
|
||
| 參數 | 值 |
|
||
|------|---|
|
||
| 模型 | `hermes3:latest` |
|
||
| Ollama URL | `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 短期內重複告警未防範 |
|
||
| P1 | `daily_sales_snapshot` 欄位防禦 | 若 Excel 欄位名變更,JOIN 條件會靜默失效 |
|
||
| P2 | Scheduler 整合 | 每6小時自動觸發 Hermes→NIM→Telegram 管線 |
|
||
| P2 | Gemini 策略師 | 週報生成(需費用審批後實作) |
|
||
|
||
---
|
||
|
||
## 八、部署拓撲(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` | 常駐排程容器 |
|
||
| Hermes 3 8B | 192.168.0.111 | Ollama 原生 | `hermes3:latest`,E2E 可達 |
|
||
| E2E 驗證容器 | 192.168.0.188 | `momo-e2e-test` | 臨時容器,含新服務模組 |
|
||
|
||
### 188 `/home/ollama/momo-pro/.env` 正確設定
|
||
|
||
```bash
|
||
TELEGRAM_BOT_TOKEN=8610496165:AAFOlcWV4oRUSC2TI-fYux7JV97fjNzsYR8 # ← 唯一正確 token
|
||
TELEGRAM_CHAT_IDS=["-1003940688311"] # 小龍蝦群組
|
||
NVIDIA_API_KEY=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
|
||
USE_POSTGRESQL=true
|
||
POSTGRES_HOST=momo-db
|
||
# POSTGRES_DB / USER / PASSWORD 使用 docker-compose.yml 預設值
|
||
```
|
||
|
||
> ⚠️ **Split-brain 陷阱**:188 容器環境曾存在舊 bot token `8569720657`(不同 bot,不在小龍蝦群組),
|
||
> 已於 2026-04-17 深夜修正為正確 token `8610496165`。
|
||
|
||
---
|
||
|
||
## 九、校對歷程
|
||
|
||
| 日期 | 問題 | 修正 |
|
||
|------|------|------|
|
||
| 2026-04-17 | `fetch_candidates()` SQL 使用 `"商品編號"` | 修正為 `"商品ID"`(與 MOMO Excel 實際欄位名一致) |
|
||
| 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=[] |
|