inter-eval
v1.4.0
Published
Interactive evaluation pipeline for AI-generated data visualization pages
Readme
inter-eval
交互式数据可视化页面自动化评测工具。基于 Playwright 对 AI 生成的单页 HTML 页面进行交互功能评测,覆盖按钮点击、滑块拖拽、下拉选择、Tab 切换、模态弹窗、Canvas/Three.js 3D 交互等 10 种交互类型,支持 SVG/CSS/JS 动画的时间序列截图评测,产出结构化评测报告。
快速开始
环境要求:Node.js >= 22
1. 安装
npm install -g inter-eval2. 运行评测
inter-eval run dataset/query.csv支持 CSV 和 JSONL 两种数据格式,通过文件扩展名自动识别。首次运行时会自动检测并安装 Chromium 浏览器引擎,无需手动操作。评测完成后打开 output/report.html 查看报告。
免安装使用
不想全局安装也可以通过 npx 直接运行:
npx inter-eval run dataset/query.csv数据格式
支持 CSV 和 JSONL(每行一个 JSON 对象),通过文件扩展名自动识别。
| 字段 | 必须 | 说明 |
|------|------|------|
| task_id | 是 | 任务唯一标识(数字或字符串,如 base64) |
| result | 否* | Markdown 文本,包含 ```html 代码块,工具会自动提取其中的 HTML |
| url | 否* | 外部页面 URL,当 result 无法提取有效 HTML 时自动回退使用 |
*
result和url至少提供一个。优先使用result中提取的 HTML;提取失败时检查url字段,若为合法 http/https 地址则直接访问该 URL 进行评测。
最小可用示例(JSONL):
{"task_id": 14794, "result": "```html\n<div>hello</div>\n```"}
{"task_id": 14795, "url": "https://example.com/page"}使用示例
# 评测指定任务
inter-eval run data.csv --task-id 14794,3035
# 使用 JSONL 格式数据
inter-eval run data.jsonl --task-id 14794
# 评测外部 URL(数据集中配置 url 字段)
inter-eval run urls.jsonl --headed
# 只处理前 100 条记录
inter-eval run data.jsonl --limit 100
# 从第 200 条开始,处理 50 条
inter-eval run data.jsonl --start 200 --limit 50
# 清理上次输出 + 重跑
inter-eval run data.csv --clean
# 断点续跑时复用已有的 test-pages(跳过 HTML 生成)
inter-eval run data.jsonl -o output_0615 --reuse-test-pages
# 有头模式(打开浏览器窗口,方便调试)
inter-eval run data.csv --headed
# 自定义输出目录和配置文件
inter-eval run data.csv -o ./results --config my-config.json
# 后台运行(nohup)
nohup inter-eval run data.jsonl --limit 500 > eval.log 2>&1 &
# 生成默认配置文件(可选)
inter-eval init
# 生成 3D 场景配置
inter-eval init 3d
# 生成游戏场景配置
inter-eval init game
# 生成动画评测配置(Timeline 模式)
inter-eval init timeline
# Timeline 模式:按时间序列截图评测 SVG/CSS/JS 动画
inter-eval run data.jsonl --timeline 0,500,1000,2000,3000
# Timeline + AI 动画判断
inter-eval run data.jsonl --timeline 0,500,1000,2000,3000 --aiCLI 参考
| 命令 | 说明 |
|------|------|
| inter-eval run [dataset] [options] | 执行评测流水线 |
| inter-eval retest [options] | 对已有结果进行复测 |
| inter-eval report [options] | 从 results.jsonl 重新生成统计报告 |
| inter-eval init [mode] | 在当前目录生成配置 .interevalrc.json(mode: website/3d/game/timeline,默认 website) |
| inter-eval --version | 显示版本号(同时检查更新) |
| inter-eval --help | 显示帮助信息 |
run 参数
| 参数 | 说明 |
|------|------|
| [dataset] | 数据文件路径,支持 CSV 和 JSONL(默认 ./dataset/query.csv)。当 result 无有效 HTML 时回退使用 url 字段 |
| --task-id, -t | 逗号分隔的 task_id,只评测指定任务 |
| --start | 起始记录位置(默认 0),跳过前 n 条有效记录 |
| --limit | 最多处理的记录条数(大文件分批评测) |
| --clean | 清理输出目录后重新运行 |
| --reuse-test-pages | 复用已有的 test-pages/ 目录,跳过 HTML 文件生成(适合断点续跑、仅改配置重跑) |
| --headed | 有头模式运行浏览器 |
| --timeline | 启用 Timeline 模式,逗号分隔的毫秒时间点(如 0,500,1000,2000,3000)。动画按时间序列截图,而非交互驱动截图 |
| --output-dir, -o | 输出目录(默认 ./output) |
| --config | 配置文件路径(默认自动查找) |
report 参数
| 参数 | 说明 |
|------|------|
| --input, -i | 输入的 jsonl 文件路径(默认 ./output/results.jsonl) |
| --output-dir, -o | 输出目录(默认 ./output) |
| --config | 配置文件路径(影响评分权重和忽略规则) |
retest 参数
| 参数 | 说明 |
|------|------|
| --failed | 复测所有失败的任务(TIMEOUT / 加载失败 / 交互失败) |
| --task-id, -t | 逗号分隔的 task_id,指定要复测的任务 |
| --score-below | 复测总分低于指定值的任务 |
| --append | 追加到已有 results-retest.jsonl(默认覆盖) |
| --headed | 有头模式运行浏览器 |
| --output-dir, -o | 输出目录(默认 ./output,test-pages 和 results.jsonl 从此目录读取) |
| --config | 配置文件路径(默认自动查找) |
环境变量
| 变量 | 说明 |
|------|------|
| AISTUDIO_PROXY_ADDR | AIStudio 代理地址。设置后启用 AIStudio 模式:HTTP server 使用代理地址对外暴露、CDN 预热使用全并发(而非本地的 4 并发)、CDN 缓存走 route intercept 模式。本地运行时无需配置,仅在 AIStudio 环境中设置(如 http://proxy.aistudio:8080) |
| HTTPS_PROXY / HTTP_PROXY | 标准代理环境变量,Playwright 浏览器实例会自动使用。AIStudio 环境通常已预设,本地无需配置 |
AI 增强配置
AI 功能需要通过 --ai 参数显式启用:
# 启用 AI 增强评测
inter-eval run data.jsonl --ai
# 不传 --ai 则使用规则评测(默认)
inter-eval run data.jsonlAI 的 API 配置通过配置文件或环境变量提供(环境变量优先级高于配置文件):
配置文件方式(.interevalrc.json):
{
"ai": {
"apiKey": "your-key",
"endpoint": "",
"apiPath": "/v1/chat/completions",
"model": "deepseek-v4-pro",
"visualJudgeMode": "all"
}
}环境变量方式(覆盖配置文件中的同名字段):
| 变量 | 配置字段 | 默认值 | 说明 |
|------|----------|--------|------|
| AI_API_KEY | ai.apiKey | — | API Key(必需) |
| AI_API_ENDPOINT | ai.endpoint | https://api.deepseek.com | API 地址(兼容 OpenAI 协议的任意服务) |
| AI_API_PATH | ai.apiPath | /v1/chat/completions | API 路径 |
| AI_MODEL | ai.model | deepseek-v4-pro | 模型名称(如 qwen-vl-max、gpt-4o 等) |
| AI_VISUAL_JUDGE_MODE | ai.visualJudgeMode | all | 视觉判定策略:all(全量 AI 判定)/ fail_only(仅复核 FAIL)/ none(禁用) |
AI 增强功能:
| 功能 | 触发条件 | 说明 | |------|----------|------| | 视觉变化判定 | 交互产生截图后 | AI 判断交互是否产生有意义的视觉变化,避免 pixelmatch 误判 | | 渲染质量评估 | baseline 截图完成后 | 检测白屏、布局错乱、CSS 丢失等渲染问题 | | 交互优先级选择 | 元素发现后 | AI 从候选池中选择最有价值的交互元素 | | 错误分类 | 检测到 JS 错误时 | 区分致命错误/警告/噪声,只有致命错误影响评分 | | 页面类型识别 | baseline 完成后 | 自动识别 website/3d/game |
# 示例:配置文件有 apiKey,通过 --ai 启用
inter-eval run data.jsonl --ai
# 示例:环境变量覆盖 + 启用 AI
AI_API_KEY="your-key" AI_API_ENDPOINT="https://antchat.alipay.com" \
inter-eval run data.jsonl --ai
# 对比跑法
inter-eval run data.jsonl -o output_rule
inter-eval run data.jsonl --ai -o output_ai配置
配置文件查找顺序
未指定 --config 时,按以下顺序自动查找:
- 当前目录下的
.interevalrc.json - 当前目录下的
config/thresholds.json - 全部不存在则使用内置默认值
运行 inter-eval init 可生成完整默认配置,按需修改即可。
常用配置项
| 参数 | 默认值 | 说明 |
|------|--------|------|
| diffThreshold | 0.001 | 视觉变化阈值(0.1% 像素差异即认为有变化) |
| threeJsDiffThreshold | 0.005 | Canvas/3D 放宽阈值(0.5%),仅用于 canvas_interact |
| loadTimeoutMs | 15000 | 页面加载超时(ms) |
| taskTimeoutMs | 60000 | 单任务总超时(ms) |
| maxRetries | 2 | 超时任务的最大重试次数 |
| batchSize | 0 | 分批大小(0 = 不分批)。每批完成后重启浏览器回收内存,大数据集建议设为 500 |
| maxTopInteractions | 3 | 配额补充阶段的交互数上限 |
| maxInteractionsPerTask | 30 | 单任务最大交互数(硬上限) |
| concurrency | 0 | 并行数(0 = 自动取 CPU 核数 / 2) |
| freezeAnimations | — | 截图前动画冻结策略:"full"(冻结 CSS + rAF + timers)/ "css_only"(仅冻结 CSS 动画)/ "none"(不冻结)。不设置时自动检测(有 Canvas 交互用 css_only,否则用 full) |
| excludedTypes | [] | 排除的交互类型,如 ["range_sweep"] |
| ignoredErrors | [] | 忽略的 JS 错误(匹配子串) |
| ignoredCdnPatterns | ["cdn.tailwindcss.com"] | 忽略的 CDN 域名(不计入加载失败) |
| screenshotFormat | "jpeg" | 截图格式:"jpeg"(体积更小,quality=80)或 "png"(无损) |
| keepSuccessScreenshots | true | 是否保留成功任务的截图。设为 false 可在每批次完成后自动清理,节省 NAS 空间 |
完整配置项参见 inter-eval init 生成的默认文件,或查看 开发文档。
交互覆盖配置
对特殊页面可通过 interactions 字段手动指定交互(与自动发现互补):
{
"excludedTypes": ["range_sweep"],
"concurrency": 3,
"interactions": {
"14794": {
"threejs_orbit": {
"type": "canvas_interact",
"selector": "canvas",
"actions": [
{ "type": "drag", "from": [500, 300], "to": [600, 400] }
]
}
}
}
}输出
| 文件 | 说明 |
|------|------|
| output/report.html | 评测概览页(评分分布、错误汇总) |
| output/reports/task_{id}.html | 单任务详情页(交互明细、截图对比) |
| output/results.json | 结构化评测结果 |
| output/results.jsonl | 增量结果(支持断点续跑) |
| output/results-retest.jsonl | 复测结果(由 retest 命令生成) |
| output/errors.json | 异常详情 |
| output/test-pages/ | 提取的 HTML 页面(URL 模式下不生成) |
| output/screenshots/ | 交互前后截图 |
| output/diffs/ | 视觉 diff 热力图 |
| output/logs/ | 控制台日志 |
结果判断
判断任务是否完全通过
const isFullyPassed = r.baseline.loaded
&& r.baseline.issues.length === 0
&& (r.interactions.length === 0 || r.interactions.every(i => i.passed));| 条件 | 含义 |
|------|------|
| baseline.loaded | 页面加载成功 |
| baseline.issues.length === 0 | 无框架级异常(超时、加载失败等) |
| interactions.every(i => i.passed) | 所有交互都有视觉反馈且无异常 |
如果还需排除伴随 console.error 的交互(更严格):
&& r.interactions.every(i => i.passed && !i.warning)关键字段说明
| 字段 | 含义 |
|------|------|
| baseline.issues | 框架级问题(加载失败、超时、viewport 溢出) |
| baseline.consoleEntries | 页面自身 JS 的 console 输出(不影响 loaded 判定) |
| baseline.pageErrors | 页面未捕获异常(uncaught exception) |
评分规则
每个任务的总分 overall(0-100)由两个维度加权计算:
overall = (基础可用性 × W1 + 交互功能 × W2) × 100默认权重:W1=0.5, W2=0.5,可通过 scoring 配置修改。
1. 基础可用性(basicAvailability,0-1)
四项检查,通过得对应权重,不通过得 0:
| 检查项 | 配置字段 | 默认权重 | 判定逻辑 |
|--------|----------|----------|----------|
| 页面加载成功 | loadedWeight | 0.4 | baseline.loaded === true |
| 无 JS 错误 | noJsErrorsWeight | 0.35 | 页面无 uncaught exception 且无 console.error(过滤 ignoredErrors 后) |
| 内容适配视口 | fitsViewportWeight | 0.1 | 页面内容未超出 viewport 边界 |
| 关键资源无 404 | noResourceErrorsWeight | 0.15 | script/stylesheet 资源全部加载成功(过滤 ignoredCdnPatterns 后) |
四项权重之和应为 1.0。
2. 交互功能(interactionFunctionality,0-1)
交互功能 = 通过的交互数 / 已测试的交互总数- 跳过的交互(
skipped)不计入分母 - 无交互元素时为 0
判定标准:
| 结果 | 条件 |
|------|------|
| PASS | 交互产生了超过阈值的视觉变化(visualChangeRatio > diffThreshold)且无异常 |
| WARN | 交互有视觉变化但伴随 console.error |
| FAIL | 交互无视觉变化 / 触发 JS 异常 / 元素不可交互 |
评分配置示例
{
"scoring": {
"basicAvailabilityWeight": 0.5,
"interactionFunctionalityWeight": 0.5,
"loadedWeight": 0.4,
"noJsErrorsWeight": 0.35,
"fitsViewportWeight": 0.1,
"noResourceErrorsWeight": 0.15
}
}report 命令会使用当前配置重新计算分数,因此修改权重后运行 inter-eval report 即可刷新报告,无需重跑评测。
断点续跑
评测中断后再次运行会自动跳过已完成的 task(通过 results.jsonl 识别)。超时(TIMEOUT)的任务不视为已完成,会在下次运行时重新评测。如需全新重跑,使用 --clean。
复测
评测完成后,可使用 retest 命令对部分任务进行复测,无需重新解析原始 dataset,直接复用 test-pages/ 中已有的 HTML 文件。复测结果写入独立的 results-retest.jsonl,不影响原始 results.jsonl。
# 复测所有失败的任务
inter-eval retest --failed
# 复测指定任务
inter-eval retest -t 14794,3035
# 复测总分低于 60 的任务
inter-eval retest --score-below 60
# 组合使用:失败 + 低分
inter-eval retest --failed --score-below 60
# 追加模式(多次复测结果累积)
inter-eval retest --failed --append
# 指定其他输出目录
inter-eval retest --failed -o ./my-output默认覆盖模式下,复测使用临时文件写入,全部完成后原子替换 results-retest.jsonl。即使复测中途出错,旧的结果文件也不会丢失。
生成报告
无需重跑评测,直接从 results.jsonl 重新生成统计报告(report.html、results.json、errors.json 及 per-task 详情页)。适用于手动修改了 jsonl 数据后需要刷新报告的场景。
# 从默认 output/results.jsonl 生成
inter-eval report
# 从 retest 结果生成
inter-eval report -i output/results-retest.jsonl
# 指定输出目录和配置
inter-eval report -i data.jsonl -o ./reports --config my.json超时自动重试
在代理网络环境下,外部 CDN 资源偶发不可达可能导致个别任务超时。Pipeline 会在所有任务完成后,自动串行重试超时的任务(最多 maxRetries 次,默认 2 次),避免因偶发网络问题导致评测结果缺失。
大数据集
处理万级数据集(如 2W 条)时,建议配置 batchSize 进行分批处理:
{
"batchSize": 500,
"concurrency": 4,
"taskTimeoutMs": 120000
}分批处理会在每批完成后重启浏览器实例回收内存,防止长时间运行导致内存泄漏。Pipeline 会在 HTML 文件写入磁盘后立即释放内存中的 HTML 内容,评测结果以增量方式写入 results.jsonl 而非在内存中累积。
降低磁盘占用
大数据集评测会产生大量截图和 diff 文件,NAS 等共享存储容易占满。可通过以下配置减少磁盘使用:
{
"screenshotFormat": "jpeg",
"keepSuccessScreenshots": false
}screenshotFormat: "jpeg":截图改用 JPEG 格式(quality=80),单张体积比 PNG 小 5-10 倍,尤其适合不需要像素级精度的场景keepSuccessScreenshots: false:每批次完成后自动删除已成功任务的screenshots/和diffs/目录,只保留失败/超时任务的截图用于排查
两个选项可组合使用,对 2W 条数据的评测任务预计可节省 90%+ 的存储空间。
Timeline 模式(动画评测)
对于自动播放的 SVG/CSS/JS 动画(无需点击即可运动),传统交互驱动截图无法捕获动画过程。Timeline 模式按配置的时间点拍摄帧序列,通过相邻帧像素差异和 VLM 语义判断来评估动画合理性。
快速开始
# CLI 方式:在 0ms、500ms、1s、2s、3s 时刻截图
inter-eval run data.jsonl --timeline 0,500,1000,2000,3000
# 配合 AI 进行动画整体合理性判断
inter-eval run data.jsonl --timeline 0,500,1000,2000,3000 --ai
# 生成 timeline 专用配置
inter-eval init timeline配置方式
除 CLI --timeline 参数外,也可在配置文件中设置(两种方式等效,CLI 优先级更高):
{
"timeline": {
"timePointsMs": [0, 500, 1000, 2000, 3000],
"minFrameChangeRatio": 0.001,
"aiAnimationJudge": true
}
}| 字段 | 默认值 | 说明 |
|------|--------|------|
| timePointsMs | — | 页面加载后的截图时间点(ms),非空时启用 timeline 模式 |
| minFrameChangeRatio | 0.001 | 相邻帧最小像素变化率,低于此值视为无动画(帧判定 FAIL) |
| aiAnimationJudge | true | 启用 AI 时是否对帧序列做整体动画合理性判断 |
工作原理
- 页面正常加载并拍摄 baseline 截图
- 不冻结动画,按
timePointsMs的时间点依次截图 - 每帧与前一帧做 pixelmatch 像素对比,生成 diff 热力图
- 若启用 AI(
--ai),将所有帧截图送入 VLM 做整体动画合理性判断 - AI 判断
coherent且有像素变化的帧标记为 PASS
与交互模式的关系
- 同一任务中 timeline 模式和交互模式互斥:有
timeline配置时跳过交互元素发现和执行 - 若需要对部分任务用交互模式、部分用 timeline 模式,可在
interactions覆盖配置中通过"__timeline": { "skip": true }让指定任务回退到交互模式
报告展示
Timeline 任务的详情页展示 filmstrip 视图:
- 帧序列:横向排列所有帧缩略图,标注时间
- Diff 热力图:相邻帧差异的红/绿可视化
- 像素变化率柱状图:展示各帧间变化趋势
- 动画整体评判(AI 模式):coherent/incoherent + 置信度 + 原因
Programmatic API
除 CLI 外,inter-eval 也可作为 Node.js 模块在代码中直接调用:
import { run } from 'inter-eval';
const { report, results, outputDir, durationMs } = await run({
csvPath: './data.jsonl',
taskIds: null,
clean: false,
headed: false,
outputDir: './output',
configPath: null,
});
// 聚合报告
console.log(`平均分: ${report.summary.averageOverallScore}`);
console.log(`通过率: ${report.summary.totalTasksClean} / ${report.totalTasks}`);
// 逐任务结果
for (const r of results) {
if (r.scores.overall < 60) {
console.log(`低分任务: ${r.taskId} (${r.scores.overall})`);
}
}返回值
run() 返回 RunResult 对象:
| 字段 | 类型 | 说明 |
|------|------|------|
| report | EvalReport | 聚合报告(平均分、通过率、错误统计等) |
| results | TaskEvalResult[] | 每个任务的详细评测结果 |
| outputDir | string | 输出目录绝对路径 |
| durationMs | number | 评测总耗时(ms) |
retest() 同样返回 RunResult。
导出的类型
import type {
RunResult, // run()/retest() 返回值
RunOptions, // run() 参数
RetestOptions, // retest() 参数
TaskEvalResult, // 单任务评测结果
EvalReport, // 聚合报告
BaselineResult, // 页面加载结果
InteractionResult, // 交互测试结果
TaskScores, // 评分明细
} from 'inter-eval';开发
项目内开发参见 开发文档。
git clone <repo-url> && cd inter-eval
pnpm install
npx playwright install chromium
pnpm start # 运行评测
pnpm test # 单元测试
pnpm build # 构建
pnpm serve # 查看报告