首页
/ llama.cpp llama-eval:面向多服务器并发的 LLM 评测工具实践(多数据集、可插拔 Grader、断点续跑)

llama.cpp llama-eval:面向多服务器并发的 LLM 评测工具实践(多数据集、可插拔 Grader、断点续跑)

2026-09-04 23:26:53作者:伍霜盼Ellen

examples/llama-eval/ 是 llama.cpp 仓库内置的 HTTP 评测工具集:它以 Python 客户端身份对接 llama-server 的 OpenAI 兼容接口,对数学与科学推理数据集(AIME 系列、GSM8K、GPQA)进行批量评测,内置正则/脚本/LLM 三种评分器、多服务器共享任务队列并发、Wilson 置信区间统计与逐案例增量落盘能力。读完本文,你能复现完整的评测命令、理解并发与断点续跑的实现细节,并会用仓库自带的 llama-server-simulator 在无模型环境下验证整条评测链路。

一、工具组成与总体数据流

评测目录包含三个文件(见 examples/llama-eval/):

  • llama-eval.py:评测主程序,负责数据集加载、任务调度、请求发送、答案抽取与评分、结果落盘;
  • llama-server-simulator.py:一个可配置正确率的假 llama-server,用于在没有真实模型/显卡时验证评测脚本本身;
  • test-simulator.sh:针对 simulator 的端到端自检脚本。

主程序的执行链路为:加载数据集 → 按 seed 生成任务列表 → 逐个服务器探活 → 多线程并发请求 /v1/chat/completions → Grader 抽取并比对答案 → 每完成一个案例就增量 dump JSON/HTML 状态 → 结束输出带置信区间的汇总。整个过程中每个案例的 prompt、期望答案、模型响应、抽取得到的答案、token 数与生成速度都会被记录,方便事后逐题复盘。

README 中还指向了上游 PR #21152 的完整设计讨论与样例结果,可结合当前源码对照阅读。

二、快速上手(Quick Start)

README.md 给出两类最小命令。

单服务器评测 GSM8K 前 100 题,使用正则评分器,单服务器 32 并发:

python3 llama-eval.py \
  --server http://localhost:8033 \
  --model my-model \
  --dataset gsm8k --n_cases 100 \
  --grader-type regex --threads 32

多服务器评测 AIME 2025 全量 240 题(逗号分隔 URL 与各自并发数):

python3 llama-eval.py \
  --server http://server1:8033,http://server2:8033 \
  --server-name server1,server2 \
  --threads 16,16 \
  --dataset aime2025 --n_cases 240 \
  --grader-type regex

几点参数含义(与 llama-eval.py 的参数解析 对应):

  • --server:一个或多个 llama-server 地址,逗号分隔,默认 http://localhost:8033
  • --threads:每个服务器分配的工作线程数,逗号分隔,数量必须与 --server 一致,否则直接报错退出;
  • --server-name:可选,用于结果中标记案例由哪个服务器完成,缺省回退为 URL 本身;
  • --model:请求体中的 model 字段,也用于校验断点续跑时的模型一致性;
  • --n_cases:评测题数,超过数据集大小时会分多轮(chunk)循环取题;
  • --grader-typeregex / cli / llm,默认 llm

三、内置数据集与提问模板

EvalState.load_dataset 支持 5 种数据集(llama-eval.py#L178-L190):

--dataset 取值 数据来源 Split / 配置 说明
aime AI-MO/aimo-validation-aime train 经典 AIME 验证集,答案字段做数字归一化
aime2025 opencompass/AIME2025 AIME2025-I + AIME2025-II 两个配置拼接,split=test 即 2025 年 I/II 卷合计 30 题
aime2026 MathArena/aime_2026 train 2026 卷
gsm8k openai/gsm8kmain test 加载时从 answer#### 后缀中预先抽取数字标准答案(Gsm8kDataset._load_dataset
gpqa OpenAI 公开的 gpqa_diamond.csv 加载时按固定 seed 打乱四选一选项并记录正确字母(GpqaDataset

数据集统一缓存到 ~/.cache/huggingface/datasets,脚本在启动时强制设置 HF_DATASETS_CACHE 并禁用 Hub 遥测(llama-eval.py#L39-L42),重复运行时直接使用缓存。

提问模板集中在 TEMPLATE_REGISTRYllama-eval.py#L79-L110):AIME 系列与 GSM8K 都要求模型把最终数值答案写进 \boxed{} 中,GPQA 则要求末行输出 Answer: A/B/C/D。模板与评分器是配套的——正则评分正是依赖这种格式约定才能可靠地抽取答案。

任务 ID 的格式为 {dataset}_{chunk_idx:03d}_{problem_idx:03d}llama-eval.py#L210),例如 gsm8k_000_042--n_cases 大于数据集长度时,chunk 号会递增,意味着同一道题会被多轮重复评测,这也是 HTML 报表能按题聚合多次运行统计的原因。

四、三种 Grader:regex、cli、llm

评分逻辑封装在 Grader 类(llama-eval.py#L977-L1114),入口为 grade(),按 --grader-type 分派。

1. regex(离线、零成本,README 示例默认用它)

各数据集的正则模式定义在 GRADER_PATTERNSllama-eval.py#L44-L49),AIME 系列与 GSM8K 均为 \boxed{(\d+)}|\b(\d+)\b 一类的数字抽取。抽取策略有两层特殊处理(_extract_answer_regex):

  • AIME 系列优先\boxed{...} 模式,并取最后一个 boxed 结果(最终答案通常出现在推理末尾);
  • 其他数据集则从文本末尾向前取数字,使“最后出现的数”优先于推理过程中的中间数字。

抽取结果与 gold 做字符串相等比较。另外注意:送入评分的响应先经 _truncate_response(..., max_lines=10) 截断为最后 10 行(llama-eval.py#L1211),避免超长推理文本干扰匹配。

2. cli(自定义评分脚本)

调用外部脚本,参数固定为 --answer <模型答案> --expected <标准答案>,以退出码 0 判对,超时 30 秒(_grade_cli)。适合容差比较、单位换算、多解判分等正则难以表达的场景。需要配合 --grader-script 指定脚本路径,脚本不存在会直接抛错。

3. llm(默认,用模型抽取答案)

向 Grader 服务器发送一个 few-shot 抽取请求:system prompt 附上 SAMPLE_ANSWERS 中的示例答案(如 "42""-123"、GPQA 的 "A"/"D"/"C"),要求模型只输出抽取出的答案本身,temperature=0;随后与 gold 忽略大小写比较(_grade_llm)。

相关约束(来自 main() 的参数校验):

  • llm 评分必须提供 --grader-model--model,否则报错退出;
  • --grader-server 缺省为第一个主服务器,此时会打印“正在用同一台服务器做 LLM 评分”的警告;
  • GPQA 数据集强制要求 --grader-type llm(选项内容本身是自由文本,数字正则无法覆盖),见 llama-eval.py#L1488-L1491

五、完整命令行参数速查

以下默认值与取值范围均来自 llama-eval.py 的参数定义

参数 默认值 说明
--server http://localhost:8033 逗号分隔的服务器 URL 列表
--server-name 空(回退 URL) 逗号分隔的服务器显示名,数量须与 --server 一致
--dataset aime aime / aime2025 / aime2026 / gsm8k / gpqa
--n_cases 全部题目 评测题数;超过数据集大小则分 chunk 循环
--seed 1234 题目洗牌随机种子(GPQA 选项打乱也用它)
--n_predict -1 每题最大生成 token 数,-1 表示不限制
--temperature / --top-k / --top-p / --min-p 不传 采样参数,仅在你显式给出时才会写进请求体
--threads 32 每服务器并发线程数,逗号分隔
--model 请求的 model 名,并用于断点续跑一致性校验
--verbose 每案例打印期望/响应/抽取答案/状态
--output llama-eval-state.json 状态文件路径(HTML 报表同名加 .html
--grader-type llm regex / cli / llm
--grader-script cli 评分器脚本路径
--grader-server 第一个主服务器 LLM 评分器地址
--grader-model --model LLM 评分器使用的模型名
--resume 基于已有状态文件续跑未完成案例

请求体的组装在 _make_request:固定包含 model、单条 user 消息(即数据集模板渲染后的 prompt)与 n_predict;四个采样参数则遵循“未指定则不发送”的原则,由 llama-server 侧使用自身默认值,避免评测工具无意覆盖服务端采样配置。

六、并发架构:共享任务队列 + 每服务器独立线程池

多服务器并发的核心在 Processor.evaluate()llama-eval.py#L1261-L1343):

  1. 服务器探活:对每个服务器 GET /v1/models,任一不可达则打印错误并退出,同时打印各服务器挂载的模型清单;
  2. 共享任务队列:所有任务先入一个 Queue;为每个服务器创建独立的 ThreadPoolExecutor(max_workers=该服务器线程数),其 worker 从同一个队列抢任务(_worker)。这意味着快服务器会自然多抢活,负载是动态均衡的,而非按题均分;
  3. 优雅退出:队列尾部放入“总线程数个 None 哨兵”,每个 worker 取到哨兵后自行结束;
  4. 结果回收:worker 把 TaskState 推入结果队列,主线程实时打印进度行(序号、任务 ID、期望答案、抽取答案、tokens、t/s、生成耗时、对错、当前正确率、服务器名);
  5. 终止原因校验:若响应 finish_reason 不是 stop(例如达到 n_predict 上限被 length 截断),该案例直接标记 error: finish_reason=...,不进入评分,避免把截断回答误判为错答(llama-eval.py#L1200-L1209)。

进度行与报表还会记录 reasoning_content(思考型模型的推理内容)与 usage.completion_tokenstimings.predicted_per_second 等指标,性能信息随正确性一起入库。

七、状态持久化、断点续跑与置信区间

增量落盘EvalState.dump()每完成一个案例后被调用(llama-eval.py#L1233),把 idmodel_name、任务列表、各案例的完整记录(prompt/expected/response/answer/grader_log/tokens/tps/t_gen/服务器/chunk 号)与聚合统计写入 --output 指定的 JSON,并同时生成同名 .html 报表。因此进程中途被 kill 也不会丢失已完成案例。

断点续跑语义main 中的状态恢复分支):

  • --output 文件已存在,程序先加载并打印全部任务与已有汇总;
  • 若所有案例都已 ok,直接结束;
  • 未完成且未加 --resume:提示“Run with --resume to continue”并退出;
  • --resume 后仅对 status != ok 的 pending 任务重新评测,历史结果保留;
  • 恢复时会校验 --model 与状态文件中记录的模型名一致,不一致直接报错,防止把两个模型的案例混进同一份报表。

统计口径:汇总与 HTML 头部的正确率均附带 Wilson 95% 置信区间wilson_interval,z=1.96,llama-eval.py#L29-L37),终端输出形如 Results: 87/100 correct (87.0%) [79.6%, 92.4%]。相比裸频率,小样本下(例如 AIME 30 题)这一区间能直观呈现结果的波动幅度。

八、输出产物:JSON 状态与 HTML 报表

JSON(llama-eval-state.json 结构见 dump():顶层含 id(数据集)、model_nametaskssampling_configtask_states 内含 totalcorrecttotal_timeci_lower/ci_uppercases 字典;未跑到的任务会以 status: "pending" 占位,这是断点续跑的数据基础。

HTML(llama-eval-state.htmldump_html() 生成,包含:

  • 顶部信息栏:数据集、模型、正确率及置信区间、正确/完成数、pending 数、总耗时、采样参数串;
  • Detailed 页签:每行一个任务(ID、对错符号、Gold、抽取答案、tokens、t/s、生成秒数、服务器),点击行展开该案例的 Prompt、Response、Reasoning 与 Grader 日志;
  • Summary 页签:按 problem_idx 聚合,给出每题的 Run 次数、正确次数,以及 tokens / t/s / 生成秒数的 min-avg-max,适合评估同一道题多轮采样(chunk 循环)的稳定性。

九、llama-server-simulator:没有模型也能测链路

llama-server-simulator.py 用标准库 HTTPServer 实现了一个假 llama-server,暴露与真服务器相同的两个端点:GET /v1/models 和 POST /v1/chat/completionsRequestHandler)。它的行为(Simulator):

  • 从 AIME/AIME2025 数据集中载入真实题目,收到请求后先剥离模板前缀,再通过子串匹配、去 LaTeX 匹配、以及基于 bigram 的 Dice 系数(阈值 0.3)在题集中定位对应题目;
  • --success-rate 概率返回正确数字答案(否则返回“答案+1”的干扰答案),并伪造 OpenAI 风格的 usage(10 万级 completion tokens)与 timings(90–110 t/s)字段——正好覆盖评测脚本会读取的全部字段;
  • 未匹配到题目时返回 {"error": "No matching question found"},模拟服务端异常分支;
  • 调试日志写入 /tmp/simulator-debug.log

启动参数(main):--port(默认 8033)、--host(默认 localhost)、--success-rate(默认 0.8)、--datasetaime/aime2025)、--dataset-split(默认 train)。

test-simulator.sh 以 80% 正确率启动 simulator 后做四类断言:已知题目应返回标准答案 116、无匹配输入应返回错误 JSON、连续 10 次请求的正确数应落在 8/10 附近,用于验证随机正确率的行为符合预期。典型用法是先在 simulator 上调通 llama-eval.py(参数、评分器、断点续跑),再原样指向真实 llama-server 运行正式评测。

十、运行依赖与适用边界

  • Python 依赖requests(HTTP 客户端)、datasets(HuggingFace 数据集加载,仅首次需要联网下载)、tqdm;GPQA 额外依赖 pandas(直接从 OpenAI 公开 CSV 加载)。建议先建虚拟环境安装后再运行,test-simulator.sh 中也体现了 venv 的用法(test-simulator.sh#L16);
  • 被评对象:任何暴露 /v1/chat/completionsllama-server 实例,支持多实例横向并发;
  • 评测性质:这是基于 HTTP 的黑盒评测,指标只有最终答案正确率(附置信区间)与附带吞吐统计,不覆盖逐 token 对数似然、perplexity 等指标——仓库内的 llama-benchllama-perplexity 等工具覆盖的是另一侧的性能/困惑度测量,两者可互补使用;
  • 已知约束:GPQA 必须用 llm 评分器;regex 评分依赖模型遵循 \boxed{} 输出约定(模板已内置该指令);--n_predict -1 时若模型不自行停止,长回答可能受服务器侧上限约束。

综上,examples/llama-eval/ 提供了一条“数据集 → 并发推理 → 多策略判分 → 可断点、可复盘报表”的完整评测流水线,其设计特点(每案例增量落盘、服务器动态抢活、finish_reason 校验、Wilson 区间)都直接体现在源码中,可作为自建 LLM 评测脚本的参考实现。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
590
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
889
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
982
503
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384