交换答案顺序,模型评审胜率从66%降至43%
作者在同一组300对答案中交换展示顺序后,新提示词胜率从66%降至43%,87次判决发生翻转。建议每对答案按两种顺序评审,仅在结果一致时计胜负,并用相同答案进行位置偏差检查。
AI 深度解读
新 system prompt 的胜率达到 66%,作者差点就宣布上线 v2。可把两份答案的 A/B 顺序交换后,同样的 300 组比较,新 prompt 的胜率变成了 43%。裁判模型、答案、评分标准和 temperature 0 都没变,变的只有座位。
这篇文章提醒我们:让 LLM 给两个答案判优,测出来的可能混有位置偏好。作者用双向评审重新计算,发现改进仍然存在,却远没有最初那么大。对正在比较 prompt、模型或 RAG 配置的程序员,这套检查值得加进评测脚本。
66% 的胜率,有多少来自答案排在前面
LLM-as-judge 是让大模型充当裁判,判断输出质量。常见做法是把同一问题的两个答案标成 A 和 B,让裁判选出更好的那个。比起定义绝对的 1—10 分标准,两两比较更容易开始。
问题出在一个不起眼的实现习惯:作者把新答案始终作为第一个参数传入,因此它永远占据 A 位。这个顺序没有经过设计,只是顺手写成了这样。
位置偏差(position bias)指的是,裁判因为答案出现的位置而偏爱它,而非因为内容更好。作者交换顺序后发现:
- 300 组比较中,87 组判决翻转,占 29%。
- 其中 78 组翻转都追随 A 位,谁坐到 A 位,谁就赢。
- 两轮共 600 次判决,A 位获胜 369 次,占 61.5%。
最初的 66%,对应新 prompt 获胜 198 次。其中只有 120 次在交换顺序后依然成立,另外 78 次恰好来自那些偏爱 A 位的比较。
一次稳定复现的高胜率,也可能稳定地包含位置偏差。
作者人工读过这 87 组翻转样本,认为其中多数答案都正确,篇幅接近,只是表达不同,自己也很难选出赢家。这支持一个解释:质量差异不明显时,位置偏好会参与决定结果。但这是作者对这批样本的观察,不能直接推广成所有裁判的规律。
temperature 0 为什么没能救场
作者认为,裁判从左到右读取内容,可能先依据 A 建立「好答案」的印象,再拿 B 去比较。除此之外,A/B 标签也可能带有训练数据中的格式偏好。
这些是文中提出的机制解释,没有对应的消融实验来区分各自影响。
偏好方向也不固定。作者的裁判偏爱 A,但其他模型与提示词组合可能偏爱 B,尤其在答案较长、后面的内容更容易影响判断时。不能把修复简化成「让旧答案排在前面」。
temperature 0 解决不了这个问题。它可以让同一输入下的偏好更容易重复,却不会验证这种偏好是否合理。固定顺序重跑几次都得出同样结果,容易让人误以为结论已经得到确认。
作者还做了镜像测试(mirror test):在 A 和 B 中放入完全相同的答案,再要求裁判判断。50 组镜像样本中,A 获胜 34 次,B 获胜 9 次,TIE 只有 7 次。
两份文本一模一样,裁判却频繁选出了赢家。这比单看一份评测报告更容易暴露问题。作者现在每次更换裁判模型或裁判 prompt,都会重做这项检查。
如果评审允许平局,同文答案理应判为 TIE。即使被迫二选一时结果接近 50/50,也只能说明没有明显的总体位置倾向,不能证明裁判理解了两份答案等价。
双向评审后,应该怎样统计胜负
作者采用的修复方法很直接:每组答案评两次,第二次交换顺序。只有两次都支持同一个实际答案,才记为胜利;判决矛盾,或者任意一次返回 TIE,都记为平局。
下面这段实现解决了顺序交换后如何映射回实际赢家的问题:
JUDGE_PROMPT = """You are comparing two answers to the same question.
Question:
{question}
Answer A:
{a}
Answer B:
{b}
Briefly list the strengths and weaknesses of each answer.
Then end with exactly one line: VERDICT: A, VERDICT: B, or VERDICT: TIE"""
def ask_judge(question, a, b):
out = call_llm(JUDGE_PROMPT.format(question=question, a=a, b=b), temperature=0)
return parse_verdict(out) # "A" | "B" | "TIE"
def judge_pair(question, new, old):
run1 = ask_judge(question, new, old) # new in slot A
run2 = ask_judge(question, old, new) # new in slot B
if run1 == "A" and run2 == "B":
return "new"
if run1 == "B" and run2 == "A":
return "old"
# Self-contradiction, or a TIE in either run: no signal.
return "tie"
关键是 run1 == "A" and run2 == "B":标签变了,支持的仍是新答案。两次都选 A,反而是在支持不同答案,不能算一致。代码中的 call_llm 和 parse_verdict 需要自行实现,原文没有给出具体 API 或解析逻辑。
修正后,作者得到 120 组新答案胜、93 组旧答案胜、87 组平局。以全部 300 组为分母,分别是 40%、31%、29%;只看能分出胜负的 213 组,新答案胜率约为 56%。
这两个口径应该一起报告。单独说 56%,读者看不到有多少样本被归入平局。
这里的平局还包括裁判自相矛盾,并不等于人工确认两份答案质量相同。它更适合解释为:这套双向评审没有提供足够一致的胜负信号。
随机打乱顺序也有帮助,可以在总体上分摊位置偏好,但每组只评一次,仍然看不出哪些结果依赖座位。双向评审能暴露这部分不稳定性,代价是裁判调用翻倍:300 组需要额外 300 次 API 调用。
作者还建议提供 TIE 选项,并让模型先比较优缺点,最后再输出 VERDICT。他表示后者降低了翻转率,但没有给出降低幅度。若 API 提供 logprobs,也可以把正序的 P("A") 与交换后的 P("B") 取平均,得到对称的软评分;原文没有展开多 token 标签或 TIE 概率该怎样处理。
我的判断
这篇最有用的地方,是把评测脚本中一个看似无关的参数顺序,变成了可以检查的实验条件。双向评审也比单纯随机顺序多提供了一层信息:哪些样本的赢家会跟着位置改变。
但作者把修正后的 56% 称为真实的小幅改进,我会保留一点判断。文中没有公开裁判模型、完整评分标准、样本构成,也没有置信区间或系统性人工复核。作者说,在抽查的顺序一致样本中,裁判几乎都与自己判断相符,但没有说明抽查数量。
因此,这批数据支持「新 prompt 在该裁判的双向一致样本中更常获胜」,尚不足以确认它在一般业务场景中更好。关于偏差产生机制,以及先解释再判决的收益,也应视为经验与解释,不能当成已验证的普遍结论。
双向一致是可靠评测的一项检查,不是答案正确的证明。
两个顺序都可能偏爱更长的答案,也可能都偏爱与裁判自身文风相似的输出。作者提醒,如果新 prompt 让答案变长 40%,部分胜利可能来自篇幅;如果用 Model X 裁判 Model X 与 Model Y,也需要检查自我偏好。这两类偏差都不会被交换顺序消除。
相比单次随机评审,双向方案更保守,也更贵。是否值得用于全部样本,要结合评测频率和调用成本决定;文章没有提供成本与准确性的对照数据。至少在准备依据胜率上线新 prompt 前,它是一项容易执行的检查。
能拿来做什么
- 比较代码生成或问答 prompt 时,把固定的
judge(new, old)改成双向评审,并按实际答案映射赢家。报告新胜、旧胜、平局三个比例,同时写清决定性样本胜率的分母。 - 每次更换裁判模型或裁判 prompt,加入同文镜像样本,分别记录 A、B、TIE 的次数。若相同答案仍频繁分出胜负,先检查裁判规则,再相信业务评测的排名。
- 人工复核时优先查看交换顺序后翻转的样本,判断它们是确实难分高下,还是裁判漏掉了代码错误、事实差异或明确的评分要求。
- 比较模型或 RAG 配置时,另外按答案长度差分组检查胜率,并留意裁判与参赛模型是否相同。顺序检查通过后,仍要单独排查篇幅偏好与自我偏好。