V10.552 收斂決策查詢新鮮度口徑
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
================================================================================
|
||||
|
||||
【已完成】
|
||||
- V10.552 收斂決策查詢的新鮮度口徑:`fetch_top_competitor_risks()`、PChome review queue、review sample 與 current PPT/AI 比價結果都不再把 `expires_at IS NULL` 當成有效現價,只接受 `expires_at > CURRENT_TIMESTAMP` 的 PChome identity_v2 價格。未知新鮮度只留在 coverage 的診斷欄位與 V10.551 刷新入口,不再進入價格風險、簡報、AI 決策或覆核排除條件。
|
||||
- V10.551 收斂未知新鮮度刷新與補抓排序:`_fetch_expired_identity_skus()` / `_fetch_expired_identity_recovery_skus()` 將 `expires_at IS NULL` 視為必須刷新或可搜尋救援的未知新鮮度 identity,和 V10.549「未知新鮮度不算可決策覆蓋率」口徑對齊;兩條路徑改用 `JOIN LATERAL` 取最新 MOMO 價,移除 product-wide window scan。`_fetch_unmatched_priority_skus()` 也改用 lateral 最新價,並優先重搜低風險 `no_result / refresh_no_result`,讓 V10.550 的安全召回詞先用在最可能被救回的商品。
|
||||
- V10.550 補安全搜尋召回詞:`_build_variant_recall_search_plan()` 對低風險穩定品類新增 `品牌 + 品類` 的補搜尋詞,讓 `no_result / refresh_no_result` 更有機會找到 PChome 候選後再交給 matcher 安全判斷;美甲片、指甲油、唇彩、香氛/精油、粉底、防曬、任選/色號/款式等高 variant 風險商品不走通用召回,DASHING DIVA 仍只走既有 line-specific recall + sort fallback。此變更不改 `MIN_MATCH_SCORE`、hard veto、fresh-search write safety 或 stronger existing match 覆寫保護。
|
||||
- V10.549 收斂比價新鮮度 KPI 口徑:coverage cache 升到 v10,`expires_at IS NULL` 不再算進「可用比價 / decision ready」,改拆成 `unknown_freshness_matches` / `unknown_freshness_count`,避免沒有到期時間的舊資料被當成可直接決策的新鮮價格。Dashboard / daily / growth 同步顯示未知新鮮度與「未形成有效身份配對」,並把 PChome/MOMO 價格方向文案改成 `PChome 價格壓力` / `MOMO 價格優勢`,降低誤讀。
|
||||
|
||||
@@ -402,7 +402,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '')
|
||||
# ==========================================
|
||||
# 系統版本與路徑
|
||||
# ==========================================
|
||||
SYSTEM_VERSION = "V10.551"
|
||||
SYSTEM_VERSION = "V10.552"
|
||||
LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log')
|
||||
public_url = PUBLIC_URL # 用於模板顯示
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
## 📅 詳細更新日誌 (考古存檔)
|
||||
|
||||
### 2026-06-01:PChome 比價新鮮度操作閉環
|
||||
- **V10.552 決策查詢新鮮度口徑收斂**: Top competitor risks、PChome review queue、review sample 與 current PPT/AI 比價結果全部改成只吃 `cp.expires_at > CURRENT_TIMESTAMP` 的有效 PChome identity_v2 價格,不再把 `expires_at IS NULL` 當作有效現價。未知新鮮度現在只作 coverage 診斷與刷新入口,不會被用來產生價格風險、簡報、AI 決策或從覆核隊列中排除。
|
||||
- **V10.551 未知新鮮度刷新與補抓排序收斂**: `expires_at IS NULL` 的 identity_v2 價格現在會被 `_fetch_expired_identity_skus()` 與 `_fetch_expired_identity_recovery_skus()` 視為需要刷新 / 可搜尋救援的未知新鮮度資料,避免 V10.549 已排除出可決策覆蓋率後卻沒有刷新入口;兩條路徑都改用 `JOIN LATERAL` 取最新 MOMO 價,不再做 product-wide window scan。`_fetch_unmatched_priority_skus()` 同步改為 lateral latest price,且把低風險 `no_result / refresh_no_result` 排到補抓前段,讓安全召回詞優先投入最可能回收的 SKU。
|
||||
- **V10.550 安全搜尋召回詞補強**: `competitor_price_feeder` 在既有精準搜尋詞之外,對低風險穩定品類補上一組 `品牌 + 品類` recall keyword,提升 `no_result / refresh_no_result` 找到候選的機率;高 variant 風險商品如美甲片、指甲油、唇彩、香氛/精油、粉底、防曬與含任選/色號/款式/香味的商品不走通用召回。DASHING DIVA 仍保留既有 line-specific recall 與 PChome sort fallback;本次不更動 `MIN_MATCH_SCORE`、hard veto、auto write safety 或 stronger existing match 保護。
|
||||
- **V10.549 比價新鮮度 KPI 口徑收斂**: `fetch_competitor_coverage()` cache 升到 v10,`expires_at IS NULL` 不再混入 `fresh_matches` / `decision_ready_rate`,改拆成 `unknown_freshness_matches` 與 `unknown_freshness_rate`,讓「可用比價覆蓋率」只代表有明確未過期時間的 identity 價格。Dashboard、daily、growth 同步顯示未知新鮮度與「未形成有效身份配對」,第一屏資料時間改看最新有效 PChome 價格抓取,並把價格方向文案改為 `PChome 價格壓力` / `MOMO 價格優勢`。
|
||||
|
||||
@@ -1152,7 +1152,7 @@ def _fetch_top_competitor_risks_uncached(engine, limit: int = 10) -> list[dict]:
|
||||
cp.crawled_at
|
||||
FROM competitor_prices cp
|
||||
WHERE cp.source = 'pchome'
|
||||
AND (cp.expires_at IS NULL OR cp.expires_at > CURRENT_TIMESTAMP)
|
||||
AND cp.expires_at > CURRENT_TIMESTAMP
|
||||
AND cp.price IS NOT NULL
|
||||
AND cp.price > 0
|
||||
AND COALESCE(cp.match_score, 0) >= {PCHOME_MATCH_SCORE_FLOOR}
|
||||
@@ -1285,7 +1285,7 @@ def _review_queue_cte_and_filter(
|
||||
FROM competitor_prices cp
|
||||
WHERE cp.source = 'pchome'
|
||||
AND cp.sku = la.sku
|
||||
AND (cp.expires_at IS NULL OR cp.expires_at > CURRENT_TIMESTAMP)
|
||||
AND cp.expires_at > CURRENT_TIMESTAMP
|
||||
AND cp.price IS NOT NULL
|
||||
AND cp.price > 0
|
||||
AND COALESCE(cp.match_score, 0) >= {PCHOME_MATCH_SCORE_FLOOR}
|
||||
@@ -1475,7 +1475,7 @@ def _fetch_competitor_review_queue_uncached(engine, limit: int = 12) -> list[dic
|
||||
cp.sku
|
||||
FROM competitor_prices cp
|
||||
WHERE cp.source = 'pchome'
|
||||
AND (cp.expires_at IS NULL OR cp.expires_at > CURRENT_TIMESTAMP)
|
||||
AND cp.expires_at > CURRENT_TIMESTAMP
|
||||
AND cp.price IS NOT NULL
|
||||
AND cp.price > 0
|
||||
AND COALESCE(cp.match_score, 0) >= {PCHOME_MATCH_SCORE_FLOOR}
|
||||
@@ -1702,7 +1702,7 @@ def fetch_competitor_comparison_results(
|
||||
'competitor_prices' AS competitor_source
|
||||
FROM competitor_prices cp
|
||||
WHERE cp.source = 'pchome'
|
||||
AND (cp.expires_at IS NULL OR cp.expires_at > CURRENT_TIMESTAMP)
|
||||
AND cp.expires_at > CURRENT_TIMESTAMP
|
||||
AND cp.price IS NOT NULL
|
||||
AND cp.price > 0
|
||||
AND COALESCE(cp.match_score, 0) >= {PCHOME_MATCH_SCORE_FLOOR}
|
||||
|
||||
@@ -99,6 +99,13 @@ def test_competitor_coverage_counts_only_active_product_intersection():
|
||||
assert "WHERE p.status = 'ACTIVE'" in coverage_source
|
||||
|
||||
|
||||
def test_competitor_decision_consumers_require_explicit_freshness():
|
||||
source = (ROOT / "services" / "competitor_intel_repository.py").read_text(encoding="utf-8")
|
||||
|
||||
assert "(cp.expires_at IS NULL OR cp.expires_at > CURRENT_TIMESTAMP)" not in source
|
||||
assert source.count("AND cp.expires_at > CURRENT_TIMESTAMP") >= 4
|
||||
|
||||
|
||||
def test_competitor_ppt_and_ai_use_momo_minus_pchome_gap_direction():
|
||||
ppt_source = (ROOT / "services" / "ppt_generator.py").read_text(encoding="utf-8")
|
||||
route_source = (ROOT / "routes" / "openclaw_bot_routes.py").read_text(encoding="utf-8")
|
||||
|
||||
Reference in New Issue
Block a user