EP02. "Prompt 评测工作流:从直觉判断到客观打分"
🔒 登录后可标记已读- Prompt engineering(怎么写好一个 prompt)跟 prompt evaluation(怎么客观量出这个 prompt 到底好不好)是两件事——写完一个 prompt 只手动测个一两次就上线,是最常见也最危险的做法,因为真实用户的提问方式远比你手动测的那几条更不可预测
- 完整的评测流程有 5 步:写 prompt 草稿 → 准备一份测试数据集 → 把每条数据丢给 Claude 跑一遍 → 用 grader 给每个回答打分(1-10 分)→ 改 prompt 再跑一次、比较平均分——分数从 v1 的 7.66 涨到 v2 的 8.7,就是「这次修改真的有用」的客观证据,不是凭感觉
- 测试数据集不用全靠手写,可以让 Claude(用 Haiku 这类快模型)自动生成,省下大量人工造数据的时间
- Grader(打分器)分三种:Code Grader(程序化检查格式/语法)、Model Grader(再调一次 API 让 Claude 评分)、Human Grader(人工审核,最灵活但最花时间)——实务上常把 Code Grader 和 Model Grader 的分数平均起来用
- 前置知识:EP01 的
chat()/messages基础调用、prefill + stop sequence 拿干净 JSON 的手法
重点内容
为什么不能只靠手动测一两次
Prompt 写完之后,一般会走三条路:
| 做法 | 风险 |
|---|---|
| 只测一次就假设没问题 | 上线后大概率翻车,生产环境风险最高 |
| 针对几个已知边界情况多测几次 | 还是覆盖不到真实用户会怎么问 |
| 系统化跑评测 pipeline、拿客观分数 | 前期投入更多,但能确定改动是真的有进步,不是运气 |
前两种是最容易掉进去的陷阱——自己测的那几条问题永远不够代表真实用户会怎么提问。第三种虽然要多花功夫搭 pipeline,但拿到的是可以量化比较的客观数据,能让 prompt 真正做到「production-ready」。
完整评测流程:5 步
Step 1:写一个 prompt 草稿
prompt = f"""
Please answer the user's question:
{question}
"""
Step 2:准备一份评测数据集
准备一组有代表性的测试输入,例如「2+2 等于多少」「怎么煮燕麦粥」「月球有多远」这三条问题。
Step 3:把每条数据丢给 Claude 跑一遍
把数据集里每一题套进 prompt 模板发给 Claude,收集「问题 - 回答」这组配对。
Step 4:用 grader 给每个回答打分
一般用 1-10 分制,例如「10 分 = 完美答案,没有改进空间」「4 分 = 及格但明显有改进空间」,然后把所有回答的分数取平均,得到一个客观的整体分数。
Step 5:改 prompt、重跑一次
例如把 prompt 改成多加一句「回答要有足够的细节」,重新跑一次评测流程。
📌 两次评测的平均分可以直接比较——课程示范里 v1 拿到 7.66 分,加了一句「回答要更详细」的 v2 拿到 8.7 分,这个分数差距就是「这次修改确实有效」的客观证据,不用靠直觉猜。
准备测试数据集:让 Claude 帮你自动生成
数据集可以手写,也可以让 Claude 自动生成——用 Haiku 这类速度快的模型来生成测试数据比较划算。
以「生成 AWS 相关的 Python 代码 / JSON 配置 / 正则表达式」这个场景为例,先定义目标 prompt:
prompt = f"""
Please provide a solution to the following task:
{task}
"""
再写一个专门生成数据集的函数,让 Claude 输出一组 JSON 格式的任务描述:
def generate_dataset():
prompt = """
Generate an evaluation dataset for a prompt evaluation. The dataset will be used to evaluate prompts
that generate Python, JSON, or Regex specifically for AWS-related tasks. Generate an array of objects,
each representing task that requires Python, JSON, or a Regex to complete.
Example output:
[ { "task": "Description of task", }, ...additional ]
* Focus on tasks that can be solved by writing a single Python function, a single JSON object, or a single regex
* Focus on tasks that do not require writing much code
Please generate 3 objects.
"""
messages = []
add_user_message(messages, prompt)
add_assistant_message(messages, "```json")
text = chat(messages, stop_sequences=["```"])
return json.loads(text)
💡 这里又是 EP01 那招「prefill \\\json + stop sequence \\\`」——生成数据集本身也要拿干净的 JSON,不要让 Claude 多加解释文字。
存成文件,之后每次评测直接读这份数据,不用每次重新生成:
dataset = generate_dataset()
with open('dataset.json', 'w') as f:
json.dump(dataset, f, indent=2)
跑评测 Pipeline:三个核心函数
run_prompt:把 prompt 模板和测试数据的输入合并,发给 Claude,拿回结果。
run_test_case:单条测试的执行入口,调用 run_prompt 拿输出、再交给 grader 打分——课程在还没实现真正的 grader 前,先用写死的 10 分当占位。
run_eval:读取整份数据集,对每一条都调用 run_test_case,把所有结果收集成一个列表。
结果的结构是每条测试案例对应一个对象,包含三个字段:
{
"output": "Claude 的完整回答",
"test_case": "原始测试输入",
"score": "评分"
}
📌 即使用速度快的 Haiku,跑完一整份数据集大概也要 30 秒左右——数据集越大、评测就越花时间,这是要考虑的实际成本。这个阶段因为还没给 prompt 加任何格式要求,Claude 的回答会偏冗长,这个问题会在后面的 grading 实现里一并处理。
三种打分方式怎么选
Grader 收到模型的输出后,返回一个可衡量的分数(通常 1-10 分,10 分代表高质量,1 分代表低质量):
方法怎么选
| 方法 | 适合场景 | 备注 |
|---|---|---|
| Code Grader | 格式/语法能被程序判断对错的场景 | 检查长度、关键字、JSON/Python/正则语法是否合法、可读性 |
| Model Grader | 需要判断「答得好不好」这种主观质量的场景 | 多花一次 API 调用,能评估是否遵循指令、内容完不完整、有没有帮助、安不安全 |
| Human Grader | 前两者都判断不了、需要人工经验的场景 | 最灵活,但最花时间,不适合大批量跑 |
在动手写 grader 之前,先定清楚评分标准——针对生成代码这类任务,通常看三件事:格式对不对、语法合不合法、内容是否真的解决了这个任务。
Model Grader:再叫一次 Claude 来评分
Model grader 的核心思路是另外写一个评分用的 prompt,让 Claude 针对「这个回答有没有达标」给出结构化的评分结果:
def grade_by_model(test_case, output):
messages = []
add_user_message(messages, eval_prompt)
add_assistant_message(messages, "```json")
eval_text = chat(messages, stop_sequences=["```"])
return json.loads(eval_text)
📌 课程页面没有完整展示 eval_prompt 这段评分提示词的具体文字,只讲了它该包含的内容方向:把测试案例和 Claude 的输出都交代清楚,要求 Claude 给出结构化的 JSON(至少包含分数和理由)。
💡 让 Model Grader 除了给分数,也要给出理由(strengths / weaknesses / reasoning)——只要一个数字,Claude 评分容易缩在中间值敷衍了事,要求写理由能逼它认真判断。
接进完整的评测流程:
def run_test_case(test_case):
output = run_prompt(test_case)
model_grade = grade_by_model(test_case, output)
score = model_grade["score"]
reasoning = model_grade["reasoning"]
return {
"output": output,
"test_case": test_case,
"score": score,
"reasoning": reasoning
}
from statistics import mean
def run_eval(dataset):
results = []
for test_case in dataset:
result = run_test_case(test_case)
results.append(result)
average_score = mean([result["score"] for result in results])
print(f"Average score: {average_score}")
return results
Code Grader:用程序检查格式对不对
针对代码生成类任务,Code Grader 验证两件事:格式(Format)和语法是否合法(Valid Syntax),再搭配 Model Grader 判断内容是否真的解决了任务。
Python / JSON / 正则各自的语法校验函数:
def validate_json(text):
try:
json.loads(text.strip())
return 10
except json.JSONDecodeError:
return 0
def validate_python(text):
try:
ast.parse(text.strip())
return 10
except SyntaxError:
return 0
def validate_regex(text):
try:
re.compile(text.strip())
return 10
except re.error:
return 0
语法合法给 10 分,解析失败给 0 分。
测试数据集的每条案例要额外标注期望的输出格式(task + format 两个字段),程序才知道该用哪个校验函数。
合并两种分数
最后把 Code Grader 和 Model Grader 的分数取平均,让「内容质量」和「技术正确性」各占一半权重——权重也可以按实际需求调整,不是一定要对半分。
📌 有了 Code Grading 提供的这个量化基线分数,改 prompt 前后的进步幅度才有客观数字可以比较,不用只靠主观感觉判断「这次改动好像有帮助」。
Sources
官方文档:
- Claude with Google Cloud's Vertex AI(Claude Academy 课程)— https://academy.claude.com/courses/claude-with-google-cloud-s-vertex-ai