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
evalharness data list     # 28 个数据集:源/子集/few-shot/split
evalharness eval list     # 28 个判分 recipe

1. 安装

Python ≥ 3.10;代码执行类 benchmark 需宿主机 Docker镜像判分时自动拉取

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%

evalharness eval run gsm8k --model mock-boxed --limit 8

2. 运行命令

六个基准一次跑完(默认用法;逐 bench 生成参数自动读 evalharness/config/default.yamlaime 系列按配置自动跑 12 遍取均值):

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

单端点(最小示例):

evalharness eval run gsm8k \
    --api-url http://localhost:8000/v1 \
    --model qwen3-8b \
    --disable-thinking \
    --limit 200 --resume \
    --out-dir results/run1

多端点池(轮询 + 自适应并发 + failover

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

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多轮工具调用

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 五个视图。

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

Python API

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=falseQwen3 类模型推荐;带 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.comOPENAI_API_KEYanthropic.comANTHROPIC_API_KEYdashscopeDASHSCOPE_API_KEYbigmodelZAI_API_KEY> OPENAI_API_KEY。自建端点无鉴权可不管。

4. 评测结果

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          总表 ExcelSummary/Perf/Categories/Samples 四 sheet主入口
├── summary.csv           同一张总表的 csv 版
└── <bench>/              每个 benchmark 一个目录
    ├── report.jsonl      完整报告流式行格式首行报告头之后每行一个样本grep/tail 友好)
    └── detail.md         该 bench 的 markdown 详情

5. 缓存与断点

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/,自动注册):

@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 = 注册原语的声明式组合):

@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-8B23/28 分差 < 0.05
  • DeepSeek-V4-Flash20+/25 分差 < 0.05mmlu_pro 0.0000

残差均已定性(金标集差异 / 排列敏感 / benchmark 侧缺陷),见各 recipe 注释。

Description
No description provided
Readme 2.7 MiB
Languages
Python 99.2%
Shell 0.8%