From 12dc4520615bb82743fec7d8046768b88c22de26 Mon Sep 17 00:00:00 2001 From: OoO Date: Mon, 1 Jun 2026 12:26:13 +0800 Subject: [PATCH] =?UTF-8?q?V10.548=20=E6=8E=A5=E7=B7=9A=20focused=20exact?= =?UTF-8?q?=20=E8=88=8A=E5=80=99=E9=81=B8=E5=9B=9E=E5=88=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TODO_NEXT_STEPS.txt | 1 + config.py | 2 +- docs/memory/history_logs.md | 1 + services/competitor_price_feeder.py | 68 +++++++++++++++++++ ...t_competitor_match_attempts_persistence.py | 9 +++ 5 files changed, 80 insertions(+), 1 deletion(-) diff --git a/TODO_NEXT_STEPS.txt b/TODO_NEXT_STEPS.txt index 4ef37a6..75c7c33 100644 --- a/TODO_NEXT_STEPS.txt +++ b/TODO_NEXT_STEPS.txt @@ -4,6 +4,7 @@ ================================================================================ 【已完成】 + - V10.548 接線更多 focused exact 舊候選回刷:把 matcher 已驗證可安全走 total-price 的 3W CLINIC 膠原蛋白粉底液 50ml x2、花美水 Moisture/Inclear 1.7g x3、KUSSEN 寶寶益菌屁屁膏 50ml 3 入、Lab52 齒妍堂嬰幼兒/汪汪隊牙刷 2 入接進 `_fetch_retryable_candidate_skus()` focused true-low / rescore 窄門。這只擴大「舊候選可被新版 matcher 重評」的入口,不改 `MIN_MATCH_SCORE`、hard veto、auto price write safety 或既有覆寫保護。 - V10.547 強化單位價覆核洞察:`manual_unit_price_required` 不再只是人工狀態,覆核隊列與商品看板會重新帶出單位價換算、MOMO/PChome 單位價方向、差距百分比與處理建議;決策信封 / OpenClaw / PPT 摘要可讀到 `unit_price_insight`。人工覆核寫回也會保留原始 `match_diagnostic_json` / comparison mode / diagnostic codes,避免後續簡報、審計或 AI 策略只剩人工文案而失去 matcher 證據鏈。 - V10.546 補近門檻舊候選回刷隊列:`run_retryable_candidate_revalidation()` 新增 `legacy_unmasked_attempt`,當最新狀態是 `no_result` / `refresh_no_result` / `expired_match` 時,可回撈同 SKU 早期近門檻候選交給最新版 matcher 重評;仍要求 candidate id、分數下限、無 hard veto、exact_identity,且不打開人工否決、單位價、identity_veto 或 protected existing match。 - V10.545 收斂 Dashboard 比價覆蓋率口徑:coverage cache 升到 v9,新增身份覆蓋、可用比價、新鮮度、待補身份、過期身份與人工閉環欄位;商品看板和 PChome 覆核頁改只把真正待處理狀態算進「比價覆核」,人工已否決 / 人工單位價 / 需補研究改列為人工閉環;PChome competitor map 只吃有效價格、新鮮、identity_v2 最新 row,資料新鮮度也改看可用比價 row。 diff --git a/config.py b/config.py index d2b1df3..0b0b2a2 100644 --- a/config.py +++ b/config.py @@ -402,7 +402,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '') # ========================================== # 系統版本與路徑 # ========================================== -SYSTEM_VERSION = "V10.547" +SYSTEM_VERSION = "V10.548" LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log') public_url = PUBLIC_URL # 用於模板顯示 diff --git a/docs/memory/history_logs.md b/docs/memory/history_logs.md index 00e3f6e..08957f9 100644 --- a/docs/memory/history_logs.md +++ b/docs/memory/history_logs.md @@ -13,6 +13,7 @@ ## 📅 詳細更新日誌 (考古存檔) ### 2026-06-01:PChome 比價新鮮度操作閉環 +- **V10.548 focused exact 舊候選回刷接線**: `_fetch_retryable_candidate_skus()` 的 focused true-low / rescore 窄門新增 3W CLINIC 膠原蛋白粉底液 50ml x2、花美水 Moisture/Inclear 1.7g x3、KUSSEN 寶寶益菌屁屁膏 50ml 3 入、Lab52 齒妍堂嬰幼兒 / 汪汪隊牙刷 2 入。這些品線在 matcher 測試中已是 `exact / total_price / price_alert_exact`,本次只讓舊 `true_low_confidence` / `rescore_accepted_current` 候選能被新版 matcher 重新判斷;仍不放寬 `MIN_MATCH_SCORE`、hard veto、auto write safety 與 stronger existing match 保護。 - **V10.547 單位價覆核洞察與證據鏈保留**: `manual_unit_price_required` 現在會和 `unit_comparable` 一樣重新產生單位價比較,並轉成 `unit_price_insight`,明確標示 PChome 或 MOMO 哪邊單位價較低、差距百分比、嚴重度與操作建議;Dashboard 覆核卡、商品列、決策信封與 OpenClaw/PPT 摘要都可讀到這個訊號。人工覆核寫回 `competitor_match_attempts` 時也會在欄位存在時保留原始 `match_diagnostic_json`、`comparison_mode`、`hard_veto`、`diagnostic_codes`,`competitor_match_reviews.candidate_diagnostic` 同步附帶 JSON 證據,避免人工閉環後只剩狀態文字。 - **V10.546 近門檻舊候選回刷隊列補漏**: `run_retryable_candidate_revalidation()` 的候選來源不再只看每個 SKU 最新一筆 attempt。新增 `legacy_unmasked_attempt`,當最新狀態是 `no_result` / `refresh_no_result` / `expired_match` 時,可回撈同 SKU 早期 `low_score`、`recoverable_low_score`、`true_low_confidence` 或 `rescore_accepted_current` 的近門檻候選,再交給最新版 matcher 重評。此入口仍要求 candidate product id、分數下限、無 hard veto、`exact_identity`,且不打開人工否決、單位價、identity_veto 或 protected existing match,避免為了覆蓋率破壞安全邊界。 - **V10.545 Dashboard 比價覆蓋率口徑收斂**: 商品看板與 PChome 覆核頁把「身份覆蓋率」與「可用比價覆蓋率」拆成明確欄位,coverage cache 更新為 v9 並回傳 `identity_coverage_*`、`pending_identity_count`、`stale_identity_count`、`last_decision_ready_crawled_at`。覆核 KPI 改只計算真正待處理狀態,人工否決 / 人工單位價 / 需補研究另列 `manual_closed_count`,避免人工閉環候選被混進待審總數。Dashboard 的 PChome competitor map 也改成只取新鮮、有效價格、identity_v2 的最新 row,資料新鮮度改看可用比價 row,不再被無效或低信心抓取紀錄撐高。 diff --git a/services/competitor_price_feeder.py b/services/competitor_price_feeder.py index 27c2bde..61868dd 100644 --- a/services/competitor_price_feeder.py +++ b/services/competitor_price_feeder.py @@ -1363,6 +1363,74 @@ class CompetitorPriceFeeder: AND COALESCE(p.name, '') LIKE '%全面修復潤唇膏%' AND COALESCE(la.best_competitor_product_name, '') LIKE '%全面修復潤唇膏%' ) + OR ( + lower(COALESCE(p.name, '')) LIKE '%3w%' + AND lower(COALESCE(p.name, '')) LIKE '%clinic%' + AND lower(COALESCE(la.best_competitor_product_name, '')) LIKE '%3w%' + AND lower(COALESCE(la.best_competitor_product_name, '')) LIKE '%clinic%' + AND COALESCE(p.name, '') LIKE '%膠原蛋白粉底液%' + AND COALESCE(la.best_competitor_product_name, '') LIKE '%膠原蛋白粉底液%' + AND COALESCE(p.name, '') ~* '50\\s*ml' + AND COALESCE(la.best_competitor_product_name, '') ~* '50\\s*ml' + ) + OR ( + COALESCE(p.name, '') LIKE '%花美水%' + AND COALESCE(la.best_competitor_product_name, '') LIKE '%花美水%' + AND lower(COALESCE(p.name, '')) LIKE '%moisture%' + AND lower(COALESCE(la.best_competitor_product_name, '')) LIKE '%moisture%' + AND COALESCE(p.name, '') LIKE '%保濕修護%' + AND COALESCE(la.best_competitor_product_name, '') LIKE '%保濕修護%' + AND COALESCE(p.name, '') LIKE '%精華凝膠%' + AND COALESCE(la.best_competitor_product_name, '') LIKE '%精華凝膠%' + AND COALESCE(p.name, '') LIKE '%原黃金%' + AND COALESCE(la.best_competitor_product_name, '') LIKE '%原黃金%' + ) + OR ( + COALESCE(p.name, '') LIKE '%花美水%' + AND COALESCE(la.best_competitor_product_name, '') LIKE '%花美水%' + AND lower(COALESCE(p.name, '')) LIKE '%inclear%' + AND lower(COALESCE(la.best_competitor_product_name, '')) LIKE '%inclear%' + AND COALESCE(p.name, '') LIKE '%櫻克麗兒%' + AND COALESCE(la.best_competitor_product_name, '') LIKE '%櫻克麗兒%' + AND COALESCE(p.name, '') LIKE '%私密淨化凝膠%' + AND COALESCE(la.best_competitor_product_name, '') LIKE '%私密淨化凝膠%' + ) + OR ( + ( + lower(COALESCE(p.name, '')) LIKE '%kussen%' + OR COALESCE(p.name, '') LIKE '%葵森%' + ) + AND ( + lower(COALESCE(la.best_competitor_product_name, '')) LIKE '%kussen%' + OR COALESCE(la.best_competitor_product_name, '') LIKE '%葵森%' + ) + AND COALESCE(p.name, '') LIKE '%寶寶益菌屁屁膏%' + AND COALESCE(la.best_competitor_product_name, '') LIKE '%寶寶益菌屁屁膏%' + AND COALESCE(p.name, '') LIKE '%50%' + AND COALESCE(la.best_competitor_product_name, '') LIKE '%50%' + ) + OR ( + ( + lower(COALESCE(p.name, '')) LIKE '%lab52%' + OR COALESCE(p.name, '') LIKE '%齒妍堂%' + ) + AND ( + lower(COALESCE(la.best_competitor_product_name, '')) LIKE '%lab52%' + OR COALESCE(la.best_competitor_product_name, '') LIKE '%齒妍堂%' + ) + AND COALESCE(p.name, '') LIKE '%牙刷%' + AND COALESCE(la.best_competitor_product_name, '') LIKE '%牙刷%' + AND ( + COALESCE(p.name, '') LIKE '%嬰幼兒%' + OR COALESCE(p.name, '') LIKE '%幼兒%' + OR COALESCE(p.name, '') LIKE '%汪汪隊%' + ) + AND ( + COALESCE(la.best_competitor_product_name, '') LIKE '%嬰幼兒%' + OR COALESCE(la.best_competitor_product_name, '') LIKE '%幼兒%' + OR COALESCE(la.best_competitor_product_name, '') LIKE '%汪汪隊%' + ) + ) OR ( ( lower(COALESCE(p.name, '')) LIKE '%flortte%' diff --git a/tests/test_competitor_match_attempts_persistence.py b/tests/test_competitor_match_attempts_persistence.py index f1c096a..b5b55bd 100644 --- a/tests/test_competitor_match_attempts_persistence.py +++ b/tests/test_competitor_match_attempts_persistence.py @@ -162,6 +162,15 @@ def test_competitor_feeder_persists_all_match_attempt_outcomes(): assert "嬰兒沐浴洗髮" in retryable_source assert "八小時潤澤護唇膏" in retryable_source assert "全面修復潤唇膏" in retryable_source + assert "膠原蛋白粉底液" in retryable_source + assert "moisture" in retryable_source + assert "保濕修護" in retryable_source + assert "inclear" in retryable_source + assert "櫻克麗兒" in retryable_source + assert "私密淨化凝膠" in retryable_source + assert "寶寶益菌屁屁膏" in retryable_source + assert "齒妍堂" in retryable_source + assert "汪汪隊" in retryable_source assert "水果沙拉系列彩色防水眼線液筆" in retryable_source assert "護手霜" in retryable_source assert "持采亮化UV防曬水凝乳" in retryable_source