diff --git a/CONSTITUTION.md b/CONSTITUTION.md index ba5bbae..9b597ab 100644 --- a/CONSTITUTION.md +++ b/CONSTITUTION.md @@ -2,7 +2,7 @@ > 本文件定義專案開發的核心準則與不可違反的規範 > **建立日期**: 2026-01-12 -> **當前版本**: V10.50 (Vendor stockout query service extraction) +> **當前版本**: V10.51 (Price adjustment actions require human review) > **最後更新**: 2026-05-01 --- diff --git a/app.py b/app.py index 32b8b69..824fda7 100644 --- a/app.py +++ b/app.py @@ -95,8 +95,8 @@ except Exception as e: sys_log.error(f"無法檢測磁碟空間: {e}") # 🚩 系統版本定義 (備份與顯示用) -# 🚩 2026-05-01 V10.50: Vendor stockout query service extraction -SYSTEM_VERSION = "V10.50" +# 🚩 2026-05-01 V10.51: Price adjustment actions require human review +SYSTEM_VERSION = "V10.51" # ========================================== # 🔒 SQL Injection 防護函數 diff --git a/docs/AI_INTELLIGENCE_MODULE_SOT.md b/docs/AI_INTELLIGENCE_MODULE_SOT.md index b171ba1..0b5595c 100644 --- a/docs/AI_INTELLIGENCE_MODULE_SOT.md +++ b/docs/AI_INTELLIGENCE_MODULE_SOT.md @@ -66,6 +66,7 @@ SQL漏斗(~300筆) - `momo-db` / `momo-postgres` 不可被 AI 自動 restart / stop / recreate。 - raw `ai_insights` insert 必須接 `enqueue_insight_embedding()` 或可被 backfill。 - ElephantAlpha 只做編排與 bridge,不可繞過 ADR-011 / ADR-012 / ADR-013。 +- ElephantAlpha / NemoTron 不可直接執行商品價格調整;`execute_price_adjustment`、`adjust_price` 等動作必須攔截並寫入 `human_review`,等待人工核准。 可觀測性: diff --git a/services/elephant_alpha_autonomous_engine.py b/services/elephant_alpha_autonomous_engine.py index 4397109..264bfd0 100644 --- a/services/elephant_alpha_autonomous_engine.py +++ b/services/elephant_alpha_autonomous_engine.py @@ -103,10 +103,20 @@ _ACTION_ZH = { "generate_market_analysis": "市場分析", "generate_pricing_strategy": "定價策略建議", "generate_meta_analysis": "AI 系統自我審視", + "execute_price_adjustment": "價格調整覆核", "adjust_price": "調整定價", "send_alert": "發送告警", } +_PRICE_ADJUSTMENT_REVIEW_ACTIONS = frozenset({ + "execute_price_adjustment", + "adjust_price", + "apply_price_change", + "update_price", + "dispatch_price_update", + "dispatch_price_updates", +}) + def _zh_trigger(trigger_type: str) -> str: return _TRIGGER_ZH.get(trigger_type, trigger_type) @@ -559,7 +569,7 @@ class ElephantAlphaAutonomousEngine: async def _execute_step(self, step: Dict[str, Any]) -> None: agent_type = step.get("agent", "").lower() action = step.get("action", "") - params = step.get("parameters", {}) + params = step.get("parameters") or step.get("params") or {} if agent_type == "hermes" and action == "analyze_price_competition": return await self._run_with_timeout( @@ -579,7 +589,10 @@ class ElephantAlphaAutonomousEngine: return if agent_type == "openclaw" and action in ( - "generate_strategic_analysis", "generate_weekly_strategy", "generate_market_analysis" + "generate_strategic_analysis", + "generate_weekly_strategy", + "generate_market_analysis", + "generate_pricing_strategy", ): return await self._run_with_timeout( self._generate_strategy_report, @@ -608,6 +621,13 @@ class ElephantAlphaAutonomousEngine: timeout=SSH_COMMAND_TIMEOUT, ) + if action in _PRICE_ADJUSTMENT_REVIEW_ACTIONS: + return await self._run_with_timeout( + self._record_price_adjustment_review, + step, + timeout=SSH_COMMAND_TIMEOUT, + ) + raise ValueError(f"Unrecognized step: agent={agent_type} action={action}") # ---- Sub-services ---- @@ -638,6 +658,64 @@ class ElephantAlphaAutonomousEngine: payload.setdefault("error_type", error_type) return auto_heal_service.handle_exception(error_type=error_type, context=payload) + def _record_price_adjustment_review(self, step: Dict[str, Any]) -> Dict[str, Any]: + """ + Price changes are business-critical. Elephant Alpha may recommend them, + but this system records the proposal for HITL review instead of applying it. + """ + params = step.get("parameters") or step.get("params") or {} + sku = ( + params.get("sku") + or params.get("product_sku") + or params.get("i_code") + or params.get("item_id") + or "unknown" + ) + action = step.get("action", "price_adjustment") + content = ( + f"[Elephant Alpha 價格調整覆核] AI 建議執行 {action}," + f"商品 {sku} 已攔截直接執行並轉入人工審核。" + ) + + session = get_session() + try: + row = session.execute( + text(""" + INSERT INTO ai_insights + (insight_type, content, confidence, created_by, status, metadata_json) + VALUES (:type, :content, :confidence, :created_by, :status, :metadata) + RETURNING id + """), + { + "type": "human_review", + "content": content, + "confidence": 0.8, + "created_by": "elephant_alpha", + "status": "pending", + "metadata": json.dumps({ + "source": "price_adjustment_review", + "step": step, + "sku": sku, + "reason": "price_adjustment_requires_human_approval", + }, ensure_ascii=False), + }, + ).fetchone() + session.commit() + insight_id = row[0] if row else None + if insight_id: + try: + from services.openclaw_learning_service import enqueue_insight_embedding + enqueue_insight_embedding(insight_id, "human_review", content) + except Exception as embed_err: + self._log.warning("Embedding enqueue failed for price adjustment review: %s", embed_err) + self._log.warning("Price adjustment intercepted for HITL review: action=%s sku=%s", action, sku) + return {"status": "pending_review", "insight_id": insight_id, "sku": sku, "action": action} + except Exception: + session.rollback() + raise + finally: + session.close() + # ---- Notification ---- async def _notify_telegram_executed( self, diff --git a/services/elephant_alpha_orchestrator.py b/services/elephant_alpha_orchestrator.py index 2b33a71..53902a3 100644 --- a/services/elephant_alpha_orchestrator.py +++ b/services/elephant_alpha_orchestrator.py @@ -146,6 +146,22 @@ DECISION FRAMEWORK: 6. Create detailed execution plans 7. Monitor and adapt based on outcomes +ALLOWED EXECUTION ACTIONS: +- hermes / analyze_price_competition +- nemotron / dispatch_alert +- openclaw / generate_strategic_analysis +- openclaw / generate_weekly_strategy +- openclaw / generate_market_analysis +- openclaw / generate_pricing_strategy +- openclaw / generate_meta_analysis +- elephant_alpha / auto_heal +- elephant_alpha / code_fix + +價格調整紅線: +- 禁止輸出 execute_price_adjustment、adjust_price、apply_price_change、update_price。 +- 若需要調價,請改輸出 openclaw / generate_pricing_strategy,並在 description 說明「需要人工核准後才能調價」。 +- 本系統不得由 AI 直接修改商品價格,只能產生建議與人工覆核項目。 + BUSINESS CONTEXT: - E-commerce platform (momo-pro-system) - Real-time price competition monitoring diff --git a/tests/test_elephant_alpha_engine.py b/tests/test_elephant_alpha_engine.py index 745af0e..3af9e19 100644 --- a/tests/test_elephant_alpha_engine.py +++ b/tests/test_elephant_alpha_engine.py @@ -42,6 +42,27 @@ def test_execute_step_routes_code_fix_to_autoheal(monkeypatch): assert calls == [("python_exception", {"target_file": "services/example.py", "error_message": "Traceback"})] +def test_execute_step_routes_price_adjustment_to_human_review(monkeypatch): + from services.elephant_alpha_autonomous_engine import ElephantAlphaAutonomousEngine + + calls = [] + engine = ElephantAlphaAutonomousEngine() + monkeypatch.setattr( + engine, + "_record_price_adjustment_review", + lambda step: calls.append(step) or {"status": "pending_review", "sku": "SKU-9"}, + ) + + result = asyncio.run(engine._execute_step({ + "agent": "nemotron", + "action": "execute_price_adjustment", + "parameters": {"sku": "SKU-9", "recommended_price": 1280}, + })) + + assert result == {"status": "pending_review", "sku": "SKU-9"} + assert calls[0]["parameters"]["recommended_price"] == 1280 + + def test_autoheal_derives_python_exception_from_traceback(): from services.auto_heal_service import AutoHealService