AI TOOLS

EP02. "Prompt 评测工作流:从测试数据集到自动打分"

首页 › AI 工具 › Claude · Developer and Platform · Amazon Bedrock · EP02
约 21 分钟· #EP02#Claude#Amazon Bedrock
🔒 登录后可标记已读
  • 只手动测几次就上线,看起来能用,其实是把「用户会怎么用」的判断权交给了自己的直觉——真实用户输入的花样,几乎一定超出你测过的范围
  • Eval(评测)是一套系统化流程:准备测试数据集 → 让 Claude 跑一遍 → 用 grader 客观打分 → 照分数改 prompt → 再跑一遍比较,不是凭感觉说「这个 prompt 好像更好」
  • Grader 分三种:code grader(程序检查格式/语法)、model grader(再呼叫一次 Claude 来评分)、human grader(人工复核)——各自适合检查不同的东西,混着用效果最好
  • 测试数据集不一定要手写,可以让 Claude(用便宜快的模型如 Haiku)自动生成一批贴近真实场景的测试用例
  • 前置知识:EP01 的 converse()/chat() 基本用法、prefill + stop sequence 抽取干净 JSON 的技巧

重点内容


为什么"多测几次"不够

写完 prompt 后一般会走三条路:

做法问题
测一次就上线用户给出预期外的输入时,出问题的风险很高
手动多测几次、小改几下还是不够,因为用户实际的输入方式往往超出你能想到的范围
建立评测 pipeline,照客观指标反复迭代前期投入更多时间和成本,但更有把握

📌 工程师天然会倾向前两条路——测的都是「自己觉得理所当然」的输入,但真实用户跟 prompt 互动的方式永远比想象中花样多。系统化评测 + 数据驱动的迭代,才是解法。


一个典型 Eval 工作流的五步

  1. 写初版 prompt:先有个能跑的基础版本
   prompt = f"""
   Please answer the user's question:

   {question}
   """
  1. 准备评测数据集:一批有代表性的测试输入,例如「2+2 等于多少」「怎么煮燕麦」「月球有多远」
  2. 喂给 Claude:把每条数据集问题套进 prompt 模板,发给 Claude,收集回答
  3. 喂给 grader 打分:拿「问题 + 回答」这一对,让 grader 评分(常用 1-10 分制:10 分=完美无可挑剔,4 分=明显有改进空间,1 分=差或答错)
  4. 改 prompt、重复流程:例如在 prompt 里加一句「Answer the question with ample detail」,重新跑一次评测

课程示例:v1 prompt 平均分 7.66,加了「回答要够详细」这句话后的 v2 涨到 8.7——分数变化就是「这次修改到底有没有用」的客观证据。


生成测试数据集

评测数据集就是一批会被喂进 prompt 的输入样本,两种做法:

  • 手写测试用例
  • 用 Claude 自动生成(生成多条用便宜快的模型,比如 Haiku,比较划算)

自动生成示例(AWS 场景:让用户描述任务,Claude 判断该写 Python / JSON / Regex 中的哪一种,只回传代码本身,不带任何说明文字):

def generate_dataset():
    prompt = """
    Generate 3 AWS-related tasks that require Python, JSON, or Regex solutions.

    Focus on tasks that can be solved by writing a single Python function,
    a single JSON object, or tasks that do not require writing much code.

    Example output:
    [
        {
            "task": "Description of task"
        },
        ...additional
    ]

    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 + stop sequence 组合拳,确保拿到的是干净的 JSON。

生成完存下来,不用每次重新生成:

dataset = generate_dataset()
with open("dataset.json", "w") as f:
    json.dump(dataset, f, indent=2)

💡 3 条测试用例只够跑通流程,真正上生产前的评测数据集需要更多条、覆盖更多样的场景。


跑评测 Pipeline:三层函数分工

run_prompt:把测试用例套进 prompt 模板,丢给 Claude:

def run_prompt(test_case):
    """Merges the prompt and test case input, then returns the result"""
    prompt = f"""
Please solve the following task:

{test_case["task"]}
"""

    messages = []
    add_user_message(messages, prompt)
    output = chat(messages)
    return output

run_test_case:调用 run_prompt,再把结果送去打分(这一步先用假分数占位):

def run_test_case(test_case):
    """Calls run_prompt, then grades the result"""
    output = run_prompt(test_case)

    # TODO - Grading
    score = 10

    return {
        "output": output,
        "test_case": test_case,
        "score": score
    }

run_eval:遍历整个数据集,逐条跑 run_test_case:

def run_eval(dataset):
    """Loads the dataset and calls run_test_case with each case"""
    results = []

    for test_case in dataset:
        result = run_test_case(test_case)
        results.append(result)

    return results

跑起来:

with open("dataset.json", "r") as f:
    dataset = json.load(f)

results = run_eval(dataset)

📌 就算用 Haiku,跑完一整个数据集也可能要 30 秒以上,第一次跑不用觉得卡住了。

这一步搭好的只是「骨架」——固定分数占位,真正有意义的是接下来把打分逻辑换成真正的 grader。


三种打分方式:Code / Model / Human Grader

Grader 类型怎么打分适合检查
Code grader程序化检查:长度、关键词、JSON/Python/Regex 语法是否合法、可读性格式对不对、语法能不能跑
Model grader再呼叫一次 Claude,请它评估原始输出有没有照指令做、答得完不完整、有没有帮到用户、安不安全
Human grader人工复核整体质量、深度、简洁度、相关性——最耗时但最贴近真实判断

以「生成代码」这类 prompt 为例,评分标准可以拆成:

  • Format:是否只回传指定语言的代码,不带解释文字
  • Valid Syntax:语法是否合法
  • Task Following:内容有没有真正解决用户的任务

其中 format 和 syntax 交给 code grader 判断最准确、最省钱;task following 这种需要理解语义的,交给 model grader。


Model Grader 怎么写

def grade_by_model(test_case, output):
    # Create evaluation prompt
    eval_prompt = """
    You are an expert code reviewer. Evaluate this AI-generated solution.

    Task: {task}
    Solution: {solution}

    Provide your evaluation as a structured JSON object with:
    - "strengths": An array of 1-3 key strengths
    - "weaknesses": An array of 1-3 key areas for improvement
    - "reasoning": A concise explanation of your assessment
    - "score": A number between 1-10
    """

    messages = []
    add_user_message(messages, eval_prompt)
    add_assistant_message(messages, "```json")

    eval_text = chat(messages, stop_sequences=["```"])
    return json.loads(eval_text)

💡 关键技巧:要求 grader 同时列出 strengths、weaknesses、reasoning,而不是直接吐一个分数——这样能防止模型偷懒,每次都给一个不上不下的中间分。

接进 run_test_case:

def run_test_case(test_case):
    output = run_prompt(test_case)
    # Get model evaluation
    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
    }

算整体平均分,追踪 prompt 迭代有没有真正变好:

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 补的是 model grader 顾不到的一块:输出到底能不能被正常解析/编译。判断很直接——能 parse 就是满分 10 分,parse 失败就是 0 分:

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

用 Python 内建能力做校验:json.loads() 验证 JSON、ast.parse() 生成 Python 抽象语法树(能生成就代表语法合法)、re.compile() 验证正则表达式。

测试数据集里每条要多带一个字段标注预期的输出格式:

{
    "task": "Description of task",
    "format": "python"
}

format 只会是 "json"、"python"、"regex" 三选一。

📌 想让 code grader 更准,prompt 本身也要更明确地要求:「只回复 Python / JSON / 纯 Regex,不要加任何注释或说明」,同样可以搭配 prefill + stop sequence 确保输出干净。


综合评分:把两种 Grader 分数合起来

model_grade = grade_by_model(test_case, output)
model_score = model_grade["score"]
syntax_score = grade_syntax(output, test_case)

score = (model_score + syntax_score) / 2

这样内容质量(model grader)跟技术正确性(code grader)各占一半权重——权重可以按实际场景调整,谁更重要就调高谁的比例。

💡 单独一次评测分数没什么意义,真正有用的是拿它当基准(baseline),每次改 prompt 后重新跑一次,比较分数有没有真的往上走。

Sources

官方文档:

  1. Claude with Amazon Bedrock(Claude Academy 课程)— https://academy.claude.com/courses/claude-with-amazon-bedrock