# AgentEvalTool — AI Agent 项目知识

> 本文件为 AI 助手提供项目上下文，帮助快速理解项目并参与开发。

## 项目概述

**AgentEvalTool** 是智能体质量评估工具集平台，评估 AI 数字员工（大模型 + RAG）和 AI 助手（OpenClaw）的服务质量。核心闭环：定义评估 → 执行评估 → 分析结果 → 改进优化。

- **当前版本**: v0.5.0-dev（2026-07-29 发布至 t480，里程碑「准」）
- **运行环境**: t480 测试服务器（192.168.8.145:8001）
- **语言**: Python 3.11（后端） + TypeScript/React 18（前端）

## 里程碑路线图

| 版本 | 代号 | 日期 | 主题 | 状态 |
|---|---|---|---|---|
| v0.1 | MVP | 2026-07-09 | 核心闭环跑通 | ✅ 已发布 |
| v0.2 | 「稳」 | 2026-07-14 | async 引擎 + 安全基线 + 测试 + 部署规范化 | ✅ 已发布(t480) |
| v0.3 | 「拓」 | 2026-07-17 | 多通道 + 规则扩展 + 模型配置中心 | ✅ 已发布(t480) |
| v0.4 | 「联」 | 2026-07-28 | AI 助手标准化(triggered_by) + 登录 + 仪表盘重构 | ✅ 已发布(t480) |
| **v0.5** | **「准」** | **2026-07-29** | **判定语义(期望叠加/连通用例) + 场景版本化 + 领域文档基线** | **✅ 已发布(t480)** |
| v0.6 | — | 规划中 | volcengine-102 同步 / 前端测试 / 报告聚合看板 / 多模态 | 📋 待规划 |

详细内容见 [docs/release-notes-v0.5.md](docs/release-notes-v0.5.md)。领域术语的唯一词汇表在 [CONTEXT.md](CONTEXT.md)，关键决策在 [docs/adr/](docs/adr/)。

## 技术栈

### 后端
- **框架**: FastAPI + Uvicorn
- **ORM**: SQLModel（基于 SQLAlchemy）
- **CLI**: Typer + Rich
- **数据库**: SQLite（StaticPool 模式）
- **模板引擎**: Jinja2（HTML 报告）
- **关键依赖**: Pydantic v2、requests、PyYAML

### 前端
- **框架**: React 18 + TypeScript + Vite 5
- **UI 库**: Ant Design 5.x + ProComponents
- **代码编辑**: Monaco Editor（JSON 场景编辑）
- **图表**: @ant-design/charts
- **路由**: react-router-dom v6
- **HTTP**: axios（带请求拦截器和统一错误提示）

## 项目结构

```
AgentEvalTool/
├── backend/
│   ├── agenteval/            # 核心 Python 库
│   │   ├── agents/           # EvalAgent 抽象（SimpleAgent）
│   │   ├── channels/         # 消息通道（TutuApiChannel）
│   │   ├── evaluation/       # 评测引擎 + 规则
│   │   │   └── rules/        # keyword_match, response_time, llm_score
│   │   ├── scenarios/        # 场景加载/校验
│   │   ├── storage/          # SQLite + Repository 模式
│   │   └── web/              # FastAPI 后端
│   │       ├── routers/      # targets, scenarios, runs, reports, stats
│   │       └── websocket.py  # WebSocket 实时进度推送
│   ├── cli/                  # CLI 入口（Typer）
│   └── plugins/openclaw/     # OpenClaw 插件示例
├── frontend/web/             # React 前端（Vite + Ant Design）
├── config/config.json        # tutu-api 配置（敏感信息）
├── data/                     # SQLite DB + 场景 YAML + 报告输出
├── deploy/t480/              # Docker 部署配置
├── docs/                     # 版本化文档集
├── scripts/mock_call.py      # 历史参考脚本
└── pyproject.toml            # hatchling 构建 + 依赖定义
```

## 核心架构

### 消息通道（EvalChannel）
- 抽象接口：`send()` / `poll_reply()` / `health_check()`
- V1 实现：`TutuApiChannel`（HTTP + Bearer Token + questionMsgId 配对）
- 工厂模式：`ChannelFactory` 按 channel_type 创建实例

### 评测引擎（EvalEngine）
- 遍历 Scenario.cases → 每个 case 遍历 messages（多轮对话）
- 发送消息 → 轮询回复 → 记录 Turn → 执行规则 → 保存 EvalResult
- 支持 `progress_callback` 实时推送事件（WebSocket 集成）
- 支持 `existing_run` 参数复用已有 run 记录

### 评估规则（EvalRule）
- `keyword_match`：关键词包含/排除匹配
- `response_time`：响应时间阈值检查
- `llm_score`：LLM-as-judge 评分（OpenAI 兼容 API）

### 判定语义（v0.5 起）
- **期望叠加**：case 的 `expectation` 始终派生隐式 llm_score 规则，与显式规则叠加（隐式是 rule_logic 之外的硬约束，reason 带 `[期望]` 前缀）
- **连通用例**：无规则且无期望的用例是合法的连通性验证，报告层标注 `connectivity`；`judged_pass_rate` 剔除连通用例，`pass_rate` 口径不变（含执行失败，见 ADR-0002）
- **场景版本**：`Scenario.version` 系统维护（仅考纲字段变更升版，见 ADR-0001）；运行创建时快照 `EvalRun.scenario_version`，对比报告要求同场景同版本

### 数据模型
- `EvalTarget` → `EvalRun` ← `Scenario`
- `EvalRun` → `Turn`（对话轮次）
- `EvalRun` → `EvalResult`（规则结果）

## 部署知识

### t480 测试服务器
- **地址**: sola-t480（192.168.8.145）
- **端口**: 8001（映射容器内 8000）
- **部署方式**: Docker Compose 双容器（agenteval + openclaw-eval）
- **Python 镜像**: python:3.11.15-slim-bookworm
- **APT/Pip 镜像**: 阿里云（绕过 GFW）

### 版本号（单一数据源）
- **pyproject.toml** 是版本号的唯一来源
- **package.json** 的版本号由 `scripts/sync_version.py` 自动同步
- 每次部署前必须运行 `python3 scripts/sync_version.py`（deploy 脚本会自动做）
- 镜像 tag 同时打 `t480-agenteval:<version>` 和 `t480-agenteval:latest`

### 部署流程（标准）
```bash
# 推荐: 一键部署（build/sync/rebuild/verify 全包）
scripts/deploy-t480.sh

# 仅代码变更（跳过镜像重建, backend 通过 volume 热加载）
scripts/deploy-t480.sh --skip-build

# 预览命令, 不实际执行
scripts/deploy-t480.sh --dry-run
```

脚本会自动:
1. 同步版本号 `pyproject.toml → package.json`
2. `npm run build` 构建前端
3. rsync 代码 + dist 到 t480
4. `docker compose build --build-arg BUILD_COMMIT=... --build-arg BUILD_TIME=...`
5. `docker tag t480-agenteval:<version>`
6. `docker compose up -d`（容器启动时自动跑 `alembic upgrade head`）
7. curl `/api/health` 验证版本号和 commit 一致

### 部署验证
```bash
curl http://192.168.8.145:8001/api/health
# 返回: {"status":"ok","version":"0.2.0-dev","commit":"abc1234","built_at":"2026-07-14T10:33:41Z"}
# 如果 version 不匹配本地 pyproject.toml → 部署失败, 脚本会报错退出
```

### 关键 Volume 挂载
- `data/` → `/app/data`（SQLite DB + 报告 + openclaw 状态, 可写）
- `config/config.json` → `/app/config/config.json:ro`
- `frontend/web/dist/` → `/app/frontend/web/dist:ro`（**必须 build 后再部署**）
- `backend/` → `/app/backend:ro`（代码热更新）
- `.env` → `/app/.env:ro`（Pydantic Settings 配置, 见 `.env.example`）

### 鉴权（v0.4 起）
- `AGENTEVAL_ADMIN_PASSWORD`：设置后 Web UI 需登录（会话 token, 关浏览器失效）；不设则无登录门
- `AGENTEVAL_API_KEY`：机器调用凭据（X-API-Key）；deploy 脚本会把它写入 openclaw 工作区
  `data/openclaw/agenteval-api-key`, agenteval-run skill 自动读取
- 两个凭据任一匹配即放行（`web/deps.py: require_auth`）；`/api/health` 与 `/api/auth/*` 始终开放

### 镜像 tag 约定
- `t480-agenteval:<version>` — 不可变, 对应某次具体构建
- `t480-agenteval:latest` — 可变, 总是指向最新部署
- 回滚: `docker tag t480-agenteval:<old-version> t480-agenteval:latest && docker compose up -d`

## 重要踩坑记录

### 1. SQLite + FastAPI 必须用 StaticPool
- 默认 QueuePool 在多线程环境下快速耗尽
- `check_same_thread=False` + `StaticPool` 是 SQLite 的正确配置

### 2. Docker COPY vs Volume 挂载
- Dockerfile 中 `COPY backend ./backend` 烘焙的代码不会随 rsync 更新
- 必须通过 volume 挂载 `backend/` 才能实现代码热更新
- 只有 Dockerfile 或 Python 依赖变更时才需要 `--build`

### 3. Session 生命周期管理
- 所有 Router 必须使用 `Depends(get_db)` 管理 Session
- `get_db()` 是 generator，`finally` 中 `session.close()` 确保连接释放
- 后台任务也需要 `try/finally` 关闭 Session

### 4. EvalEngine.run() 与 API 端点的 run 创建
- API 端点先创建 run（pending），返回 run_id 给前端
- 后台任务通过 `existing_run` 参数复用该记录，避免重复创建

### 5. 中国网络环境
- Docker Hub 和 npm 在国内访问极慢或被墙
- 使用阿里云 APT/Pip 镜像
- Clash 代理不可靠时，优先使用镜像策略

### 6. 前端 dist 必须显式构建再部署
- `frontend/web/dist/` 是构建产物, 不是源码
- rsync 时如果 `--exclude='frontend/web/dist'`, t480 会一直跑旧 dist, 看起来像"页面回到老版本"
- 必须 `npm run build` 之后再 rsync, 或者直接用 `scripts/deploy-t480.sh`(自动做这两步)
- 部署后用 `curl /api/health` 验证 version/commit 和本地一致, 脚本会自动做

### 7. OpenClaw 免登录依赖 Origin 注入
- OpenClaw 的 `gateway.controlUi.allowedOrigins` 检查 WebSocket 的 Origin header
- `proxy.py` 必须注入 `origin=http://192.168.8.145:8001`(或 settings.allowed_origins 的第一项)
- 如果 Origin 缺失, OpenClaw 拒收 WS 连接, SPA 会显示登录页
- v0.2 起 `openclaw_ws_origin` 从 `allowed_origins` 自动推导, 不再硬编码

## CLI 命令速查

```bash
agenteval target add --name "名称" --config config/config.json
agenteval target list
agenteval target test <id>

agenteval scenario import data/scenarios/xxx.yaml
agenteval scenario list

agenteval run start --target-id <id> --scenario-id <id>
agenteval run list

agenteval report show <run-id> --format json
agenteval report generate <run-id> --format html

agenteval server start --host 0.0.0.0 --port 8000
```

## 当前数据（t480）

- **评测对象**: 1（社区医院AI客服，tutu-api 通道）
- **评测场景**: 2（社区医院基础服务评测 + 科室导航与挂号咨询）
- **API 端点**: `http://192.168.8.145:8001/`
- **WebSocket**: `ws://192.168.8.145:8001/ws/runs/{run_id}`

## 开发约定

- Python 代码使用 `ruff` 做 lint（line-length 120, target py310）
- 前端 TypeScript 使用 `tsc --noEmit` 类型检查
- 文档文件名带版本号（如 `architecture-v1.0.md`）
- 用户偏好简体中文交互

## CI 检查

- `scripts/ci-check.sh` 一键跑：版本号一致性 + ruff + pytest + tsc（`--fast` 跳过 tsc）
- 本地 `git push` 会自动触发 pre-push hook 执行 `ci-check.sh --fast`（`.git/hooks/pre-push`，新 clone 需手动重装）
- Gitea Actions 工作流：`.gitea/workflows/ci.yml`（需 Gitea 实例启用 Actions + 注册 runner 才生效）
- 紧急绕过：`git push --no-verify`（仅限救急，事后必须补跑）

