11 KiB
AGENTS.md
This file provides guidance to Codex (Codex.ai/code) when working with code in this repository.
项目概述
AgentEvalTool 是智能体质量评估工具集平台(v0.3.0-dev),用于评估 AI 数字员工(tutu-api 通道)和 AI 助手(OpenClaw)的服务质量。Python 3.11 后端 + TypeScript/React 前端,SQLite 持久化。
常用命令
后端
# 安装(含 dev 依赖)
pip install -e ".[dev]"
# 启动 Web 后台
agenteval server start --host 0.0.0.0 --port 8000
# 运行全部测试
pytest
# 运行单个测试文件
pytest tests/unit/test_engine.py -v
# 运行带覆盖率的测试
pytest --cov=backend/agenteval --cov-report=term-missing
# Lint
ruff check backend/
ruff format backend/
前端
cd frontend/web
npm install
npm run dev # 开发服务器
npm run build # 构建生产产物(部署前必须执行)
npx tsc --noEmit # 类型检查
数据库迁移
alembic upgrade head # 升级到最新
alembic revision --autogenerate -m "描述" # 生成新迁移
注意:init_db() 调用 SQLModel.metadata.create_all() 直接建表(用于全新 DB),Alembic 用于已有 DB 的增量变更。migrations/env.py 设置了 render_as_batch=True(SQLite 不支持 ALTER TABLE,batch mode 通过重建表来模拟)。
部署(t480)
scripts/deploy-t480.sh # 一键部署(推荐)
scripts/deploy-t480.sh --skip-build # 仅代码变更,跳过镜像重建
scripts/deploy-t480.sh --dry-run # 预览命令
核心架构
三层结构
CLI (Typer) / Web API (FastAPI)
↓
EvalEngine(异步评测执行引擎)
↓ 通过抽象接口
EvalChannel → 消息通道(TutuApiChannel)
EvalRule → 评估规则(keyword_match / response_time / llm_score)
↓
SQLite Storage(Repository 模式)
关键抽象(均在 backend/agenteval/)
channels/base.py—EvalChannel抽象:send()/poll_reply()/health_check(),V1 实现为TutuApiChannel(tutu-api HTTP + Bearer Token)evaluation/engine.py—EvalEngine:异步执行,遍历 Scenario.cases → messages → 调用 channel → 执行规则 → 写入 DB;通过asyncio.Eventcancel_token 支持真取消;progress_callback实时推送事件给 WebSocketevaluation/rules/—EvalRule抽象,三种实现:keyword.py(关键词匹配)、response_time.py(延迟阈值)、llm_score.py(LLM-as-judge)models.py— 共享 Pydantic 模型:EvalTarget→EvalRun←Scenario,EvalRun→Turn+EvalResultstorage/—db.py(SQLite StaticPool + SQLModel 表定义)+repository.py(TargetRepository/ScenarioRepository/RunRepository/ResultRepository)+file_repository.py(FileCategoryRepository/FileRecordRepository)config/settings.py— Pydantic Settings,加载AGENTEVAL_前缀的环境变量(.env文件),@lru_cache单例web/deps.py— FastAPI 依赖注入:get_db()(Session 生命周期管理)、require_api_key()(X-API-Key 鉴权)
扩展性模式:
- 规则注册表(
evaluation/rules/__init__.py):@register_rule装饰器将规则类注册到_RULE_REGISTRY,get_rule(type, params)按名称实例化。添加新规则只需创建类并加装饰器。 - 通道工厂(
channels/factory.py):ChannelFactory维护ChannelType → EvalChannel映射,register()类方法支持插件式注册新通道类型。
API 端点 / Run 生命周期
POST /api/runs → API 先创建 pending 状态的 run,返回 run_id → 后台 asyncio.Task 调用 EvalEngine.run(existing_run=...) 复用该记录 → ws://…/ws/runs/{run_id} 推送实时进度。
WebSocket 事件类型(/ws/runs/{run_id},由 ConnectionManager.emit() 广播):
| 事件 | 触发时机 | payload |
|---|---|---|
case_start |
开始评测一个 case | {case_id, case_index, total_cases} |
messages_generated |
动态 case 的 AI 生成消息完成 | {case_id, messages} |
turn_start |
开始发送一轮对话 | {case_id, turn_index} |
turn_end |
收到回复 | {case_id, turn_index, reply, latency_ms} |
turn_error |
发送或接收失败 | {case_id, turn_index, error} |
rule_result |
单条规则评估完成 | {case_id, turn_index, rule_type, passed, score, reason} |
case_end |
case 评测完成 | {case_id, passed, score, error?} |
run_completed |
整个 run 完成 | {run_id, status} |
error |
case 级或 run 级错误 | {case_id?, message} |
前端 sessionReducer.ts 的 WS_EVENT action 处理以上所有事件类型。
OpenClaw 代理
后端通过 routers/proxy.py 提供 HTTP + WebSocket 反向代理(路径前缀 /openclaw),将请求转发到 OpenClaw 服务(docker-compose 中 openclaw-eval 容器)。WS 桥接会自动注入认证 token 到 connect.authenticate 消息并重写 Origin 头。前端 /openclaw 路由以全屏 iframe 嵌入代理地址。
文件管理模块
/api/files 端点提供分类树 + 文件上传/下载功能。FileCategoryDB 自引用(parent_id)实现树形结构,FileRecordDB 关联分类。文件物理存储在 data/uploads/,按分类子目录组织。删除分类会级联删除子分类 + 文件记录 + 物理文件。
模型配置中心
/api/model-configs 统一管理工程使用的外部模型连接。provider 表示调用协议,capability 表示评测用途能力(对话、向量、审核),input_modalities / output_modalities 表示模型自身支持的文本、图像、音频和视频模态,三者禁止混用。厂商、区域、上下文窗口、最大输出和模型特性属于描述性元数据,会写入评测运行快照,但不直接改变网关请求参数。
前端
SPA 由 FastAPI 托管(GET /{full_path:path} → index.html)。frontend/web/src/api.ts 是所有接口定义的单一出口,axios 拦截器统一处理错误。useRunSession.ts hook 管理 WebSocket 实时状态。
路由采用 keep-alive 标签页模式(非标准 <Routes>):所有页面同时挂载在 DOM 中,通过 display: block/none 切换可见性,保证页面状态在切 tab 时不丢失。路由配置集中在 App.tsx 的 routeConfigs 数组中,Zustand tabStore 管理标签页的打开/关闭/激活状态,react-router 仅用于 URL 同步。
三种状态管理策略:
- Zustand(
stores/tabStore.ts):全局标签页状态,唯一一个 store - useReducer(
hooks/sessionReducer.ts):评测会话状态(cases/turns/results/progress),纯 reducer 无副作用,hook 负责 WS/REST 副作用 - useState:各页面本地数据,无全局缓存(不使用 SWR/React Query)
Vite 开发代理:dev server 端口 3000,/api、/ws、/openclaw 全部代理到 localhost:8000;生产构建时 dist/ 由 FastAPI 直接托管。
Ant Design CSS-in-JS 冲突:Ant Design v5 的 emotion 运行时注入会覆盖静态 CSS 的 flex/height 规则。Runs 页采用"导航与内容分离"方案——Tabs 组件仅用于渲染导航头(.nav-only-tabs CSS 隐藏 .ant-tabs-content-holder),实际内容 div 由 flex 直接控制高度,绕过 Ant Design 内部 DOM。
构建注意:@monaco-editor/react 独立拆分为 vendor-monaco chunk(vite.config.ts 的 manualChunks)。设计 Token 在 tokens.ts 集中管理(组件直接 import,非 CSS 变量方案)。
开发约定
- 版本号单一数据源:
pyproject.toml→ 由scripts/sync_version.py同步到package.json,部署脚本自动执行 - Python lint:ruff,line-length 120,target py310
- 前端类型检查:
tsc --noEmit - 文档文件名带版本号,如
architecture-v1.0.md - SQLite 必须用
StaticPool+check_same_thread=False,否则多线程下连接耗尽 - 所有 Router 用
Depends(get_db)管理 Session,后台任务也需try/finally关闭 frontend/web/dist/是构建产物,部署前必须npm run build,否则 t480 跑旧版
配置
.env(参考.env.example):AGENTEVAL_API_KEY(X-API-Key 鉴权)、AGENTEVAL_ALLOWED_ORIGINS(CORS,首个非 localhost URL 用作 OpenClaw WS Origin)、AGENTEVAL_OPENCLAW_*系列(代理上游地址 + 认证 token)config/config.json:tutu-api 通道配置(敏感信息,只读挂载到容器)- 无 CI/CD,部署依赖手动执行
scripts/deploy-t480.sh
测试结构
tests/
├── conftest.py # 共享 fixtures(内存 SQLite DB、TestClient)
├── unit/
│ ├── mock_channel.py # 测试用 MockChannel
│ ├── test_engine.py # EvalEngine 单元测试(asyncio_mode=auto)
│ ├── test_cascade.py # 级联删除测试
│ └── test_settings.py # Pydantic Settings 测试
└── integration/
└── test_runs_api.py # Runs API 集成测试
pytest.ini_options 中 pythonpath = ["backend", "."],测试直接 import agenteval 和 cli。
部署关键信息
- t480:
sola-t480(192.168.8.145:8001),Docker Compose 双容器(agenteval+openclaw-eval) - 端口映射:主机 8001 → 容器 8000(主机 8000 已被占用)
- 健康检查:
curl http://192.168.8.145:8001/api/health返回 version/commit,脚本验证与本地pyproject.toml一致 - 回滚:
docker tag t480-agenteval:<old-version> t480-agenteval:latest && docker compose up -d - backend/ 通过 volume 热加载,只有 Python 依赖变更才需要
--build - 前端 dist/ 也通过 volume 挂载,因此每次前端变更也需要重新构建(
./scripts/deploy-t480.sh自动处理) - openclaw-eval 容器使用
ghcr.io/openclaw/openclaw:latest镜像,通过AGENTEVAL_API_URL环境变量连接 agenteval
已知陷阱
-
index.css必须显式 import:Vite 不会自动包含未被引用的 CSS。main.tsx必须有import './index.css',否则全局样式(动画、布局覆盖、nav-only-tabs)全部无效且无报错。这是历史遗留 bug(项目创建以来就缺失),已修复但容易在重构时再次丢失。 -
UTC 时间戳解析:后端返回的 datetime 字符串不含时区标记(如
"2026-07-14T16:40:00"),JS 的new Date()会按本地时间解析导致偏差 8 小时。前端必须使用utils/date.ts的toDate()函数(检测并追加Z后缀),禁止直接new Date(str)。根本修复需后端序列化时附带时区。 -
Ant Design CSS-in-JS 覆盖:Ant Design v5 的 emotion 运行时注入优先级高于静态 CSS 文件中的
flex/height/overflow规则。需要弹性布局的容器避免依赖.ant-tabs-content等 Ant 内部 DOM,改用导航与内容分离方案。见上方前端架构说明。 -
SQLite 连接池:必须用
StaticPool+check_same_thread=False。使用默认连接池会导致多线程下连接耗尽(async + FastAPI 线程池混合场景)。 -
.env在 t480 上需手动维护:部署脚本不会自动同步.env文件,新增环境变量需 SSH 到主机手动补全。容易导致 OpenClaw 代理等功能静默失效。