diff --git a/TODO_NEXT_STEPS.txt b/TODO_NEXT_STEPS.txt index 2952cab..462cfae 100644 --- a/TODO_NEXT_STEPS.txt +++ b/TODO_NEXT_STEPS.txt @@ -4,6 +4,7 @@ ================================================================================ 【已完成】 + - V10.581 將 V10.580 的重複單品組安全線接進 PChome retryable revalidation 窄門:只允許 `true_low_confidence`、舊診斷為 `match_type=exact / price_basis=manual_review`、無阻擋原因且命中具名安全商品線(Bioneo 150ml x2、Cetaphil 150ml x2、Avene 300ml x4、Schick 2+1 入、KOSE 雪肌精 500ml x2 禮盒)的候選進小批次重評;仍由最新版 matcher 最終判斷是否寫入正式 `competitor_prices`。 - V10.580 補 PChome 重複單品組 total-price 窄門:同品牌、同入數、同基礎規格且名稱高度對齊的 150ml x2、300ml x4、2+1 入等候選,可由 `exact / manual_review` 進 `exact / total_price / price_alert_exact`,正式部署前估算 213 筆高分 `true_low_confidence` 中只有 7 筆會轉自動寫入。同步新增 NEW DIRECTIONS 甜杏仁油 vs 杏桃核仁油核心油種 hard veto,避免規格一樣但油種不同的錯配污染正式價差;Paula's Choice 這類 PChome 端缺 30ml 規格的雙入組仍保留 manual review,不放寬全域門檻。 - V10.579 補 PChome 高信心 total-price safe family:SAB 私密防護舒緩噴霧 30ml 與 Herbacin 小甘菊 20ml 護手霜在同款式、同規格、無 variant/commercial gap 時可由 focused matcher 進 `exact / total_price / price_alert_exact`,讓近門檻重評能真正寫入正式比價;Herbacin 柔皙 vs 野生玫瑰等跨 variant 仍保留在 review,不放寬全域門檻。同版將 Code Review GCP-B secondary timeout 預設由 60 秒收斂到 25 秒,GCP-A preflight 不通且 GCP-B 生成卡住時更快回 deterministic local degraded,不呼叫 Gemini/111。 - V10.578 修正 Code Review 靜態掃描 timeout 誤報:Hermes deterministic scan 對 `requests.get/post/...` 會檢查同一呼叫 block 的後續行,多行呼叫已帶 `timeout=` 時不再報「HTTP request 未設定 timeout」。避免 V10.577 的 preflight helper 因多行格式被自己誤判為 MEDIUM。 diff --git a/config.py b/config.py index 7800c1d..bae9ee1 100644 --- a/config.py +++ b/config.py @@ -402,7 +402,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '') # ========================================== # 系統版本與路徑 # ========================================== -SYSTEM_VERSION = "V10.580" +SYSTEM_VERSION = "V10.581" LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log') public_url = PUBLIC_URL # 用於模板顯示 diff --git a/docs/memory/current_execution_queue_20260524.md b/docs/memory/current_execution_queue_20260524.md index 4485f0e..7e1f33c 100644 --- a/docs/memory/current_execution_queue_20260524.md +++ b/docs/memory/current_execution_queue_20260524.md @@ -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-04 起,`V10.579` 補 PChome 高信心 total-price safe family:SAB 私密防護舒緩噴霧 30ml、Herbacin 小甘菊 20ml 護手霜在同款式同規格且無 variant/commercial gap 時可進 `exact / total_price / price_alert_exact`;跨款式反測仍擋在 review,`MIN_MATCH_SCORE` 不變。同版將 Code Review GCP-B secondary timeout 收斂到 25 秒,GCP-A/GCP-B 都慢時更快回 local degraded。 - 2026-06-04 起,`V10.580` 補 PChome 重複單品組 total-price 窄門與核心油種 veto:同品牌、同入數、同基礎規格且名稱高度對齊的重複單品組(例如 Bioneo 150ml x2、Cetaphil 150ml x2、Avene 300ml x4、Schick 2+1 入)可進 `exact / total_price / price_alert_exact`;正式部署前估算 213 筆高分 `true_low_confidence` 中僅 7 筆會被自動寫入。NEW DIRECTIONS 甜杏仁油 vs 杏桃核仁油改 hard veto,Paula's Choice 缺 30ml 規格的雙入組仍留 manual review;`MIN_MATCH_SCORE` 不變。 +- 2026-06-04 起,`V10.581` 將重複單品組安全線接進 retryable revalidation:只收 `true_low_confidence` 中舊診斷為 `match_type=exact / price_basis=manual_review`、無 commercial / variant / count / bundle 等阻擋,且命中具名安全商品線的候選;最後仍由最新版 matcher 與 overwrite protection 決定是否寫正式比價。 - 2026-06-04 起,`V10.578` 修正 Code Review deterministic scan 的 timeout 判定,多行 `requests.*(... timeout=...)` 不再被誤報為未設定 timeout。 - 2026-06-04 起,`V10.577` Code Review OpenClaw 會在 explicit Ollama host generate 前先做短 `/api/version` preflight;GCP-A 不通時快速跳 GCP-B,避免 15 秒 timeout 後才降級,且仍不呼叫 Gemini / 111。 - 2026-06-04 起,`V10.576` 修正 GCP-only Ollama retry:caller 禁用 111 fallback 時,resolver 若回到 111 會改試 GCP-A/GCP-B allowlist,不再讓 Hermes / Code Review 類任務因 resolver 快取到 111 而 `all 0 hosts failed`。 diff --git a/docs/memory/history_logs.md b/docs/memory/history_logs.md index 6452f15..281d7c1 100644 --- a/docs/memory/history_logs.md +++ b/docs/memory/history_logs.md @@ -13,6 +13,7 @@ ## 📅 詳細更新日誌 (考古存檔) ### 2026-06-01:PChome 比價新鮮度操作閉環 +- **V10.581 重複單品組 retryable revalidation 接線**: 將 V10.580 的安全 matcher 線接到 `run_retryable_candidate_revalidation()`,但只收 `true_low_confidence` 中舊診斷已是 `match_type=exact / price_basis=manual_review`、沒有 review block,且命中具名安全商品線的候選。這讓 Bioneo / Cetaphil / Avene / Schick / KOSE 等重複單品組可以小批次重評並由最新版 matcher 決定是否寫入正式 `competitor_prices`,仍不開放泛用高分掃描。 - **V10.580 PChome 重複單品組 total-price 窄門 + 核心油種 veto**: matcher 將同品牌、同入數、同基礎規格且名稱高度對齊的重複單品組,從 `exact / manual_review` 收斂到 `exact / total_price / price_alert_exact`;正式部署前以最新 `true_low_confidence` 重算,213 筆高分候選中僅 7 筆符合自動寫入安全條件。同步新增 NEW DIRECTIONS 甜杏仁油 vs 杏桃核仁油 hard veto,避免同容量同按壓頭但核心油種不同的候選被誤放行;PChome 端缺規格的 Paula's Choice 雙入組仍停在 manual review。 - **V10.579 PChome 高信心 total-price safe family + Code Review timeout 收斂**: matcher 新增 SAB 私密防護舒緩噴霧 30ml 與 Herbacin 小甘菊 20ml 護手霜的窄範圍 total-price safe 路徑。這些候選仍必須通過既有 score、hard veto、variant/commercial gap 與 overwrite protection;Herbacin 柔皙 vs 野生玫瑰跨 variant 反測維持不進正式價差。目的在不放寬 `MIN_MATCH_SCORE` 的前提下,把可證明同款的高信心 `true_low_confidence` 轉進正式比價覆蓋。同版將 Code Review GCP-B secondary timeout 預設由 60 秒收斂到 25 秒,GCP-A preflight 不通且 GCP-B 生成卡住時更快回 deterministic local degraded,不呼叫 Gemini/111。 - **V10.578 Code Review 靜態掃描 timeout 誤報修正**: Hermes deterministic scan 對 `requests.get/post/put/delete/patch` 改檢查同一呼叫 block 的後續行,已在多行呼叫中帶 `timeout=` 時不再報「HTTP request 未設定 timeout」。這修掉 V10.577 preflight helper 被 Code Review 自己誤判為 MEDIUM 的噪音。 diff --git a/services/competitor_price_feeder.py b/services/competitor_price_feeder.py index 98f53ef..47597b9 100644 --- a/services/competitor_price_feeder.py +++ b/services/competitor_price_feeder.py @@ -1577,6 +1577,93 @@ class CompetitorPriceFeeder: ?| array[{REVALIDATABLE_REVIEW_BLOCK_SQL_REASON_LIST}] ) ) + OR ( + la.attempt_status = 'true_low_confidence' + AND COALESCE(la.best_match_score, 0) >= 0.95 + AND COALESCE(la.match_diagnostic_json->>'match_type', '') = 'exact' + AND COALESCE(la.match_diagnostic_json->>'price_basis', '') = 'manual_review' + AND COALESCE(la.match_diagnostic_json->'reasons', '[]'::jsonb) ? 'strong_product_line_match' + AND NOT ( + COALESCE(la.match_diagnostic_json->'reasons', '[]'::jsonb) + ?| array[{REVALIDATABLE_REVIEW_BLOCK_SQL_REASON_LIST}] + ) + AND ( + ( + lower(COALESCE(p.name, '')) LIKE '%bioneo%' + AND lower(COALESCE(la.best_competitor_product_name, '')) LIKE '%bioneo%' + AND COALESCE(p.name, '') LIKE '%微煥膚藻晶去角質凝膠%' + AND COALESCE(la.best_competitor_product_name, '') LIKE '%微煥膚藻晶去角質凝膠%' + AND lower(COALESCE(p.name, '')) ~* '150\\s*ml\\s*[x**]\\s*2' + AND lower(COALESCE(la.best_competitor_product_name, '')) ~* '150\\s*ml\\s*[x**]\\s*2' + ) + OR ( + ( + lower(COALESCE(p.name, '')) LIKE '%cetaphil%' + OR COALESCE(p.name, '') LIKE '%舒特膚%' + ) + AND ( + lower(COALESCE(la.best_competitor_product_name, '')) LIKE '%cetaphil%' + OR COALESCE(la.best_competitor_product_name, '') LIKE '%舒特膚%' + ) + AND COALESCE(p.name, '') LIKE '%舒新雪潤安撫水%' + AND COALESCE(la.best_competitor_product_name, '') LIKE '%舒新雪潤安撫水%' + AND lower(COALESCE(p.name, '')) ~* '150\\s*ml\\s*[x**]\\s*2' + AND lower(COALESCE(la.best_competitor_product_name, '')) ~* '150\\s*ml\\s*[x**]\\s*2' + ) + OR ( + ( + lower(COALESCE(p.name, '')) LIKE '%avene%' + OR COALESCE(p.name, '') LIKE '%雅漾%' + ) + AND ( + lower(COALESCE(la.best_competitor_product_name, '')) LIKE '%avene%' + OR COALESCE(la.best_competitor_product_name, '') LIKE '%雅漾%' + ) + AND COALESCE(p.name, '') LIKE '%舒護活泉水%' + AND COALESCE(la.best_competitor_product_name, '') LIKE '%舒護活泉水%' + AND lower(COALESCE(p.name, '')) ~* '300\\s*ml\\s*[x**]\\s*4' + AND lower(COALESCE(la.best_competitor_product_name, '')) ~* '300\\s*ml\\s*[x**]\\s*4' + ) + OR ( + ( + lower(COALESCE(p.name, '')) LIKE '%schick%' + OR COALESCE(p.name, '') LIKE '%舒適牌%' + ) + AND ( + lower(COALESCE(la.best_competitor_product_name, '')) LIKE '%schick%' + OR 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 COALESCE(p.name, '') LIKE '%滋潤型%' + AND COALESCE(la.best_competitor_product_name, '') LIKE '%滋潤型%' + ) + ) + AND lower(COALESCE(p.name, '')) ~* '2\\s*\\+\\s*1\\s*入' + AND lower(COALESCE(la.best_competitor_product_name, '')) ~* '2\\s*\\+\\s*1\\s*入' + ) + OR ( + ( + lower(COALESCE(p.name, '')) LIKE '%kose%' + OR COALESCE(p.name, '') LIKE '%高絲%' + ) + AND ( + lower(COALESCE(la.best_competitor_product_name, '')) LIKE '%kose%' + OR COALESCE(la.best_competitor_product_name, '') LIKE '%高絲%' + ) + AND COALESCE(p.name, '') LIKE '%雪肌精限量豪華禮盒%' + AND COALESCE(la.best_competitor_product_name, '') LIKE '%雪肌精限量豪華禮盒%' + AND lower(COALESCE(p.name, '')) ~* '500\\s*ml\\s*[x**]\\s*2' + AND lower(COALESCE(la.best_competitor_product_name, '')) ~* '500\\s*ml\\s*[x**]\\s*2' + ) + ) + ) OR ( la.attempt_status = 'true_low_confidence' AND COALESCE(la.best_match_score, 0) >= 0.95 diff --git a/tests/test_competitor_match_attempts_persistence.py b/tests/test_competitor_match_attempts_persistence.py index ae49f29..0e247ce 100644 --- a/tests/test_competitor_match_attempts_persistence.py +++ b/tests/test_competitor_match_attempts_persistence.py @@ -186,6 +186,13 @@ def test_competitor_feeder_persists_all_match_attempt_outcomes(): assert "focused_exact_total_price_safe" in retryable_source.split( "FOCUSED_TOTAL_PRICE_IDENTITY_SQL_REASON_LIST", 1 )[0] + assert "COALESCE(la.match_diagnostic_json->>'match_type', '') = 'exact'" in retryable_source + assert "COALESCE(la.match_diagnostic_json->>'price_basis', '') = 'manual_review'" in retryable_source + assert "微煥膚藻晶去角質凝膠" in retryable_source + assert "舒新雪潤安撫水" in retryable_source + assert "舒護活泉水" in retryable_source + assert "超捍輕便刀" in retryable_source + assert "雪肌精限量豪華禮盒" in retryable_source assert "focused_exact_identity_romand_juicy_lip_tint_2_catalog" not in retryable_source assert "focused_exact_identity_solone_longlasting_eyeliner" not in retryable_source assert "focused_exact_identity_summer_eve_full_skin_wash_2pack" not in retryable_source