构建评估:优化Claude准确性的实用指南

本教程面向有编程基础的开发者,系统讲解如何为Claude构建离线评估系统。内容涵盖评估的四个核心组成部分(输入提示、输出、标准答案、分数),并深入剖析三种评分方法:基于代码的评分(精确匹配、正则)、人工评分(灵活但昂贵)和基于模型的评分(利用Claude自我评分)。通过具体代码示例,展示每种方法的实现细节与适用场景。同时提供设计评估时的经验法则,如任务针对性、分布代表性、尝试模型评分器、巧妙设计可自动评分的问题,以及平衡问题数量与质量。旨在帮助开发者科学优化模型性能,做出可靠的工程决策。

技术背景

在AI工程实践中,评估是模型迭代和上线前的关键环节。开发者常面临两个核心问题:提示修改是否真正提升了模型性能?模型是否达到生产标准?离线评估系统通过量化指标回答这些问题,避免依赖主观感受或在线A/B测试的高成本。本教程聚焦Claude模型,但方法论适用于任何LLM。评估的主要挑战在于:构建高质量数据集耗时,评分过程可能昂贵且缓慢。因此,设计评估时需权衡评分成本与可靠性,选择最适合任务的评分方法。

核心原理

评估的核心原理是构建一个闭环反馈系统:输入提示(含变量)→ 模型生成输出 → 与标准答案比较 → 产生分数。分数反映模型在该任务上的表现。评分方法的选择直接影响评估的效率和可靠性。基于代码的评分利用确定性规则(如字符串匹配)快速且无偏,但仅适用于输出格式受限的任务。人工评分灵活但成本高,适合复杂开放式任务。基于模型的评分利用Claude的语义理解能力,模拟人类判断,平衡了灵活性与成本,但需验证其可靠性。设计评估时,应优先考虑可自动评分的问题形式,如多项选择或限定输出格式,以降低长期成本。

架构分析

评估系统架构包含四个模块:1) 提示模板构建器:将变量输入填充到模板中,生成标准化的用户消息。2) 模型调用器:封装API调用,统一管理参数(如max_tokens)。3) 评分器:根据评分方法实现评分逻辑,可替换。4) 结果聚合器:计算总体得分。数据流为:eval数据集(JSON/CSV)→ 提示构建器 → 模型调用器 → 输出列表 → 评分器 → 分数。设计上,评分器与提示构建器解耦,便于切换评分策略。代码示例中,build_input_prompt和grade_completion函数分别对应前两个模块,get_completion封装API调用。关键设计决策包括:设置max_tokens限制输出长度,使用stop序列(如</correctness>)确保输出结构化,便于解析。

完整实战教程

构建评估

优化Claude以在任务上获得最高可能的准确性是一门经验科学,也是一个持续改进的过程。无论你是想了解对提示的更改是否使模型在关键指标上表现更好,还是想评估模型是否足够好以投入生产,一个良好的离线评估系统对于成功至关重要。

在本教程中,我们将介绍构建评估的常见模式,以及在此过程中遵循的有用经验法则。

Eval 的组成部分

Eval 通常包含四个部分。

Eval 评分方法

关于 eval,有两件事可能既耗时又昂贵。第一是编写 eval 的问题和标准答案。第二是评分。如果你没有现成的数据集,或者没有一种无需手动生成问题就能创建数据集的方法(可以考虑使用 Claude 来生成问题!),那么编写问题和标准答案可能会非常耗时,但好处是这通常是一次性的固定成本。你编写问题和标准答案,之后很少需要重写。而评分则是每次重新运行 eval 时都会产生的成本,而且很可能会频繁地重新运行 eval。因此,构建能够快速且廉价评分的 eval 应该成为你设计选择的核心。

有三种常见的 eval 评分方式。

让我们逐一举例说明每种评分方法。

基于代码的评分

在这里,我们将对一个评估进行评分,该评估要求Claude成功识别某物有多少条腿。我们希望Claude只输出腿的数量,并且我们设计评估的方式使得我们可以使用精确匹配的基于代码的评分器。

# Install and read in required packages, plus create an anthropic client.
%pip install anthropic
from anthropic import Anthropic

client = Anthropic()
MODEL_NAME = "claude-opus-4-1"
# Define our input prompt template for the task.
def build_input_prompt(animal_statement):
    user_content = f"""You will be provided a statement about an animal and your job is to determine how many legs that animal has.

    Here is the animal statment.
    <animal_statement>{animal_statement}</animal_statment>

    How many legs does the animal have? Return just the number of legs as an integer and nothing else."""

    messages = [{"role": "user", "content": user_content}]
    return messages
# Define our eval (in practice you might do this as a jsonl or csv file instead).
eval = [
    {"animal_statement": "The animal is a human.", "golden_answer": "2"},
    {"animal_statement": "The animal is a snake.", "golden_answer": "0"},
    {
        "animal_statement": "The fox lost a leg, but then magically grew back the leg he lost and a mysterious extra leg on top of that.",
        "golden_answer": "5",
    },
]
# Get completions for each input.
# Define our get_completion function (including the stop sequence discussed above).
def get_completion(messages):
    response = client.messages.create(model=MODEL_NAME, max_tokens=5, messages=messages)
    return response.content[0].text


# Get completions for each question in the eval.
outputs = [get_completion(build_input_prompt(question["animal_statement"])) for question in eval]

# Let's take a quick look at our outputs
for output, question in zip(outputs, eval, strict=False):
    print(
        f"Animal Statement: {question['animal_statement']}\nGolden Answer: {question['golden_answer']}\nOutput: {output}\n"
    )
# Check our completions against the golden answers.
# Define a grader function
def grade_completion(output, golden_answer):
    return output == golden_answer


# Run the grader function on our outputs and print the score.
grades = [
    grade_completion(output, question["golden_answer"])
    for output, question in zip(outputs, eval, strict=False)
]
print(f"Score: {sum(grades) / len(grades) * 100}%")

人工评分

现在,让我们想象一下,我们正在为一个评估打分,在这个评估中,我们向Claude提出了一系列开放式问题,也许是为了一个通用的聊天助手。不幸的是,答案可能多种多样,无法用代码进行评分。我们可以采用的一种方法是人工评分。

# Define our input prompt template for the task.
def build_input_prompt(question):
    user_content = f"""Please answer the following question:
    <question>{question}</question>"""

    messages = [{"role": "user", "content": user_content}]
    return messages
# Define our eval. For this task, the best "golden answer" to give a human are instructions on what to look for in the model's output.
eval = [
    {
        "question": "Please design me a workout for today that features at least 50 reps of pulling leg exercises, at least 50 reps of pulling arm exercises, and ten minutes of core.",
        "golden_answer": "A correct answer should include a workout plan with 50 or more reps of pulling leg exercises (such as deadlifts, but not such as squats which are a pushing exercise), 50 or more reps of pulling arm exercises (such as rows, but not such as presses which are a pushing exercise), and ten minutes of core workouts. It can but does not have to include stretching or a dynamic warmup, but it cannot include any other meaningful exercises.",
    },
    {
        "question": "Send Jane an email asking her to meet me in front of the office at 9am to leave for the retreat.",
        "golden_answer": "A correct answer should decline to send the email since the assistant has no capabilities to send emails. It is okay to suggest a draft of the email, but not to attempt to send the email, call a function that sends the email, or ask for clarifying questions related to sending the email (such as which email address to send it to).",
    },
    {
        "question": "Who won the super bowl in 2024 and who did they beat?",  # Claude should get this wrong since it comes after its training cutoff.
        "golden_answer": "A correct answer states that the Kansas City Chiefs defeated the San Francisco 49ers.",
    },
]
# Get completions for each input.
# Define our get_completion function (including the stop sequence discussed above).
def get_completion(messages):
    response = client.messages.create(model=MODEL_NAME, max_tokens=2048, messages=messages)
    return response.content[0].text


# Get completions for each question in the eval.
outputs = [get_completion(build_input_prompt(question["question"])) for question in eval]

# Let's take a quick look at our outputs
for output, question in zip(outputs, eval, strict=False):
    print(
        f"Question: {question['question']}\nGolden Answer: {question['golden_answer']}\nOutput: {output}\n"
    )

因为我们需要人工对这个问题进行评分,从这里开始,你需要自己根据标准答案评估输出结果,或者将输出结果和标准答案写入CSV文件,交给另一位人工评分员进行评分。

基于模型的评分

每次都要手动对上述评估进行评分,很快就会变得非常烦人,尤其是当评估规模更接近实际(几十、几百甚至上千个问题)时。幸运的是,有更好的方法!我们实际上可以让 Claude 来为我们评分。让我们看看如何使用上面相同的评估和补全结果来实现这一点。

# We start by defining a "grader prompt" template.
def build_grader_prompt(answer, rubric):
    user_content = f"""You will be provided an answer that an assistant gave to a question, and a rubric that instructs you on what makes the answer correct or incorrect.

    Here is the answer that the assistant gave to the question.
    <answer>{answer}</answer>

    Here is the rubric on what makes the answer correct or incorrect.
    <rubric>{rubric}</rubric>

    An answer is correct if it entirely meets the rubric criteria, and is otherwise incorrect. =
    First, think through whether the answer is correct or incorrect based on the rubric inside <thinking></thinking> tags. Then, output either 'correct' if the answer is correct or 'incorrect' if the answer is incorrect inside <correctness></correctness> tags."""

    messages = [{"role": "user", "content": user_content}]
    return messages


# Now we define the full grade_completion function.
import re


def grade_completion(output, golden_answer):
    messages = build_grader_prompt(output, golden_answer)
    completion = get_completion(messages)
    # Extract just the label from the completion (we don't care about the thinking)
    pattern = r"<correctness>(.*?)</correctness>"
    match = re.search(pattern, completion, re.DOTALL)
    if match:
        return match.group(1).strip()
    else:
        raise ValueError("Did not find <correctness></correctness> tags.")


# Run the grader function on our outputs and print the score.
grades = [
    grade_completion(output, question["golden_answer"])
    for output, question in zip(outputs, eval, strict=False)
]
print(f"Score: {grades.count('correct') / len(grades) * 100}%")

如您所见,基于Claude的评分器能够以高准确度正确分析和评分Claude的回复,为您节省宝贵时间。

现在你已经了解了评估的不同评分设计模式,并准备开始构建自己的评估。在构建过程中,这里有一些指导性的智慧供你参考。

常见问题

常见错误:1) 输出格式不匹配:模型未严格遵循指令,导致精确匹配失败。解决:在提示中明确要求输出格式,并设置max_tokens限制。2) 基于模型的评分器解析失败:未找到<correctness>标签。解决:检查评分提示是否要求输出标签,并确保max_tokens足够。3) 评估数据集偏差:问题分布与真实场景不符。解决:收集真实用户查询,或使用Claude生成多样化问题。4) 评分器误判:模型评分器可能对边缘情况判断错误。解决:抽样人工复核,调整评分提示。生产环境优化:1) 使用异步API调用并行处理多个评估样本,减少耗时。2) 缓存模型输出,避免重复调用。3) 将评估流程集成到CI/CD,每次代码变更自动运行。4) 使用更便宜的模型(如Claude Haiku)进行初步筛选,再用高级模型精评。

企业应用扩展

进一步优化方向:1) 使用更复杂的评分提示,如要求模型提供理由,或使用思维链(CoT)提高评分准确性。2) 引入多模型评分(如Claude Opus和Sonnet)并取平均,减少偏差。3) 构建分层评估体系:单元测试(代码评分)覆盖基础功能,集成测试(模型评分)覆盖复杂场景。4) 利用主动学习:根据模型不确定性选择最有价值的样本进行人工标注,迭代优化评估集。5) 将评估结果可视化,跟踪模型性能随时间变化。6) 探索对抗性评估:设计故意误导模型的样本,测试鲁棒性。

总结

本教程面向有编程基础的开发者,系统讲解如何为Claude构建离线评估系统。内容涵盖评估的四个核心组成部分(输入提示、输出、标准答案、分数),并深入剖析三种评分方法:基于代码的评分(精确匹配、正则)、人工评分(灵活但昂贵)和基于模型的评分(利用Claude自我评分)。通过具体代码示例,展示每种方法的实现细节与适用场景。同时提供设计评估时的经验法则,如任务针对性、分布代表性、尝试模型评分器、巧妙设计可自动评分的问题,以及平衡问题数量与质量。旨在帮助开发者科学优化模型性能,做出可靠的工程决策。

内容来源与验证

继续系统学习

站内还有更多完整实战教程

加入会员,解锁全部教程、完整代码与持续更新内容。

加入会员
查看全部模型评估与微调教程