From 24597dcebb089a5e1007a0e639d60ba886ab4fbd Mon Sep 17 00:00:00 2001 From: OoO Date: Mon, 1 Jun 2026 14:44:57 +0800 Subject: [PATCH] =?UTF-8?q?V10.555=20=E6=8E=A5=E7=B7=9A=20focused=20reason?= =?UTF-8?q?=20=E5=9B=9E=E5=88=B7=E7=AA=84=E9=96=80?= 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 | 29 +++++++++++++++++++ ...t_competitor_match_attempts_persistence.py | 6 ++++ 5 files changed, 38 insertions(+), 1 deletion(-) diff --git a/TODO_NEXT_STEPS.txt b/TODO_NEXT_STEPS.txt index 57be58b..200cfce 100644 --- a/TODO_NEXT_STEPS.txt +++ b/TODO_NEXT_STEPS.txt @@ -4,6 +4,7 @@ ================================================================================ 【已完成】 + - V10.555 補 focused total-price reason-based 回刷:`_fetch_retryable_candidate_skus()` 新增一條結構化 reason 窄門,只要舊 attempt 已帶 `focused_exact_total_price_safe` 且命中 matcher 的 `FOCUSED_IDENTITY_TOTAL_PRICE_REASONS`,即可進近門檻重評;仍要求無 hard veto、`exact_identity`、分數下限,並排除 commercial / variant / count / bundle 等阻擋理由。這讓已經被 matcher 明確判為 total-price exact 的舊候選不再依賴手寫商品名 SQL 才能回刷,同時 rom&nd / Solone / Summer’s Eve 等 review-only 品線仍不會進自動價差線。 - V10.554 接線香氛 / 精油 focused exact 回刷:Herb24 晨霧純精油擴香儀黑色、Pavaruni 40 香味 10ml 精油、Pavaruni 20 香味 450g 香氛蠟燭、Derma 大地有機植萃護膚油 150ml 現在明確標記 `focused_exact_total_price_safe`,並接進 `_fetch_retryable_candidate_skus()` 近門檻舊候選回刷。此入口只收 `low_score / refresh_low_score / true_low_confidence` 中命中精準名稱錨點、無 hard veto、`exact_identity` 且沒有變體 / 商業條件 / 件數衝突的候選;Laundrin、好物良品融蠟燈、Yuskin 等仍保留人工覆核,不為了拉覆蓋率強推自動價差。 - V10.553 優化 current PPT/AI 比價結果查詢:`fetch_competitor_comparison_results()` 的 current/latest MOMO 價格改用 `JOIN LATERAL` 取單品最新價,移除 `ROW_NUMBER() OVER (PARTITION BY p.id ...)` window scan;歷史報表的 `end_date` cutoff 仍保留在 lateral 子查詢內,維持「指定期間截止日前最新 MOMO 價」語意不變。這能降低簡報、OpenClaw/AI payload 與比價匯出在大量 price_records 下的查詢成本。 - 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 決策或覆核排除條件。 diff --git a/config.py b/config.py index 32aeddb..da606ce 100644 --- a/config.py +++ b/config.py @@ -402,7 +402,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '') # ========================================== # 系統版本與路徑 # ========================================== -SYSTEM_VERSION = "V10.554" +SYSTEM_VERSION = "V10.555" 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 fb720c4..deab91b 100644 --- a/docs/memory/history_logs.md +++ b/docs/memory/history_logs.md @@ -13,6 +13,7 @@ ## 📅 詳細更新日誌 (考古存檔) ### 2026-06-01:PChome 比價新鮮度操作閉環 +- **V10.555 focused total-price reason-based 回刷窄門**: `_fetch_retryable_candidate_skus()` 新增結構化 diagnostics reason 回刷入口,舊 attempt 若已帶 `focused_exact_total_price_safe` 且命中 matcher 的 `FOCUSED_IDENTITY_TOTAL_PRICE_REASONS`,即可進近門檻重評,不再完全依賴手寫商品名 SQL。此路徑仍要求分數下限、無 hard veto、`exact_identity`,並套用 commercial / variant / count / bundle 等阻擋理由;rom&nd、Solone、Summer’s Eve 等 review-only focused line 不在 total-price reason set,仍不會被推入自動價差。 - **V10.554 香氛 / 精油 focused exact 回刷接線**: Herb24 晨霧純精油擴香儀黑色、Pavaruni 40 香味 10ml 精油、Pavaruni 20 香味 450g 香氛蠟燭、Derma 大地有機植萃護膚油 150ml 已明確列入 matcher 的 `focused_exact_total_price_safe`,並接入 `_fetch_retryable_candidate_skus()` 的近門檻舊候選回刷入口。SQL 仍要求 `low_score / refresh_low_score / true_low_confidence`、分數下限、無 hard veto、`exact_identity`,且排除變體、商業條件與件數衝突;Laundrin、好物良品融蠟燈、Yuskin 等帶人工覆核訊號的品線不納入本輪自動回刷。 - **V10.553 current PPT/AI 比價結果查詢瘦身**: `fetch_competitor_comparison_results()` 不再用 `ROW_NUMBER() OVER (PARTITION BY p.id ...)` 掃 `price_records` 取得 current/latest MOMO 價格,改為 `JOIN LATERAL` 逐商品取最新價;有指定 `end_date` 的歷史報表仍把 `pr.timestamp < DATE(:end_date) + INTERVAL '1 day'` 放在 lateral 子查詢中,保留「截止日前最新 MOMO 價」語意。這降低簡報、OpenClaw/AI payload 與比價匯出在大量價格紀錄下的查詢成本。 - **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 決策或從覆核隊列中排除。 diff --git a/services/competitor_price_feeder.py b/services/competitor_price_feeder.py index 0ca3e9a..10f06df 100644 --- a/services/competitor_price_feeder.py +++ b/services/competitor_price_feeder.py @@ -33,6 +33,8 @@ from datetime import datetime, timedelta, timezone from typing import Optional from urllib.parse import quote_plus +from services.marketplace_product_matcher import FOCUSED_IDENTITY_TOTAL_PRICE_REASONS + logger = logging.getLogger(__name__) # ── 比對參數 ───────────────────────────────────────── @@ -113,6 +115,16 @@ RECOVERABLE_DIAGNOSTIC_REASONS = { } RECOVERABLE_SQL_REASON_LIST = ", ".join(f"'{reason}'" for reason in sorted(RECOVERABLE_DIAGNOSTIC_REASONS)) +FOCUSED_TOTAL_PRICE_SAFE_DIAGNOSTIC_REASONS = { + "focused_exact_total_price_safe", + *{ + f"focused_exact_identity_{reason}" + for reason in FOCUSED_IDENTITY_TOTAL_PRICE_REASONS + }, +} +FOCUSED_TOTAL_PRICE_SAFE_SQL_REASON_LIST = ", ".join( + f"'{reason}'" for reason in sorted(FOCUSED_TOTAL_PRICE_SAFE_DIAGNOSTIC_REASONS) +) REVALIDATABLE_REVIEW_GATE_REASONS = { "strong_exact_spec_match", "spec_name_alignment", @@ -1379,6 +1391,23 @@ class CompetitorPriceFeeder: OR COALESCE(la.match_diagnostic_json->'reasons', '[]'::jsonb) ?| array[{RECOVERABLE_SQL_REASON_LIST}] ) ) + OR ( + la.attempt_status IN ( + 'low_score', + 'refresh_low_score', + 'true_low_confidence', + 'rescore_accepted_current' + ) + AND COALESCE(la.best_match_score, 0) >= :min_score + AND COALESCE(la.hard_veto, false) = false + AND COALESCE(la.match_diagnostic_json->>'comparison_mode', 'exact_identity') = 'exact_identity' + AND COALESCE(la.match_diagnostic_json->'reasons', '[]'::jsonb) ? 'focused_exact_total_price_safe' + AND COALESCE(la.match_diagnostic_json->'reasons', '[]'::jsonb) ?| array[{FOCUSED_TOTAL_PRICE_SAFE_SQL_REASON_LIST}] + AND NOT ( + COALESCE(la.match_diagnostic_json->'reasons', '[]'::jsonb) + ?| array[{REVALIDATABLE_REVIEW_BLOCK_SQL_REASON_LIST}] + ) + ) 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 7487785..cf88560 100644 --- a/tests/test_competitor_match_attempts_persistence.py +++ b/tests/test_competitor_match_attempts_persistence.py @@ -110,6 +110,8 @@ def test_competitor_feeder_persists_all_match_attempt_outcomes(): assert "preview_expired_identity_recovery" in source assert "_fetch_expired_identity_recovery_skus" in source assert "STALE_IDENTITY_RECOVERY_BLOCK_REASONS" in source + assert "FOCUSED_IDENTITY_TOTAL_PRICE_REASONS" in source + assert "FOCUSED_TOTAL_PRICE_SAFE_SQL_REASON_LIST" in source assert r"\+|[xX]\s*\d|[**]\s*\d" in source assert "湛藍|麋香|海洋|玫瑰|薰衣草" in source assert "read_only_no_crawl_no_llm_no_db_write" in source @@ -166,6 +168,10 @@ def test_competitor_feeder_persists_all_match_attempt_outcomes(): assert "COALESCE(la.best_match_score, 0) >= 0.76" in retryable_source assert "COALESCE(la.best_match_score, 0) >= 0.95" in retryable_source assert "strong_exact_spec_match" in retryable_source + assert "focused_exact_total_price_safe" 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 assert "REVALIDATABLE_REVIEW_BLOCK_SQL_REASON_LIST" in retryable_source assert "commercial_condition_gap" in source assert "variant_selection_review" in source