跳到主要内容

第 4 章:巧妙之处、边界与横向对比

这章收尾:先讲几个值得抄进自己评测系统的技巧,再诚实划出它不做什么、会在哪崩,最后和货架兄弟项目对比并给总代码地图。

4.1 巧妙之处(可借鉴的技术)

防泄题:XOR + SHA256 加密题库

妙在哪: 评测题库一旦明文进了网络,就会被未来的模型「背下来」(数据污染 / contamination),分数失真。BrowseComp 的题目和答案在 CSV 里是加密存放的,运行时才用每行自带的 canary 口令解密。

机制极简(browsecomp_eval.py:50-63):用 SHA256 把口令拉成定长密钥,与密文逐字节 XOR:

# 示意,非源码 —— 提炼自 derive_key / decrypt
def decrypt(ciphertext_b64, password):
encrypted = base64.b64decode(ciphertext_b64)
key = derive_key(password, len(encrypted)) # SHA256(password) 重复填到等长
return bytes(a ^ b for a, b in zip(encrypted, key)).decode()

解密在判分前一刻才做(browsecomp_eval.py:97-98)。这不是为了保密强度(XOR 很弱),而是为了让题库不以明文形式出现在公开仓库,降低被爬进训练集的概率。

GPQA:选项乱序消除位置偏差

妙在哪: 模型对选项位置有偏好(比如偏爱 C)。如果正确答案总在固定位置,分数会被这种偏差污染。GPQA 给每道题随机一个 permutation,把四个选项打乱后再问,正确答案落在哪个字母是随机的(gpqa_eval.py:3345-47)。

配合 n_repeats(默认 10,simple_evals.py:366),同一题用不同乱序跑多遍,把位置偏差「平均掉」。

bootstrap 误差棒

妙在哪: 一个 92.9 的分数,换一批题会抖多少?库提供 bootstrap_std(common.py:175-178):对分数列表有放回重采样 1000 次、各算均值、取这些均值的标准差,作为分数的不确定度。HealthBench 默认就报它(healthbench_eval.py:231-236)。报分数时附误差棒,是诚实评测的基本功。

处处固定随机种子 → 可复现

妙在哪: 抽子集、打乱选项、采少样本,全部用固定种子:MMLU/MATH/GPQA/SimpleQA 用 random.Random(0)(如 mmlu_eval.py:93gpqa_eval.py:28),DROP 用 random.Random(42)(drop_eval.py:238)。于是「随机抽 10 道题」对所有人都是同一批 10 道题——--examples 10 的结果可复现、可对比。

推理模型的 reasoning_effort 旋钮

妙在哪: 同一个模型在不同「思考深度」下分数差别很大,所以注册表把 o3_high / o3_low 等当不同模型注册(simple_evals.py:155-179),让 README 的表能逐档汇报(README.md:16-22)。评测把「推理预算」当成一个一等的实验变量。

4.2 边界与局限(诚实)

它刻意不做 / 做不了:

  • 已停止维护。 README 顶部明示 2025 年 7 月起不再更新模型与跑分,只保留 HealthBench/BrowseComp/SimpleQA 作参考实现(README.md:1-3);明确不收新 eval(README.md:66)。
  • 不是综合评测平台。 自陈不打算替代 openai/evals(README.md:71)。
  • 无缓存、无断点续跑。 主循环就是直接 eval_obj(sampler) 跑完写 /tmp(simple_evals.py:474-507);跑到一半挂了,得从头再来。大规模跑(尤其 HealthBench 要为每条 rubric 各调一次裁判)会很费 API。
  • 结果只落 /tmp 报告/JSON 全写 /tmp/...(simple_evals.py:481-505),非持久化存储设计。

会在哪「崩」或失真:

  • 判分依赖正则命中。 模型若不照「Answer: X」格式输出,正则抽不到答案,即便答案对也判错(mmlu_eval.py:107-114 抽不到时 extracted_answerNone)。归一化(§2.1)缓解但不能根治。
  • 裁判模型即天花板。 SimpleQA/BrowseComp/HealthBench 的分数上限由裁判模型的判断力决定;裁判偏了,分数就偏了——这正是 §3.7 meta-eval 要量化的风险。
  • MGSM 把异常吞成空串。 采样抛异常时 response_text = ""(mmlu… 不,见 mgsm_eval.py:169-170),该题直接判错——网络抖动会被记成模型答错。
  • 依赖外网题库。 题库都从 openaipublic.blob.core.windows.net 现拉(如 mmlu_eval.py:87-90),离线跑不了。

4.3 横向对比(货架内)

本项目属于 ai-frontier-referenceevals-observability 区。它的定位与取舍:

维度simple-evals 的取舍
范围窄而精:少数高信号基准 + 参考判分逻辑,不求大而全
提示风格坚持零样本 + 思维链,反对少样本/角色扮演(README.md:60-64)
抽象极薄:只有 Sampler + Eval 两个接口,无插件系统、无配置 DSL
判分从正则到 LLM 裁判到加权 rubric,判分逻辑本身就是被开源的主角
工程化故意不做缓存/续跑/持久化——它是「参考实现」,不是生产评测平台

一句话:它和重型评测框架(harness/平台)的根本区别是——它把「怎么判分」当成要讲清楚的内容本身,而不是藏在框架背后的实现细节。

4.4 总代码地图(全库导航)

主题文件符号
编排:模型注册表 + eval 工厂 + 主循环simple_evals.pymainget_evals
两个核心接口types.pySamplerBaseEvalSamplerResponseEvalResult
并发 / 聚合 / 报告 / 正则 / 归一化common.pymap_with_progressaggregate_resultsmake_reportMULTILINGUAL_ANSWER_REGEXESnormalize_response
模型适配器(4 个)sampler/*.pyChatCompletionSamplerOChatCompletionSamplerResponsesSamplerClaudeCompletionSampler
选择题(正则判分)mmlu_eval.pygpqa_eval.pyMMLUEvalGPQAEval
数学(等价裁判)math_eval.pycommon.pyMathEvalcheck_equality
阅读理解(F1 + 匈牙利)drop_eval.pyDropEval_align_bags_compute_f1
多语言数学mgsm_eval.pyMGSMEvalparse_answer
代码(执行判分)humaneval_eval.pyHumanEvalevaluate_functional_correctness
事实性(LLM 裁判)simpleqa_eval.pybrowsecomp_eval.pySimpleQAEvalBrowseCompEvaldecrypt
医疗(加权 rubric)healthbench_eval.pyhealthbench_meta_eval.pyHealthBenchEvalRubricItemcalculate_scoreHealthBenchMetaEval