数据截至 (上游 commit 8eac7a7de521)
01 · CLI 与注册表:名字怎么变成可运行对象
本章讲 Evals 的"装配车间":你在命令行写的两个字符串名字,是怎么变成真实的考生对象和卷子对象的。读完你会明白为什么大多数 eval "只写 YAML、不写代码"。
1. 这一章要解决的问题
你敲 oaieval gpt-3.5-turbo test-match,框架手里只有两个字符串。它得回答两件事:
gpt-3.5-turbo是个什么考生?(一个 OpenAI 模型?还是注册表里某个带脚手架的系统?)test-match是哪张卷子?(用哪个 Eval 类、配哪个数据集、什么参数?)
把字符串解析成对象 的这套机制,就是 Registry。
2. CLI 入口:run() 的编排
oaieval 的 main 只是解析参数后调 run(evals/cli/oaieval.py:297-307)。真正的编排在 run(oaieval.py:118-239),它的步骤很直白:
1. registry.get_eval(args.eval) → 拿到 EvalSpec(卷子规格)
2. registry.make_completion_fn(url) → 造出考生实例(可有多个,逗号分隔)
3. registry.get_class(eval_spec) → 拿到 Eval 子类(还没实例化)
4. eval = eval_class(completion_fns=..., name=..., ...) → 实例化卷子
5. result = eval.run(recorder) → 跑!
6. recorder.record_final_report(result) → 落盘
几个值得注意的设计:
- 考生可以是多个。
args.completion_fn.split(",")(oaieval.py:168)——一条命令能传多个考生,逗号分隔。这是"模型 vs 模型"或"模型 + 裁判模型"类评测的入口。 - 额外参数走
--extra_eval_params。 形如key1=val1,key2=val2,由parse_extra_eval_params(oaieval.py:136-155)解析,并智能转成 int/float,合并进eval_spec.args。 - 运行有唯一 id。
RunSpec在__post_init__里用时间戳 + 随机后缀生成run_id(evals/base.py:85-89),用作日志文件名和事件归属。
3. 注册表:YAML 即配置
3.1 它要解决的小问题
要做到"零代码加 eval",就得有个地方把"名字 → (用哪个类、什么参数)"的映射声明出来。Evals 用一堆 YAML 文件干这事,Registry 负责加载和查询。
3.2 注册表长什么样
注册表按资源类型分目录:evals/、completion_fns/、solvers/、eval_sets/、modelgraded/。Registry 用 @cached_property 懒加 载每一类(registry.py:312-330)。一个最简单的 eval YAML 是这样(真实例子,evals/registry/evals/test_japanese_english_numerals.yaml):
test_japanese_english_numerals: # 基础名(别名)
id: test_japanese_english_numerals.dev.v0
description: ...
metrics: [accuracy]
test_japanese_english_numerals.dev.v0: # 真正的规格
class: evals.elsuite.basic.match:Match # 用哪个 Eval 类
args:
samples_jsonl: test_japanese_english_numerals/samples.jsonl # 数据集
注意两点:一是命名约定 {base_eval}.{split}(如 .dev.v0),Eval.__init__ 会断言名字至少有这两段(eval.py:65-67);二是 class: 字段在加载时被改写成内部的 cls 键(见下)。