评测工具比业务代码更危险:一次让历史分数全部作废的排查

业务代码写错了,用户一眼能看出来;评测代码写错了,所有人都看不见,而且你会拿着错误的天平,一路自信地往前走。这篇记录我发现自己评测体系里有三处静默缺陷的过程——其中一处,直接让此前的历史分数全部作废。

一、先想清楚:RAG 最危险的失效是什么

很多人做 RAG 评测只盯 Recall@K。但在这个项目里,最危险的失效不是「没检索到」,而是「编造」——检索是对的,材料是对的,模型在组织语言的时候自己加戏了。

这类问题 Recall 完全测不出来,因为它发生在检索之后的生成环节。所以除了召回率,我额外做了两个指标:

指标实现方式测什么
忠实度 LLM 评判(裁判模型逐条断言判定) 回答里的每个断言,是否被本轮检索上下文支持
引用准确率 确定性实现,不调 LLM 回答标注的来源,是否真实存在于检索结果里

为什么引用准确率要刻意做成确定性的?因为它是个「客观事实」类的检查——来源在不在检索结果里,用字符串匹配就能判,不需要也不应该交给模型去「感觉」。能确定的地方交给代码,不确定的地方才交给模型。

二、第一处缺陷:取错了产物

这是最根本的一个。我的工作流是主图 + 子图结构,其中子图负责一部分中间处理,主图产出最终的 final_text。

而评测脚本取的是子图的中间产物,用户实际看到的是主图的最终文本。两者根本不是同一个东西,导致:

原则:评测必须跑「用户看到的那一层」。不是「逻辑上等价的一层」,不是「更容易拿到的一层」,就是用户屏幕上最终呈现的那一层。

三、第二处缺陷:str(list) 把上下文污染成 Python repr

这个更隐蔽,也更致命。

构造评判 prompt 时,我需要把检索到的上下文列表拼进去。当时图省事直接写了 str(contexts),而 str() 一个列表会调用每个元素的 __repr__:

# 原始上下文(真实换行)
"第一条材料
 第二条材料"

# 经过 str(list) 之后
'[\'第一条材料\\n 第二条材料\']'
#   ↑ 真实换行变成了字面的 \n 两个字符
#   ↑ 外面还裹上了引号、方括号、转义反斜杠

后果有两层:

  1. 来源名被污染——引用校验按文件名匹配,而文件名现在被包在转义字符里,匹配不上;
  2. 裁判模型读到的是畸形文本——它看到的不是正常段落,而是一坨带转义的字符串字面量。

这意味着在修好之前,所有历史忠实度分数都是不可信的。不是偏低或偏高,是根本不知道它测了什么。

四、第三处缺陷:指纹没包含被测代码

为了防止「拿旧结果冒充新结果」,我给评测报告加了指纹机制:报告里记录题集内容哈希,一旦题集变了,指纹不匹配就强制全量重跑,避免沿用过期数据。

但第一版指纹只包含题集,没包含被测代码。问题很直接:我改了业务代码,题集没动,指纹不变,脚本就认为「可以复用旧报告」——于是拿修好的代码,去展示修复前的分数。

修正后的指纹要覆盖:题集内容、被测代码、裁判模型、关键提示词、结构化查询代码、数据范围、检索配置。

还有一个边界要想清楚:指纹不应该包含评测脚本自身。因为改评测脚本的展示逻辑(比如换个表格格式),不应该让历史分数全部作废——那是工具在变,不是被测对象在变。

五、题目污染:评测前必须隔离画像

这个坑埋得比较深,但后果很直观。

主图的第一站是 update_profile,它会把用户问题里的分数、位次等条件写进考生画像。而我的评测题集里全是「浙江 600 分能上什么」这种带具体条件的题目。

如果不做隔离,跑一轮评测 = 批量改写用户画像。评测跑完了,真实用户的画像被一堆虚拟考生条件覆盖掉了。

解法是给评测传一个独立的 profile_id(形如 eval-{eval_set}),让评测数据落在自己的沙箱里,与真实用户完全隔离。

六、增量落盘:崩溃不该毁掉整轮结果

最后是一个工程细节,但很实用。评测跑一百道题、每题要调 LLM,动辄半小时以上。早期版本是全部跑完才写一次报告——中间任何一次崩溃,整轮结果全丢。

改成逐题增量落盘 + --resume 断点续跑:每题判完立刻写盘,中断后可以从已完成的位置继续。

配套还做了一个 JSON 破损兜底:LLM 有时会把裸换行写进 JSON 字符串里,触发 Invalid control character,导致整题被记成 error、分数丢失。加上容错解析后,这类破损不再是「整题作废」。

七、可复用的四条判据

  1. 评测要跑用户看到的那一层。产物取错,后面所有指标都是幻觉。
  2. 能确定性判定的,不要交给 LLM。来源是否存在、引用是否有效,用代码判就够了。
  3. 指纹必须覆盖被测对象,但不覆盖评测工具自身。前者防止拿旧数据充新结果,后者避免误伤历史分数。
  4. 评测可能会写数据。跑之前先问:它会动到哪些真实数据?需不需要隔离?

业务错了用户能看见,度量错了所有人都看不见——包括你自己。所以评测工具本身,值得用比业务代码更高的标准去对待。

← 返回笔记列表