[控场AI]
· 7 分钟阅读· 3,690 字

LLM裁判评分标准实战:promptfoo与DeepEval对比

LLM裁判评分标准实战:promptfoo与DeepEval对比

对比 promptfoo 与 DeepEval 后发现,修复评分标准本身的错误,比换评测框架更能提升与人工标注的一致率。

一位开发者用 40 条客服回复、312 字退款政策文档做了对照实验,分别用 promptfoo 和 DeepEval 跑同一套三维度评分标准。promptfoo 配置更快(25 分钟)、更便宜($0.94)、默认重跑翻转更少,首次一致率 34/40,但不会警告权重之和为 1.1 的错误;DeepEval 理由更具体,但默认自动生成 evaluation_steps 导致 7 条结论翻转,固定步骤后降到 3 条,一致率从 31/40 升到 35/40。开发者另写了一个只校验权重和锚点、不调用模型的构建器,发现语气维度声明为通过/不通过却挂了五个 Likert 锚点。清理后 promptfoo 一致率升至 37/40,DeepEval 升至 36/40——评分标准本身的缺陷对一致率的伤害大于框架差异。

为什么评分标准比评测框架更重要

一位开发者用40条客服回复做了个实验:他手工标注了29条通过、11条失败,然后分别用promptfoo和DeepEval来跑同一套评分标准。结果发现,把评分标准本身修好,对一致率的提升比换框架大得多。

实验用的是一份312字的退款政策文档,涵盖30天窗口期、年度计划按比例退款、附加积分用完不退等规则。评分标准分三个维度:政策准确性(权重0.5,1-5分)、下一步指引(权重0.3,1-5分)、语气(权重0.2,通过/不通过)。加权得分0.7以上算通过。

LLM judge rubrics in 2026: promptfoo vs DeepEval vs my builder

promptfoo:快、便宜,但会静默吞掉配置错误

promptfoo的配置方式是每个维度写成一个llm-rubric断言,带weight。首次运行花了25分钟配置,3分12秒跑完40条,花费0.94美元,与手工标注的一致率是34/40。

问题出在一个打字错误上:语气的权重写成了0.3而不是0.2,三个权重加起来是1.1。promptfoo完全没有报错,它只是按你给的权重算加权平均。开发者为此写了个50行的Python校验脚本,检查权重之和是否为1.0,以及每个维度的锚点描述是否与声明的量表匹配。

另一个摩擦点是promptfoo的llm-rubric只认0-1分数,想用1-5分的Likert量表就得在提示词里写清楚换算方式,用起来有点别扭。

llm-rubric 断言与 Likert 量表的换算

promptfoo 中配置评分标准的单位是“断言”(assertion),每个维度写成一个 llm-rubric 断言,并附带 weight 字段。断言本质上是一段交给评审模型的提示词,模型根据提示词对输出打分。

一个容易踩的坑是量表:llm-rubric 默认接受的分数范围是 0-1 的连续值,而文章使用的政策准确性、下一步指引都是 1-5 分的 Likert 量表(一种常见的等级评分法,1 代表最差、5 代表最好)。要让 promptfoo 处理 1-5 分,必须在提示词里明确写出换算方式——比如“请输出 1 到 5 的整数分,最终我们按 (分数-1)/4 折算到 0-1”。如果省略这层说明,模型可能直接返回 1-5 的原始分,而 promptfoo 会把它当作 0-1 区间来处理,导致加权得分整体偏移。这也是文章说“用起来有点别扭”的原因:量表不匹配不会报错,只会让分数悄悄失真。

DeepEval:理由更详细,但默认设置下漂移明显

DeepEval用GEval指标,如果你不传evaluation_steps,它会先根据你的criteria自动生成步骤,再按步骤打分。配置花了约40分钟,运行4分48秒,花费1.31美元,与手工标注的一致率只有31/40。

但G-Eval给出的理由质量是最好的,能具体指出回复违反了哪条政策。更说个细节漂移问题:重跑三次,有7条回复的通过/失败结论至少翻转过一次。开发者推测是因为步骤在每次运行时重新生成,内容略有不同。手工固定evaluation_steps后,翻转降到3条,一致率升到35/40。对比之下,promptfoo三次重跑只有4条翻转。

G-Eval 与 evaluation_steps 的工作机制

DeepEval 的核心指标是 G-Eval(GEval),它源自一篇关于“让大模型按步骤评分”的论文思路。使用时你给出一个 criteria(例如“回复是否准确说明了退款政策”),然后有两种模式:

  • 不传 evaluation_steps:DeepEval 会先调用一次模型,根据你的 criteria 自动生成一组评分步骤,再把这些步骤连同 criteria 一起交给评审模型逐条打分。优点是省事,缺点是每次运行生成的步骤措辞略有差异,导致同一份回复在重跑时可能得到相反的结论——这就是文章所说的“漂移”。
  • 手工固定 evaluation_steps:你自己写好一组稳定的评分步骤并传入,每次运行都复用同一套步骤。步骤不再随机生成,评分标准被锚定,重跑翻转数从7条降到3条,一致率从31/40升到35/40。

这也解释了为什么文章建议“第一天就要固定 evaluation_steps”:默认模式下步骤的不稳定性会悄悄污染你的评测结果,而你很难从最终分数里直接看出来。

评分标准构建器:先验证,再跑评测

开发者的构建器不调用模型、不跑评测,只做一件事:在写评分标准的阶段做校验。它会检查权重是否等于1.0,以及每个维度的锚点描述是否和声明的量表一致。

重建退款评分标准时,它发现了两个问题。权重1.1是已知的,另一个是语气维度声明为通过/不通过,但描述里却复制了五个Likert锚点。这个矛盾被promptfoo和DeepEval照单全收,跑了好几天都没报错。

把清理后的锚点和提示词分别贴回两个框架后,promptfoo的一致率从34升到37,DeepEval从35升到36。开发者猜测promptfoo更依赖评分标准文本本身,而G-Eval的步骤结构在一定程度上弥补了标准文本的粗糙。

什么是加权得分与权重校验

文章反复提到的“权重”是评分标准中的核心机制:当评分标准由多个维度(如政策准确性、下一步指引、语气)组成时,每个维度会被赋予一个权重,代表它在总评中的相对重要性。最终得分是所有维度得分的加权平均。例如权重分别为0.5、0.3、0.2,三者之和必须等于1.0,否则加权平均的尺度就被破坏了——所有回复的得分会被系统性放大或缩小,接近0.7的通过线可能因此被误判。

更隐蔽的风险在于,权重之和不为1时,框架通常不会报错。promptfoo和DeepEval都只是“按你给的权重算”,不做合理性校验。这就是为什么开发者要额外写一个50行的校验脚本:它只做两件小事——检查权重之和是否等于1.0,以及检查每个维度的锚点描述是否与声明的量表匹配。这类校验不调用模型、不跑评测,开销极低,却能在写评分标准的阶段拦截掉最致命的一类错误。

各维度的取舍

维度promptfooDeepEval
配置到首次结果25分钟40分钟
首次一致率34/4031/40
清理锚点后一致率37/4036/40
三次重跑翻转数47(固定步骤后3)
40条成本$0.94$1.31
检查权重和否否
理由质量简短可用最长、最具体
CI集成CLI和YAMLpytest

实际怎么选

只选一个的话,promptfoo更合适:更便宜、更快、默认设置下翻转更少,而且已经在他的CI流水线里。如果你重度依赖pytest、没有YAML驱动的评测体系,选DeepEval,但第一天就要固定evaluation_steps。

他现在的做法是:先写评分标准并校验,然后放到promptfoo里跑CI。如果某个维度大量失败、需要理解原因,就把那一部分拿到DeepEval里用固定步骤跑一遍,因为G-Eval的理由更适合调试。

核心教训

花了几天对比框架之后,他发现最弱的环节其实是评分标准本身。一个0.3写成0.2,一个通过/不通过维度挂了五个锚点,造成的准确率损失比框架之间的差异更大。当然,这只是40条数据、一个人标注的结果,只能当轶事看,不能当基准。如果你的评分维度比“是否说对了退款政策”更模糊,漂移可能会更明显。

什么是“锚点”与量表一致性

文章多次提到“锚点”(anchor)与量表(scale)的矛盾。量表是评分等级的定义,例如“通过/不通过”是二值量表,“1-5 分”是五点 Likert 量表。锚点则是量表每一级对应的文字描述,例如“1 分:完全未提及退款政策”“5 分:准确说明所有适用规则”。

评分标准要能正常工作,锚点必须与量表严格对应。二值量表只能有两个锚点(通过、不通过),五点量表必须有五个锚点。文章中的语气维度声明为“通过/不通过”,描述里却复制了五个 Likert 锚点,这会让评审模型无所适从:它不知道该输出两个值还是五个值,每次运行可能按不同的解读打分。这种矛盾 promptfoo 和 DeepEval 都不会主动检测,跑好几天也不会报错,但它带来的噪声会直接拉低一致率。校验锚点与量表是否匹配,和校验权重一样,是写评分标准时最容易漏掉、代价却很高的一步。

#LLM judge#promptfoo#DeepEval#LLM评测#评分标准#G-Eval#AI评测框架
分享:

相关推荐