面试回答评估方案

记录评估思路演进、各方案对比、以及开源项目参考。 对应代码:evaluate_answer() in 11_模拟面试官.py


一、问题定义

面试评估需要回答:候选人的回答有多好?

这不是一个单一维度的问题。以下三个维度相互独立,缺一不可:

维度问题当前支持
知识覆盖 Recall说了多少该说的?✅ key_points 覆盖率
知识正确性 Correctness说的有没有错?❌ 不扣错误分
理解深度 Depth知道 what,还是也知道 why?❌ 无

只有第一维度会导致:

  • 候选人大量说错也不扣分
  • 候选人用不同措辞说出正确认知却被判 miss

二、评估方案三级演进

Level 1(当前):key_points 二值覆盖

对照 key_points 列表,每条判断:命中 or 未命中
得分 = 命中条数 / 总条数

优点:简单、可解释、易实现 缺点

  • 语义等价不识别(换个说法就 miss)
  • 开放题强依赖预设答案(Turn 2 问题)
  • 不惩罚错误

Level 2(推荐下一步):语义覆盖 + 正确性惩罚

核心改动:

  1. key_points 判断从”原文匹配”改为”语义等价”
  2. 增加错误陈述检测,命中一条扣 1 分
prompt = """
面试题:{question}
参考答案:{reference_answer}
评分要点(语义等价即可,不要求原文):
{key_points}
 
候选人回答:{user_answer}
 
请分两步评估:
Step 1:逐条判断候选人是否表达了该要点的核心含义(不要求原文,意思对即可)
Step 2:候选人是否有明显错误陈述?每条错误 -1 分
 
返回 JSON:
{
  "key_points_hit": [...],
  "key_points_missed": [...],
  "errors": ["错误陈述1",...],   // 有则列出,无则空数组
  "score": <覆盖得分 - 错误扣分,最低0>,
  "max_score": {max_score},
  "feedback": "1-2句点评"
}
"""

对 Turn 2 的改善:用户说了 3 个合理误解(内容正确,只是不在预设列表)→ 语义判断后应得 2-3 分。


Level 3(完整方案):G-Eval 模式

来自论文 G-Eval: NLG Evaluation using GPT-4 with Better Human Alignment(2023)。

核心思路:让 LLM 先写推理过程再打分,而非直接给分。

prompt = """
你是一位严格的面试评估员。
 
【第一步:逐条分析】
对每个评分要点,分析候选人回答是否覆盖了其核心含义。
允许不同表述,判断语义是否等价。
 
【第二步:错误检测】
候选人是否有明显错误认知?列出。
 
【第三步:开放题判断(如适用)】
对"列举X个..."类问题,候选人给出的内容是否是该领域真实存在的问题?
不要求与参考答案完全一致,只要内容本身正确即可。
 
【第四步:给分】
综合以上分析,给出最终得分。
 
返回 JSON:{score, max_score, key_points_hit, key_points_missed, errors, reasoning, feedback}
"""

论文数据:加 CoT 步骤后,LLM 裁判与人类评分的一致性提升约 15%


三、开放题的特殊处理

开放题定义:没有唯一正确答案的题目,如”列举 3 个误解”、“说说你的理解”。

当前问题:预设 key_points 是参考答案,但候选人完全可以说出同样有效但不在列表里的内容。

判断标准切换

题型判断标准
事实题(公式/定义/流程)对照 key_points,语义等价即可
开放题(列举/分析/理解)判断候选人的内容是否领域正确,而非是否和参考答案重合

开放题识别方式:在 interview_qa.json 中增加 "type": "open" 字段,评估时走不同分支。


四、与向量相似度结合

除 LLM 判断外,可加入 embedding 余弦相似度作为辅助信号:

# 计算用户回答与参考答案的语义相似度
sim = cosine_similarity(embed(user_answer), embed(reference_answer))
# sim > 0.8:很相关;0.5~0.8:部分相关;< 0.5:偏离

与 LLM 得分加权融合:

final_score = 0.7 * llm_score + 0.3 * (sim * max_score)

优点:embedding 判断快、成本低,可做 LLM 打分前的快速过滤。


五、评估可信度验证

无论用哪种方案,LLM 裁判本身需要校准:

  1. 人工标注 20 条:手动给每条回答打分,与 LLM 分数对比,计算 Pearson 相关系数
  2. 多次评估取均值:同一回答评 3 次,分数方差 > 1 说明裁判不稳定
  3. 引入 RAGAS 框架:用 interview_qa.json 作为 golden dataset,跑 Context Recall / Faithfulness

六、待实现优先级

优先级方案预期收益
P0Level 2:语义覆盖 + 错误惩罚解决 Turn 2/5 误判,评分更准
P1开放题 type 字段 + 分支判断消除预设答案绑定
P2G-Eval CoT 模式提升一致性 15%
P3embedding 辅助打分低成本双重验证
P4RAGAS 接入系统级评估,量化知识库质量

七、开源项目实现参考

来源:RAGAS / DeepEval / G-Eval 论文 / Prometheus / LangChain OpenEvals

7.1 RAGAS 的核心模式

Faithfulness(忠实性)— 两阶段 NLI

Stage 1: 把回答拆成独立陈述句(去代词、可独立理解)
Stage 2: 每条陈述逐一判断:能否从检索内容直接推导?返回 {statement, reason, verdict: 0/1}
Score = 命中陈述数 / 总陈述数

Answer Relevancy(答案相关性)— 逆向工程 + embedding

不直接打分,而是:

  1. 让 LLM 生成 N 个”这个回答能回答什么问题”
  2. 计算生成问题与原始问题的 embedding 余弦相似度
  3. Score = mean cosine similarity

设计洞见:不问”答案好不好”,而问”答案指向的问题和原始问题是否一致”——绕开主观判断。

Factual Correctness(事实正确性)— 原子声明 F1

分解 response → 原子声明列表
分解 reference → 原子声明列表
TP = 两者共有的声明
FP = response 有但 reference 没有(多说的/错的)
FN = reference 有但 response 没有(漏说的)
Score = F1(TP, FP, FN)  ← 同时惩罚多说和少说

这是最适合面试评估的 RAGAS 指标,直接对应”覆盖率 + 正确性”双维度。


7.2 DeepEval 的 G-Eval 实现

两步算法

Step 1: 给 LLM 评估标准 → 让它生成评估步骤(自动 CoT)
Step 2: LLM 按步骤评分 1-5
        但不取整数,而是对每个 token(1/2/3/4/5)取对数概率加权:
        final_score = Σ P(token_i) × value_i
        → 输出连续值,比离散整数更细粒度

Rubric 多维度分解(DeepEval 特有):

rubric = [
    Rubric(score=[0, 3], criteria="知识覆盖:是否提到关键概念"),
    Rubric(score=[0, 4], criteria="事实正确:有无错误陈述"),
    Rubric(score=[0, 3], criteria="理解深度:是否解释了 why"),
]
# 每个维度独立评估,总分 0-10 再归一化

关键发现:三个维度不要合在一个 prompt 里打分——LLM 同时处理多个独立标准时得分不一致,分开调用后合并效果更好。


7.3 Prometheus — 专为评估微调的模型

开源评估专用模型(基于 LLaMA 微调),Pearson 相关系数 0.897,接近 GPT-4。

核心 Prompt 结构

[参考答案(Score 5 的锚点)]: {reference_answer}
[评分 Rubric]:
  Score 1: 完全偏离,有严重错误
  Score 2: 基本意识到概念但有显著错误
  Score 3: 基本正确但缺乏深度或有小错
  Score 4: 准确且覆盖主要知识点
  Score 5: 全面、准确、展示了专家理解

输出格式:先写 Feedback(推理),再写 [RESULT] 分数

设计洞见先写反馈再打分,强制 LLM 先推理再得出结论,一致性显著高于直接出分。这与 G-Eval 的 CoT 思路一致,但 Prometheus 把它固化进了模型权重。


7.4 QAG 模式(原子问答生成)

RAGAS / DeepEval 底层都用这个模式,是目前最主流的评估架构:

reference_answer
    ↓ 拆解
原子声明1:RRF 只看排名不看原始得分
原子声明2:公式 1/(k+rank),k=60
原子声明3:两路量纲不同,无法直接加权
    ↓ 对每条问是/否
"候选人的回答是否提到了'RRF 只看排名不看原始得分'?" → yes/no
    ↓ 汇总
score = yes_count / total_claims

为什么比关键词匹配好

  • 每条声明是独立的是/否判断,LLM 可以理解语义等价
  • 可追溯:知道哪条声明被覆盖、哪条没有
  • 开放题也适用:把”合理的误解”作为声明,判断候选人是否给出了领域有效的答案

7.5 关键工程经验汇总

经验来源应用到面试评估
先写推理再打分Prometheus / G-Evalevaluate_answer() prompt 加 “Step 1 分析… Step 2 打分”
二值比聚合好过单一 1-10EvidentlyAIkey_points 每条 yes/no,不要整体给一个分
多维度分开调用DeepEval覆盖率/正确性/深度三次独立评估再合并
参考答案锚定 Score 5Prometheusreference_answer 明确标注为”满分示例”
不惩罚额外正确信息RAGAS Answer Relevancy”候选人额外说的正确内容不扣分” 写进 prompt
token 概率加权G-Eval 论文需要 logprobs API 支持(OpenAI 支持,部分 provider 不支持)

参考链接