1. 为什么我要用 Claude 来设计 eval,而不是自己硬写
做模型应用的人迟早会撞上一堵墙:你觉得自己把 prompt 调得挺好了,上线之后用户随便换个问法,输出就开始飘。更麻烦的是,你根本不知道它飘了多少、飘在哪里、下次改完 prompt 到底是变好了还是变差了。没有 eval,调 prompt 就像闭着眼睛开车——方向盘打得再猛,也不知道自己是在往哪偏。
我最早做 eval 的方式特别原始:手写几十条测试用例,跑一遍,人眼看输出,觉得差不多就过了。这套方法在项目早期还能凑合,一旦用例上到两三百条,人眼根本看不过来,而且判断标准会随着疲劳程度漂移。上午觉得这个回答可以,下午再看同一段输出又觉得不行。这种主观性极强的评估,本质上没有复现性,团队里两个人对同一个结果能吵起来。
后来我把 eval 的设计工作交给了 Claude。注意,不是让 Claude 去当裁判打分,而是让它帮我设计评估体系本身——包括拆解评估维度、生成测试用例、写打分脚本、分析失败模式。这个分工很关键:Claude 擅长的是把模糊的需求结构化,而不是替你做最终判断。评估的尺子得你自己定,但让 Claude 帮你把尺子刻出来,效率能翻好几倍。
这套方法适合谁?如果你正在做基于大模型的问答、摘要、代码生成、客服机器人这类应用,手里有一批真实用户 query,但不知道怎么系统性地衡量效果,那这套流程可以直接抄。哪怕你只有几十条用例,用这套思路也能把评估做得比现在扎实得多。核心关键词就三个:eval 设计、Claude 辅助、分数爬坡。下面我一步步拆。
2. 整体思路:把 eval 拆成四层,让 Claude 逐层帮你搭
2.1 先想清楚 eval 到底在评什么
很多人一上来就让模型“给这个回答打个分”,这是最粗糙的做法。一个回答好不好,至少涉及四个层面:事实准确性(有没有胡说)、指令遵循度(有没有按格式和要求来)、完整性(该说的点有没有漏)、表达质量(读起来顺不顺、有没有废话)。这四个维度混在一起打分,最后你只知道“总分 7 分”,但不知道是哪里扣的分,改 prompt 的时候完全没有方向。
我的做法是先把维度拆开,每个维度单独定义评分标准。比如事实准确性可以分三档:完全正确、部分正确但有遗漏、存在明显错误。指令遵循度可以分:完全符合格式、格式有小瑕疵、格式完全不对。拆到这个粒度,Claude 才能帮你写出可执行的打分逻辑,而不是笼统地“感觉一下”。
这里有个经验:维度不要超过五个。我试过拆到七八个维度,结果打分脚本复杂到没人愿意维护,而且很多维度之间高度相关,拆了等于没拆。四到五个维度是甜点区,既能定位问题,又不至于让评估本身变成负担。
2.2 让 Claude 生成测试用例,而不是自己憋
测试用例的质量直接决定 eval 的可信度。自己憋用例最大的问题是覆盖不全——你会不自觉地围绕自己熟悉的场景出题,忽略掉那些边缘但真实存在的输入。Claude 在这件事上的价值在于,它能基于你给的业务描述,快速铺开一个用例矩阵。
具体操作是:把你产品的功能描述、目标用户、典型使用场景喂给 Claude,让它按“正常场景 / 边缘场景 / 对抗场景”三类分别生成用例。正常场景就是标准问法,边缘场景是那种模棱两可、信息不全的输入,对抗场景则是故意诱导模型出错的输入(比如让它编造不存在的事实)。这三类用例的比例我一般控制在 5:3:2,正常场景占大头保证基础盘,对抗场景少而精,专门用来暴露弱点。
生成完之后一定要人工过一遍。Claude 生成的用例里会有重复的、不切实际的、或者答案本身就有争议的。我通常会删掉 20% 到 30%,再手动补几条 Claude 没想到的。这一步不能省,用例是 eval 的地基,地基歪了后面全白搭。
2.3 打分逻辑:规则优先,模型兜底
打分这件事,能用规则就别用模型。比如格式检查、关键词是否出现、长度是否超标,这些用代码几行就能搞定,又快又准。真正需要模型判断的是那些主观维度,比如“这个解释是否清晰”“这个摘要是否抓住了重点”。
我的打分架构是这样的:先跑一遍规则检查,把格式类、硬性要求类的分数算出来;剩下的主观维度,再交给 Claude 按预设的评分标准逐条打分。这样既保证了硬性指标的确定性,又保留了主观评估的灵活性。而且规则部分的分数是 100% 可复现的,模型打分部分即使有波动,也不会影响整体评估的稳定性。
注意:让模型打分时,一定要把评分标准写死在 prompt 里,并且要求它输出打分理由。没有理由的分数没法复盘,出了问题你都不知道它为什么给这个分。
2.4 分数爬坡:一轮只改一个变量
eval 搭好之后,就进入爬坡阶段。这里最大的坑是一次改太多东西。你同时改了 prompt、换了模型、调了温度参数,分数上去了,但你不知道是哪个改动起了作用。正确的做法是每轮只动一个变量,跑完 eval 看分数变化,确认有效再动下一个。
我一般把爬坡分成三个阶段:第一阶段修硬伤,把格式错误、事实错误这类明显问题先解决掉,这阶段分数涨得最快;第二阶段抠细节,优化表达、补全遗漏点,分数涨幅会放缓;第三阶段对抗优化,针对对抗场景做专项提升,这阶段最费劲,但能把 eval 分数推到比较高的水平。每个阶段跑完都要存档,记录改了什么、分数从多少到多少,方便回溯。
3. 核心细节:Claude 设计 eval 时我踩过的那些坑
3.1 评分标准要可操作,不能是形容词堆砌
我第一版评分标准写的是“回答要准确、完整、清晰”。Claude 拿到这个标准之后,打分打得特别随意,同一个回答两次打分能差两分。问题出在“准确”“完整”“清晰”这些词太虚了,模型没法稳定地映射到具体判断上。
后来我改成可操作的描述。比如“准确”改成:回答中的每个事实性陈述都能在给定资料中找到依据,若出现资料中没有的信息,标记为事实错误。“完整”改成:回答覆盖了问题涉及的所有子问题,每遗漏一个子问题扣一分。“清晰”改成:回答使用了分点或分段结构,没有超过三行的连续长句。改完之后,打分的稳定性明显提升,同一个回答多次打分基本能稳定在同一档。
这个经验很通用:任何交给模型执行的判断标准,都要能翻译成“看到什么就扣分”的具体规则。形容词是给人看的,规则才是给模型看的。
3.2 用例要带参考答案,但参考答案不是唯一解
设计用例时,我一开始只写问题不写答案,结果模型打分时没有参照,只能凭感觉。后来我给每条用例都补了参考答案,但很快发现另一个问题:模型会把参考答案当成唯一正确答案,稍微不一样的表述就判错。
解决办法是在参考答案里注明“核心要点”和“可接受的变体”。比如问“如何重置密码”,核心要点是“进入设置-账户-安全-重置密码”,可接受变体包括“通过登录页的忘记密码链接”等。打分时只要命中核心要点就算对,变体不扣分。这样既保证了评估有锚点,又不会把合理的多样性误判为错误。
3.3 对抗用例要真对抗,不能只是换个说法
我见过很多人的对抗用例,其实就是把正常问题换个问法,比如“请告诉我 X”改成“你能说一下 X 吗”。这不叫对抗,这叫同义改写。真正的对抗用例是那些故意触发模型弱点的输入,比如:
- 前提错误的输入:“既然 X 已经被证明是错的,那 Y 是不是也错了?”(X 其实没被证明是错的)
- 信息不全的输入:“帮我处理一下那个东西。”(没有上下文)
- 诱导编造的输入:“请给出 X 在 2023 年的具体数据。”(X 可能根本没有公开数据)
- 超长输入的边界测试:把问题塞在几千字的无关文本中间
这类用例才能真正暴露模型的短板。我一般会让 Claude 专门生成一批对抗用例,然后人工筛选,保留那些确实能难住当前模型的。如果一条对抗用例模型轻松答对,说明它不够对抗,可以删掉或者加难度。
3.4 打分脚本要能定位到具体失败点
eval 跑完只给一个总分,价值有限。我要求打分脚本对每条用例输出:总分、各维度得分、扣分理由、原始输出。这样跑完一轮,我能直接筛出“事实准确性扣分最多”的用例,集中看这些用例的输出,很快就能发现模型在哪个类型的问题上容易出错。
这个定位能力是爬坡的关键。没有它,你只知道分数低,不知道低在哪,改 prompt 只能瞎猜。有了它,你能精准地知道“模型在处理否定句时容易理解反”,然后针对性地在 prompt 里加一条“注意否定词”的说明。
4. 实操全流程:从零搭一套能爬坡的 eval
4.1 第一步:定义评估维度和评分档位
先拿一张纸(或者文档),把你的评估维度列出来。我以问答场景为例,列四个维度:
| 维度 | 权重 | 评分档位 |
|---|---|---|
| 事实准确性 | 40% | 3=完全正确,2=部分正确有遗漏,1=存在事实错误 |
| 指令遵循度 | 25% | 3=完全符合格式,2=格式有小瑕疵,1=格式错误 |
| 完整性 | 20% | 3=覆盖所有子问题,2=遗漏一个,1=遗漏两个以上 |
| 表达质量 | 15% | 3=结构清晰无废话,2=基本可读,1=混乱或冗长 |
权重根据业务重要性来定。事实准确性通常权重最高,因为错了就全错了。表达质量权重最低,因为它是锦上添花,不影响核心信息传递。这个权重表不是拍脑袋定的,你可以让 Claude 基于你的业务场景给个建议,然后自己微调。
4.2 第二步:用 Claude 批量生成测试用例
把下面这段 prompt 喂给 Claude:
你是一个测试用例生成专家。我的产品是一个[产品描述],目标用户是[用户描述]。 请生成 50 条测试用例,按以下比例分布: - 正常场景 25 条:标准问法,信息完整 - 边缘场景 15 条:信息不全、模棱两可、多轮对话中的追问 - 对抗场景 10 条:前提错误、诱导编造、超长输入、否定句陷阱 每条用例输出格式: 问题:[具体问题] 类型:[正常/边缘/对抗] 核心要点:[回答必须包含的要点] 可接受变体:[其他合理的表述方式]生成完之后,人工过一遍,删掉重复和不合理的,补上你从真实用户日志里摘出来的典型问题。最终保留 40 到 60 条,这个量级既能覆盖主要场景,跑一轮的时间又不会太长。
4.3 第三步:写打分脚本,规则和模型混合
打分脚本分两部分。规则部分用 Python 写,检查格式、关键词、长度这些硬性指标:
def rule_score(answer, case): score = 0 # 检查是否包含核心要点 for point in case["core_points"]: if point in answer: score += 1 # 检查长度是否超标 if len(answer) > case.get("max_length", 500): score -= 1 return score模型打分部分,把评分标准和用例一起塞给 Claude:
请根据以下标准给回答打分: [评分标准] 问题:[问题] 参考答案要点:[核心要点] 模型回答:[回答] 请输出: - 事实准确性得分(1-3)及理由 - 指令遵循度得分(1-3)及理由 - 完整性得分(1-3)及理由 - 表达质量得分(1-3)及理由两部分分数按权重加权,得到最终分。规则部分和模型部分分开记录,方便排查是规则误判还是模型误判。
4.4 第四步:跑第一轮,建立基线
第一轮跑完,你会得到一个基线分数。这个分数大概率不好看,没关系,它的作用是给你一个起点。把每条用例的得分、扣分理由、原始输出都存下来,按扣分从多到少排序。排在最前面的那几条,就是当前最严重的问题。
我第一轮跑完,发现事实准确性平均只有 1.8 分,主要问题是模型在回答“对比类”问题时容易把两个对象的属性搞混。这就是一个非常具体的失败模式,比“模型不够准”这种模糊判断有用得多。
4.5 第五步:一轮改一个变量,记录分数变化
针对第一轮暴露的问题,改 prompt。比如针对属性搞混的问题,在 prompt 里加一条:“当对比两个对象时,先分别列出各自的属性,再逐项对比,不要交叉描述。”改完重跑 eval,看事实准确性分数有没有提升。
每轮改动都记录在一个表格里:
| 轮次 | 改动内容 | 事实准确性 | 指令遵循 | 完整性 | 表达质量 | 总分 |
|---|---|---|---|---|---|---|
| 0 | 基线 | 1.8 | 2.5 | 2.2 | 2.6 | 2.15 |
| 1 | 加对比类指令 | 2.3 | 2.5 | 2.2 | 2.6 | 2.38 |
| 2 | 加格式示例 | 2.3 | 2.9 | 2.2 | 2.6 | 2.48 |
这样你能清楚地看到每个改动的收益。如果某轮改动分数没涨甚至跌了,就回滚,换下一个方向。爬坡的过程就是不断试错,但因为有 eval 在,每次试错都有数据支撑,不是瞎试。
4.6 第六步:对抗场景专项优化
当正常场景和边缘场景的分数都上到 2.5 以上之后,重点转向对抗场景。对抗场景的失败往往不是靠加一条 prompt 能解决的,可能需要调整整个处理流程。比如对于“前提错误”的输入,模型容易顺着错误前提往下答,这时候需要在流程里加一个“前提检查”步骤,先判断问题本身是否成立,再决定怎么回答。
这一步的改动幅度会比较大,建议单独开一个分支做,不要和前面的优化混在一起。对抗场景的分数提升通常比较慢,但每提升一点,模型的鲁棒性就实打实地强一分。
5. 常见问题与排查技巧实录
5.1 模型打分不稳定,同一个回答两次打分不一样
这是最常见的问题,原因通常是评分标准太模糊。排查步骤:先看两次打分的理由,如果理由本身就不一致,说明标准有歧义;如果理由一致但分数不同,说明模型对档位的边界理解不清。解决办法是把每个档位的边界写死,比如“3 分要求所有事实都有依据,2 分允许一个事实无依据,1 分有两个以上无依据”。边界越清晰,打分越稳定。
5.2 用例跑完分数很高,但线上效果还是差
这说明你的用例和真实场景有偏差。排查方法:从线上日志里随机抽 50 条真实 query,人工标注期望输出,然后拿这 50 条跑一遍 eval。如果分数明显低于你的测试集分数,说明测试集覆盖的场景和真实场景不一致。解决办法是把真实 query 补充进测试集,并且定期用线上数据刷新测试集。
5.3 分数涨到一定程度就上不去了
这是正常的,说明当前方案的天花板到了。这时候有两个方向:一是换更强的模型,二是改流程架构。换模型是最直接的,但成本会上去。改流程架构比如加检索、加多轮验证,效果可能更好但工作量更大。我的建议是先分析剩余扣分集中在哪个维度,如果是事实准确性上不去,优先考虑加检索;如果是指令遵循上不去,优先考虑换模型或加 few-shot 示例。
5.4 对抗用例模型总是答错,是不是没救了
不一定。先看模型答错的方式:如果是“顺着错误前提答”,可以在 prompt 里加前提检查;如果是“编造数据”,可以加“不确定时明确说不知道”的指令;如果是“超长输入丢失信息”,可以考虑分段处理。每种失败模式都有对应的解法,关键是先定位清楚。
5.5 打分脚本跑得太慢
如果用例多、模型打分调用频繁,跑一轮可能要十几分钟。优化方向:规则部分能并行的并行;模型打分部分可以批量提交,把多条用例打包成一个请求;主观维度如果相关性高,可以合并成一个维度减少调用次数。我实测下来,把 50 条用例打包成 5 个批次提交,时间能压缩到原来的三分之一。
5.6 团队里每个人对分数的理解不一样
这是协作问题,不是技术问题。解决办法是写一份评估手册,把每个维度的定义、每个档位的边界、典型示例都写清楚。新成员上手前先跑一遍标注练习,和手册对齐之后再正式参与评估。手册不用写得多漂亮,但一定要具体到“看到什么扣几分”的程度。
6. 几个让我少走弯路的实操心得
第一,eval 的用例要版本管理。每次改动用例,都要记录改了什么、为什么改。我吃过亏,改了一轮用例之后分数掉了,结果忘了改了什么,排查了半天。后来用 Git 管理用例文件,每次改动都有 commit message,回溯起来很方便。
第二,打分理由比分数本身更重要。分数只告诉你“好不好”,理由告诉你“为什么不好”。我每次看 eval 结果,先看扣分最多的用例的理由,往往一眼就能看出问题所在。理由写得越具体,排查效率越高。
第三,不要追求满分。eval 分数到 2.8 以上之后,每提升 0.1 的边际成本急剧上升,但业务收益可能微乎其微。这时候应该把精力转向覆盖更多场景,而不是死磕现有用例的分数。我一般把 2.8 作为“可以上线”的阈值,之后根据线上反馈再决定要不要继续优化。
第四,定期用新数据刷新测试集。用户的行为会变,产品的功能会变,半年前的测试集可能已经不能反映当前的真实场景。我一般每个月从线上抽一批新 query 补充进测试集,同时淘汰一批已经不再出现的旧用例。保持测试集的“新鲜度”,eval 的结果才有参考价值。
第五,让 Claude 帮你分析失败模式。把扣分最多的 20 条用例的输出喂给 Claude,让它总结这些失败有什么共同点。它经常能发现我忽略的模式,比如“所有失败用例都涉及时间相关的表述”。这种模式总结比人眼逐条看快得多,而且不容易漏。
这套流程我跑了大概三个月,eval 分数从最初的 2.1 爬到了 2.9,线上用户反馈的 bad case 明显减少。最关键的收获不是分数本身,而是有了一个可以持续迭代的评估体系——每次改 prompt 都知道自己在往哪个方向走,而不是凭感觉。如果你也在做类似的事情,建议先从 20 条用例、两个维度开始,跑通整个流程之后再逐步扩展。一开始就追求大而全,很容易卡在搭建阶段就放弃了。