diff --git a/config.py b/config.py index 89360e3..a34923b 100644 --- a/config.py +++ b/config.py @@ -325,7 +325,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '') # ========================================== # 系統版本與路徑 # ========================================== -SYSTEM_VERSION = "V10.403" +SYSTEM_VERSION = "V10.404" 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 83db66f..9801621 100644 --- a/docs/memory/history_logs.md +++ b/docs/memory/history_logs.md @@ -13,6 +13,7 @@ ## 📅 詳細更新日誌 (考古存檔) ### 2026-05-24:PChome 近門檻身份回收第二輪 +- **V10.404 Hermes 競價威脅漏斗只吃 direct alert**: `HermesAnalystService.fetch_candidates()` 的 `competitor_prices` JOIN 新增硬條件,只讓 `match_type=exact`、`price_basis=total_price`、`alert_tier=price_alert_exact` 的 identity_v2 配對進入 Hermes 競價威脅分析;`identity_review`、`unit_price_review` 與 `suppress` 仍保留在 dashboard / 人工覆核資料流,但不再消耗 Hermes token 或被上游視為直接價格威脅候選。production fresh 配對分布顯示直接告警約 497 筆、覆核型約 216 筆,本版將兩者在 AI 威脅入口切開。同版 matcher 追加 KATE 怪獸級持色唇膏、植村秀武士刀眉筆筆蕊、The Forest 焦糖楓葉擴香禮盒等近門檻 review-only 回收線,仍只進人工覆核,不直接價格告警。 - **V10.403 近門檻候選重驗擴池**: production 最新 72h attempt audit 顯示 7853 筆近門檻/被擋候選中,有 105 筆可被現行 matcher 回收,其中 46 筆已達 `price_alert_exact`、59 筆只進 `identity_review`。`CompetitorPriceFeeder._fetch_retryable_candidate_skus()` 擴大每日 revalidation 候選池,納入 `true_low_confidence`、`unit_comparable`、`refresh_unit_comparable` 與高分 `identity_veto`;但仍要求 PChome product_id、`best_match_score >= 0.70`,且正式寫入前必須由現行 matcher 重判、通過 hard-veto 與既有配對保護。 - **V10.402 catalog variant identity-review 回收**: marketplace matcher 新增 Johnson’s 嬰兒潤膚乳 catalog 對同香型、IM MEME 涼感定妝噴霧、SO NATURAL FIXX 定妝噴霧三條 review-only focused identity,讓同品線 catalog / variant 候選可脫離 `true_low_confidence` 但只進 `identity_review`,不直接價格告警;同版明確保留 MUJI 品牌缺漏護手霜等 brandless catalog 案例在低信心,避免品牌缺漏與多款任選同時存在時被過度救回。 - **V10.401 focused identity 邊界整理**: 將 Laundrin TOKYO 車用夾式消臭芳香劑判定抽成 `_has_laundrin_tokyo_car_freshener_alignment()`,並在 variant descriptor guard 中豁免同一條強身份線,避免 TOKYO / 車用 / 芳香劑語序差異造成誤擋。Yuskin 經典乳霜 30g 6 入組改用 `_has_exact_count_alignment()` 判斷包數,保留 6入 vs 6盒這類同數量但不同容器字詞的人工覆核路徑。 diff --git a/services/hermes_analyst_service.py b/services/hermes_analyst_service.py index 833e200..e3af272 100644 --- a/services/hermes_analyst_service.py +++ b/services/hermes_analyst_service.py @@ -449,6 +449,15 @@ class HermesAnalystService: AND cp.expires_at > NOW() AND cp.match_score >= 0.76 AND COALESCE(cp.tags, '[]'::jsonb) ? 'identity_v2' + AND ( + cp.match_diagnostic_json @> '{{"match_type":"exact","price_basis":"total_price","alert_tier":"price_alert_exact"}}'::jsonb + OR ( + cp.match_diagnostic_json IS NULL + AND COALESCE(cp.tags, '[]'::jsonb) ? 'match_type_exact' + AND COALESCE(cp.tags, '[]'::jsonb) ? 'price_basis_total_price' + AND COALESCE(cp.tags, '[]'::jsonb) ? 'alert_tier_price_alert_exact' + ) + ) WHERE lmp.rn = 1 AND rs.sales_7d_prev > 0 AND (rs.sales_7d_curr - rs.sales_7d_prev) / rs.sales_7d_prev < -0.10 diff --git a/services/marketplace_product_matcher.py b/services/marketplace_product_matcher.py index 8dfbe68..e356654 100644 --- a/services/marketplace_product_matcher.py +++ b/services/marketplace_product_matcher.py @@ -365,6 +365,8 @@ SEARCH_IDENTITY_ANCHORS = ( "嬰兒潤膚乳", "我愛超磁妝定妝噴霧", "全天候超完美定妝噴霧", + "怪獸級持色唇膏", + "焦糖楓葉香氛擴香花禮盒", "香氛蠟燭20種香味", "tokyo車用夾式消臭芳香劑", "北歐簡樸融蠟燈桌面氣氛夜燈", @@ -373,6 +375,7 @@ SEARCH_IDENTITY_ANCHORS = ( "細芯睛彩雙頭眉筆", "雙頭旋轉極細眉筆", "武士刀眉筆", + "自動武士刀眉筆筆蕊", "無極限保濕防曬妝前乳", "水凝光透 妝前防護乳", "水凝光透妝前防護乳", @@ -466,6 +469,13 @@ FOCUSED_IDENTITY_REVIEW_ONLY_REASONS = { "johnsons_baby_lotion_variant_catalog", "im_meme_fixx_cool_setting_spray", "so_natural_fixx_setting_spray_catalog", + "kate_monster_lipstick_catalog", + "shu_auto_hard_formula_refill_catalog", + "the_forest_maple_diffuser_flower_brandless", +} + +FOCUSED_IDENTITY_BRANDLESS_REVIEW_REASONS = { + "the_forest_maple_diffuser_flower_brandless", } SEARCH_BROAD_ANCHORS = { @@ -1862,6 +1872,25 @@ def score_marketplace_match( and sequence_score >= 0.40 and not variant_descriptor_conflict ) + focused_exact_review_boost_safe = ( + focused_exact_line_reason + and not hard_veto + and spec_score >= 0.45 + and token_score >= 0.30 + and sequence_score >= 0.40 + and not variant_descriptor_conflict + and ( + brand_score >= 0.95 + or ( + focused_exact_line_reason in FOCUSED_IDENTITY_BRANDLESS_REVIEW_REASONS + and brand_score == 0.55 + and bool(left.brand_tokens) != bool(right.brand_tokens) + and spec_score >= 0.85 + and token_score >= 0.55 + and sequence_score >= 0.50 + ) + ) + ) comparison_mode = "exact_identity" if _is_unit_comparable_candidate( @@ -1974,14 +2003,8 @@ def score_marketplace_match( score += 0.04 reasons.append("cushion_refill_pack_alignment_score") if ( - focused_exact_line_reason - and brand_score >= 0.95 - and not hard_veto + focused_exact_review_boost_safe and price_penalty == 0 - and spec_score >= 0.45 - and token_score >= 0.30 - and sequence_score >= 0.40 - and not variant_descriptor_conflict ): score += 0.16 reasons.append(f"focused_exact_identity_{focused_exact_line_reason}") @@ -2922,6 +2945,27 @@ def _has_focused_low_score_exact_identity_line(left: ProductIdentity, right: Pro and "全天候超完美定妝噴霧" in right_text ): return "so_natural_fixx_setting_spray_catalog" + if ( + {"kate", "凱婷"} & (left.brand_tokens & right.brand_tokens) + and "怪獸級持色唇膏" in left_text + and "怪獸級持色唇膏" in right_text + ): + return "kate_monster_lipstick_catalog" + if ( + {"shu uemura", "植村秀"} & (left.brand_tokens & right.brand_tokens) + and "自動武士刀眉筆" in left_text + and "自動武士刀眉筆" in right_text + and "筆蕊" in left_text + and "筆蕊" in right_text + ): + return "shu_auto_hard_formula_refill_catalog" + if ( + "焦糖楓葉香氛擴香花禮盒" in left_text + and "焦糖楓葉香氛擴香花禮盒" in right_text + and _has_shared_volume(left, right, 30) + and bool(left.brand_tokens) != bool(right.brand_tokens) + ): + return "the_forest_maple_diffuser_flower_brandless" if ( "gatsby" in (left.brand_tokens & right.brand_tokens) and "爆水擦澡濕巾" in left_text diff --git a/tests/test_hermes_ollama_cascade.py b/tests/test_hermes_ollama_cascade.py index 0f77e42..de3b50a 100644 --- a/tests/test_hermes_ollama_cascade.py +++ b/tests/test_hermes_ollama_cascade.py @@ -3,6 +3,7 @@ """Hermes 分析師必須透過 OllamaService 三主機級聯。""" import time +import inspect from types import SimpleNamespace import pytest @@ -141,3 +142,15 @@ def test_hermes_batch_analyze_uses_ollama_service_and_logs_secondary(monkeypatch assert rec['caller'] == 'hermes_analyst' assert rec['provider'] == 'ollama_secondary' assert rec['meta']['host_label'] == 'GCP-SSD-2' + + +def test_hermes_candidate_sql_only_joins_direct_price_alert_matches(): + sql_text = inspect.getsource(hermes_mod.HermesAnalystService.fetch_candidates) + compact_sql = "".join(sql_text.split()) + + assert '"match_type":"exact"' in compact_sql + assert '"price_basis":"total_price"' in compact_sql + assert '"alert_tier":"price_alert_exact"' in compact_sql + assert "match_type_exact" in sql_text + assert "price_basis_total_price" in sql_text + assert "alert_tier_price_alert_exact" in sql_text diff --git a/tests/test_marketplace_product_matcher.py b/tests/test_marketplace_product_matcher.py index 046c6fd..4e064ce 100644 --- a/tests/test_marketplace_product_matcher.py +++ b/tests/test_marketplace_product_matcher.py @@ -875,6 +875,18 @@ def test_marketplace_matcher_keeps_catalog_variant_recoveries_in_identity_review "【SO NATURAL】FIXX 全天候超完美定妝噴霧 經典款/光澤款/霧面款/夏日款", "【SO NATURAL】FIXX 全天候超完美定妝噴霧 120ml", ), + ( + "【KATE 凱婷】怪獸級持色唇膏-經典款(獨家技術持久不沾 高保濕)", + "【KATE 凱婷】怪獸級持色唇膏 (3g)", + ), + ( + "【植村秀】官方直營 自動武士刀眉筆筆蕊(Shu uemura/筆蕊 8色任選)", + "《Shu Uemura 植村秀》自動武士刀眉筆 -筆蕊 0.3g", + ), + ( + "【The Forest 癒森林】焦糖楓葉香氛擴香花禮盒 含30ml品牌香氛油(居家香氛/香水精油/香氛擴香花/擴香禮物)", + "焦糖楓葉香氛擴香花禮盒 含30ml品牌香氛油", + ), ] for momo_name, competitor_name in cases: @@ -1172,6 +1184,21 @@ def test_marketplace_matcher_promotes_focused_low_score_exact_identity_lines(): "【SO NATURAL】FIXX 全天候超完美定妝噴霧 120ml", "focused_exact_identity_so_natural_fixx_setting_spray_catalog", ), + ( + "【KATE 凱婷】怪獸級持色唇膏-經典款(獨家技術持久不沾 高保濕)", + "【KATE 凱婷】怪獸級持色唇膏 (3g)", + "focused_exact_identity_kate_monster_lipstick_catalog", + ), + ( + "【植村秀】官方直營 自動武士刀眉筆筆蕊(Shu uemura/筆蕊 8色任選)", + "《Shu Uemura 植村秀》自動武士刀眉筆 -筆蕊 0.3g", + "focused_exact_identity_shu_auto_hard_formula_refill_catalog", + ), + ( + "【The Forest 癒森林】焦糖楓葉香氛擴香花禮盒 含30ml品牌香氛油(居家香氛/香水精油/香氛擴香花/擴香禮物)", + "焦糖楓葉香氛擴香花禮盒 含30ml品牌香氛油", + "focused_exact_identity_the_forest_maple_diffuser_flower_brandless", + ), ( "【GATSBY】爆水擦澡濕巾24張入(涼感乾洗澡)", "GATSBY 爆水擦澡濕巾24張入(240g)", @@ -1213,8 +1240,30 @@ def test_marketplace_matcher_keeps_high_variant_low_score_lines_outside_focused_ "【Schick 舒適牌】舒芙仕女除毛刀把刀片組 (1刀把2刀片+刀片3入)", "【Schick舒適牌】舒綺 仕女除毛刀 敏感肌用 1刀把+2刀片 送卡娜赫拉束口袋", ) + lancome_line_gap = score_marketplace_match( + "【LANCOME 蘭蔻】官方直營 超極光活粹晶露150ml(LANCOME/四重酸極光水/化妝水/精華水)", + "LANCOME 蘭蔻 超極限肌因精華露150ml 專櫃公司貨", + ) + mac_finish_gap = score_marketplace_match( + "【M.A.C】MACximal極自我柔霧唇膏3.5g(子彈唇膏 #柔霧大巨彈_全新質地_新色登場)", + "M.A.C MACximal 極自我緞光唇膏 3.5g 多款可選", + ) + ysl_powder_variant_guard = score_marketplace_match( + "【YSL】官方直營 恆久完美持久柔霧蜜粉餅(任選1款/皮革蜜粉)", + "YSL 恆久完美持久柔霧蜜粉餅(7.5g) [百貨公司專櫃貨]", + ) - for diagnostics in (lush, lactacyd, lunasol, muji_swab, kate_limited, schick_bundle_gap): + for diagnostics in ( + lush, + lactacyd, + lunasol, + muji_swab, + kate_limited, + schick_bundle_gap, + lancome_line_gap, + mac_finish_gap, + ysl_powder_variant_guard, + ): assert diagnostics.score < 0.76 assert not any(reason.startswith("focused_exact_identity_") for reason in diagnostics.reasons)