V10.546 補近門檻舊候選回刷

This commit is contained in:
OoO
2026-06-01 12:09:29 +08:00
parent e4534c2f96
commit e5ecf5512e
5 changed files with 50 additions and 3 deletions

View File

@@ -4,6 +4,7 @@
================================================================================
【已完成】
- 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。
- V10.544 收斂變體安全與 YES 指甲工具線:新增 YES 德悅氏指甲剪附除垢銼刀、腳皮銼腳板、藍寶石銼刀、三面拋光棒與 6/8cm 指甲剪的精準 total-price 線,要求同品牌、同工具名稱、同尺寸與同亮面/霧面/可收納/三面/不掉屑等款式訊號;同步接進 revalidation SQL。新增 MUJI / COCODOR 未知香味差異與 OPI 無型號不同色名 hard vetoHOOOME 暖燈材質差留人工覆核,搜尋詞也會優先帶香味/色名,提升 crawler 精準候選率。
- V10.543 打通 `rescore_accepted_current` 窄門回刷:已進人工覆核池的候選若命中具名 focused exact 線,可進 `run_retryable_candidate_revalidation()` 重新評分;新增 SK-II 青春露 330ml 兩入、AMIINO 安美諾 30ml、YES 腳指甲剪刀 10.5cm、YES 極細指甲緣硬皮剪刀 9cm 的安全 total-price 線,並補 ANNY / OPI 指甲油型號 code hard veto避免不同色號被錯配。

View File

@@ -402,7 +402,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '')
# ==========================================
# 系統版本與路徑
# ==========================================
SYSTEM_VERSION = "V10.545"
SYSTEM_VERSION = "V10.546"
LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log')
public_url = PUBLIC_URL # 用於模板顯示

View File

@@ -13,6 +13,7 @@
## 📅 詳細更新日誌 (考古存檔)
### 2026-06-01PChome 比價新鮮度操作閉環
- **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不再被無效或低信心抓取紀錄撐高。
- **V10.544 變體安全與 YES 工具線收斂**: 延續近門檻 `low_score` 救回,但把安全邊界補得更硬。新增 YES 德悅氏指甲工具精準線,只有同品牌、同工具線、同尺寸且同亮面/霧面/可收納/三面等關鍵款式時才進 `total_price`,並接入 revalidation SQL。同步新增未知香味差異與無型號指彩色名差異 hard vetoMUJI / COCODOR 不同香型、OPI 無型號不同色名不再被高分誤配HOOOME 暖燈陶瓷/玻璃/水晶/金屬等材質差保留人工覆核。搜尋詞對護手霜、擴香瓶、無型號指彩優先帶上香味/色名,提升 crawler 找到真同款候選的機率。
- **V10.543 rescore accepted 窄門回刷與高信心線補強**: `run_retryable_candidate_revalidation()` 追加 `rescore_accepted_current` 窄門,只允許已進人工池且命中具名 focused exact 品線的候選重新評分,仍由 matcher 判定是否可寫正式價差。新增 SK-II 青春露 330ml 兩入、AMIINO 安美諾美白修護霜 30ml、YES 腳指甲剪刀 10.5cm、YES 極細指甲緣硬皮剪刀 9cm 的 total-price 安全線同時新增指甲油型號衝突防線ANNY `A10.074.60` vs `A10.500`、OPI 不同 `ISL...` 型號都會 hard veto不會為了拉覆蓋率誤配色號。

View File

@@ -1159,14 +1159,49 @@ class CompetitorPriceFeeder:
WHERE cma.source = 'pchome'
ORDER BY cma.sku, cma.attempted_at DESC, cma.id DESC
),
legacy_unmasked_attempt AS (
SELECT DISTINCT ON (cma.sku, cma.best_competitor_product_id)
cma.sku,
cma.best_competitor_product_id,
cma.best_competitor_product_name,
cma.best_match_score,
cma.attempt_status,
cma.hard_veto,
cma.match_diagnostic_json,
cma.attempted_at
FROM competitor_match_attempts cma
JOIN latest_attempt current_la
ON current_la.sku = cma.sku
WHERE cma.source = 'pchome'
AND current_la.attempt_status IN (
'refresh_no_result',
'no_result',
'expired_match'
)
AND cma.attempt_status IN (
'low_score',
'refresh_low_score',
'recoverable_low_score',
'true_low_confidence',
'rescore_accepted_current'
)
AND cma.attempted_at < current_la.attempted_at
ORDER BY cma.sku, cma.best_competitor_product_id, cma.attempted_at DESC, cma.id DESC
),
candidate_attempt AS (
SELECT la.*
FROM latest_attempt la
SELECT DISTINCT ON (la.sku, la.best_competitor_product_id)
la.*
FROM (
SELECT * FROM latest_attempt
UNION ALL
SELECT * FROM legacy_unmasked_attempt
) la
WHERE la.best_competitor_product_id IS NOT NULL
AND la.best_competitor_product_id <> ''
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'
ORDER BY la.sku, la.best_competitor_product_id, la.attempted_at DESC
)
SELECT
p.id AS product_id,

View File

@@ -177,6 +177,16 @@ def test_competitor_feeder_persists_all_match_attempt_outcomes():
assert "match_diagnostic_json->>'comparison_mode'" in retryable_source
assert "?| array[" in retryable_source
assert "candidate_attempt AS" in retryable_source
assert "legacy_unmasked_attempt AS" in retryable_source
assert "JOIN latest_attempt current_la" in retryable_source
assert "current_la.attempt_status IN (" in retryable_source
assert "'refresh_no_result'" in retryable_source
assert "'expired_match'" in retryable_source
assert "cma.attempt_status IN (" in retryable_source
assert "cma.attempted_at < current_la.attempted_at" in retryable_source
assert "UNION ALL" in retryable_source
assert "SELECT * FROM legacy_unmasked_attempt" in retryable_source
assert "SELECT DISTINCT ON (la.sku, la.best_competitor_product_id)" in retryable_source
assert "FROM candidate_attempt la" in retryable_source
assert "JOIN LATERAL" in retryable_source
assert "ORDER BY pr.timestamp DESC, pr.id DESC" in retryable_source