1. 为什么我要用 Claude 来设计 eval
1.1 从“凭感觉调 prompt”到“用数据说话”的转变
做 AI 应用开发的人都有一个共同的痛点:改了一版 prompt,感觉效果好像好了,但到底好了多少、有没有在别的场景上变差,心里完全没底。我以前也是这么干的,改完 prompt 随手试几个 case,觉得回答通顺就上线了,结果用户反馈某些边界场景直接崩掉。后来我意识到,没有 eval 的 prompt 迭代就是在赌博。
所谓 eval,就是评估集加评估流程。你需要一组有代表性的输入样本,每个样本有明确的期望输出或者评分标准,然后让模型跑一遍,用自动或半自动的方式打分,最终得到一个可比较的数值。有了这个数值,每次改 prompt、换模型、调参数,你都能立刻知道是涨了还是跌了。
那为什么我选择用 Claude 来设计 eval 本身?原因很直接:设计 eval 这件事本身也是一个需要智力的任务。你需要想清楚评估维度、设计评分 rubric、构造边界 case、写自动打分脚本。这些工作如果全靠人脑硬想,很容易遗漏关键维度。而 Claude 在理解任务意图、拆解评估维度、生成多样化测试样本方面表现得相当靠谱,尤其是配合 Claude Code 这种能直接读写文件、执行命令的工具,整个流程可以做到半自动化。
1.2 这套方法适合谁,不适合谁
这套方法最适合两类人:一是正在做 AI 应用但还没有系统化 eval 流程的开发者,二是已经有 eval 但分数卡住了不知道怎么继续提升的人。如果你完全没接触过 API 调用,可能需要先补一下基础,但整体门槛并不高,因为 Claude Code 可以帮你写大部分代码。
不适合的情况也有:如果你的任务完全没有客观标准(比如纯创意写作),自动打分的效果会打折扣,需要更多人工介入。另外如果你的样本量极小(比如只有三五个 case),那 eval 的统计意义有限,不如直接人工看。
1.3 整体思路:让 Claude 当你的 eval 工程师
我的做法分三步走。第一步,把任务描述和初步想法丢给 Claude,让它帮我设计评估维度、生成测试样本、写打分脚本。第二步,跑一轮 baseline,看看当前 prompt 在 eval 上的得分。第三步,进入 hillclimb 循环:分析失败 case、提出改进方案、修改 prompt、重新跑 eval、对比分数,直到分数收敛或者达到预期。
这个循环听起来简单,但实际操作中有很多细节决定成败。比如样本怎么选才有代表性、打分脚本怎么避免误判、每次改几个变量才能归因。下面我会一步步拆开讲。
2. 用 Claude 设计 eval 的完整流程
2.1 第一步:把任务讲清楚,让 Claude 帮你拆评估维度
很多人用 Claude 设计 eval 时犯的第一个错误是:只给了一个模糊的任务描述,比如“帮我评估一下这个问答系统的质量”。这样 Claude 只能给你一些泛泛的维度,比如准确性、流畅性、相关性,没什么实际指导意义。
正确的做法是把任务背景、输入输出格式、业务场景、已知的失败模式全部告诉 Claude。我通常会写一段类似这样的 prompt:
我有一个客服问答系统,输入是用户的问题(中文),输出是一段回答(中文)。 业务场景是电商售后,用户会问退货、换货、物流、退款等问题。 目前已知的失败模式包括: 1. 回答里编造了不存在的退货政策 2. 对多步骤问题只回答了第一步 3. 语气过于机械,缺乏共情 请帮我设计一套评估方案,包括评估维度、每个维度的评分标准(1-5分)、以及每个维度下应该构造什么样的测试样本。这样 Claude 给出的评估维度就会非常具体,比如“政策准确性”维度下会明确说“回答中提到的退货天数、条件必须与知识库一致,编造任何不存在的信息得1分”。这比你自己拍脑袋想维度要全面得多。
2.2 第二步:让 Claude 生成多样化的测试样本
评估维度确定后,下一步是构造测试样本。这里的关键是多样性:不能全是简单 case,也不能全是极端 case,要有梯度。
我一般会让 Claude 按以下结构生成样本:
- 简单 case(占 30%):直接匹配知识库的标准问题
- 中等 case(占 40%):需要组合多条知识才能回答的问题
- 困难 case(占 20%):包含干扰信息、多步骤、或者知识库没有覆盖的问题
- 边界 case(占 10%):极端输入,比如空输入、超长输入、包含特殊字符的输入
让 Claude 生成样本时,一定要给它一个明确的输出格式,比如 JSONL,每行包含input、expected_behavior、difficulty、category四个字段。这样后续处理起来非常方便。
注意:Claude 生成的样本不能直接拿来用,必须人工过一遍。我遇到过 Claude 生成的问题里包含知识库里根本没有的政策细节,这种样本如果直接用来评估,会把模型带偏。
2.3 第三步:写自动打分脚本,让 Claude 帮你处理边界情况
自动打分是整个 eval 流程中最容易出问题的环节。常见的做法有两种:一种是基于规则的打分(比如关键词匹配、正则表达式),另一种是基于模型的打分(用另一个 LLM 来评判输出质量)。
我的经验是两者结合:能用规则的地方用规则,规则覆盖不了的地方用模型打分。比如“回答中是否包含退货天数”可以用正则提取数字然后比对,“语气是否共情”就只能用模型打分。
让 Claude 写打分脚本时,要特别强调边界情况的处理。比如模型输出为空怎么办、输出格式不符合预期怎么办、模型拒绝回答怎么办。这些情况如果不处理,打分脚本会直接报错,整个 eval 流程就断了。
def score_policy_accuracy(response, expected_policy): if not response or len(response.strip()) == 0: return 1 # 空回答直接最低分 # 提取回答中提到的退货天数 days = re.findall(r'(\d+)\s*天', response) if not days: return 2 # 没提到天数,部分分 if str(expected_policy['return_days']) in days: return 5 return 1 # 提到了但数字错误这段代码看起来简单,但实际跑起来你会发现各种意外情况。比如模型可能说“七天”而不是“7天”,或者用“一周”来表达。这些都需要在脚本里做归一化处理。
3. 一轮轮把分数提上去:hillclimb 实操
3.1 建立 baseline:第一轮 eval 怎么跑
在开始优化之前,你必须先有一个 baseline。这个 baseline 就是你当前线上使用的 prompt 在 eval 上的得分。没有 baseline,你后面所有的改进都无法量化。
跑 baseline 的流程很简单:把 eval 样本逐条喂给模型,收集输出,用打分脚本打分,最后算平均分和各个维度的分项得分。我一般会输出一个表格,像这样:
| 维度 | 平均分 | 样本数 | 最低分样本ID |
|---|---|---|---|
| 政策准确性 | 3.2 | 50 | case_023 |
| 多步骤完整性 | 2.8 | 50 | case_041 |
| 语气共情 | 3.5 | 50 | case_012 |
| 总体 | 3.17 | 50 | - |
这个表格告诉你两件事:哪些维度是短板,哪些具体 case 是重灾区。接下来所有的优化都应该围绕短板维度展开。
3.2 分析失败 case:让 Claude 帮你找规律
拿到 baseline 结果后,不要急着改 prompt。先把所有低分 case 拿出来,让 Claude 帮你分析失败模式。我通常会把低分 case 的输入、期望输出、实际输出一起贴给 Claude,然后问它:“这些失败 case 有什么共同规律?最可能的根因是什么?”
Claude 的分析往往能发现你忽略的模式。比如它可能会指出:“所有失败 case 都涉及多个子问题,而当前 prompt 没有明确要求模型先拆解问题再逐一回答。”这种洞察直接指向了 prompt 的改进方向。
实操心得:分析失败 case 时,不要一次看太多。我一般每次只看 10-15 个,分批次分析,这样 Claude 的注意力更集中,分析质量更高。
3.3 提出改进方案:每次只改一个变量
这是 hillclimb 中最关键的纪律:每次只改一个变量。如果你同时改了 prompt 的措辞、加了 few-shot 示例、又调了 temperature,那分数涨了你也不知道是哪个改动起了作用。
我的做法是维护一个改进日志,每次只做一个改动,记录改动内容、预期影响、实际分数变化。比如:
| 轮次 | 改动内容 | 政策准确性 | 多步骤完整性 | 语气共情 | 总体 |
|---|---|---|---|---|---|
| 0 | baseline | 3.2 | 2.8 | 3.5 | 3.17 |
| 1 | 加“先拆解再回答”指令 | 3.2 | 3.6 | 3.5 | 3.43 |
| 2 | 加 3 个 few-shot 示例 | 3.8 | 3.7 | 3.6 | 3.70 |
| 3 | 调整语气指令 | 3.8 | 3.7 | 4.2 | 3.90 |
这样你就能清楚地看到每个改动的边际收益。有时候你会发现某个改动根本没效果,那就果断回滚,不要因为“已经改了”就舍不得删。
3.4 迭代到收敛:什么时候该停
hillclimb 不可能无限进行下去。一般来说,当连续两轮改进的分数提升小于 0.05 时,就可以考虑停了。这时候要么是当前方案已经接近上限,要么是需要更根本性的改变(比如换模型、改架构)。
另一个停止信号是过拟合。如果你发现 eval 分数一直在涨,但人工抽查实际输出感觉没变好,那很可能是 prompt 过拟合到了 eval 样本上。这时候需要重新审视 eval 样本的代表性,或者引入新的 hold-out 样本集。
4. 常见问题与排查技巧实录
4.1 打分脚本误判怎么办
打分脚本误判是 eval 中最常见的问题。典型表现是:人工看输出明明是对的,但脚本给了低分。原因通常有三种:一是正则表达式太严格,二是模型打分器的 prompt 不够清晰,三是归一化处理不到位。
排查方法很简单:把所有脚本打低分但人工认为正确的 case 拿出来,逐条分析误判原因。如果是正则问题,就放宽匹配规则;如果是模型打分器问题,就修改打分 prompt,加入更多示例。
避坑技巧:在打分脚本里加一个
debug模式,输出每条样本的打分依据。这样误判时你能立刻定位到是哪一步出了问题。
4.2 Claude 生成的样本质量不稳定
Claude 生成样本时,有时候会生成一些质量很差的样本,比如问题表述不清、期望行为模糊、或者与任务无关。这很正常,不要指望一次生成就能直接用。
我的做法是让 Claude 生成两倍于需要的样本量,然后人工筛选。筛选标准包括:问题是否清晰、期望行为是否明确、是否覆盖了目标维度。筛选后的样本还要做去重,避免高度相似的样本拉高分数。
4.3 分数涨了但实际效果没变好
这是最让人沮丧的情况。原因通常是 eval 样本不能代表真实分布。比如你的 eval 样本全是标准问法,但真实用户会用各种口语化、错别字、甚至语音转文字的输入。这时候 eval 分数再高也没用。
解决办法是定期从真实日志里采样,补充到 eval 集中。我一般每个月会从线上日志里随机抽 20-30 条真实请求,人工标注后加入 eval 集。这样 eval 集就能持续反映真实分布的变化。
4.4 模型拒绝回答导致分数异常
有些模型在遇到敏感或不确定的问题时会拒绝回答,输出类似“我无法回答这个问题”的内容。这种输出在打分脚本里往往会被判低分,但实际上模型的行为可能是合理的。
处理方式是在打分脚本里单独识别拒绝回答的情况,根据业务需求决定是给中等分还是低分。如果业务上允许拒绝回答,那就给中等分;如果不允许,那就给低分并在分析时单独归类。
5. 工具链与效率提升
5.1 用 Claude Code 自动化整个流程
Claude Code 最大的价值是它能直接读写文件、执行命令。这意味着你可以让它帮你完成从生成样本到跑 eval 到分析结果的全流程。我通常会把 eval 相关的文件组织成这样的结构:
eval/ samples/ baseline.jsonl holdout.jsonl scripts/ run_eval.py score.py results/ round_0.json round_1.json prompts/ current.txt round_1.txt然后让 Claude Code 执行类似“读取 samples/baseline.jsonl,用 prompts/current.txt 跑一遍,用 scripts/score.py 打分,结果保存到 results/round_0.json”的指令。整个过程不需要你手动操作,效率提升非常明显。
5.2 版本管理与可复现性
eval 流程必须可复现。这意味着你需要对 prompt、样本、打分脚本都做版本管理。我一般用 git 管理这些文件,每次跑 eval 都打一个 tag,记录当时的 commit hash。这样任何时候你都能回到某个历史版本,重新跑一遍验证结果。
另外,模型本身也在更新。同样的 prompt 和样本,今天跑和一个月后跑,分数可能不一样。所以每次跑 eval 都要记录模型版本和调用参数,否则结果无法比较。
5.3 成本控制:不是每次都要跑全量
跑 eval 是要花钱的,尤其是样本量大、模型贵的时候。我的策略是:日常迭代用一个小规模快速 eval 集(比如 20 条),只覆盖核心维度;重大改动才跑全量 eval 集(比如 200 条)。这样既能快速反馈,又能控制成本。
快速 eval 集的样本要精选,确保每条都能区分好坏方案。我一般会从全量集里挑那些历史上区分度最高的样本,组成快速集。
6. 我踩过的坑和最后的小技巧
6.1 不要过早优化打分脚本
刚开始做 eval 时,我花了很多时间打磨打分脚本,想让每个维度都精确无误。结果发现,打分脚本再精确,如果 eval 样本本身没有代表性,分数也没有意义。后来我调整了优先级:先保证样本质量,再优化打分脚本。样本质量是 1,打分脚本是后面的 0。
6.2 保留人工抽查环节
无论自动打分多完善,我都会保留人工抽查环节。每轮 eval 跑完后,随机抽 5-10 条输出人工看一遍。这不仅能发现打分脚本的误判,还能发现一些自动打分覆盖不了的问题,比如回答虽然事实正确但读起来很别扭。
6.3 记录每次改动的直觉
除了记录分数变化,我还会记录每次改动时的直觉判断。比如“我觉得加这个指令应该能提升多步骤完整性,但可能对语气有负面影响”。这样当结果出来时,你可以对比直觉和实际,逐渐培养对 prompt 工程的判断力。
6.4 最后分享一个小技巧
如果你发现分数卡住了,试试让 Claude 扮演“最挑剔的用户”来审视当前输出。具体做法是:把当前 prompt 和几条典型输出给 Claude,让它以“一个非常挑剔、容易生气的用户”的视角,指出所有可能引起不满的地方。这种方法往往能发现一些你完全没想到的问题,比如回答里用了用户看不懂的术语、或者语气显得居高临下。
这个技巧我用了很多次,每次都能找到新的改进点。而且 Claude 扮演挑剔用户时给出的反馈非常具体,直接就能转化成 prompt 的修改指令。