ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

用 Claude 设计 Eval:从凭感觉调 Prompt 到数据驱动的 Hillclimb 实战

用 Claude 设计 Eval:从凭感觉调 Prompt 到数据驱动的 Hillclimb 实战 1. 为什么我要用 Claude 来设计 eval1.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.250case_023多步骤完整性2.850case_041语气共情3.550case_012总体3.1750-这个表格告诉你两件事哪些维度是短板哪些具体 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那分数涨了你也不知道是哪个改动起了作用。我的做法是维护一个改进日志每次只做一个改动记录改动内容、预期影响、实际分数变化。比如轮次改动内容政策准确性多步骤完整性语气共情总体0baseline3.22.83.53.171加“先拆解再回答”指令3.23.63.53.432加 3 个 few-shot 示例3.83.73.63.703调整语气指令3.83.74.23.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 的修改指令。
返回列表