EvalHarness/README.md
sora 97a458d9b8 README: agent-env extra dependencies (tau2-bench engine, swebench)
Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-17 08:55:05 +00:00

342 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# EvalHarness
插件式 LLM / Agent 评测框架:**28 个 benchmark 开箱即用,官方口径 prompt 与判分,任意 OpenAI 兼容端点,断点续跑,与 evalscope 对齐**。
```
提供端点 → 拉数据 → 渲染官方 prompt → 并发生成 → 官方判分 → Excel/JSON 报告
```
## 特性
- **28 个 benchmark**:数学 / 知识 / 问答 / 长上下文 / 代码Docker 沙箱)/ Agent 工具调用prompt 与判分器按官方实现逐字节复刻,与 evalscope 双层验证对齐(见 §10
- **任意端点**:单端点、多端点池(轮询 + 自适应并发 + failover、云 API 全兼容vLLM / sglang / lmdeploy / ollama / OpenAI 协议直连
- **断点续跑**:每条预测完成即落盘,中断重跑只补缺口;`repeats` 多轮采样自动隔离断点
- **YAML 配置**:逐 bench 生成参数temperature / max_tokens / repeats …)集中管理,命令行保持极简
- **长输入安全**tokenizer 中段截断(`max_input_tokens`+ 上下文溢出自适应收缩重试
- **性能统计**:延迟 / 吞吐 / token 用量默认采集;`--perf` 追加流式 TTFT / ITL
- **全链路插件化**数据集、判分、prompt 渲染、适配器、沙箱、进度条、主题、钩子均可单文件注册扩展
- **模型指纹核验**fp_fusion 五维探针电池,检测 API 换模 / 降配 / 冒充 / 套壳
## 0. Benchmarks
| 族 | benchmark |
|---|---|
| 数学 | `gsm8k` `competition_math` `aime24/25/26` `hmmt26` `imo_answerbench` |
| 知识/选择题 | `mmlu` `mmlu_pro` `cmmlu` `gpqa_diamond` `arc` `hellaswag` `winogrande` `bbh` |
| 问答 | `trivia_qa` `drop` `simple_qa` `hle` |
| 长上下文 | `longbench_v2` `openai_mrcr` |
| 代码Docker 沙箱) | `humaneval` `bigcodebench` `live_code_bench` |
| Agent/工具 | `bfcl_v3` `general_fc` `tau2_bench` `swe_bench_verified` |
```bash
evalharness data list # 28 个数据集:源/子集/few-shot/split
evalharness eval list # 28 个判分 recipe
```
## 1. 安装
| 依赖 | 说明 |
|---|---|
| Python ≥ 3.10 | 核心零三方依赖pydantic 除外) |
| Docker可选 | 代码执行类 benchmarkhumaneval 等)需要;镜像判分时自动拉取 |
| `rich`(可选) | 进度条;缺失自动降级纯文本 |
```bash
git clone https://git.meta-stone.net/sora/EvalHarness.git
cd EvalHarness
conda create -n evalharness python=3.10 -y
conda activate evalharness
pip install .
```
离线自检(不联网、不接模型,应得 acc 100%
```bash
evalharness eval run gsm8k --model mock-boxed --limit 8
```
## 2. 快速开始
六个基准一次跑完(默认用法;逐 bench 生成参数自动读 `evalharness/config/default.yaml`aime 系列按配置自动跑 12 遍取均值):
```bash
evalharness eval run humaneval aime25 aime26 gpqa_diamond mmlu_pro longbench_v2 \
--api-url http://174.1.60.4:30000/v1 \
--model /data/hf_models/GLM-5.3-NVFP4 \
--disable-thinking \
--resume \
--concurrency 4 \
--out-dir /data1/sora/temp/results \
--cache-dir /data1/sora/temp \
--hf-endpoint https://hf-mirror.com
```
单端点最小示例:
```bash
evalharness eval run gsm8k \
--api-url http://localhost:8000/v1 \
--model qwen3-8b \
--disable-thinking \
--limit 200 --resume \
--out-dir results/run1
```
## 3. 配置YAML
逐 bench 生成参数集中在 `evalharness/config/<name>.yaml`。目录里只有一个 yaml 时**自动加载**`--config <name>` 显式指定。
```yaml
default: # 所有 bench 继承的协议级默认
temperature: 0.0
top_p: 1.0
max_tokens: 32768
aime25: # 逐 bench 覆盖
temperature: 1.0 # 采样温度temp=1 方差测量)
max_tokens: 8192 # 生成预算
repeats: 12 # 跑 12 遍报均值 ± 极差(断点按轮隔离,互不串用)
longbench_v2:
max_input_tokens: 128000 # 超长输入的 tokenizer 中段截断预算
```
优先级(低 → 高):
```
DatasetSpec.gen_config < default 段 < bench 段 < 命令行显式参数
```
当前支持的键:`temperature` `top_p` `max_tokens` `max_input_tokens` `repeats`
## 4. 进阶用法
多端点池(轮询 + 自适应并发 + failover
```bash
evalharness eval run mmlu \
--provider openai-pool \
--api-url 'http://gpu1:{8123..8130}/v1,http://gpu2:{8200..8203}/v1' \
--model qwen3-8b --disable-thinking
```
需要 judge 的 benchhle / simple_qa / imo
```bash
evalharness eval run hle \
--api-url http://localhost:8000/v1 --model qwen3-8b --disable-thinking \
--judge-model dp4-flash \
--judge-api-url http://judge-host:30000/v1 \
--limit-per-task 25
```
Agent bench多轮工具调用
```bash
evalharness eval run bfcl_v3 --api-url http://localhost:8000/v1 --model qwen3-8b \
--env bfcl_mock
```
Python API
```python
import evalharness
rep = evalharness.run('gsm8k', 'openai/http://localhost:8000/v1?qwen3-8b', limit=200)
rep.save('gsm8k.report.json')
# notebook / async 环境用 await evalharness.arun(...)
```
### Agent 环境的额外依赖
`tau2_bench` 需要官方引擎(**PyPI 上的 `tau2` 是同名无关项目,别装错**
```bash
pip install -e /path/to/tau2-bench # 本机源码es 仓库 tools/ 下有)
# 或: pip install git+https://github.com/sierra-research/tau2-bench
```
`swe_bench_verified` 需要 `pip install swebench` + 逐题 Docker 镜像(见 §8
`bfcl_v3` / `general_fc` 无额外依赖。
## 5. 参数速查
| 参数 | 作用 |
|---|---|
| `--api-url` `--model` `--provider` | 端点、模型名(纯名字,无需拼 spec、协议`openai-chat`/`openai-pool` |
| `--api-key` | 显式 key优先于环境变量只进请求头不写入 spec/报告) |
| `--disable-thinking` | `enable_thinking=false`Qwen3/GLM 类模型推荐;带 tools 的请求自动退回兼容软开关) |
| `--judge-model` `--judge-api-url` `--judge-api-key` `--judge-provider` | judge 端四件套,语义与主模型对称 |
| `--config NAME` | 指定 `evalharness/config/<NAME>.yaml`(省略时唯一 yaml 自动加载) |
| `--profile NAME` | 命名生成参数集(`dp4-nothink` / `qwen3-es-parity` / `t1-short` 或自定义) |
| `--limit N` / `--limit-per-task N` | 全局前 N / 每子集前 N多科目 bench 用后者;可组合取交集) |
| `--subset` `--split` `--source` | 覆盖子集 / split / 数据源(可指本地路径离线跑) |
| `--concurrency N` | 并发(默认 32长输出 bench 建议 8-16 |
| `--concurrency auto` | 自适应并发门:从 2 起步,健康且供不应求时 +1 爬坡,请求失败 ×0.7 退避(服务端 `/metrics` 可用时按排队信号调节);当前值显示在进度条 `gate N` |
| `--resume [PATH]` | 断点续跑;默认 `<cache-dir>/ckpt/<bench>.jsonl` |
| `--env NAME` | agent 环境(`bfcl_mock` 等) |
| `--perf` | 采集流式 TTFT / ITL / 重试率入报告 |
| `--hf-endpoint URL` | 数据下载端点(如 `https://hf-mirror.com`,免手动 export |
| `--cache-dir DIR` | 缓存根目录(数据缓存 + 断点同根;默认 `$EVALHARNESS_CACHE``~/.cache/evalharness` |
| `--out FILE` / `--out-dir DIR` | 报告落盘,见 §6 输出结构 |
| `--style text\|md\|md_compare\|excel\|radar\|errors` | 结果渲染样式 |
| `--no-progress` | 关闭 Rich 进度条(重定向日志时用) |
API key 解析顺序:`--api-key` > 按端点域名的环境变量(`api.openai.com``OPENAI_API_KEY``anthropic.com``ANTHROPIC_API_KEY``dashscope``DASHSCOPE_API_KEY``bigmodel``ZAI_API_KEY`> `OPENAI_API_KEY`。自建端点无鉴权可不管。
## 6. 输出结构
`--out-dir` 产出(不传时自动保存到 `evalharness-results/<时间戳>-<模型>/`
```
<out-dir>/
├── summary.xlsx 总表Summary / Perf / Categories / Samples 四 sheet主入口
├── summary.csv 同一张总表的 csv 版
└── <bench>/ 每个 benchmark 一个目录
├── report.jsonl 完整报告流式行格式首行报告头之后每行一个样本grep/tail 友好)
└── <bench>.xlsx 该 bench 的独立 ExcelSummary/Perf/Categories/Samples
```
查看与对照:
```bash
evalharness viz show gsm8k.report.json # 控制台表格
evalharness viz show a.json b.json --style md_compare # 多模型对照(含差值标记)
evalharness viz show report.json --style excel # 4-sheet 仪表盘
evalharness viz show report.json --style errors # 失败样本下钻
```
## 7. 性能统计
每轮运行的延迟与 token 用量默认采集,进 `summary.csv` 的 Perf 列与 `metric_groups['perf']`
| 指标 | 采集条件 |
|---|---|
| `latency_mean/p50/p90/p95/p99_s` | 始终采集(逐请求墙钟) |
| `output_tps` `request_qps` `input/output_tokens` `success_rate` `retry_rate` | 始终采集 |
| `ttft_mean/p90/p99_s``tpot_*`(逐 token 延迟) | 仅 `--perf`(流式才有首 token 时刻) |
`repeats` 模式下:分数报**均值**`<metric>_last_run` 保留末轮值),时间 / token 报**全部轮次总和**。
## 8. 缓存与断点
```bash
evalharness data fetch gsm8k mmlu --workers 8 # 预取(首次运行也会自动下载)
evalharness data stats cmmlu # 条数/长度/答案分布
evalharness data show gsm8k -n 2 # 看前 2 条样本
evalharness data unload gsm8k # 删缓存
```
缓存根目录由 `--cache-dir` 指定(或 `$EVALHARNESS_CACHE`,默认 `~/.cache/evalharness`
```
<cache-dir>/
├── datasets/<bench>/<subset>_<split>-<hash>/ 数据缓存raw/ 原始字节 + samples.jsonl 统一样本)
├── .raw/<repo-hash>/ 跨条目共享的下载 blob多子集只下载一次
└── ckpt/<bench>[_<subset>][:repN]-<hash>.jsonl 预测断点每条完成即追加repeats 按轮隔离)
```
- 数据缓存内容寻址subset/split/source 变更自动新条目)
- **改了 prompt 模板须删旧断点**`rm <cache-dir>/ckpt/<bench>*.jsonl`),否则复用旧预测
- 网络抖动三层防护15s 连接超时快速失败 → 池内换端点 → 指数退避重试,断网不丢批次
- 判分与生成解耦:换 recipe / grader 对存量预测直接重判(`evaluate(ds, preds)`),模型不被重复调用
- Docker 镜像源回退链可用 `EVALHARNESS_DOCKER_MIRRORS` 覆盖(逗号分隔模板,`{img}` 占位)
## 9. 扩展
加数据集(单文件放入 `evalharness/data/datasets/`,自动注册):
```python
@register_dataset(DatasetSpec(name='mybench', source='org/mybench',
split='test', task_type='mcq'))
def mybench():
return FieldSpec(input='question', choices='options', target='answer_key')
```
绑定判分recipe = 注册原语的声明式组合):
```python
@register_eval('mybench')
def mybench():
return EvalRecipe(
extract=['my_answer', 'answer_phrase'], # 级联,首个成功者胜
scorers={'acc': 'exact'}, # math_equal/em_f1/execution/env_reward/llm_judge
aggregators={'acc': 'mean'}, # pass_at_k/grouped_avg/binned_avg
)
```
其余插件点同构:`@register_prompt_renderer``@register_extractor/scorer/aggregator``@register_adapter``@register_sandbox``@register_env``@register_renderer`
运行外壳同样是插件:
| 插件点 | 注册 | 说明 |
|---|---|---|
| 进度报告 | `@register_progress('rich'/'plain'/...)` | `--progress-plugin` 选择rich 是终端进度条plain 是纯叙事行CI/日志) |
| 叙事主题 | `@register_theme('default'/...)` | `--theme` 选择;图标/配色/句子高亮的映射表 |
| 生命周期钩子 | `@register_hook('on_benchmark_failed'/'on_benchmark_done')` | 观察/扩展运行webhook 通知、失败重试策略),钩子报错不影响主流程 |
| 端点探针 | `@register_prober('ping'/...)` | 运行前的端点可用性验证策略(`EVALHARNESS_PROBER` 环境变量选择) |
## 10. 对齐验证
prompt 与判分器经双层验证(字符串级:同一记录双侧渲染逐字节一致;分数级:同题同参数对比 evalscope
- Qwen3-8B23/28 分差 < 0.05
- DeepSeek-V4-Flash20+/25 分差 < 0.05mmlu_pro 0.0000
残差均已定性金标集差异 / 排列敏感 / benchmark 侧缺陷见各 recipe 注释
## 附录 A模型指纹核验fp_fusion
回答一个问题**API 背后跑的到底是不是它声称的那个模型** OpenAI 兼容端点发送探针电池回答分布 / 自我身份 / 元知识 / 能力边界 / 文风五维与内置参考指纹库比对输出五档裁决 + 0~1 融合分 + 证据链检测偷梁换柱降配缩水主动冒充伪身份注入屈服)、套壳拼装与中转代理`--mode full` 一次运行产出 verify / attribution / variant / adversarial / robustness 五个视图
```bash
evalharness fingerprint list # 内置参考指纹库fp_fusion 口径 + detector 旧口径)
evalharness fingerprint run \
--api-url http://localhost:8000/v1 --model Qwen3-8B \
--mode full --cells core16 --text-skip pruned7 \
--d-samples 25 --baseline-samples 5 --timeout 90 \
--impersonate "You are Kimi, Moonshot AI virtual assistant." \
--reference glm53 \
--report-path reports/fp_qwen.json
```
- `--reference` 接受短名 `glm53` `fingerprint list` JSON 路径省略 = 自证模式(裁决上限 LIKELY_MATCH
- `--impersonate` 注入伪身份启用对抗视图冒充检测剪枝定稿协议即上例参数单次 505 条请求公网 14.528 min本地 vllm 5.67 min
- 报告写入 `--report-path`同目录 `raw_answers.jsonl` 存全部探针原文
- 离线分析与参考采集脚本`cell_snr.py` / `validate_*.py` / `collect_ref.sh` 可直接 `python <script>` 运行与完整方法论文档见 `evalharness/fingerprint/fp_fusion_介绍.md`
## 附录 B架构
```
data/ 统一 Sample schema惰性物化内容寻址缓存28 个单文件插件)
model/ adapter协议+ pool端点池/AIMD/failover+ prompt_renderers + gen_profiles
eval/ extract → score → aggregate 流水线 + recipes
sandbox/ docker 硬隔离执行 / local镜像引用计数
agent/ 消息泵 + Environment 插件bfcl/tau2/swe
fingerprint/ fp_fusion 模型指纹基准(探针电池 → 并发采集 → 五视图打分;独立纵向,
不走 data/eval 管线,自带 engine 与参考库)
viz/ text/md/md_compare/excel/radar/errors
progress/ Rich 每样本进度(缺 rich 自动降级)
```
层间严格分离数据层只回答"题目与金标"判分层只回答"如何评判"模型层只回答"如何触达"预测是不可变 artifact
## 附录 C常见问题
**端点不可达ConnectError跑样本前即中止**
探针在首个样本前发 1-token ping失败即给出 curl 验证命令并中止 token 浪费按提示 `curl -m 5 <api-url>/chat/completions ...` 手工确认服务在跑
**上下文溢出400 maximum context length**
自动截短 prompt 15% 重试 tokenizer 安全边际1-2 次收敛`max_input_tokens`YAML可预先做中段截断长上下文 bench 建议配置
**思考模式thinking关不掉**
`--disable-thinking` `chat_template_kwargs`非流式请求完全生效sglang/vLLM 已验证)。注意部分网关在**流式**路径会剥掉该参数——本框架仅在 `max_tokens > 100000` 时自动转流式聚合还原为非流式响应常规 8k/32k 预算不受影响智谱云 API 风格的 `thinking: {"type": ...}` 参数 sglang 不识别自建端点请用本框架的开关
**GLM 大预算生成挂起**
非流式大 body100k+ token 输出部分网关会缓冲到超时自动流式路径已内置见上条无需手工干预
**Docker 拉镜像慢/失败**
`EVALHARNESS_DOCKER_MIRRORS="mirror.example.com/{img},docker.io/{img}"` 覆盖回退链