docs(ai-context): 合并 AI 上下文文档为单一 AGENTS.md 权威源
All checks were successful
CI / test (pull_request) Successful in 4m22s
All checks were successful
CI / test (pull_request) Successful in 4m22s
- AGENTS.md 改为工具无关的唯一权威正文:迁移 AGENT.md/CLAUDE.md 中仍有效的独有内容(判定与报告口径、require_auth 鉴权、ci-check.sh/pre-push、Gitea Actions 生效条件、简体中文约定),头部声明 CLAUDE.md/AGENT.md 为指针 - CLAUDE.md / AGENT.md 缩为入口指针,禁止再写入正文,杜绝三份副本漂移 - 同步修正关键抽象/配置中已过时的鉴权与 CI 描述(v0.5/v1.0 时代内容 vs 实际 v1.3.1 代码)
This commit is contained in:
parent
685d45243d
commit
1636ad9a66
240
AGENT.md
240
AGENT.md
@ -1,235 +1,13 @@
|
|||||||
# AgentEvalTool — AI Agent 项目知识
|
# AGENT.md
|
||||||
|
|
||||||
> 本文件为 AI 助手提供项目上下文,帮助快速理解项目并参与开发。
|
> 历史遗留文件名(不属于主流工具的自动加载约定),现仅作入口指针;请勿在此写入正文性内容。
|
||||||
|
|
||||||
## 项目概述
|
本仓库唯一权威的 AI 助手上下文是 **`AGENTS.md`**(同目录,Claude Code / Codex / Qoder 等所有工具通用)——架构、命令、开发约定、已知陷阱全部在彼处,请先通读再动手。
|
||||||
|
|
||||||
**AgentEvalTool** 是智能体质量评估工具集平台,评估 AI 数字员工(大模型 + RAG)和 AI 助手(OpenClaw)的服务质量。核心闭环:定义评估 → 执行评估 → 分析结果 → 改进优化。
|
辅助参考(按需读取):
|
||||||
|
- `CONTEXT.md` — 领域术语唯一词汇表
|
||||||
- **当前版本**: v0.5.0-dev(2026-07-29 发布至 t480,里程碑「准」)
|
- `docs/adr/` — 关键架构决策
|
||||||
- **运行环境**: t480 测试服务器(192.168.8.145:8001)
|
- `docs/agents/` — AI 协作工作流(Gitea Issues / triage 标签 / 领域文档约定)
|
||||||
- **语言**: Python 3.11(后端) + TypeScript/React 18(前端)
|
- `CHANGELOG.md` — 版本历史
|
||||||
|
|
||||||
## 里程碑路线图
|
|
||||||
|
|
||||||
| 版本 | 代号 | 日期 | 主题 | 状态 |
|
|
||||||
|---|---|---|---|---|
|
|
||||||
| 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`(仅限救急,事后必须补跑)
|
|
||||||
|
|
||||||
|
需要更新项目知识时,改 `AGENTS.md`,不要改本文件。
|
||||||
|
|||||||
71
AGENTS.md
71
AGENTS.md
@ -1,10 +1,10 @@
|
|||||||
# AGENTS.md
|
# AGENTS.md
|
||||||
|
|
||||||
This file provides guidance to Codex (Codex.ai/code) when working with code in this repository.
|
本文件是仓库**唯一权威**的 AI 助手上下文,适用于所有按 `AGENTS.md` / `CLAUDE.md` / `AGENT.md` 约定加载上下文的工具(Claude Code、Codex、Qoder 等)。根目录 `CLAUDE.md` 与 `AGENT.md` 仅为入口指针,正文一律维护在本文件。
|
||||||
|
|
||||||
## 项目概述
|
## 项目概述
|
||||||
|
|
||||||
**AgentEvalTool** 是智能体质量评估工具集平台(v1.0.0),用于评估 AI 数字员工(tutu-api 通道)和 AI 助手(OpenClaw)的服务质量。Python 3.11 后端 + TypeScript/React 前端,SQLite 持久化。领域术语词汇表见 `CONTEXT.md`,关键决策见 `docs/adr/`。
|
**AgentEvalTool** 是智能体质量评估工具集平台(版本号以 `pyproject.toml` 为单一数据源,勿在此硬编码),用于评估 AI 数字员工(tutu-api 通道)和 AI 助手(OpenClaw)的服务质量。Python 3.11 后端 + TypeScript/React 前端,SQLite 持久化。领域术语词汇表见 `CONTEXT.md`,关键决策见 `docs/adr/`。
|
||||||
|
|
||||||
## 常用命令
|
## 常用命令
|
||||||
|
|
||||||
@ -67,26 +67,33 @@ CLI (Typer) / Web API (FastAPI)
|
|||||||
↓
|
↓
|
||||||
EvalEngine(异步评测执行引擎)
|
EvalEngine(异步评测执行引擎)
|
||||||
↓ 通过抽象接口
|
↓ 通过抽象接口
|
||||||
EvalChannel → 消息通道(TutuApiChannel)
|
EvalChannel → 消息通道(tutu-api / openclaw / http)
|
||||||
EvalRule → 评估规则(keyword_match / response_time / llm_score)
|
EvalRule → 评估规则(keyword / response_time / llm_score / safety / semantic / …)
|
||||||
↓
|
↓
|
||||||
SQLite Storage(Repository 模式)
|
SQLite Storage(Repository 模式)
|
||||||
```
|
```
|
||||||
|
|
||||||
### 关键抽象(均在 `backend/agenteval/`)
|
### 关键抽象(均在 `backend/agenteval/`)
|
||||||
|
|
||||||
- **`channels/base.py`** — `EvalChannel` 抽象:`send()` / `poll_reply()` / `health_check()`,V1 实现为 `TutuApiChannel`(tutu-api HTTP + Bearer Token)
|
- **`channels/base.py`** — `EvalChannel` 抽象:`send()` / `poll_reply()` / `health_check()`;三种实现 `tutu.py`(tutu-api HTTP + Bearer Token)/ `openclaw.py` / `http.py`,`ChannelType` 枚举(`tutu-api` / `openclaw` / `http`)与工厂注册见 `channels/factory.py`
|
||||||
- **`evaluation/engine.py`** — `EvalEngine`:异步执行,遍历 Scenario.cases → messages → 调用 channel → 执行规则 → 写入 DB;通过 `asyncio.Event` cancel_token 支持真取消;`progress_callback` 实时推送事件给 WebSocket
|
- **`evaluation/engine.py`** — `EvalEngine`:异步执行,遍历 Scenario.cases → messages → 调用 channel → 执行规则 → 写入 DB;通过 `asyncio.Event` cancel_token 支持真取消;`progress_callback` 实时推送事件给 WebSocket
|
||||||
- **`evaluation/rules/`** — `EvalRule` 抽象,三种实现:`keyword.py`(关键词匹配)、`response_time.py`(延迟阈值)、`llm_score.py`(LLM-as-judge)
|
- **`evaluation/rules/`** — `EvalRule` 抽象,注册表(`__init__.py` 导入即注册)已扩到 7 种实现:`keyword.py` / `response_time.py` / `llm_score.py`(LLM-as-judge,支持多维评分)/ `safety.py` / `semantic.py` / `fluency.py` / `json_schema.py`;跨规则隐式逻辑在 `evaluation/implicit_rules.py`
|
||||||
- **`models.py`** — 共享 Pydantic 模型:`EvalTarget` → `EvalRun` ← `Scenario`,`EvalRun` → `Turn` + `EvalResult`
|
- **`models.py`** — 共享 Pydantic 模型:`EvalTarget` → `EvalRun` ← `Scenario`,`EvalRun` → `Turn` + `EvalResult`
|
||||||
- **`storage/`** — `db.py`(SQLite StaticPool + SQLModel 表定义)+ `repository.py`(`TargetRepository` / `ScenarioRepository` / `RunRepository` / `ResultRepository`)+ `file_repository.py`(`FileCategoryRepository` / `FileRecordRepository`)
|
- **`storage/`** — `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` 单例
|
- **`config/settings.py`** — Pydantic Settings,加载 `AGENTEVAL_` 前缀的环境变量(`.env` 文件),`@lru_cache` 单例
|
||||||
- **`web/deps.py`** — FastAPI 依赖注入:`get_db()`(Session 生命周期管理)、`require_api_key()`(X-API-Key 鉴权)
|
- **`web/deps.py`** — FastAPI 依赖注入:`get_db()`(Session 生命周期管理)、`require_auth()`(鉴权:`X-API-Key` 匹配 `AGENTEVAL_API_KEY` 或 `X-Auth-Token` 匹配 Web 登录会话,任一通过;两个凭据都未配置时 no-op 放行;`require_api_key` 是兼容别名)
|
||||||
|
|
||||||
**扩展性模式**:
|
**扩展性模式**:
|
||||||
- **规则注册表**(`evaluation/rules/__init__.py`):`@register_rule` 装饰器将规则类注册到 `_RULE_REGISTRY`,`get_rule(type, params)` 按名称实例化。添加新规则只需创建类并加装饰器。
|
- **规则注册表**(`evaluation/rules/__init__.py`):`@register_rule` 装饰器将规则类注册到 `_RULE_REGISTRY`,`get_rule(type, params)` 按名称实例化。添加新规则只需创建类并加装饰器。
|
||||||
- **通道工厂**(`channels/factory.py`):`ChannelFactory` 维护 `ChannelType → EvalChannel` 映射,`register()` 类方法支持插件式注册新通道类型。
|
- **通道工厂**(`channels/factory.py`):`ChannelFactory` 维护 `ChannelType → EvalChannel` 映射,`register()` 类方法支持插件式注册新通道类型。
|
||||||
|
|
||||||
|
### 判定与报告口径
|
||||||
|
|
||||||
|
通过率/判定口径是跨版本演进的敏感点(v0.5 判定语义 → v1.3 go/no-go),改动前先读相关 ADR,避免与报告层分叉:
|
||||||
|
- case 判定在 `evaluation/case_verdict.py`;run 级汇总在 `evaluation/run_summary.py`——`judged_pass_rate` 剔除连通用例、`pass_rate` 含执行失败(ADR-0002/0004)
|
||||||
|
- 隐式期望规则(期望叠加)在 `evaluation/implicit_rules.py`;go/no-go 判定在 `evaluation/go_no_go.py`
|
||||||
|
- 术语与口径基线见 `CONTEXT.md`
|
||||||
|
|
||||||
### API 端点 / Run 生命周期
|
### API 端点 / Run 生命周期
|
||||||
|
|
||||||
`POST /api/runs` → API 先创建 `pending` 状态的 run,返回 run_id → 后台 `asyncio.Task` 调用 `EvalEngine.run(existing_run=...)` 复用该记录 → `ws://…/ws/runs/{run_id}` 推送实时进度。
|
`POST /api/runs` → API 先创建 `pending` 状态的 run,返回 run_id → 后台 `asyncio.Task` 调用 `EvalEngine.run(existing_run=...)` 复用该记录 → `ws://…/ws/runs/{run_id}` 推送实时进度。
|
||||||
@ -119,6 +126,33 @@ SQLite Storage(Repository 模式)
|
|||||||
|
|
||||||
`/api/model-configs` 统一管理工程使用的外部模型连接。`provider` 表示调用协议,`capability` 表示评测用途能力(对话、向量、审核),`input_modalities` / `output_modalities` 表示模型自身支持的文本、图像、音频和视频模态,三者禁止混用。厂商、区域、上下文窗口、最大输出和模型特性属于描述性元数据,会写入评测运行快照,但不直接改变网关请求参数。
|
`/api/model-configs` 统一管理工程使用的外部模型连接。`provider` 表示调用协议,`capability` 表示评测用途能力(对话、向量、审核),`input_modalities` / `output_modalities` 表示模型自身支持的文本、图像、音频和视频模态,三者禁止混用。厂商、区域、上下文窗口、最大输出和模型特性属于描述性元数据,会写入评测运行快照,但不直接改变网关请求参数。
|
||||||
|
|
||||||
|
### 智能评估执行机制(方案③触发式执行,ADR-0009/0010/0011/0012)
|
||||||
|
|
||||||
|
Worker 自动化由平台掌控节奏:`web/app.py` lifespan 只做 `scheduler_runtime.start()/stop()`,实际循环在 `intelligent_eval/scheduler.py`——每 60s(`SCAN_INTERVAL_SECONDS`)执行一次 `scan_once()`,单个节拍依次:
|
||||||
|
|
||||||
|
1. **requeue_stale_assigned_tasks**(task_queue.py):assigned 超时(>10min)且评估 executing → 回 pending(卡死恢复)
|
||||||
|
2. **expire_stale_running_sessions**:running 会话 60min 无新轮次 → expired
|
||||||
|
3. **watchdog 判死**:planning 双闸 / executing 超窗 / 连续触发失败超限 → failed
|
||||||
|
4. **scan_and_enqueue_tasks**:扫描 executing 评估,按时段到期 + 欠账(`domain.py::get_attention_reason`)→ 入队(每评估去重一条 pending/assigned)
|
||||||
|
5. **settle_tasks_for_finished_evals**:评估离开 executing 后,其 pending/assigned 任务回收为 completed
|
||||||
|
6. **supplement_decision_logs**:agent 未上报决策日志时按状态兜底补录(审计)
|
||||||
|
7. **analyst 催促**:末会话终态 10min 后落账 nudge,交给 worker 触发转 analyst 汇总报告
|
||||||
|
8. **触发**:fire-and-forget(不阻塞 60s 节拍)唤醒 worker / planner
|
||||||
|
|
||||||
|
关键约束(改动此机制前必读):
|
||||||
|
- **触发原语**:`docker exec openclaw-eval timeout 600 openclaw agent --agent main --session-id <prefix>-<ts> -m <msg> --json`,subprocess 侧 timeout 660
|
||||||
|
- **独立 session**:session-id 形如 `agenteval-worker-<ts>` / `agenteval-planner-<ts>`,**禁止复用 `--agent main` 的持久 session**——main session 多次触发累积上下文缓存(~12 万 token)后 worker 会幻觉输出而不执行 API 步骤(ADR-0010)
|
||||||
|
- **时段分布**:触发消息强制"仅执行当前时间对应时段(time_distribution)内欠账的会话",绝不创建未来时段会话——未来时段由后续 60s 节拍再次触发;所有时段会话到终态才转 analyst
|
||||||
|
- **状态一致性**:`submit_report` 要求会话全部 close 才允许 completed(`intelligent_eval/lifecycle/report_submission.py`);per-eval 触发冷却 10min 防 60s 节拍堆叠并发 agent
|
||||||
|
|
||||||
|
### 扩展域速览(v1.1+ 新增域,早期架构文档未覆盖)
|
||||||
|
|
||||||
|
- **Campaigns(评估活动)**:`evaluation/campaign_{lifecycle,runner,scheduler,read_model,report}.py` + `models.py` 的 `CampaignDB`;路由在 `web/routers/campaigns.py`;跨 run 聚合见 `comparison.py` / `analysis.py` / `go_no_go.py` / `cost_tracking.py`;后台任务登记在 `task_registry.py`
|
||||||
|
- **IntelligentEvals(智能评估域)**:`intelligent_eval/`——`domain.py`(优先级/注意力)、`lifecycle/`(状态机,终止态纪律见 ADR-0011/0012)、`task_queue.py`、`decision_logs.py`、`config_snapshot.py`、`read_model.py` / `report.py`;软删语义只在 `repository.py`(见陷阱 6)
|
||||||
|
- **Exploration(探索巡检)**:`exploration/patrol.py`(巡检)+ `judge.py` + `summary.py`,自带 models/errors/lifecycle
|
||||||
|
- **模型网关**:`model_gateway.py` 是统一模型调用出口,`model_protocols/` 为 openai / anthropic / dashscope / gemini 协议适配器;`services/` 承载 runs / model_configs / files 业务
|
||||||
|
- **AI 协作文档**:Gitea Issues 工作流、triage 标签、领域文档约定见 `docs/agents/`(issue-tracker / triage-labels / domain)
|
||||||
|
|
||||||
### 前端
|
### 前端
|
||||||
|
|
||||||
SPA 由 FastAPI 托管(`GET /{full_path:path}` → `index.html`)。`frontend/web/src/api.ts` 是所有接口定义的单一出口,axios 拦截器统一处理错误。`useRunSession.ts` hook 管理 WebSocket 实时状态。
|
SPA 由 FastAPI 托管(`GET /{full_path:path}` → `index.html`)。`frontend/web/src/api.ts` 是所有接口定义的单一出口,axios 拦截器统一处理错误。`useRunSession.ts` hook 管理 WebSocket 实时状态。
|
||||||
@ -153,28 +187,27 @@ SPA 由 FastAPI 托管(`GET /{full_path:path}` → `index.html`)。`frontend
|
|||||||
- **SQLite 必须用 `StaticPool` + `check_same_thread=False`**,否则多线程下连接耗尽
|
- **SQLite 必须用 `StaticPool` + `check_same_thread=False`**,否则多线程下连接耗尽
|
||||||
- **所有 Router 用 `Depends(get_db)` 管理 Session**,后台任务也需 `try/finally` 关闭
|
- **所有 Router 用 `Depends(get_db)` 管理 Session**,后台任务也需 `try/finally` 关闭
|
||||||
- **`frontend/web/dist/`** 是构建产物,部署前必须 `npm run build`,否则 t480 跑旧版
|
- **`frontend/web/dist/`** 是构建产物,部署前必须 `npm run build`,否则 t480 跑旧版
|
||||||
|
- **本地 CI 入口**:`scripts/ci-check.sh`(版本一致性 + ruff + pytest + tsc;`--fast` 跳过 tsc)。pre-push hook 在 `.git/hooks/pre-push`(新 clone 需手动重装),push 自动跑 `ci-check.sh --fast`;紧急 `git push --no-verify` 后必须补跑
|
||||||
|
- **交互与文档使用简体中文**
|
||||||
|
|
||||||
## 配置
|
## 配置
|
||||||
|
|
||||||
- `.env`(参考 `.env.example`):`AGENTEVAL_API_KEY`(X-API-Key 鉴权)、`AGENTEVAL_ALLOWED_ORIGINS`(CORS,首个非 localhost URL 用作 OpenClaw WS Origin)、`AGENTEVAL_OPENCLAW_*` 系列(代理上游地址 + 认证 token)
|
- `.env`(参考 `.env.example`):`AGENTEVAL_API_KEY`(机器调用凭据,X-API-Key)、`AGENTEVAL_ADMIN_PASSWORD`(Web 登录口令,设置后走会话 token)、`AGENTEVAL_ALLOWED_ORIGINS`(CORS,首个非 localhost URL 用作 OpenClaw WS Origin)、`AGENTEVAL_OPENCLAW_*` 系列(代理上游地址 + 认证 token)
|
||||||
- `config/config.json`:tutu-api 通道配置(敏感信息,只读挂载到容器)
|
- `config/config.json`:tutu-api 通道配置(敏感信息,只读挂载到容器)
|
||||||
- 无 CI/CD,部署依赖手动执行 `scripts/deploy-t480.sh`
|
- 无托管 CI 实际运行:Gitea Actions 工作流(`.gitea/workflows/ci.yml`)需实例启用 Actions + runner 才生效;本地质量门是 pre-push hook;部署依赖手动执行 `scripts/deploy-t480.sh`
|
||||||
|
|
||||||
## 测试结构
|
## 测试结构
|
||||||
|
|
||||||
```
|
```
|
||||||
tests/
|
tests/
|
||||||
├── conftest.py # 共享 fixtures(内存 SQLite DB、TestClient)
|
├── conftest.py # 根级 fixtures(内存 SQLite DB、TestClient)
|
||||||
├── unit/
|
├── unit/ # 数十个按领域划分的单测(engine/各规则、campaign_*、intelligent_eval_*、exploration_*、model_*、task_queue 等)
|
||||||
│ ├── mock_channel.py # 测试用 MockChannel
|
│ └── mock_channel.py # 测试用 MockChannel
|
||||||
│ ├── test_engine.py # EvalEngine 单元测试(asyncio_mode=auto)
|
└── integration/ # 数十个集成测试:每 router 一个 test_<area>_api.py(runs/campaigns/intelligent_evals/targets/...)+ 调度与端到端(test_*_scheduler*.py、test_intelligent_eval_e2e.py)
|
||||||
│ ├── test_cascade.py # 级联删除测试
|
└── conftest.py # 集成级 fixtures
|
||||||
│ └── test_settings.py # Pydantic Settings 测试
|
|
||||||
└── integration/
|
|
||||||
└── test_runs_api.py # Runs API 集成测试
|
|
||||||
```
|
```
|
||||||
|
|
||||||
`pytest.ini_options` 中 `pythonpath = ["backend", "."]`,测试直接 import `agenteval` 和 `cli`。
|
pytest 配置集中在 `pyproject.toml` 的 `[tool.pytest.ini_options]`:`pythonpath = ["backend", "."]`(测试直接 import `agenteval` 和 `cli`)、`asyncio_mode = "auto"`(async 测试无需显式装饰器)。
|
||||||
|
|
||||||
## 部署关键信息
|
## 部署关键信息
|
||||||
|
|
||||||
@ -199,3 +232,5 @@ tests/
|
|||||||
5. **`.env` 在 t480 上需手动维护**:部署脚本不会自动同步 `.env` 文件,新增环境变量需 SSH 到主机手动补全。容易导致 OpenClaw 代理等功能静默失效。
|
5. **`.env` 在 t480 上需手动维护**:部署脚本不会自动同步 `.env` 文件,新增环境变量需 SSH 到主机手动补全。容易导致 OpenClaw 代理等功能静默失效。
|
||||||
|
|
||||||
6. **智能评估可见性接缝**:逻辑删除(`deleted` 状态)语义只住在 `intelligent_eval/repository.py`——查询走 `IntelligentEvalRepository.visible()` 谓词,存在性校验走 `require_live_eval`(路由层翻译为 404)。禁止在其他模块直查 `IntelligentEvalDB` 判断删除态,否则删除语义会在多处漂移(Phase 1 收敛前的教训:快照、决策日志、任务监控各自实现了一遍"已删即 404")。
|
6. **智能评估可见性接缝**:逻辑删除(`deleted` 状态)语义只住在 `intelligent_eval/repository.py`——查询走 `IntelligentEvalRepository.visible()` 谓词,存在性校验走 `require_live_eval`(路由层翻译为 404)。禁止在其他模块直查 `IntelligentEvalDB` 判断删除态,否则删除语义会在多处漂移(Phase 1 收敛前的教训:快照、决策日志、任务监控各自实现了一遍"已删即 404")。
|
||||||
|
|
||||||
|
7. **AI 上下文文档结构**:`AGENTS.md` 是仓库唯一权威正文;`CLAUDE.md` / `AGENT.md` 仅是指针文件(引导工具读本文件),禁止往指针文件写入正文性内容——三份全量副本时代各自漂移的历史教训(各自持有独有段落、互不一致)勿重演。
|
||||||
|
|||||||
219
CLAUDE.md
219
CLAUDE.md
@ -1,216 +1,13 @@
|
|||||||
# CLAUDE.md
|
# CLAUDE.md
|
||||||
|
|
||||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
> 本文件仅作入口指针(Claude Code 按此文件名约定自动加载),避免多份上下文文档漂移;请勿在此写入正文性内容。
|
||||||
|
|
||||||
## 项目概述
|
本仓库唯一权威的 AI 助手上下文是 **`AGENTS.md`**(同目录,Claude Code / Codex / Qoder 等所有工具通用)——架构、命令、开发约定、已知陷阱全部在彼处,请先通读再动手。
|
||||||
|
|
||||||
**AgentEvalTool** 是智能体质量评估工具集平台(v0.2.0),用于评估 AI 数字员工(tutu-api 通道)和 AI 助手(OpenClaw)的服务质量。Python 3.11 后端 + TypeScript/React 前端,SQLite 持久化。
|
辅助参考(按需读取):
|
||||||
|
- `CONTEXT.md` — 领域术语唯一词汇表
|
||||||
|
- `docs/adr/` — 关键架构决策
|
||||||
|
- `docs/agents/` — AI 协作工作流(Gitea Issues / triage 标签 / 领域文档约定)
|
||||||
|
- `CHANGELOG.md` — 版本历史
|
||||||
|
|
||||||
## 常用命令
|
需要更新项目知识时,改 `AGENTS.md`,不要改本文件。
|
||||||
|
|
||||||
### 后端
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 安装(含 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/
|
|
||||||
```
|
|
||||||
|
|
||||||
### 前端
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd frontend/web
|
|
||||||
npm install
|
|
||||||
npm run dev # 开发服务器
|
|
||||||
npm run build # 构建生产产物(部署前必须执行)
|
|
||||||
npx tsc --noEmit # 类型检查
|
|
||||||
```
|
|
||||||
|
|
||||||
### 数据库迁移
|
|
||||||
|
|
||||||
```bash
|
|
||||||
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)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
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.Event` cancel_token 支持真取消;`progress_callback` 实时推送事件给 WebSocket
|
|
||||||
- **`evaluation/rules/`** — `EvalRule` 抽象,三种实现:`keyword.py`(关键词匹配)、`response_time.py`(延迟阈值)、`llm_score.py`(LLM-as-judge)
|
|
||||||
- **`models.py`** — 共享 Pydantic 模型:`EvalTarget` → `EvalRun` ← `Scenario`,`EvalRun` → `Turn` + `EvalResult`
|
|
||||||
- **`storage/`** — `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 嵌入代理地址。
|
|
||||||
|
|
||||||
### 智能评估执行机制(方案③触发式执行)
|
|
||||||
|
|
||||||
智能评估的 Worker 自动化(取代旧 Cron 池,见 ADR-0009/0010)由 `app.py::_intelligent_eval_scan_loop` 驱动(lifespan 后台任务,每 60s):
|
|
||||||
|
|
||||||
1. **requeue_stale_assigned_tasks**:assigned 超时(>10min)且评估 executing → 回 pending(卡死恢复)
|
|
||||||
2. **scan_and_enqueue_tasks**:扫描 executing 评估,时段到期/欠账 → 入队(任务队列)
|
|
||||||
3. **settle_tasks_for_finished_evals**:评估离开 executing 后,其 pending/assigned 任务回收为 completed
|
|
||||||
4. **_supplement_decision_logs**:agent 未上报决策日志时按状态兜底补录
|
|
||||||
5. **触发**:有 planning 评估 → 触发 planner skill;有 pending 任务 → 触发 worker skill
|
|
||||||
|
|
||||||
关键约束:
|
|
||||||
- **独立 session**:触发命令必须带 `--session-id`(`agenteval-worker-<ts>` / `agenteval-planner-<ts>`),**禁止复用 `--agent main` 的持久 session**——main session 多次触发累积上下文缓存后 worker 会幻觉不执行(见 ADR-0010)
|
|
||||||
- **时段分布**:触发指令明确"仅执行当前到期时段内欠账的会话",平台每 60s 持续触发推进后续时段,保证 1h 窗口按时段分批
|
|
||||||
- **状态一致性**:`submit_report` 要求会话全部 close 才允许 completed;worker 触发 timeout 600s
|
|
||||||
|
|
||||||
### 文件管理模块
|
|
||||||
|
|
||||||
`/api/files` 端点提供分类树 + 文件上传/下载功能。`FileCategoryDB` 自引用(`parent_id`)实现树形结构,`FileRecordDB` 关联分类。文件物理存储在 `data/uploads/`,按分类子目录组织。删除分类会级联删除子分类 + 文件记录 + 物理文件。
|
|
||||||
|
|
||||||
### 前端
|
|
||||||
|
|
||||||
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`。
|
|
||||||
|
|
||||||
## Agent skills
|
|
||||||
|
|
||||||
### Issue tracker
|
|
||||||
|
|
||||||
工作项跟踪使用 `git.solahqb22.cn/solahqb/AgentEvalTool` 的 Gitea Issues。详见 `docs/agents/issue-tracker.md`。
|
|
||||||
|
|
||||||
### Triage labels
|
|
||||||
|
|
||||||
Triage 使用五个默认角色标签。详见 `docs/agents/triage-labels.md`。
|
|
||||||
|
|
||||||
### Domain docs
|
|
||||||
|
|
||||||
仓库采用单一上下文:根目录 `CONTEXT.md` 配合 `docs/adr/`。详见 `docs/agents/domain.md`。
|
|
||||||
|
|
||||||
## 部署关键信息
|
|
||||||
|
|
||||||
- **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
|
|
||||||
|
|
||||||
## 已知陷阱
|
|
||||||
|
|
||||||
1. **`index.css` 必须显式 import**:Vite 不会自动包含未被引用的 CSS。`main.tsx` 必须有 `import './index.css'`,否则全局样式(动画、布局覆盖、nav-only-tabs)全部无效且无报错。这是历史遗留 bug(项目创建以来就缺失),已修复但容易在重构时再次丢失。
|
|
||||||
|
|
||||||
2. **UTC 时间戳解析**:后端返回的 datetime 字符串不含时区标记(如 `"2026-07-14T16:40:00"`),JS 的 `new Date()` 会按本地时间解析导致偏差 8 小时。前端必须使用 `utils/date.ts` 的 `toDate()` 函数(检测并追加 `Z` 后缀),禁止直接 `new Date(str)`。根本修复需后端序列化时附带时区。
|
|
||||||
|
|
||||||
3. **Ant Design CSS-in-JS 覆盖**:Ant Design v5 的 emotion 运行时注入优先级高于静态 CSS 文件中的 `flex`/`height`/`overflow` 规则。需要弹性布局的容器避免依赖 `.ant-tabs-content` 等 Ant 内部 DOM,改用导航与内容分离方案。见上方前端架构说明。
|
|
||||||
|
|
||||||
4. **SQLite 连接池**:必须用 `StaticPool` + `check_same_thread=False`。使用默认连接池会导致多线程下连接耗尽(async + FastAPI 线程池混合场景)。
|
|
||||||
|
|
||||||
5. **`.env` 在 t480 上需手动维护**:部署脚本不会自动同步 `.env` 文件,新增环境变量需 SSH 到主机手动补全。容易导致 OpenClaw 代理等功能静默失效。
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user