跳到主要内容

03 · 模型当裁判(LLM-as-judge)

本章讲 Evals 最有借鉴价值的一块:当答案是开放式(摘要、解释、翻译)、没法字符串比对时,怎么"让另一个模型来打分",并且把它的打分逼成机器可解析的形式。

1. 这一章要解决的问题

问"法国首都是?"可以字符串比对;但问"总结这段话"或"这个回答事实对不对?"——答案有无穷种合法写法,startswith 完全失效。

思路:用模型评模型。 把"原始问题 + 被测模型的回答 + 标准答案"包进一段评分 prompt,交给一个裁判模型,让它输出一个可枚举的选项(如 A/B/C/D/EYes/No),再把选项解析成分数。裁判模型和被测模型可以是同一个,也可以不同。

2. 数据流:一题怎么被裁判

test_sample
{input, ideal}


① 被测模型答题(若样本里没带现成 completion)
completion = completion_fn(input)


② 填进评分模板(把 {input}{ideal}{completion} 代入)
prompt = mg.prompt.format(...)


③ 追加"答题格式"指令(eval_type 决定:先推理后选项 / 只给选项)
prompt += ANSWER_PROMPTS[eval_type]


④ 裁判模型打分
evaluation = eval_completion_fn(prompt)


⑤ 解析出选项 → 映射成分数
choice = get_choice(evaluation, ...) # 取不到 → "__invalid__"
score = choice_scores[choice]

对应 ModelBasedClassify.eval_sample(evals/elsuite/modelgraded/classify.py:53-102)和 classify(classify_utils.py:51-87)。

3. 裁判规格:ModelGradedSpec

一个模型裁判由 YAML 声明,加载成 ModelGradedSpec(evals/elsuite/modelgraded/base.py:11-26)。真实例子 evals/registry/modelgraded/fact.yaml(事实一致性判定):

fact:
prompt: |-
[Question]: {input}
[Expert]: {ideal}
[Submission]: {completion}
... 比较 submission 和 expert 的事实内容,选一个:
(A) submission 是 expert 的子集且一致
(B) submission 是 expert 的超集且一致
(C) 细节完全一致
(D) 二者冲突
(E) 有差异但不影响事实性
choice_strings: ABCDE # 期望裁判输出的可枚举选项
input_outputs:
input: completion # 用 input 这个字段去生成 completion

关键字段:

字段作用
prompt评分 prompt,{key} 由样本字段或额外 args 填充
choice_strings裁判应输出的选项集合(如 "ABCDE"["Yes","No"])
input_outputs映射:用哪个输入字段生成哪个 completion 字段
choice_scores(可选)每个选项 → 分数,用于汇总平均分
eval_type(可选)期望的答题格式(见下节)

4. 关键技巧一:eval_type 把"怎么答"标准化

裁判模型不会自动用可解析格式回答,所以框架自动追加一句格式指令。三种 eval_type 对应三种 ANSWER_PROMPTS(classify_utils.py:13-28):

eval_type含义追加的指令(大意)
cot_classify先推理,后选项(推荐)先一步步推理,然后单独一行打印 {choices} 之一,最后再重复一遍答案
classify_cot先选项,后推理先打印选项,再从下一行解释理由
classify只给选项只打印 {choices} 之一,别的都不要

为什么默认推荐 cot_classify?因为让模型先推理再下结论通常更准(避免"开口就拍脑袋")。文档明确把它列为默认(docs/eval-templates.md:37)。

指令是 append_answer_prompt(classify_utils.py:131-149)拼到 prompt 末尾的,{choices} 被替换成 "A" or "B" or ...(choice_to_str,classify_utils.py:105-107)。

5. 关键技巧二:解析选项时"反向扫行"

裁判输出一大段推理 + 选项,怎么稳健地抠出那个选项?get_choice(classify_utils.py:110-128)的做法很巧:

# 示意,非源码:从裁判的多行输出里抠出选项
lines = text.strip().split("\n")
if eval_type.startswith("cot_classify"):
lines = lines[::-1] # 关键:CoT 模式下反转,从最后往前找
for line in lines:
line = strip_punctuation(line) # 去标点,避免 "(A)." 干扰
for choice in choice_strings:
if match_fn(line, choice): # 默认 starts_or_endswith
return choice
return "__invalid__" # 一个都没匹配上

两处设计值得记:

  • CoT 模式反向扫行(classify_utils.py:117-118):因为 cot_classify 让模型"最后再重复一遍答案",最终选项在末尾,反转后第一个命中的就是它,避开了推理正文里偶然出现的选项字母。
  • 解析失败不崩,记 __invalid__(常量 INVALID_STR,classify_utils.py:10):裁判跑偏时这一题被标记为无效,而不是让整轮评测挂掉。

匹配函数可换(MATCH_FNS,classify_utils.py:29-34:include/exact/endswith/starts_or_endswith),默认 starts_or_endswith

6. 关键技巧三:选项 → 分数,以及元评测

选项是离散标签,要汇总成数字得映射成分数。get_choice_score(classify_utils.py:90-102)处理:

  • choice_scores{选项: 分} 字典;特殊值 "from_strings" 表示"选项本身就是数字"。
  • __invalid__ 取最低分(classify_utils.py:99-101)——无效判定不该被当作高分。

run(classify.py:104-127)汇总时:统计各选项出现次数(counts/Acounts/B...)、对有分的选项求平均 score

还有一个"评测裁判本身"的模式 metaeval(classify.py:96-98):如果样本带了人工标注的 choice,就比对"裁判选的"和"人标的"是否一致,产出 metascore——这是用来验证裁判靠不靠谱的。

7. 巧妙之处

  • 把"主观打分"工程化成"受限分类"。 不让裁判自由发挥,而是用 prompt + 选项集 + 格式指令把它逼进可枚举、可解析的输出空间。这是 LLM-as-judge 落地的核心套路。
  • CoT + 末尾复述 + 反向解析三件套配合:既拿到推理(更准)、又能稳健抽取最终选项(忽略推理里的噪音)。
  • 失败可降级:__invalid__ 既不崩溃也不污染分数(取最低),让大规模自动评测鲁棒。
  • 裁判可被元评测:用人标数据回头校验裁判,直面"谁来评判裁判"的问题。

8. 边界与局限

  • 裁判有偏见。 同模型自评、对长答案的偏好、位置偏好等是已知问题;框架提供 metaeval 帮你度量但不能消除。
  • 解析靠选项不含标点(get_choice_strings 里 assert 选项无标点,classify_utils.py:45-47),所以选项标签必须干净(A/Yes),不能是 "Yes!"
  • 多模型对比有约束:multicomp_n 与传入模型数必须自洽(classify.py:42-49 的断言),配置不当会直接 assert 失败。

9. 横向对比

同属"自动评测"思路,但与基础模板(02 章)取舍不同:基础模板确定、便宜、不需额外模型,适合有唯一答案的题;模型裁判灵活、能评开放式,但(多一次模型调用)、有偏、需要 metaeval 兜底。实践中先尽量用基础模板,实在判不了才上裁判(docs/eval-templates.md:20 也建议"用最佳判断在三者间取舍")。

10. 代码地图

主题文件路径符号名
裁判 Eval 主类evals/elsuite/modelgraded/classify.pyModelBasedClassifyeval_samplerun
裁判核心逻辑evals/elsuite/modelgraded/classify_utils.pyclassifyget_choiceget_choice_scoreappend_answer_promptANSWER_PROMPTSMATCH_FNSINVALID_STR
裁判规格evals/elsuite/modelgraded/base.pyModelGradedSpec
规格在注册表的查询evals/registry.pyget_modelgraded_spec
示例裁判evals/registry/modelgraded/fact.yamlclosedqa.yamlbattle.yaml