V10.557 收緊 focused reason 回刷 guard

This commit is contained in:
OoO
2026-06-01 15:02:16 +08:00
parent 66f2c50964
commit 7a26bfa388
5 changed files with 36 additions and 6 deletions

View File

@@ -4,6 +4,7 @@
================================================================================
【已完成】
- V10.557 收緊 focused reason-based 回刷 guard上一版 reason-based 回刷現在不只要求 `focused_exact_total_price_safe`,還必須同時命中一條具名 `focused_exact_identity_*` 且該 identity 屬於 matcher 的 total-price safe set。這避免未來只有總開關、缺少具名身份證據的舊 attempt 被納入回刷rom&nd / Solone / Summers Eve 等 review-only focused line 仍被測試鎖在自動價差線外。
- V10.556 修 Ollama GCP-B model fallbackGCP-B 若缺 coder/large 指定模型,先用 host-compatible fallback `gemma3:4b` 留在 GCP-B不直接把流量推到 111`model not found` 404 視為模型缺失,不再把整台 GCP-B 標 unhealthy。主機順序仍維持 GCP-A → GCP-B → 111。
- 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 / Summers 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 等仍保留人工覆核,不為了拉覆蓋率強推自動價差。

View File

@@ -402,7 +402,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '')
# ==========================================
# 系統版本與路徑
# ==========================================
SYSTEM_VERSION = "V10.556"
SYSTEM_VERSION = "V10.557"
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.557 focused reason-based 回刷具名 identity guard**: V10.555 的結構化 reason 回刷再收緊,`_fetch_retryable_candidate_skus()` 不只要求 `focused_exact_total_price_safe`,還必須同時命中一條具名 `focused_exact_identity_*` 且該 identity 來自 matcher 的 total-price safe set。這避免只有總開關、缺少身份線索的舊 attempt 被納入回刷rom&nd、Solone、Summers Eve 等 review-only focused line 仍被測試鎖在自動價差線外。
- **V10.556 GCP-B model fallback 防 111 過載**: `OllamaService.generate()` 現在會在 GCP-B 對 coder/large 模型使用 host-compatible fallback預設 `gemma3:4b`),避免 GCP-B 缺 `qwen2.5-coder:7b` 時直接被標成 unhealthy 並把流量推到 111。HTTP 404 且訊息為 model not found 時視為模型缺失,不再 mark 整台 host unhealthy其他 HTTP / timeout 仍照舊標 unhealthy。主機順序仍是 GCP-A → GCP-B → 111。
- **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、Summers 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 等帶人工覆核訊號的品線不納入本輪自動回刷。

View File

@@ -115,13 +115,17 @@ RECOVERABLE_DIAGNOSTIC_REASONS = {
}
RECOVERABLE_SQL_REASON_LIST = ", ".join(f"'{reason}'" for reason in sorted(RECOVERABLE_DIAGNOSTIC_REASONS))
FOCUSED_TOTAL_PRICE_IDENTITY_DIAGNOSTIC_REASONS = {
f"focused_exact_identity_{reason}"
for reason in FOCUSED_IDENTITY_TOTAL_PRICE_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_IDENTITY_DIAGNOSTIC_REASONS,
}
FOCUSED_TOTAL_PRICE_IDENTITY_SQL_REASON_LIST = ", ".join(
f"'{reason}'" for reason in sorted(FOCUSED_TOTAL_PRICE_IDENTITY_DIAGNOSTIC_REASONS)
)
FOCUSED_TOTAL_PRICE_SAFE_SQL_REASON_LIST = ", ".join(
f"'{reason}'" for reason in sorted(FOCUSED_TOTAL_PRICE_SAFE_DIAGNOSTIC_REASONS)
)
@@ -1402,7 +1406,7 @@ class CompetitorPriceFeeder:
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 COALESCE(la.match_diagnostic_json->'reasons', '[]'::jsonb) ?| array[{FOCUSED_TOTAL_PRICE_IDENTITY_SQL_REASON_LIST}]
AND NOT (
COALESCE(la.match_diagnostic_json->'reasons', '[]'::jsonb)
?| array[{REVALIDATABLE_REVIEW_BLOCK_SQL_REASON_LIST}]

View File

@@ -111,6 +111,7 @@ def test_competitor_feeder_persists_all_match_attempt_outcomes():
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_IDENTITY_SQL_REASON_LIST" in source
assert "FOCUSED_TOTAL_PRICE_SAFE_SQL_REASON_LIST" in source
assert r"\+|[xX]\s*\d|[*]\s*\d" in source
assert "湛藍|麋香|海洋|玫瑰|薰衣草" in source
@@ -169,6 +170,7 @@ def test_competitor_feeder_persists_all_match_attempt_outcomes():
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_TOTAL_PRICE_IDENTITY_SQL_REASON_LIST" 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
@@ -266,6 +268,28 @@ def test_competitor_feeder_persists_all_match_attempt_outcomes():
assert "idx_comp_match_attempts_sku_source_time" in migration
def test_competitor_feeder_focused_total_price_reason_gate_requires_named_identity():
from services.competitor_price_feeder import (
FOCUSED_TOTAL_PRICE_IDENTITY_DIAGNOSTIC_REASONS,
FOCUSED_TOTAL_PRICE_SAFE_DIAGNOSTIC_REASONS,
)
assert "focused_exact_total_price_safe" in FOCUSED_TOTAL_PRICE_SAFE_DIAGNOSTIC_REASONS
assert "focused_exact_total_price_safe" not in FOCUSED_TOTAL_PRICE_IDENTITY_DIAGNOSTIC_REASONS
assert (
"focused_exact_identity_pavaruni_20_scent_candle"
in FOCUSED_TOTAL_PRICE_IDENTITY_DIAGNOSTIC_REASONS
)
assert (
"focused_exact_identity_romand_juicy_lip_tint_2_catalog"
not in FOCUSED_TOTAL_PRICE_IDENTITY_DIAGNOSTIC_REASONS
)
assert (
"focused_exact_identity_solone_longlasting_eyeliner"
not in FOCUSED_TOTAL_PRICE_IDENTITY_DIAGNOSTIC_REASONS
)
def test_competitor_feeder_blocks_identity_review_from_auto_price_write():
from types import SimpleNamespace