AgentEvalTool/backend/agenteval/storage/db/intelligent_eval_tables.py
sinohqb ca208232c7 perf(db): WAL 模式 + 性能索引 + N+1 查询消除
- SQLite 启用 WAL,允许读写并发
- 新增 6 个索引(eval_runs.status/campaign_id、eval_results.run_id、
  turns.run_id、intelligent_evals.status、task_queue.assigned_at)
- 幂等 Alembic 迁移(列/索引存在性检查)
- domain.py 计数改 func.count 聚合,get_attention_reason 单次加载 sessions
- scenario list_all 批量加载 bindings(1+N → 2 查询)
- mark_orphans_failed 批量加载 campaigns(N → 1 IN 查询)
2026-08-24 23:17:51 +08:00

216 lines
7.1 KiB
Python

"""Intelligent evaluation tables (智能评估)."""
from datetime import datetime
from typing import Any, Optional
import sqlalchemy as sa
from sqlmodel import Field, SQLModel
from agenteval.storage.db.core import _json_dumps, _json_loads, new_uuid, utc_now
class IntelligentEvalDB(SQLModel, table=True):
"""Intelligent evaluation (智能评估) — independent entity, peer to Campaign."""
__tablename__ = "intelligent_evals"
__table_args__ = (sa.Index("ix_intelligent_evals_status", "status"),)
id: Optional[str] = Field(default_factory=new_uuid, primary_key=True)
name: str
target_id: str = Field(foreign_key="eval_targets.id")
status: str = "draft"
# User input (四件套)
goal: str = ""
seeds: str = "{}"
intent: str = ""
role_description: str = ""
# Coarse plan (OpenClaw produces)
plan: Optional[str] = None
plan_feedback: Optional[str] = None
# Time window
time_window_hours: int = 24
# Report
report: Optional[str] = None
# Timestamps
created_at: Optional[datetime] = Field(default_factory=utc_now)
updated_at: Optional[datetime] = Field(default_factory=utc_now)
started_at: Optional[datetime] = None
completed_at: Optional[datetime] = None
def get_seeds(self) -> dict[str, Any]:
return _json_loads(self.seeds)
def set_seeds(self, seeds: dict[str, Any]) -> None:
self.seeds = _json_dumps(seeds)
def get_plan(self) -> Optional[dict[str, Any]]:
return _json_loads(self.plan) if self.plan else None
def set_plan(self, plan: dict[str, Any]) -> None:
self.plan = _json_dumps(plan)
def get_report(self) -> Optional[dict[str, Any]]:
return _json_loads(self.report) if self.report else None
def set_report(self, report: dict[str, Any]) -> None:
self.report = _json_dumps(report)
class IntelligentEvalSessionDB(SQLModel, table=True):
"""A virtual-user session within an intelligent evaluation."""
__tablename__ = "intelligent_eval_sessions"
id: Optional[str] = Field(default_factory=new_uuid, primary_key=True)
eval_id: str = Field(index=True, foreign_key="intelligent_evals.id")
target_id: str = Field(foreign_key="eval_targets.id")
persona: str = "{}"
goal: str = ""
dimension: Optional[str] = None
status: str = "running"
verdict: Optional[str] = None
turn_count: int = 0
created_at: Optional[datetime] = Field(default_factory=utc_now)
closed_at: Optional[datetime] = None
def get_persona(self) -> dict[str, Any]:
return _json_loads(self.persona)
def set_persona(self, persona: dict[str, Any]) -> None:
self.persona = _json_dumps(persona)
def get_verdict(self) -> Optional[dict[str, Any]]:
return _json_loads(self.verdict) if self.verdict else None
def set_verdict(self, verdict: dict[str, Any]) -> None:
self.verdict = _json_dumps(verdict)
class IntelligentEvalMessageDB(SQLModel, table=True):
"""One chat message inside an intelligent eval session."""
__tablename__ = "intelligent_eval_messages"
id: Optional[str] = Field(default_factory=new_uuid, primary_key=True)
session_id: str = Field(index=True, foreign_key="intelligent_eval_sessions.id")
role: str = "user"
content: str = ""
latency_ms: Optional[int] = None
created_at: Optional[datetime] = Field(default_factory=utc_now)
class IntelligentEvalTaskQueueDB(SQLModel, table=True):
"""Task queue for intelligent evaluations (任务队列).
Platform scans executing evals every minute and enqueues tasks for
OpenClaw workers to pick up.
"""
__tablename__ = "intelligent_eval_task_queue"
id: Optional[str] = Field(default_factory=new_uuid, primary_key=True)
eval_id: str = Field(index=True, foreign_key="intelligent_evals.id")
# Task status
status: str = Field(index=True) # pending / assigned / completed / failed
priority: int = Field(index=True) # Lower is higher priority
reason: str # Why this task needs attention (e.g., "slot_due", "all_sessions_completed")
# Retry budget (ADR-0011): each stale requeue increments; >= MAX caps to failed
attempts: int = Field(default=0)
# Assignment info
assigned_cron_id: Optional[str] = None
assigned_at: Optional[datetime] = None
# Completion info
completed_at: Optional[datetime] = None
error: Optional[str] = None
# Timestamps
created_at: Optional[datetime] = Field(default_factory=utc_now)
updated_at: Optional[datetime] = Field(default_factory=utc_now)
__table_args__ = (
sa.Index("idx_task_queue_status_priority", "status", "priority"),
sa.Index("idx_task_queue_eval_status", "eval_id", "status"),
sa.Index("idx_task_queue_assigned_at", "assigned_at"),
)
class IntelligentEvalConfigSnapshotDB(SQLModel, table=True):
"""Config snapshot for intelligent evaluations (配置快照).
Automatically saved when eval is created, plan is submitted, or config is updated.
"""
__tablename__ = "intelligent_eval_config_snapshots"
id: Optional[str] = Field(default_factory=new_uuid, primary_key=True)
eval_id: str = Field(index=True, foreign_key="intelligent_evals.id")
# Snapshot type
snapshot_type: str # created / plan_submitted / config_updated
# Config snapshot
goal: str
seeds: str # JSON
intent: str
role_description: str
time_window_hours: int
plan: Optional[str] = None # JSON, coarse plan snapshot
# Metadata
created_at: Optional[datetime] = Field(default_factory=utc_now)
created_by: str = "user" # user / openclaw
__table_args__ = (sa.Index("idx_config_snapshots_eval_created", "eval_id", "created_at"),)
def get_seeds(self) -> dict[str, Any]:
return _json_loads(self.seeds)
def set_seeds(self, seeds: dict[str, Any]) -> None:
self.seeds = _json_dumps(seeds)
def get_plan(self) -> Optional[dict[str, Any]]:
return _json_loads(self.plan) if self.plan else None
def set_plan(self, plan: dict[str, Any]) -> None:
self.plan = _json_dumps(plan)
class IntelligentEvalDecisionLogDB(SQLModel, table=True):
"""Decision log for intelligent evaluations (决策日志).
Records every decision made by OpenClaw workers (execute_session / wait / start_analysis).
"""
__tablename__ = "intelligent_eval_decision_logs"
id: Optional[str] = Field(default_factory=new_uuid, primary_key=True)
eval_id: str = Field(index=True, foreign_key="intelligent_evals.id")
# Decision info
decision_type: str # execute_session / wait / start_analysis
reason: str # Why this decision was made
context: str # JSON, decision context (current time slot, completed sessions, etc.)
# Execution info
cron_id: str # Which cron made this decision
# Timestamp
created_at: Optional[datetime] = Field(default_factory=utc_now)
__table_args__ = (sa.Index("idx_decision_logs_eval_created", "eval_id", "created_at"),)
def get_context(self) -> dict[str, Any]:
return _json_loads(self.context)
def set_context(self, context: dict[str, Any]) -> None:
self.context = _json_dumps(context)