From 9e2337764bb89b4bd5215ca21c6db52cf09821f3 Mon Sep 17 00:00:00 2001 From: OoO Date: Fri, 1 May 2026 16:24:15 +0800 Subject: [PATCH] fix(ai): supersede old product picks --- CONSTITUTION.md | 2 +- app.py | 4 ++-- config.py | 2 +- docs/AI_INTELLIGENCE_MODULE_SOT.md | 2 +- services/ai_product_pick_agent.py | 25 +++++++++++++++++++++++++ tests/test_frontend_v2_assets.py | 2 ++ 6 files changed, 32 insertions(+), 5 deletions(-) diff --git a/CONSTITUTION.md b/CONSTITUTION.md index ced71da..3866b74 100644 --- a/CONSTITUTION.md +++ b/CONSTITUTION.md @@ -2,7 +2,7 @@ > 本文件定義專案開發的核心準則與不可違反的規範 > **建立日期**: 2026-01-12 -> **當前版本**: V10.63 (Warm dashboard cache after AI pick regeneration) +> **當前版本**: V10.64 (Keep only latest 50 AI product picks pending) > **最後更新**: 2026-05-01 --- diff --git a/app.py b/app.py index 14cbaeb..9e734e1 100644 --- a/app.py +++ b/app.py @@ -95,8 +95,8 @@ except Exception as e: sys_log.error(f"無法檢測磁碟空間: {e}") # 🚩 系統版本定義 (備份與顯示用) -# 🚩 2026-05-01 V10.63: Warm dashboard cache after AI pick regeneration -SYSTEM_VERSION = "V10.63" +# 🚩 2026-05-01 V10.64: Keep only latest 50 AI product picks pending +SYSTEM_VERSION = "V10.64" # ========================================== # 🔒 SQL Injection 防護函數 diff --git a/config.py b/config.py index 6dc308a..2974e07 100644 --- a/config.py +++ b/config.py @@ -254,7 +254,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '') # ========================================== # 系統版本與路徑 # ========================================== -SYSTEM_VERSION = "V10.63" +SYSTEM_VERSION = "V10.64" LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log') public_url = PUBLIC_URL # 用於模板顯示 diff --git a/docs/AI_INTELLIGENCE_MODULE_SOT.md b/docs/AI_INTELLIGENCE_MODULE_SOT.md index 7809d86..b863e8a 100644 --- a/docs/AI_INTELLIGENCE_MODULE_SOT.md +++ b/docs/AI_INTELLIGENCE_MODULE_SOT.md @@ -31,7 +31,7 @@ SQL漏斗(~300筆) `services/ai_product_pick_agent.py` 新增 PChome 銷售用挑品 Agent: - 只讀真實資料表:`products`、`price_records`、`competitor_prices`、`competitor_price_history`,若 `daily_sales_snapshot` 可用則納入近 7 天銷售額、數量、毛利或成本推算毛利率。 -- 將 PChome 比 MOMO 有價格優勢、比對信心足夠、且有歷史快照或銷售動能的品項寫入 `ai_price_recommendations`。信心度不以固定倍率灌高,而是由商機分數與證據完整度共同決定,證據包含 PChome match score、歷史快照、銷售/毛利、PChome 商品 ID/名稱、抓取時間與促銷/評價/庫存標籤。 +- 將 PChome 比 MOMO 有價格優勢、比對信心足夠、且有歷史快照或銷售動能的品項寫入 `ai_price_recommendations`。信心度不以固定倍率灌高,而是由商機分數與證據完整度共同決定,證據包含 PChome match score、歷史快照、銷售/毛利、PChome 商品 ID/名稱、抓取時間與促銷/評價/庫存標籤。每次重算只保留最新 50 品為 `status='pending'`,未進榜舊品標為 `superseded`,避免統計與清單超量。 - 寫入策略使用 `strategy='product_pick'`,保留在既有 AI 決策表,不新增假頁面或暫存 JSON。 - 後台入口:`POST /api/ai/product-picks/generate`,`/ai_intelligence` 可手動產生清單。 - 配對來源仍以 PChome crawler 真實搜尋結果為準;無競品資料時不生成挑品。 diff --git a/services/ai_product_pick_agent.py b/services/ai_product_pick_agent.py index 6aa1c7e..f125b6f 100644 --- a/services/ai_product_pick_agent.py +++ b/services/ai_product_pick_agent.py @@ -423,6 +423,30 @@ def _write_pick(conn, pick: Dict[str, Any]) -> None: }) +def _supersede_old_picks(conn, current_skus: List[str]) -> None: + from sqlalchemy import bindparam, text + + if not current_skus: + conn.execute(text(""" + UPDATE ai_price_recommendations + SET status = 'superseded', + updated_at = CURRENT_TIMESTAMP + WHERE strategy = 'product_pick' + AND status = 'pending' + """)) + return + + stmt = text(""" + UPDATE ai_price_recommendations + SET status = 'superseded', + updated_at = CURRENT_TIMESTAMP + WHERE strategy = 'product_pick' + AND status = 'pending' + AND sku NOT IN :current_skus + """).bindparams(bindparam("current_skus", expanding=True)) + conn.execute(stmt, {"current_skus": [str(sku) for sku in current_skus]}) + + def generate_product_pick_list(engine, limit: int = 50) -> ProductPickResult: """產生並保存 AI 建議挑品清單。""" generated_at = datetime.now().isoformat(timespec="seconds") @@ -439,6 +463,7 @@ def generate_product_pick_list(engine, limit: int = 50) -> ProductPickResult: picks = picks[:limit] for pick in picks: _write_pick(conn, pick) + _supersede_old_picks(conn, [pick["sku"] for pick in picks]) return ProductPickResult( candidates=len(rows), diff --git a/tests/test_frontend_v2_assets.py b/tests/test_frontend_v2_assets.py index 97f10c1..79d4f18 100644 --- a/tests/test_frontend_v2_assets.py +++ b/tests/test_frontend_v2_assets.py @@ -197,6 +197,8 @@ def test_ai_product_pick_agent_uses_real_competitor_data_and_dashboard_action(): assert "evidence_quality" in agent_source assert "opportunity_score" in agent_source assert "margin_rate" in agent_source + assert "_supersede_old_picks" in agent_source + assert "status = 'superseded'" in agent_source assert "{date_col}::date" in agent_source assert "conn.rollback()" in agent_source