New evalharness/fingerprint/ package (from evalstone fp_fusion v1.1, 2026-09-07 pruning final): probe battery -> concurrent collection -> five scoring views (verify/attribution/variant/adversarial/robustness), bundled family aliases + 27 reference fingerprints (12 fp_fusion schema). - CLI: 'evalharness fingerprint run ...' (REMAINDER passthrough, single source of arg definitions) + 'fingerprint list' for bundled references - imports rewritten package-relative; direct 'python3 run_fp_fusion.py' execution kept working via package bootstrap - offline analysis/collection scripts made path-independent (previously pinned to a /opt/evalscope path absent on this host) - shell scripts: hardcoded API key -> FP_API_KEY/OPENAI_API_KEY env vars - --reference accepts short names resolved against bundled references/ - pyproject: +httpx dependency, package-data references/*.json - tests/test_fingerprint.py: 10 offline tests (battery definitions, assembly counts, normalization, signals, verdict ladder, CLI wiring) - README: fingerprint section + architecture entry Verified on H20-1: tests 10/10, installed CLI OK, full-protocol run vs vectron GLM-5.3 reproduces baseline (score 0.9451, s_idn 0.846).
239 lines
11 KiB
Markdown
239 lines
11 KiB
Markdown
# EvalHarness
|
||
|
||
插件式 LLM/Agent 评测框架:28 个 benchmark 开箱即用,官方口径 prompt 与判分,任意 OpenAI 兼容端点,断点续跑。
|
||
|
||
```
|
||
提供端点 → 拉数据 → 渲染官方 prompt → 并发生成 → 官方判分 → 报告
|
||
```
|
||
|
||
## 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;代码执行类 benchmark 需宿主机 Docker(镜像判分时自动拉取)。
|
||
|
||
```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. 运行命令
|
||
|
||
单端点:
|
||
|
||
```bash
|
||
evalharness eval run gsm8k \
|
||
--api-url http://localhost:8000/v1 \
|
||
--model qwen3-8b \
|
||
--disable-thinking \
|
||
--limit 200 --resume \
|
||
--out-dir results/run1
|
||
```
|
||
|
||
多端点池(轮询 + 自适应并发 + 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 的 bench(hle / 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
|
||
```
|
||
|
||
### 指纹核验(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.5–28 min,本地 vllm 5.6–7 min
|
||
- 报告写入 `--report-path`,同目录 `raw_answers.jsonl` 存全部探针原文
|
||
- 离线分析与参考采集脚本(`cell_snr.py` / `validate_*.py` / `collect_ref.sh` 等,可直接 `python <script>` 运行)与完整方法论文档见 `evalharness/fingerprint/fp_fusion_介绍.md`
|
||
|
||
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(...)
|
||
```
|
||
|
||
## 3. 参数速查
|
||
|
||
| 参数 | 作用 |
|
||
|---|---|
|
||
| `--api-url` `--model` `--provider` | 端点、模型名(纯名字,无需拼 spec)、协议(`openai-chat`/`openai-pool`) |
|
||
| `--api-key` | 显式 key(优先于环境变量;只进请求头,不写入 spec/报告) |
|
||
| `--disable-thinking` | `enable_thinking=false`(Qwen3 类模型推荐;带 tools 的请求自动退回兼容软开关) |
|
||
| `--judge-model` `--judge-api-url` `--judge-api-key` `--judge-provider` | judge 端四件套,语义与主模型对称 |
|
||
| `--limit N` / `--limit-per-task N` | 全局前 N / 每子集前 N(多科目 bench 用后者;可组合取交集) |
|
||
| `--subset` `--split` `--source` | 覆盖子集 / split / 数据源(可指本地路径离线跑) |
|
||
| `--concurrency N` | 并发(默认 32;长输出 bench 建议 8-16) |
|
||
| `--resume [PATH]` | 断点续跑;默认 `<cache-dir>/ckpt/<bench>.jsonl` |
|
||
| `--profile NAME` | 命名生成参数集(`dp4-nothink` / `qwen3-es-parity` / `t1-short` 或自定义) |
|
||
| `--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` | 报告落盘;多 bench 时写 `reports/` + `viz/` + `summary.md` |
|
||
| `--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`。自建端点无鉴权可不管。
|
||
|
||
## 4. 评测结果
|
||
|
||
```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 # 失败样本下钻
|
||
```
|
||
|
||
`--out-dir` 产出结构(不传时自动保存到 `evalharness-results/<时间戳>-<模型>/`):
|
||
|
||
```
|
||
<out-dir>/
|
||
├── summary.xlsx 总表 Excel(Summary/Perf/Categories/Samples 四 sheet,主入口)
|
||
├── summary.csv 同一张总表的 csv 版
|
||
└── <bench>/ 每个 benchmark 一个目录
|
||
├── report.jsonl 完整报告,流式行格式(首行报告头,之后每行一个样本;grep/tail 友好)
|
||
└── detail.md 该 bench 的 markdown 详情
|
||
```
|
||
|
||
## 5. 缓存与断点
|
||
|
||
```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>].jsonl 预测断点(每条完成即追加)
|
||
```
|
||
|
||
- 数据缓存内容寻址(subset/split/source 变更自动新条目),位置 `<cache-dir>/datasets/`
|
||
- 断点每条预测即写盘;**改了 prompt 模板须删旧断点**(`rm <cache-dir>/ckpt/<bench>*.jsonl`),否则复用旧预测
|
||
- 网络抖动三层防护:15s 连接超时快速失败 → 池内换端点 → 分钟级退避重试,断网不丢批次
|
||
- 判分与生成解耦:换 recipe / grader 对存量预测直接重判(`evaluate(ds, preds)`),模型不被重复调用
|
||
- Docker 镜像源回退链可用 `EVALHARNESS_DOCKER_MIRRORS` 覆盖(逗号分隔模板,`{img}` 占位)
|
||
|
||
## 6. 扩展
|
||
|
||
加数据集(单文件放入 `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` 环境变量选择) |
|
||
|
||
## 7. 架构
|
||
|
||
```
|
||
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。
|
||
|
||
## 8. 对齐验证
|
||
|
||
prompt 与判分器经双层验证(字符串级:同一记录双侧渲染逐字节一致;分数级:同题同参数对比 evalscope):
|
||
|
||
- Qwen3-8B:23/28 分差 < 0.05
|
||
- DeepSeek-V4-Flash:20+/25 分差 < 0.05(mmlu_pro 0.0000)
|
||
|
||
残差均已定性(金标集差异 / 排列敏感 / benchmark 侧缺陷),见各 recipe 注释。
|