收緊 PChome 近門檻自動回刷隊列

This commit is contained in:
OoO
2026-05-31 12:47:04 +08:00
parent d98f24e8eb
commit 06e196a282
5 changed files with 27 additions and 15 deletions

View File

@@ -4,6 +4,8 @@
================================================================================
【已完成】
- V10.493 新增市場情報 MCP Fetch Candidate Handoff Review 安全預覽 gate只審核 parser review 後的候選交接包,確認 source/candidate key 對齊、queue policy 仍是 manual preview、候選數維持小批次、無 raw/secret/side-effectAPI 不建立 queue、不寫 DB、不讀 artifact、不連外、不掛 scheduler。
- V10.492 收緊 PChome 近門檻自動回刷隊列:`retryable_candidate_revalidation` 不再把 `identity_veto`、`unit_comparable`、`true_low_confidence` 納入每日自動回刷;只處理 `recoverable_low_score` 與 legacy `low_score / refresh_low_score`,並要求無 hard veto、仍在 `exact_identity`、且具備同品線/identity anchor 證據。這讓「可救回」與「正確阻擋」在操作層面真正分流,避免為了壓低 low_score 而重跑不該自動推進的候選。
- V10.491 新增市場情報 MCP Fetch Result Parser Review 安全預覽 gate只審核操作員貼回的 parser 結構化摘要,對齊 receipt source/path、候選必要欄位、公開 URL、小批次上限與 raw HTML/secret/side-effect 風險API 不讀 artifact、不執行 parser CLI、不抓外站、不寫 DB、不掛 scheduler。
- V10.489 補 PChome 低分同款人工覆核回收與 gate-pass 風險邊界TS6 超美白香氛誘霜 120g/ml、W 修護保養蝸牛特潤修護面膜 6 片、Derma 大地 Eco 植萃護膚油 2 入,從低信心升成 `identity_review` 人工覆核候選Clarins 輕盈美體護理油 vs 身體調和護理油、台塑生醫嬰兒沐浴/洗髮組合數量反轉、isLeaf 私密慕絲香型數量不一致改 hard vetoHOOOME 大理石暖燈 vs 泛稱經典款改留 `variant_selection_review`。正式價差表仍需人工採用才會寫入。Production 已部署 `/health=V10.489`500 筆 read-only audit 由 V10.486 基線 `gate_pass=129 / identity_veto=1 / still_low=370` 收斂為 `gate_pass=124 / identity_veto=4 / still_low=372`。測試:完整 `pytest` 1289 passed / 9 skipped。
- V10.488 新增市場情報 MCP Fetch Run Receipt 安全預覽 gate只審核操作員 dry-run receipt不執行 CLI、不抓外站、不寫 DB。

View File

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

View File

@@ -87,6 +87,8 @@
- 2026-05-29 起,`V10.489` 補 PChome 低分同款人工覆核回收與 gate-pass 風險邊界TS6 超美白香氛誘霜 120g/ml、W 修護保養蝸牛特潤修護面膜 6 片、Derma 大地 Eco 植萃護膚油 2 入,從低信心升成 `identity_review` 候選Clarins 輕盈美體護理油 vs 身體調和護理油、台塑生醫嬰兒沐浴/洗髮組合數量反轉、isLeaf 私密慕絲香型數量不一致改 hard vetoHOOOME 大理石暖燈 vs 泛稱經典款只留 `variant_selection_review`,不進 total-price accepted。Production 已部署 `/health=V10.489`500 筆 read-only audit 由 V10.486 基線 `gate_pass=129 / identity_veto=1 / still_low=370` 收斂為 `gate_pass=124 / identity_veto=4 / still_low=372`,三應用容器 healthy、`momo-db` 未 recreate。
- 2026-05-31 起,`V10.490` 補 ElephantAlpha / OpenClaw 舊策略 action 相容:正式日誌觀察到 `agent=openclaw action=generate_market_strategy` 被當未知步驟丟錯;此類 OpenClaw strategy 產生步驟已定義為 advisory skipped只記 warning不觸發 circuit breaker、不執行外部策略、不影響價格行動。測試覆蓋 `generate_market_strategy``generate_resource_optimization_strategy`,未知 action 仍維持 fail-fast。
- 2026-05-31 起,`V10.491` 新增市場情報 MCP Fetch Result Parser Review gate在 receipt gate 後只審核操作員 shell parser 貼回的結構化摘要,對齊 source/path、公開 URL、候選必要欄位、小批次上限、raw HTML/secret/side-effect 風險;仍不讀 artifact、不執行 CLI、不連外、不寫 DB、不掛 scheduler。
- 2026-05-31 起,`V10.492` 收緊 PChome 近門檻自動回刷:`run_retryable_candidate_revalidation()` 只回刷 `recoverable_low_score` 與 legacy `low_score / refresh_low_score`,且 SQL 端要求 `hard_veto=false``comparison_mode=exact_identity`、diagnostic reasons 命中同品線/identity anchor`identity_veto``unit_comparable``true_low_confidence` 不再進每日自動回刷隊列,需等待新證據或人工處理。
- 2026-05-31 起,`V10.493` 新增市場情報 MCP Fetch Candidate Handoff Review gate在 parser review 通過後只審核候選交接包,要求 source/candidate key 完全對齊、queue policy 維持 manual preview、小批次上限與操作員無寫入/無連外/無排程確認;仍不建立 queue、不寫 DB、不讀 artifact、不連外、不掛 scheduler。
## 3. 12 Agent 決策信封整合

View File

@@ -65,6 +65,8 @@ RECOVERABLE_DIAGNOSTIC_REASONS = {
"spec_name_alignment",
}
RECOVERABLE_SQL_REASON_LIST = ", ".join(f"'{reason}'" for reason in sorted(RECOVERABLE_DIAGNOSTIC_REASONS))
# ── Feeder 結果 ───────────────────────────────────────
@dataclass
class FeederResult:
@@ -1037,16 +1039,17 @@ class CompetitorPriceFeeder:
"""
取得近門檻候選,供 matcher 升級後重新評分。
這條路徑不重新搜尋,只用前次留下的 PChome product_id 批次查詢最新商品資料
適合把舊 scorer 卡在 0.70~0.759 的真同款重新推進正式比價
僅重跑明顯有回收價值的候選;最後仍由現行 matcher 重新判斷,
不因舊 attempt_status 自動寫入正式比價
這條路徑優先用前次留下的 PChome product_id 批次查詢最新商品資料
若 product_id 過期或重評仍低分,才走受控 fresh search recovery
自動隊列只收「近門檻、無 hard veto、仍在 exact_identity 軌道、已有同品線證據」
的候選identity_veto、unit_comparable、true_low_confidence 不在自動回刷主戰場
最後仍由現行 matcher 重新判斷,不因舊 attempt_status 自動寫入正式比價。
"""
if self.engine is None:
raise RuntimeError("需要注入 SQLAlchemy engine")
from sqlalchemy import text
sql = text("""
sql = text(f"""
WITH latest_momo AS (
SELECT
p.id AS product_id,
@@ -1067,6 +1070,7 @@ class CompetitorPriceFeeder:
cma.best_match_score,
cma.attempt_status,
cma.hard_veto,
cma.match_diagnostic_json,
cma.attempted_at
FROM competitor_match_attempts cma
WHERE cma.source = 'pchome'
@@ -1096,18 +1100,16 @@ class CompetitorPriceFeeder:
AND la.attempt_status IN (
'low_score',
'refresh_low_score',
'recoverable_low_score',
'true_low_confidence',
'unit_comparable',
'refresh_unit_comparable',
'identity_veto'
'recoverable_low_score'
)
AND 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'
AND (
COALESCE(la.hard_veto, false) = false
OR la.attempt_status = 'identity_veto'
la.attempt_status = 'recoverable_low_score'
OR COALESCE(la.match_diagnostic_json->'reasons', '[]'::jsonb) ?| array[{RECOVERABLE_SQL_REASON_LIST}]
)
ORDER BY la.best_match_score DESC NULLS LAST, lm.momo_price DESC NULLS LAST, lm.sku
LIMIT :limit

View File

@@ -111,13 +111,19 @@ def test_competitor_feeder_persists_all_match_attempt_outcomes():
"'low_score'",
"'refresh_low_score'",
"'recoverable_low_score'",
):
assert status in retryable_source
retryable_status_list = retryable_source.split("la.attempt_status IN (", 1)[1].split(")", 1)[0]
for status in (
"'true_low_confidence'",
"'unit_comparable'",
"'refresh_unit_comparable'",
"'identity_veto'",
):
assert status in retryable_source
assert "OR la.attempt_status = 'identity_veto'" in retryable_source
assert status not in retryable_status_list
assert "COALESCE(la.hard_veto, false) = false" in retryable_source
assert "match_diagnostic_json->>'comparison_mode'" in retryable_source
assert "?| array[" in retryable_source
latest_attempt_source = retryable_source.split("latest_attempt AS", 1)[1].split(
"SELECT\n lm.product_id", 1
)[0]