跳到主要内容

OpenAI Evals — 架构与原理

30 秒导读: OpenAI Evals 是一个评测 LLM(或基于 LLM 的系统)的框架。你给它一份 JSONL 数据集和一条"怎么算答对"的规则,它就并行地把每个样本喂给模型、判分、把全过程逐条记成日志,最后汇总出准确率这类指标。它的精髓是"声明式":大多数评测不用写代码,只写一个 YAML 把现成的评测模板和数据集拼起来。

1. 这是什么(零基础也能懂)

一句话定义: Evals 是一个测 LLM 好不好用的框架——给定一批题目和"标准答案/评分规则",自动跑模型、对答案、出分数。

解决什么问题 / 给谁用。 假设你在用 GPT 做"把用户问题分类"或"翻译",换了个模型版本后,你怎么知道是变好了还是变差了?靠人工抽看几条不靠谱。Evals 让你把"判断标准"固化成一个可复跑的测试集:换模型、改 prompt,跑一遍就有可比的数字。它面向用 LLM 搭东西的工程师,以及想给模型能力建基准(benchmark)的研究者

它能做什么。

  • 内置一个评测注册表(registry):仓库里有 400+ 个现成 eval(evals/registry/evals/ 下有 463 个 YAML),覆盖数学、翻译、推理、安全等维度。
  • 提供几类评测模板,让常见评测零代码:精确匹配、包含匹配、模糊匹配、JSON 匹配、以及"模型当裁判"。
  • 支持自定义评测逻辑(写一个 Eval 子类)和自定义被测系统(写一个 CompletionFn / Solver),也能接非 OpenAI 的模型。
  • 把每次运行的每一步(采样、判分、指标)流式写成 JSONL 日志,可事后分析。

用起来什么样。 一条命令就能跑(README 的 "Running evals" 节):

# 用 gpt-3.5-turbo 跑名为 test-match 的评测
oaieval gpt-3.5-turbo test-match

左边 gpt-3.5-turbo 是"谁来答题"(completion_fn),右边 test-match 是"考哪张卷子"(eval 名字,去注册表里查)。跑完结果汇总打到日志、明细落到 /tmp/evallogs/<run_id>_....jsonl(见 evals/cli/oaieval.py:197-201record_path)。

一句话直觉/类比。 把它想成自动化考试系统:注册表是"题库",eval 是"一张卷子+判分标准",completion_fn/solver 是"考生",recorder 是"监考+成绩单"。框架负责把考生叫来、发卷、收卷、判分、登记成绩——你只需要声明用哪张卷子、哪个考生。

本节不碰任何底层细节。记住三个角色就行:卷子(Eval)/ 考生(CompletionFn/Solver)/ 成绩单(Recorder)

2. 顶层全景(它大概怎么转)

这一节看懂"一条 oaieval 命令从敲下到出分"的大盘。

怎么读下面这张图: 从上到下是一次运行的时间顺序;oaieval 是总指挥,先去注册表把两个字符串名字解析成真实对象,再交给 Eval 主循环并行跑样本,每跑一步就经 Recorder 落盘。

oaieval <completion_fn> <eval> ← 命令行两个名字(字符串)


┌──────────────────────┐
│ Registry(注册表) │ 读 evals/registry/**/*.yaml
│ 名字 → 规格 → 对象 │ 解引用别名、构造实例
└──────────────────────┘
│ │
完成对象 ▼ ▼ 完成对象
┌───────────────┐ ┌──────────────────┐
│ CompletionFn │ │ Eval 子类实例 │ 如 Match / ModelBasedClassify
│ /Solver(考生)│ │ (卷子+判分) │
└───────────────┘ └──────────────────┘
│ eval.run(recorder)

┌─────────────────────────────┐
│ 主循环 eval_all_samples │ 线程池并行
│ 对每个样本: eval_sample() │ → 调考生 → 判分
└─────────────────────────────┘
│ 每步 record_*()

┌─────────────────────────────┐
│ Recorder(成绩单) │ 事件流 → JSONL
│ sampling / match / metrics │ 最后 record_final_report
└─────────────────────────────┘

部件一句话职责:

部件干什么在哪个文件
oaieval CLI解析参数,串起注册表→构造→运行→落盘evals/cli/oaieval.py(run)
Registry把名字(模型名或注册表 key)解引用成 CompletionFn/Eval 实例evals/registry.py(Registry)
Eval 基类定义"加载数据→并行跑样本→汇总"的骨架evals/eval.py(Eval)
基础模板(Match 等)用一行规则判对错(startswith / in / 模糊)evals/elsuite/basic/
ModelBasedClassify模型当裁判,把开放式回答判成单选evals/elsuite/modelgraded/classify.py
CompletionFn / Solver被测系统:prompt→文本 / TaskState→SolverResultevals/api.pyevals/solvers/solver.py
Recorder把每步事件流式写成 JSONL,汇总最终报告evals/record.py

主线走一遍(高层、不进代码):

  1. 解析名字。 oaieval gpt-3.5-turbo test-match:run()Registrytest-match 查成一个 EvalSpec(指向某个 Eval 类 + 数据集路径),把 gpt-3.5-turbo 造成一个 OpenAIChatCompletionFn
  2. 构造卷子。 用 spec 里的 cls 实例化 Eval 子类(如 Match),把考生(completion_fn)和数据集路径塞进去。
  3. 跑。 eval.run(recorder) → 读 JSONL 样本 → 线程池并行,对每个样本调 eval_sample:调考生拿回答、和 ideal 对比、record_match 记一笔。
  4. 汇总。 run 收集所有 match 事件,算 accuracybootstrap_std,record_final_report 写进日志末尾。

看懂这条主线,后面四章只是把每个方框拆开看内部。

3. 阅读地图(建议顺序)

按"由浅入深"读:

  1. 01-cli-and-registry.md —— 先搞懂"名字怎么变对象":CLI 参数、注册表的 YAML 加载与别名解引用、动态构造。这是整个框架的"装配车间"。
  2. 02-eval-loop.md —— 评测主循环的契约:eval_sample / run 两个方法、并行执行、确定性随机种子;再看 Match/Includes 这类最简单的"对答案"规则。
  3. 03-model-graded.md —— 当答案开放、没法字符串比对时,怎么"让模型当裁判":ModelBasedClassify + cot_classify + 选项解析。这是 Evals 最有借鉴价值的一块。
  4. 04-completion-fns-and-solvers.md —— 被测系统的两层抽象演化(CompletionFn → Solver)、为什么要分,以及事件记录器的设计。

4. 代码地图(导航索引)

主题文件路径符号名
CLI 主流程evals/cli/oaieval.pyrunget_parserbuild_recorder
注册表evals/registry.pyRegistrymake_completion_fn_dereference_load_registry
规格数据类evals/base.pyEvalSpecCompletionFnSpecRunSpec
评测基类evals/eval.pyEvalSolverEvaleval_all_samples_index_samples
基础模板evals/elsuite/basic/match.pyincludes.pyMatchIncludes
匹配工具evals/api.pyrecord_and_check_matchCompletionFn
模型裁判evals/elsuite/modelgraded/classify.pyclassify_utils.pyModelBasedClassifyclassifyget_choiceANSWER_PROMPTS
裁判规格evals/elsuite/modelgraded/base.pyModelGradedSpec
Solver 抽象evals/solvers/solver.pySolverSolverResultNestedSolvercreate_solver
任务状态evals/task_state.pyTaskStateMessage
记录器evals/record.pyRecorderBaseLocalRecorderrecord_matchrecord_sampling
指标evals/metrics.pyget_accuracyget_bootstrap_accuracy_stdget_confusion_matrix