V10.569 串接 Webcrumbs 比價信封摘要
All checks were successful
CD Pipeline / deploy (push) Successful in 1m8s
All checks were successful
CD Pipeline / deploy (push) Successful in 1m8s
This commit is contained in:
@@ -141,6 +141,7 @@
|
||||
- AI 自動化 Session SOP: `docs/guides/ai_automation_session_sop.md`
|
||||
- Browse.sh 爬蟲診斷手冊: `docs/guides/browse_sh_crawler_playbook.md`
|
||||
- Webcrumbs 共用 UI Runtime: `docs/guides/webcrumbs_shared_runtime.md`
|
||||
- 外部專業 Benchmark: `docs/guides/external_professional_benchmark.md`
|
||||
- AI 競價情報 SOT: `docs/AI_INTELLIGENCE_MODULE_SOT.md`
|
||||
- Agent 角色矩陣: `docs/guides/codex_agent_roles.md`
|
||||
- ADR 索引: `docs/adr/README.md`
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
================================================================================
|
||||
|
||||
【已完成】
|
||||
- 外部專業 benchmark 固定節奏:已建立每週一 09:30 自動檢視,並新增 `docs/guides/external_professional_benchmark.md`,把 Google Merchant Center、Google Product structured data、Schema.org Product/Offer/AggregateOffer 與 Baymard 電商 UX 做法轉成可落地準則:identity evidence、fresh offer、review 差異高亮、PPT/AI evidence 分層。
|
||||
- V10.565 補 PChome 覆蓋率操作建議:`/api/ai/pchome-match/backfill/status` 會把低覆蓋率拆成 `operation_backlog`,分別列出刷新舊 identity、重評近門檻、補抓未配對、人工覆核、單位價覆核與過期搜尋救援預覽;同時回傳 `recommended_next_action`,Dashboard 狀態摘要會顯示「建議執行比價補強 / 刷新過期 identity / 處理覆核」等下一步,讓覆蓋率 KPI 直接連到可執行行動。
|
||||
- V10.563 收斂正式 preview 假可救候選:M.A.C 超持妝輕透濾鏡蜜粉若只有 PChome 端出現明確色號(例如 `#絕絕紫`),會標成 `variant_selection_review` 並維持 `true_low_confidence`,不再佔 recoverable 池;SAUGELLA 賽吉兒菁萃潔浴凝露新增潤澤 / 日用型 / 加強 / 黃金女郎型變體互斥,避免同品線不同私密清潔款式被誤救成 matched。
|
||||
- V10.566 新增市場情報 Professional Source Governance:把 robots/REP、sitemap/lastmod、JSON-LD / schema.org structured data、canonical URL、rate limit、公開資料邊界、provenance、snapshot hash 與 idempotency key 變成可審核 source contract。新增 `/api/market_intel/mcp_professional_source_governance` 與市場情報頁卡片、deployment readiness smoke target;API/UI 只審核操作員貼回的治理摘要,不抓外站、不讀 robots/sitemap、不開 DB、不寫檔、不掛 scheduler,後續 fetch target review 才能引用通過治理的來源。
|
||||
|
||||
@@ -402,7 +402,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '')
|
||||
# ==========================================
|
||||
# 系統版本與路徑
|
||||
# ==========================================
|
||||
SYSTEM_VERSION = "V10.568"
|
||||
SYSTEM_VERSION = "V10.569"
|
||||
LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log')
|
||||
public_url = PUBLIC_URL # 用於模板顯示
|
||||
|
||||
|
||||
@@ -58,6 +58,7 @@
|
||||
- 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 數量;不得另寫一套 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 路由架構
|
||||
@@ -445,6 +446,7 @@ python3 -m services.competitor_identity_revalidator --limit 500 --apply
|
||||
4. **底部運算足跡** — FinOps + Observability,用分隔線隔開主訊息
|
||||
5. **EA HITL 專業 brief** — `ea_escalation` 必須分成決策狀態、背景摘要、風險摘要、TOP 待審 SKU 與建議處置;價格類行動不得用長 bullet 串接,必須拆出 MOMO/PChome 價格、價差、人工處置與 PChome ID。
|
||||
6. **價格類決策信封專業 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`。
|
||||
7. **Shared UI 信封摘要共用** — Webcrumbs host data 與其他共用 UI runtime 必須讀 `reviewDecisionBrief` / `decision_envelope` 的結構化證據,不在瀏覽器端重組比價判斷;這些 payload 只能讀 DB 既有覆核資料,不呼叫 LLM、不抓外站、不寫資料。
|
||||
|
||||
### 5.2 語意化 Emoji 字典
|
||||
|
||||
|
||||
71
docs/guides/external_professional_benchmark.md
Normal file
71
docs/guides/external_professional_benchmark.md
Normal file
@@ -0,0 +1,71 @@
|
||||
# 外部專業做法 Benchmark
|
||||
|
||||
> 用途:定期把外部電商、商品資料與 UX 專業做法轉成 EwoooC / MOMO Pro 的可執行產品準則。
|
||||
|
||||
## 固定節奏
|
||||
|
||||
- 每週一 09:30 執行外部 benchmark,自動輸出可落地建議。
|
||||
- 只採用能改善核心價值的做法:商品身份比對準確率、可用比價覆蓋率、價格新鮮度、人工覆核效率、競品情報決策品質。
|
||||
- 外部資料必須保留來源、讀取日期、觀察結論與不採用原因。
|
||||
|
||||
## 2026-06-02 初始觀察
|
||||
|
||||
### 1. 商品 identity 必須優先吃結構化 identifiers
|
||||
|
||||
Google Merchant Center 的商品資料規格把 `id`、`brand`、`gtin`、`mpn`、`price`、`availability` 視為商品資料核心;Schema.org / Google Product structured data 也把 `Product`、`Offer`、`AggregateOffer`、`sku`、`gtin`、`brand`、`price`、`availability` 放在商品與報價語意中心。
|
||||
|
||||
落地到本產品:
|
||||
|
||||
- 比對引擎不能只靠商品名稱 token;應逐步建立 `identity_evidence` 欄位,分層保存品牌、SKU、GTIN/條碼、MPN/型號、容量、入數、色號、香味、款式。
|
||||
- 若雙方有 GTIN / MPN / 明確型號,應優先作為 strong evidence。
|
||||
- 若缺 GTIN / MPN,不得自動推定同款;要清楚標示 `identifier_missing` 或 `identifier_weak`。
|
||||
|
||||
### 2. 價格可用性必須和 freshness 綁在一起
|
||||
|
||||
Google Merchant Center 要求價格與庫存狀態要和 landing page / checkout 保持一致;Schema.org Offer 也有 `price`、`priceCurrency`、`availability` 等報價欄位。
|
||||
|
||||
落地到本產品:
|
||||
|
||||
- `decision_ready` 只能計入明確未過期價格,不應把未知 freshness 當可決策。
|
||||
- Dashboard 必須拆開 identity coverage、fresh price coverage、pending identity、stale identity。
|
||||
- 目前 V10.549-V10.565 的方向正確:未知新鮮度不得灌高覆蓋率,並要進刷新/救援流程。
|
||||
|
||||
### 3. 多 offer / 多平台比價應該呈現為 offer evidence,不只是單一低價
|
||||
|
||||
Schema.org `AggregateOffer` 用於同一商品對應多個商家 offer。這個概念適合我們把 MOMO / PChome 的同款證據與價格證據分開保存。
|
||||
|
||||
落地到本產品:
|
||||
|
||||
- `competitor_prices` 應逐步從單一 match row,演進成「identity pair + offer snapshot」兩層。
|
||||
- PPT / AI 決策不只顯示價差,也要顯示 identity confidence、freshness、offer source、last crawled、manual review state。
|
||||
|
||||
### 4. Product comparison UX 要讓使用者比較規格差異
|
||||
|
||||
Baymard 的商品頁與比較 UX 研究強調:使用者需要清楚的 product comparison,尤其是規格驅動品類。
|
||||
|
||||
落地到本產品:
|
||||
|
||||
- 人工覆核頁不能只列 MOMO/PChome 名稱與價格;要突出「不一致欄位」:色號、香味、容量、入數、套組、任選、效期、航空版。
|
||||
- 對 `identity_veto` / `true_low_confidence` 要顯示人可以理解的原因,不只顯示 `待審`。
|
||||
- Dashboard 建議下一步要直接連到對應操作:刷新、補抓、重評、單位價覆核、人工覆核。
|
||||
|
||||
## 目前不採用
|
||||
|
||||
- 不採用「只靠低價/高相似度自動配對」:價格相近不是 identity evidence。
|
||||
- 不採用「大量放寬 threshold 來拉覆蓋率」:會污染核心比價資料。
|
||||
- 不採用「把外部網站 UI 風格直接照搬」:只吸收資訊架構、證據呈現與工作流做法。
|
||||
|
||||
## 下一步 TODO 候選
|
||||
|
||||
1. 建立 `identity_evidence` 正規化 payload,讓 matcher 回傳 identifier/spec/variant evidence。
|
||||
2. 在覆核頁新增差異高亮:色號、香味、容量、入數、任選、效期、來源新鮮度。
|
||||
3. 將 PPT / AI payload 的比價項目拆成 identity evidence 與 offer evidence。
|
||||
4. 每週 benchmark 結果若命中上述 TODO,回寫 `TODO_NEXT_STEPS.txt` 或新增 ADR / memory。
|
||||
|
||||
## 參考來源
|
||||
|
||||
- Google Merchant Center Product data specification: https://support.google.com/merchants/answer/7052112
|
||||
- Google Search Central Product structured data: https://developers.google.com/search/docs/appearance/structured-data/product
|
||||
- Schema.org Product / Offer / AggregateOffer: https://schema.org/Product, https://schema.org/Offer, https://schema.org/AggregateOffer
|
||||
- Baymard Product Page UX Best Practices: https://baymard.com/blog/current-state-ecommerce-product-page-ux
|
||||
- Baymard Product Comparison UX: https://baymard.com/blog/provide-comparison-features
|
||||
@@ -106,6 +106,7 @@
|
||||
- 2026-06-01 起,`V10.566` 新增市場情報 Professional Source Governance gate:將 robots/REP、sitemap/lastmod、JSON-LD / schema.org structured data、canonical URL、rate limit、公開資料邊界、provenance、snapshot hash 與 idempotency key 納入 source contract,並接上 `/api/market_intel/mcp_professional_source_governance`、UI preview panel、deployment readiness check 與 production smoke target;仍不抓外站、不讀 robots/sitemap、不開 DB、不寫檔、不掛 scheduler。
|
||||
- 2026-06-02 起,`V10.567` 將 MCP 市場洞察 fallback 收斂為 GCP-A / GCP-B only,不再讓 111 承接非即時市場分析長任務;預設 timeout 25 秒、`num_predict` 500,GCP 不可用時直接保守降級,避免 Elephant Alpha 60 秒 timeout 與 111 負載尖峰。
|
||||
- 2026-06-02 起,`V10.568` 將價格類 `decision_envelope` 的 Telegram 直送訊息改為專業 brief:標的、價格證據、比對證據、人工下一步四段式;review queue 信封 subject 同步帶 `momo_price` / `competitor_price`,讓 Telegram、PPT、Webcrumbs 與 AI 摘要共用價格證據。
|
||||
- 2026-06-02 起,`V10.569` 將 Webcrumbs host data 串到 `summarize_review_decision_envelopes()`,payload 新增 `reviewDecisionBrief` 與 review queue / HITL / auto-execute-blocked metadata;共用 UI runtime 讀同一份 PChome 覆核信封摘要,仍只讀 DB、不呼叫 LLM、不抓外站、不寫資料。
|
||||
|
||||
## 3. 12 Agent 決策信封整合
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
## 📅 詳細更新日誌 (考古存檔)
|
||||
|
||||
### 2026-06-01:PChome 比價新鮮度操作閉環
|
||||
- **V10.569 Webcrumbs 比價信封摘要串接**: `build_webcrumbs_marketplace_host_data()` 讀取 `fetch_competitor_review_queue()` 後統一走 `summarize_review_decision_envelopes()`,在 host data payload 輸出 `reviewDecisionBrief`,並於 metadata 增加 `review_queue_count`、`hitl_count`、`auto_execute_blocked_count` 與 `decision_envelope_source`。Webcrumbs / Shared UI 現在和 Telegram、OpenClaw、PPT 共用同一份 PChome 覆核信封摘要,仍維持只讀、不呼叫 LLM、不抓外站、不寫 DB;同版收錄 `docs/guides/external_professional_benchmark.md` 作為外部專業做法週巡檢落地準則入口。
|
||||
- **V10.568 價格類決策信封專業 brief**: `decision_envelope` 的價格 / PChome 覆核事件在 Telegram EventRouter 直送時,改以「標的、價格證據、比對證據、人工下一步」四段式排版呈現,保留 `momo:eig:` 忽略按鈕且不進 L1/L2 AI 重摘要。`competitor_intel_repository` 同步在 review queue 信封 subject 補上 `momo_price` / `competitor_price`,讓 Telegram、PPT、Webcrumbs 與 AI 摘要可共用同一份價格證據,不再各自補查或重組。
|
||||
- **V10.567 MCP 市場洞察 GCP-only fallback**: `MCPCollectorService._ollama_topic_fallback()` 改成只使用 GCP-A / GCP-B Ollama,失敗後保守回本地 fallback,不再把市場洞察長分析轉嫁到 111。預設 timeout 收斂為 25 秒、`num_predict` 收斂為 500,避免 Elephant Alpha 在 GCP-A/GCP-B 短暫不可用時撞 60 秒總上限或造成 111 負載尖峰;Gemini 仍維持 `GEMINI_API_HARD_DISABLED=true` 預設硬封鎖。
|
||||
- **V10.565 PChome 覆蓋率操作建議**: 補強 `/api/ai/pchome-match/backfill/status`,將低覆蓋率拆成 `operation_backlog`:刷新舊 identity、重評近門檻、補抓未配對、人工覆核、單位價覆核與過期搜尋救援預覽;並新增 `recommended_next_action`,Dashboard 狀態摘要會直接顯示建議下一步,避免使用者只看到低覆蓋率卻不知道該按哪條產線。
|
||||
|
||||
@@ -14,7 +14,9 @@ from typing import Any
|
||||
|
||||
from services.competitor_intel_repository import (
|
||||
fetch_competitor_coverage,
|
||||
fetch_competitor_review_queue,
|
||||
fetch_top_competitor_risks,
|
||||
summarize_review_decision_envelopes,
|
||||
)
|
||||
|
||||
|
||||
@@ -99,6 +101,27 @@ def _coverage_metadata(coverage: dict, row_count: int) -> dict:
|
||||
}
|
||||
|
||||
|
||||
def _review_decision_brief(engine, limit: int) -> dict:
|
||||
"""Read the shared review decision brief without blocking host-data preview."""
|
||||
try:
|
||||
review_queue = fetch_competitor_review_queue(engine, limit=min(max(limit, 1), 8))
|
||||
return summarize_review_decision_envelopes(review_queue, limit=min(max(limit, 1), 5))
|
||||
except Exception:
|
||||
return summarize_review_decision_envelopes([], limit=min(max(limit, 1), 5))
|
||||
|
||||
|
||||
def _attach_review_brief(payload: dict, review_brief: dict) -> dict:
|
||||
payload["reviewDecisionBrief"] = review_brief
|
||||
metadata = payload.setdefault("metadata", {})
|
||||
metadata["review_queue_count"] = len(review_brief.get("items") or [])
|
||||
metadata["hitl_count"] = int(review_brief.get("hitl_count") or 0)
|
||||
metadata["auto_execute_blocked_count"] = int(
|
||||
review_brief.get("auto_execute_blocked_count") or 0
|
||||
)
|
||||
metadata["decision_envelope_source"] = "competitor_intel_repository"
|
||||
return payload
|
||||
|
||||
|
||||
def build_webcrumbs_marketplace_host_data(engine=None, limit: int = 5) -> dict:
|
||||
"""Build read-only host data for the Webcrumbs diagnostic page.
|
||||
|
||||
@@ -117,11 +140,12 @@ def build_webcrumbs_marketplace_host_data(engine=None, limit: int = 5) -> dict:
|
||||
raw_risks = fetch_top_competitor_risks(engine, limit=max(limit * 4, 20)) or []
|
||||
risks = [item for item in raw_risks if _is_direct_price_alert(item)][:limit]
|
||||
coverage = fetch_competitor_coverage(engine) or {}
|
||||
review_brief = _review_decision_brief(engine, limit=limit)
|
||||
|
||||
if not risks:
|
||||
payload = _empty_payload("no_current_exact_risk")
|
||||
payload["metadata"] = _coverage_metadata(coverage, row_count=0)
|
||||
return payload
|
||||
return _attach_review_brief(payload, review_brief)
|
||||
|
||||
rows = []
|
||||
for item in risks[:limit]:
|
||||
@@ -146,7 +170,7 @@ def build_webcrumbs_marketplace_host_data(engine=None, limit: int = 5) -> dict:
|
||||
sku = str(top.get("sku") or "-")
|
||||
evidence_refs = ["competitor_prices", "price_records", "exact", "total_price", "price_alert_exact"]
|
||||
|
||||
return {
|
||||
payload = {
|
||||
"marketSnapshot": rows,
|
||||
"aiCandidate": {
|
||||
"ticker": sku,
|
||||
@@ -165,3 +189,4 @@ def build_webcrumbs_marketplace_host_data(engine=None, limit: int = 5) -> dict:
|
||||
},
|
||||
"metadata": _coverage_metadata(coverage, row_count=len(rows)),
|
||||
}
|
||||
return _attach_review_brief(payload, review_brief)
|
||||
|
||||
@@ -49,6 +49,43 @@ def test_webcrumbs_host_data_maps_price_alert_exact_rows(monkeypatch):
|
||||
"pending": 612,
|
||||
},
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
svc,
|
||||
"fetch_competitor_review_queue",
|
||||
lambda passed_engine, limit: [
|
||||
{
|
||||
"sku": "SKU-REVIEW",
|
||||
"name": "人工覆核精華液",
|
||||
"decision_envelope": {
|
||||
"decision_id": "review_queue:SKU-REVIEW",
|
||||
"severity": "P2",
|
||||
"subject": {
|
||||
"sku": "SKU-REVIEW",
|
||||
"name": "人工覆核精華液",
|
||||
"momo_price": 399,
|
||||
"competitor_price": 329,
|
||||
"competitor_product_id": "PCH-REVIEW",
|
||||
},
|
||||
"recommended_action": {
|
||||
"action": "review_accept_identity",
|
||||
"requires_hitl": True,
|
||||
},
|
||||
"expected_impact": {"candidate_gap_pct": 21.3},
|
||||
"guardrails": {
|
||||
"data_quality": "complete",
|
||||
"can_auto_execute": False,
|
||||
},
|
||||
"evidence": [
|
||||
{
|
||||
"metric": "match_score",
|
||||
"value": 0.91,
|
||||
"basis": "exact/total_price/identity_review",
|
||||
}
|
||||
],
|
||||
},
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
payload = svc.build_webcrumbs_marketplace_host_data(engine=engine, limit=5)
|
||||
|
||||
@@ -71,11 +108,19 @@ def test_webcrumbs_host_data_maps_price_alert_exact_rows(monkeypatch):
|
||||
assert payload["metadata"]["fresh_match_rate"] == 79.5
|
||||
assert payload["metadata"]["stale_match_count"] == 18
|
||||
assert payload["metadata"]["pending_match_count"] == 612
|
||||
assert payload["metadata"]["review_queue_count"] == 1
|
||||
assert payload["metadata"]["hitl_count"] == 1
|
||||
assert payload["metadata"]["auto_execute_blocked_count"] == 1
|
||||
assert payload["metadata"]["decision_envelope_source"] == "competitor_intel_repository"
|
||||
assert payload["reviewDecisionBrief"]["items"][0]["sku"] == "SKU-REVIEW"
|
||||
assert payload["reviewDecisionBrief"]["items"][0]["can_auto_execute"] is False
|
||||
assert "review_accept_identity" in payload["reviewDecisionBrief"]["items"][0]["action"]
|
||||
assert all(row["freshness_status"] == "price_alert_exact" for row in payload["marketSnapshot"])
|
||||
|
||||
|
||||
def test_webcrumbs_host_data_uses_empty_state_without_risks(monkeypatch):
|
||||
monkeypatch.setattr(svc, "fetch_top_competitor_risks", lambda engine, limit: [])
|
||||
monkeypatch.setattr(svc, "fetch_competitor_review_queue", lambda engine, limit: [])
|
||||
monkeypatch.setattr(
|
||||
svc,
|
||||
"fetch_competitor_coverage",
|
||||
@@ -91,6 +136,8 @@ def test_webcrumbs_host_data_uses_empty_state_without_risks(monkeypatch):
|
||||
assert payload["metadata"]["fresh_match_count"] == 1
|
||||
assert payload["metadata"]["stale_match_count"] == 2
|
||||
assert payload["metadata"]["pending_match_count"] == 9
|
||||
assert payload["metadata"]["review_queue_count"] == 0
|
||||
assert payload["reviewDecisionBrief"]["text"] == "(目前沒有待覆核決策信封)"
|
||||
assert "非同款、單位價或變體候選" in payload["aiCandidate"]["thesis"]
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user