AI 评估方法论:LLM-as-Judge 与 FDE 面试分水岭

约 60 分钟L2L3T1D3

课程来源

[来源] FDE 实战课(fde-teach)

[收录说明] 本课未随文署名,按课程线署名。

本课目录(17 节)

🎯 本节目标

学完这一节,你能:

  • 回答 OpenAI/Anthropic 面试经典问题:"How do you know your AI system is actually working?"
  • 掌握三种评估方法(人工基准、LLM-as-Judge、G-Eval),且每种都能用"考试/班长"的大白话讲明白
  • 说清楚三种评测偏见——不是背名词,而是能讲出"班长会怎么偏心"

🍎 生活化类比

没评估的 AI 系统 = 没质检的工厂。

你做了一个客服机器人,自己测了几条觉得"还行"——上线后客户骂声一片。为什么?因为你测的是"正常问题",客户问的是千奇百怪的边角案例。这就像工厂不质检就把货发出去,自己觉得"看着挺好",客户收到全是坏的。

评估要解决的就是这件事:给你的 AI 系统建一条质检流水线——每改一次代码,自动跑一批测试题,达不到标准就不让出厂。

那"质检"具体怎么干?下面三种方法,我们全用"班级考试"这一套类比贯穿,你会发现它们其实一点都不神秘。

📖 概念讲解

为什么要评估(面试原题)

"How do you know your AI system is actually working?" (你怎么知道你的 AI 系统真的在正常工作?)

这是 OpenAI / Anthropic FDE 面试的分水岭问题。80% 的候选人答不上来——他们只会说"我跑了几条,看着感觉不错"。能答出系统化评估方法的,直接进下一轮。

回到类比:面试官问的就是"你们工厂出货前到底做了什么质检?"。回答"我看了两眼觉得没问题"的,等于没质检。回答"我有标准答案考卷、有班长打分、还有防作弊的"——这才是合格答案。

三种评估方法(考试类比贯穿)

我们先把三种方法和"考试"对应起来,这张表是本节的钥匙:

班级考试里的说法评估方法的名字在干嘛
期末考试有标准答案,老师对答案批改人工基准(Golden Set)准备一批"问题+标准答案",看 AI 答对率
让班长给同学的作业打分LLM-as-Judge用一个强模型给另一个模型打分
班长先写评语,再根据评语打分G-EvalJudge 先写评分理由,再给分,更稳

下面一个个拆开讲。

1. 人工基准(Golden Set)= 期末考试有标准答案

人工基准就是你自己提前准备 50-200 个"问题 + 标准答案"。每次改完代码,把这批题丢给 AI 答一遍,对照标准答案看答对率。

回到类比:这就像期末考试——卷子是同一套(题目不变),标准答案也是提前定好的(老师手里有答案),学生(AI)每次考完按答案批改,对就是对、错就是错,谁也别想蒙混过关。改了代码相当于换了教法,下次再考同一套卷子,看分数是涨了还是跌了。

它的好处是最准——因为是人对答案。坏处是最费力——你得自己一道道准备题、一道道批改。所以通常 50-200 题就够了,不用太多,但题目一定要从真实用户问题里抽,不能拍脑袋编。

2. LLM-as-Judge = 让班长给同学作业打分

LLM-as-Judge(用大模型当评委)就是请一个"更强的大模型"(比如 GPT-4 / Claude)来给另一个模型的输出打分。这样你不用自己一道道批改了,让"班长"帮你批。

回到类比:班里同学(AI)交了作业,老师(你)不想一道道批,于是请班长(一个强模型)帮忙打分。班长水平高、批得快、还不要钱(比人工便宜太多),于是你可以一口气批几百份。但——班长可能有偏心,这点下一节专门讲。

它的好处是便宜、能大规模跑。坏处是班长不一定公平,会有偏见(下面三种偏见就是讲这个)。

3. G-Eval = 班长先写评语,再根据评语打分

G-Eval 是 LLM-as-Judge 的进阶版。区别就一个:让班长先把评语写出来(比如"这个答案没引用资料、还编了一个数字,扣分"),再根据评语给最终分。比让班长直接甩一个分数要稳得多。

回到类比:同样是班长打分。普通 Judge 是"啪,给你 7 分"——你可能不知道为啥。G-Eval 是班长先写"你这题第二步算错了、第三步漏了一个条件,所以给你 7 分",然后再落笔打 7 分。先讲理再打分,分数更靠谱——因为班长被自己写的评语"绑住"了,不能随便拍脑袋改分。

为什么这招更稳?因为直接打分时,模型容易"拍脑袋";先写理由,模型就得真的"看一遍",分数自然更稳。

三种评测偏见(班长可能怎么偏心)

LLM-as-Judge 再好用,也有个大坑:班长会偏心。下面三种偏见,就是最常见的三种"偏心",每种我都配一个生活场景,你一看就懂。

偏见的名字班级里的场景在 AI 里是啥样对策
位置偏见班长偏好"先交卷"或"最后交卷"的同学Judge 偏好排在第一个或最后一个的答案随机打乱顺序,跑两次取平均
Self-preference(自偏好)班长偏好"和自己写字像的"同学Judge 偏好和自己同款模型生成的答案用不同家族模型当 Judge(别让 GPT 给 GPT 批作业)
冗长偏见班长偏好"写得长"的同学,哪怕内容啰嗦Judge 偏好更长的答案,不管对不对加长度惩罚项(写得越长反而越扣分)

一个个看:

回到类比:位置偏见——班长批作业时,老是先夸第一个交卷的,或者老觉得"压轴出场"的最后一份最好。对策很简单:让交卷顺序随机,同一份作业在第一位和最后一位各跑一次,取平均,偏见就抵消了。

回到类比:Self-preference——班长偏好和自己写字像的同学。换成 AI,就是"GPT 当评委,偏爱 GPT 写的答案;Claude 当评委,偏爱 Claude 写的"。对策是别让一个模型给自己的同门批作业——用不同家族的模型交叉当评委。

回到类比:冗长偏见——班长觉得"写得长就是用功",哪怕你啰嗦半天没说到点上也给高分。对策是加一个"长度惩罚"——写得越长反而越扣分,逼 AI 说人话、说短话。

记住这张表,面试被问到"评测偏见怎么处理"时,你就能用"班长偏心"的故事讲得明明白白——这比背名词强 10 倍。

🛠️ 动手实操:给模块 4 的 RAG 建评估

光讲概念不练假把式。我们给模块 4 那个 RAG 应用搭一个最小评估:

  1. 先出题:准备 20 个"问题 + 标准答案"(Golden Set)。题目必须从真实用户问题里抽,别自己拍脑袋出题。
  2. 跑评估:用 RAGAS 跑两个指标——faithfulness(答得有没有编)和 answer_relevancy(答得对不对题)。RAGAS 在模块 4 第 2 节已经装过,下一节还会细讲三件套。
  3. 卡通过线:faithfulness 必须 ≥ 0.8。低于这条线,就回去调检索或加 Rerank,别让它上线。

回到类比:这就是给工厂定了"出厂合格率必须 80% 以上",低于这个数整批打回去重做,绝不让次品混出去。

⚠️ 常见错误

问题原因解决
评估指标"看起来很好",上线翻车评测集和真实分布不一致Golden Set 必须从真实用户问题里抽,不是自己拍脑袋出题
LLM-as-Judge 偏袒某个答案位置偏见 / Self-preference随机打乱顺序跑两次取平均,Judge 用不同家族模型
只测了"正常输入"边角案例没覆盖加红队对抗(Promptfoo),测 injection / 越狱 / PII
G-Eval 分数还是不稳评语写得太空(比如只写"还行")评语必须具体(指出哪条没引用资料、哪句编了),分数才稳

✅ 自测题

<summary>1. OpenAI/Anthropic 面试的分水岭问题是什么?怎么答?</summary>

"How do you know your AI system is actually working?" 答:用 人工基准(Golden Set)+ LLM-as-Judge + 红队对抗三件套,指标不达标不上线。能系统化回答就甩开 80% 候选人。用类比说就是:工厂出货前,既要有标准答案考卷(人工基准)、又要让班长打分(LLM-as-Judge)、还要防作弊(红队对抗),三关都过了才准出厂。

</details> <summary>2. 三种评测偏见是哪些?用"班长打分"讲明白。</summary>
  • 位置偏见:班长偏好先交卷或最后交卷的 → 答案随机打乱顺序,跑两次取平均。
  • Self-preference:班长偏好和自己写字像的 → 用不同家族模型当 Judge。
  • 冗长偏见:班长偏好写得长的 → 加长度惩罚项。
</details> <summary>3. LLM-as-Judge 和 G-Eval 有啥区别?为什么 G-Eval 更稳?</summary>

LLM-as-Judge 是直接让班长(强模型)甩一个分数;G-Eval 是让班长先写评语,再根据评语打分。G-Eval 更稳,因为先写理由逼着模型"真的看一遍",被自己的评语绑住,不能拍脑袋改分。

</details>

📚 总结 & 下一节

评估是 FDE 分水岭。三句话回顾:

  1. 没评估的 AI = 没质检的工厂,必须建一条质检流水线。
  2. 三种方法:人工基准(标准答案考卷)、LLM-as-Judge(班长打分)、G-Eval(先写评语再打分)。
  3. 三种偏见:位置、Self-preference、冗长——每种都用"班长偏心"记住对策。

下一节:工具实战——RAGAS + Promptfoo + Langfuse,把这套评估落成代码、接进 CI。

🏋️ 配套自练

  • Anthropic 评估指南 — 读这篇,自己设计一个 20 题的 Golden Set(免费)
  • DeepEval 文档 — 跟 quickstart 跑通第一个 CI gate 测试(免费)
  • 给模块 4 的 RAG 应用设计 3 个评估指标,想清楚每个指标的"通过线"是多少

🔗 延伸阅读


← 上一节:Agent 实战 | 下一节:评估工具实战 →


动手实验

实验改编自 ai-engineer-notebooks(MIT License)第 04 章 LLM as Judge(02-llm-as-judge.ipynb)· github.com/calmrocks/ai-engineer-notebooks 运行方式:Colab 或本地 Jupyter 任选;模型用 Groq 免费 API(OpenAI 兼容),换成任何 OpenAI 兼容端点(含公司内部网关)只需改 base_url 与 key。

实验目标

为一个小型 RAG 问答系统构建 LLM-as-Judge:写锚定 rubric 的 judge prompt,用强制工具调用拿结构化评分,再亲手触发「偏爱冗长」「分数压缩」两种失真并修复。对应本课时讲义「黄金集 + LLM-as-Judge」与「judge 本身也是 LLM,也要被校准」。

前置条件

  1. 免费 GROQ_API_KEY;本实验 token 消耗大,429 就换有额度的模型(被测系统与 judge 分开换,保持两者不同)。
  2. Colab 或本地 Jupyter;依赖 sentence-transformers、numpy。
  3. 安装 aien 包(自带 groq 客户端)。

实验步骤

步骤 1:安装与初始化

%pip install -q "git+https://github.com/calmrocks/ai-engineer-notebooks.git@v0.1.0" sentence-transformers numpy

from aien import setup
client, MODEL = setup()  # 被测系统用的大模型

步骤 2:被测系统与黄金集

被测系统与上一课时实验同构(RFC 切块 → 向量检索 → 带引用作答,完整管线见原 notebook)。黄金集每例含 question 与人工写的参考答案,并混入语料无答案的 unanswerable 用例(正确行为是拒答):

GOLDEN_SET = [
  {'question': 'What does the HTTP 404 status code mean?',
   'reference_answer': '404 Not Found: the origin server did not find a current '
                       'representation for the target resource.',
   'unanswerable': False},
  # ... 原 notebook 共 16 例(13 可答 + 3 不可答),整段复制即可
]

步骤 3:judge——锚定 rubric + 强制工具调用

import json
from groq import BadRequestError

JUDGE_MODEL = 'openai/gpt-oss-20b'  # 与被测系统不同模型,避免自我偏好

JUDGE_TOOL = {'type': 'function', 'function': {
  'name': 'grade_answer',
  'parameters': {'type': 'object',
    'properties': {'reasoning': {'type': 'string'},
                   'faithful': {'type': 'boolean'},
                   'complete': {'type': 'boolean'},
                   'score': {'type': 'integer'}},
    'required': ['reasoning', 'faithful', 'complete', 'score']}}}

JUDGE_SYSTEM = """You grade answers from a retrieval-augmented QA system against a reference answer.

Rubric -- anchor your score to these definitions and use the full range:
1 = wrong or fabricated: contradicts the reference or invents facts
2 = mostly wrong: a relevant fragment, but the main point is missing or incorrect
3 = partially correct: main point present, but a real gap or a minor unsupported claim
4 = correct: matches the reference on every key point; only minor omissions
5 = correct and complete: nothing missing, nothing unsupported

If the reference says the correct behavior is to decline, a candidate that declines
scores 5 and a candidate that invents an answer scores 1.

Write your reasoning FIRST, then decide the score. Judge substance, not style:
a short correct answer outranks a long vague one. Extra length earns nothing."""

def judge(question, reference, candidate, tries=3):
    for _ in range(tries):  # 强制工具调用偶发 400 tool_use_failed,重试兜底
        try:
            resp = client.chat.completions.create(
                model=JUDGE_MODEL, max_tokens=700, tools=[JUDGE_TOOL],
                tool_choice={'type': 'function', 'function': {'name': 'grade_answer'}},
                messages=[{'role': 'system', 'content': JUDGE_SYSTEM},
                          {'role': 'user', 'content':
                           f'Question: {question}\n\nReference answer:\n{reference}'
                           f'\n\nCandidate answer:\n{candidate}'}])
            return json.loads(resp.choices[0].message.tool_calls[0].function.arguments)
        except BadRequestError as e:
            if 'tool_use_failed' in str(e):
                continue
            raise
    return {'score': 0}  # fail safe:抖动显示为失败,不崩整轮

schema 里 reasoning 排在 score 前、prompt 也强调先写理由再打分:先给分再找理由只是事后合理化,顺序影响评分质量。

步骤 4:给整趟黄金集打分

scores = []
for case in GOLDEN_SET:  # 每例 2 次调用:生成(大模型)+ judge(小模型)
    answer, _ = answer_question(case['question'])
    g = judge(case['question'], case['reference_answer'], answer)
    scores.append(g['score'])
    print(g['score'], case['question'][:55])
print('mean:', sum(scores) / len(scores), 'dist:', sorted(scores))

重点读低分的 reasoning:judge 能抓住关键词覆盖抓不住的问题——术语全对但因果讲反,或悄悄加了参考答案不支持的论断;unanswerable 用例上,「该拒答却编造」的判定也比字符串扫描可靠。

步骤 5:亲手触发失真

q, ref = GOLDEN_SET[1]['question'], GOLDEN_SET[1]['reference_answer']
terse = ('404 Not Found: the server has no current representation for the '
         'target resource, or will not disclose that one exists.')
padded = ('Great question! HTTP status codes are an essential part of how the '
          'web communicates outcomes. The 404 code belongs to the 4xx class, '
          'which broadly indicates the client request was problematic in some '
          'way. It is extremely common in day-to-day browsing.')  # 冗长但没说 not found
for name, cand in [('terse-but-correct', terse), ('padded-but-vague', padded)]:
    g = judge(q, ref, cand)
    print(name, 'score=', g['score'], '|', g['reasoning'][:120])

预期:rubric 锚定时简短正确者赢;删掉 JUDGE_SYSTEM 里「Judge substance, not style / Extra length earns nothing」两行重跑,冗长答案分数上浮。把 rubric 整个换成一句「Grade on a 1-5 scale」,分数会压缩到 3-4 区间——回归从此测不出来。

自查问题

  1. judge 为什么必须用与被测系统不同的模型?「自我偏好」长什么样?
  2. 分数压缩(什么都打 3.5)为什么让评估失去意义?锚定 rubric 怎么对抗它?
  3. 拿前 10 条结果自己人工判一遍再对答案——分歧说明 judge 有 bug,还是你的标准模糊?

随堂测

逐题作答,即时反馈

0 / 3 已作答
  1. 1. OpenAI/Anthropic 面试的分水岭问题“How do you know your AI system is actually working?”的正确应答思路是什么?

  2. 2. 关于讲义中的三种评估方法,下列哪项对应关系是正确的?

  3. 3. 以下对三种评测偏见及其对策的配对,正确的是?