# database/ai_models.py from sqlalchemy import Column, Integer, String, DateTime, Text, Float, ForeignKey, Index from sqlalchemy.orm import relationship from database.models import Base from datetime import datetime, timezone # Helper for default timestamps datetime_now = lambda: datetime.now(timezone.utc) class AgentContext(Base): """ 共享上下文表(替代硬編碼鏈),支援多 Agent 存取與 TTL。 索引:(session_id, agent_name, context_key) 以加速跨 Agent 查詢。 """ __tablename__ = 'agent_context' id = Column(Integer, primary_key=True, autoincrement=True) session_id = Column(String(64), nullable=False, index=True) agent_name = Column(String(50), nullable=False, index=True) context_key = Column(String(100), nullable=False) context_val = Column(Text) # JSON 字串 created_at = Column(DateTime, default=datetime_now) ttl_minutes = Column(Integer, default=60) __table_args__ = ( Index('idx_agent_context_session_key', 'session_id', 'agent_name', 'context_key'), Index('idx_agent_context_session_ttl', 'session_id', 'created_at'), ) class ActionPlan(Base): """ 行動計畫表(NemoTron 輸出,等待審核與執行追蹤)。 """ __tablename__ = 'action_plans' id = Column(Integer, primary_key=True, autoincrement=True) session_id = Column(String(64), nullable=True) plan_type = Column(String(50), nullable=True) # price_adjust / restock / campaign sku = Column(String(100), nullable=True, index=True) payload = Column(Text) # JSON 行動內容 status = Column(String(20), default='pending') # pending/approved/rejected/executed created_by = Column(String(50)) # nemotron / openclaw approved_by = Column(String(100), nullable=True) # Telegram user_id created_at = Column(DateTime, default=datetime_now) executed_at = Column(DateTime, nullable=True) __table_args__ = ( Index('idx_action_plan_sku_status', 'sku', 'status'), Index('idx_action_plan_created', 'created_at'), ) class ActionOutcome(Base): """ 行動結果追蹤(閉環學習核心)。 """ __tablename__ = 'action_outcomes' id = Column(Integer, primary_key=True, autoincrement=True) plan_id = Column(Integer, ForeignKey('action_plans.id'), nullable=False) metric_type = Column(String(50), nullable=True) # sales_7d / price_rank / conversion before_val = Column(Float) after_val = Column(Float) measured_at = Column(DateTime) verdict = Column(String(20)) # effective / neutral / backfired created_at = Column(DateTime, default=datetime_now) plan = relationship("ActionPlan", backref="outcomes") class AgentStrategyWeights(Base): """ Agent 策略權重(OpenClaw 學習累積)。 索引:strategy_key 以便快速更新與查詢。 """ __tablename__ = 'agent_strategy_weights' id = Column(Integer, primary_key=True, autoincrement=True) strategy_key = Column(String(100), unique=True, nullable=False) # e.g. price_cut_when_gap_gt_5pct weight = Column(Float, default=1.0) success_cnt = Column(Integer, default=0) fail_cnt = Column(Integer, default=0) updated_at = Column(DateTime, default=datetime_now) __table_args__ = ( Index('idx_strategy_key', 'strategy_key'), )