V10.535 優化 ElephantAlpha 價格 trigger 查詢

This commit is contained in:
OoO
2026-06-01 02:21:56 +08:00
committed by AiderHeal Bot
parent 2a11ba26a7
commit 5e1428c887
6 changed files with 136 additions and 52 deletions

View File

@@ -4,6 +4,7 @@
================================================================================
【已完成】
- V10.535 修 ElephantAlpha 價格 trigger statement timeout`price_drop_alert` / `market_opportunity` / DB evidence prefetch 改為先篩最近有效 PChome identity_v2再用 `JOIN LATERAL` 查單一 SKU 最新 MOMO 價格;保留 match_score/tags/diagnostic evidence避免 scheduler 週期性重查整張 `price_records`。
- V10.534 收緊 PChome rescore accepted gate`no_match / price_basis=none / alert_tier=suppress` 不得再進 `rescore_accepted_current`,並新增 `--retract-unsafe-accepted` 退回舊的 unsafe accepted rowsDashboard / daily / growth / OpenClaw 文案改為「重算待人工覆核」,避免操作員把人工覆核隊列誤解為可直接採用或可自動寫價。
- V10.533 補 ElephantAlpha legacy OpenClaw advisory 相容:`generate_dynamic_pricing_strategy` 與既有 `generate_market_strategy` / `generate_resource_optimization_strategy` 一樣只記錄為 skipped不再觸發 `Unrecognized step` 與 circuit breaker避免舊協調器輸出的建議型動態定價步驟被誤解為真正可執行任務。
- V10.532 修正 PChome coverage / review queue 口徑落差:`fetch_competitor_coverage()` 的 `attempt_status` / `rescore_accepted_count` / `actionable_review_count` 改跟 review queue 一樣統計「沒有新鮮有效 identity」的商品而不是只看「完全沒有 identity」這讓已過期 identity 的 `rescore_accepted_current` 待審能正確顯示在 Dashboard / 狀態 API。

View File

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

View File

@@ -160,6 +160,7 @@ SQL漏斗(~300筆)
- ElephantAlpha 使用 NVIDIA NIM hosted APIproduction 預設模型為 `nvidia/llama-3.3-nemotron-super-49b-v1.5``ELEPHANT_ALPHA_FALLBACK_MODELS` 需保留至少一個可呼叫備援403/404、408/409/425/429、5xx、timeout 與 connection error 必須嘗試下一個模型。
- ElephantAlpha L3 HITL 只允許發送有實證、可審核、可行動的升級告警;價格類 trigger 無 Hermes 具體威脅時,只記錄 suppressed escalation telemetry 與 cooldown不寫 pending `human_review`,不發 Telegram 空告警。
- ElephantAlpha 價格類 trigger 的 HITL / 決策 prefetch 必須先使用觸發 SQL 與 `competitor_prices` / `price_records` 的 DB 實證生成 SKU、MOMO / PChome 價差與建議 action lines完整 Hermes LLM prefetch 預設關閉(`ELEPHANT_ALPHA_HERMES_LLM_PREFETCH_ENABLED=false`),避免 5s timeout 後落入無實證摘要或雲端備援。若無 DB 實證,只記錄 suppressed telemetry / cooldown不發 Telegram 空告警。
- ElephantAlpha `price_drop_alert` / `market_opportunity` trigger 不得對整張 `price_records` 做全表最新價聚合;必須先篩最近有效 `identity_v2` PChome 候選,再用 per-SKU `JOIN LATERAL` 讀最新 MOMO 價格,並把 `match_score``tags``match_diagnostic_json` 帶入 evidence。
- ElephantAlpha 協調器收到非純 JSON、fenced JSON 或混文字 JSON 時,必須先做容錯抽取;仍無法解析時,只能使用 DB/Hermes 實證生成保守 HITL fallback。fallback 不得放入 OpenClaw `generate_*` 類舊策略步驟,也不得暗示已自動調價。
- ElephantAlpha 執行器若遇到舊版 OpenClaw strategy 類步驟(含 `generate_market_strategy` / `generate_dynamic_pricing_strategy` / `generate_resource_optimization_strategy`),只能記錄為 advisory skipped不得觸發 circuit breaker也不得轉成實際排程、外部呼叫或價格行動。
- `resource_optimization` 不再交給 LLM 生成「預期效益 / 已執行」敘事,顯示名稱統一為「資源壓力治理」。此 trigger 必須先由程式量測 `action_plans` backlog、P1/P2 數、pending_review、逾時項目與 CPU load只有 CPU 達門檻、P1/P2 積壓或逾時積壓才發 Telegram「資源壓力告警」。單純 queue 大但 CPU 正常只記錄 telemetry不派發 Hermes/NemoTron、不宣稱 48 小時效益Telegram 段落使用「系統處置紀錄」而非泛稱「已執行」,避免暗示 AI 已完成未經驗證的外部動作。

View File

@@ -13,6 +13,7 @@
## 📅 詳細更新日誌 (考古存檔)
### 2026-06-01PChome 比價新鮮度操作閉環
- **V10.535 ElephantAlpha price trigger 查詢瘦身**: 正式 scheduler 日誌顯示 `price_drop_alert` trigger 對整張 `price_records``DISTINCT ON` 最新價造成 statement timeout。`price_drop_alert``market_opportunity` 與 EA DB evidence prefetch 改為先篩最近有效 PChome identity_v2 競品,再用 `JOIN LATERAL` 只查該 SKU 最新 MOMO 價格,保留 match_score/tags/diagnostic evidence 給 Telegram HITL不再用全表最新價子查詢。
- **V10.534 rescore accepted gate 收緊與語意修正**: 正式 96 筆 `rescore_accepted_current` 盤點顯示多數仍是 `manual_review / identity_review`,且有 2 筆 `no_match / none / suppress` 混入。收緊 `classify_match_attempt_row()``no_match``price_basis=none``alert_tier=suppress` 不得 gate pass新增 `--retract-unsafe-accepted` 可把既有 unsafe accepted 退回 `true_low_confidence`。Dashboard / daily / growth / OpenClaw 文案改成「重算待人工覆核」,明確表示仍需人工確認身份後才可寫正式價差。
- **V10.533 ElephantAlpha legacy OpenClaw advisory 相容**: 正式 scheduler 日誌出現 `Unrecognized step: agent=openclaw action=generate_dynamic_pricing_strategy`,屬於舊協調器把建議型策略文字放進 execution plan。執行器現在將 `generate_dynamic_pricing_strategy` 納入既有 OpenClaw advisory no-op 清單,只記錄 skipped不觸發 circuit breaker也不轉成自動調價或外部呼叫。
- **V10.532 coverage / review queue 口徑對齊**: V10.531 materialize 96 筆 `rescore_accepted_current`DB 最新狀態正確,但 `/api/ai/pchome-match/backfill/status``rescore_accepted_count` 仍為 0。原因是 coverage 的 `attempt_status` 統計只看「完全沒有 identity」商品而 review queue 看的是「沒有新鮮有效 identity」商品。改為以 `fresh_competitor` 排除條件統計,讓 stale identity 的重算可採用待審能正確上屏;正式價差表仍未被 rescore materialize 寫入。

View File

@@ -393,23 +393,46 @@ class ElephantAlphaAutonomousEngine:
rows = session.execute(
text("""
SELECT p.i_code AS sku, p.name, p.category,
cp.price AS competitor_price, pr.price AS momo_price,
((pr.price - cp.price) / NULLIF(pr.price, 0) * 100) AS price_gap_pct,
cp.competitor_price, pr.momo_price,
((pr.momo_price - cp.competitor_price) / NULLIF(pr.momo_price, 0) * 100) AS price_gap_pct,
cp.competitor_product_id,
cp.competitor_product_name,
cp.match_score,
cp.tags,
cp.match_diagnostic_json,
cp.crawled_at
FROM products p
JOIN (
SELECT DISTINCT ON (product_id) product_id, price
FROM price_records
ORDER BY product_id, timestamp DESC
) pr ON pr.product_id = p.id
JOIN competitor_prices cp ON cp.sku = p.i_code
WHERE cp.expires_at > NOW()
AND COALESCE(cp.match_score, 0) >= 0.76
AND COALESCE(cp.tags, '[]'::jsonb) ? 'identity_v2'
AND cp.price < pr.price * 0.85
AND cp.crawled_at >= NOW() - INTERVAL '2 hours'
FROM (
SELECT cp.sku,
cp.price AS competitor_price,
cp.competitor_product_id,
cp.competitor_product_name,
cp.match_score,
cp.tags,
cp.match_diagnostic_json,
cp.crawled_at
FROM competitor_prices cp
WHERE cp.source = 'pchome'
AND (cp.expires_at IS NULL OR cp.expires_at > NOW())
AND cp.price IS NOT NULL
AND cp.price > 0
AND cp.crawled_at >= NOW() - INTERVAL '2 hours'
AND COALESCE(cp.match_score, 0) >= 0.76
AND COALESCE(cp.tags, '[]'::jsonb) ? 'identity_v2'
) cp
JOIN products p ON p.i_code = cp.sku
JOIN LATERAL (
SELECT pr.price AS momo_price
FROM price_records pr
WHERE pr.product_id = p.id
AND pr.price IS NOT NULL
AND pr.price > 0
ORDER BY pr.timestamp DESC, pr.id DESC
LIMIT 1
) pr ON TRUE
WHERE p.status = 'ACTIVE'
AND cp.competitor_price < pr.momo_price * 0.85
ORDER BY ((pr.momo_price - cp.competitor_price) / NULLIF(pr.momo_price, 0)) DESC NULLS LAST,
cp.crawled_at DESC NULLS LAST
LIMIT 10
""")
).mappings().fetchall()
@@ -430,23 +453,46 @@ class ElephantAlphaAutonomousEngine:
rows = session.execute(
text("""
SELECT p.i_code AS sku, p.name, p.category,
cp.price AS competitor_price, pr.price AS momo_price,
((pr.price - cp.price) / NULLIF(pr.price, 0) * 100) AS price_gap_pct,
cp.competitor_price, pr.momo_price,
((pr.momo_price - cp.competitor_price) / NULLIF(pr.momo_price, 0) * 100) AS price_gap_pct,
cp.competitor_product_id,
cp.competitor_product_name,
cp.match_score,
cp.tags,
cp.match_diagnostic_json,
cp.crawled_at
FROM products p
JOIN (
SELECT DISTINCT ON (product_id) product_id, price
FROM price_records
ORDER BY product_id, timestamp DESC
) pr ON pr.product_id = p.id
JOIN competitor_prices cp ON cp.sku = p.i_code
WHERE cp.expires_at > NOW()
AND COALESCE(cp.match_score, 0) >= 0.76
AND COALESCE(cp.tags, '[]'::jsonb) ? 'identity_v2'
AND cp.price > pr.price * 1.05
AND cp.crawled_at >= NOW() - INTERVAL '1 hour'
FROM (
SELECT cp.sku,
cp.price AS competitor_price,
cp.competitor_product_id,
cp.competitor_product_name,
cp.match_score,
cp.tags,
cp.match_diagnostic_json,
cp.crawled_at
FROM competitor_prices cp
WHERE cp.source = 'pchome'
AND (cp.expires_at IS NULL OR cp.expires_at > NOW())
AND cp.price IS NOT NULL
AND cp.price > 0
AND cp.crawled_at >= NOW() - INTERVAL '1 hour'
AND COALESCE(cp.match_score, 0) >= 0.76
AND COALESCE(cp.tags, '[]'::jsonb) ? 'identity_v2'
) cp
JOIN products p ON p.i_code = cp.sku
JOIN LATERAL (
SELECT pr.price AS momo_price
FROM price_records pr
WHERE pr.product_id = p.id
AND pr.price IS NOT NULL
AND pr.price > 0
ORDER BY pr.timestamp DESC, pr.id DESC
LIMIT 1
) pr ON TRUE
WHERE p.status = 'ACTIVE'
AND cp.competitor_price > pr.momo_price * 1.05
ORDER BY ((cp.competitor_price - pr.momo_price) / NULLIF(pr.momo_price, 0)) DESC NULLS LAST,
cp.crawled_at DESC NULLS LAST
LIMIT 5
""")
).mappings().fetchall()
@@ -1468,21 +1514,7 @@ class ElephantAlphaAutonomousEngine:
try:
rows = session.execute(
text("""
WITH latest_momo AS (
SELECT DISTINCT ON (p.i_code)
p.i_code AS sku,
p.name,
p.category,
pr.price AS momo_price,
pr.timestamp
FROM products p
JOIN price_records pr ON pr.product_id = p.id
WHERE p.status = 'ACTIVE'
AND pr.price IS NOT NULL
AND pr.price > 0
ORDER BY p.i_code, pr.timestamp DESC, pr.id DESC
),
latest_competitor AS (
WITH latest_competitor AS (
SELECT DISTINCT ON (cp.sku)
cp.sku,
cp.price AS competitor_price,
@@ -1502,21 +1534,33 @@ class ElephantAlphaAutonomousEngine:
AND cp.crawled_at >= NOW() - INTERVAL '2 hours'
ORDER BY cp.sku, cp.crawled_at DESC NULLS LAST
)
SELECT lm.sku, lm.name, lm.category,
lm.momo_price,
SELECT p.i_code AS sku, p.name, p.category,
pr.momo_price,
lc.competitor_price,
((lm.momo_price - lc.competitor_price) / NULLIF(lm.momo_price, 0) * 100) AS price_gap_pct,
((pr.momo_price - lc.competitor_price) / NULLIF(pr.momo_price, 0) * 100) AS price_gap_pct,
lc.competitor_product_id,
lc.competitor_product_name,
lc.match_score,
lc.tags,
lc.match_diagnostic_json,
lc.crawled_at
FROM latest_momo lm
JOIN latest_competitor lc ON lc.sku = lm.sku
WHERE lc.competitor_price < lm.momo_price * 0.85
OR lc.competitor_price > lm.momo_price * 1.05
ORDER BY ABS((lm.momo_price - lc.competitor_price) / NULLIF(lm.momo_price, 0)) DESC NULLS LAST,
FROM latest_competitor lc
JOIN products p ON p.i_code = lc.sku
JOIN LATERAL (
SELECT pr.price AS momo_price
FROM price_records pr
WHERE pr.product_id = p.id
AND pr.price IS NOT NULL
AND pr.price > 0
ORDER BY pr.timestamp DESC, pr.id DESC
LIMIT 1
) pr ON TRUE
WHERE p.status = 'ACTIVE'
AND (
lc.competitor_price < pr.momo_price * 0.85
OR lc.competitor_price > pr.momo_price * 1.05
)
ORDER BY ABS((pr.momo_price - lc.competitor_price) / NULLIF(pr.momo_price, 0)) DESC NULLS LAST,
lc.crawled_at DESC NULLS LAST
LIMIT :limit
"""),

View File

@@ -198,6 +198,43 @@ def test_execute_autonomous_decision_uses_db_evidence_without_hermes_prefetch(mo
assert notified == ["price_drop_alert"]
def test_price_trigger_queries_use_lateral_latest_price_lookup(monkeypatch):
import services.elephant_alpha_autonomous_engine as engine_module
from services.elephant_alpha_autonomous_engine import (
AutonomousTrigger,
ElephantAlphaAutonomousEngine,
)
captured_sql = []
class _FakeResult:
def mappings(self):
return self
def fetchall(self):
return []
class _FakeSession:
def execute(self, statement, params=None):
captured_sql.append(str(statement))
return _FakeResult()
def close(self):
return None
monkeypatch.setattr(engine_module, "get_session", lambda: _FakeSession())
engine = ElephantAlphaAutonomousEngine()
asyncio.run(engine._check_price_drop_trigger(AutonomousTrigger("price_drop_alert", {}, 0.8, True)))
asyncio.run(engine._check_market_opportunity_trigger(AutonomousTrigger("market_opportunity", {}, 0.8, True)))
assert engine._fetch_recent_competitor_evidence_actions(top_n=2) is None
assert len(captured_sql) == 3
assert all("JOIN LATERAL" in sql for sql in captured_sql)
assert all("SELECT DISTINCT ON (product_id)" not in sql for sql in captured_sql)
assert all("latest_momo" not in sql for sql in captured_sql)
def test_escalate_resource_optimization_without_evidence_is_suppressed(monkeypatch):
import services.elephant_alpha_autonomous_engine as engine_module
from services.elephant_alpha_autonomous_engine import (