全文逐字翻译(只输出翻译后结果): # 为您的提示模板生成合成测试数据

想象一下您有类似以下的提示:

"""这是我想让您分析的一些内容:

{{thing1}} {{thing2}}

这些内容是[事物的描述]。请仔细阅读它们并[执行某项任务]。”"""

在这里,我们将thing1和thing2称为“变量”——您希望您的提示能够很好地处理thing1和thing2的许多不同可能值。

您如何测试这个提示模板?也许您有一些可以替换的真实值。但也许您没有,或者出于隐私原因不允许使用您拥有的值进行测试。没关系——Claude可以自己生成!本指南演示了如何使用Claude和Anthropic API为您的提示生成合成测试数据。它包括用于从模板中提取变量、构造示例块、生成测试用例以及迭代优化结果的函数。这样做的好处是双重的:

  1. 提示评估 您可以使用这些测试用例来查看Claude在现实示例上的表现。

  2. 使用多示例改进提示 提供示例可能是提高Claude性能的最佳方法。这个笔记本可以帮助您生成现实的输入,这是获得理想输入/输出对的一半战斗。

% pip install anthropic IPython
import re
import anthropic
from IPython.display import display, HTML
#在此处输入您的API密钥
api_key = ""
CLIENT = anthropic.Anthropic(api_key=api_key)
MODEL_NAME = "claude-3-5-sonnet-20241022"

让我们从定义一些我们将在整个笔记本中使用的辅助函数开始。

# 首先,我们有 `extract_variables` 函数,
# 它接收一个提示模板并提取其中包含的双大括号“变量”。
def extract_variables(prompt_template):
    """从提示模板中提取变量。"""
    pattern = r'{{([^}]+)}}'
    variables = re.findall(pattern, prompt_template)
    return set(variables)

# 接下来,我们有 `construct_variables_names`,它只是将它们连接起来,用换行符分隔。
def construct_variables_names(prompt_template):
    """从提示模板构造变量名字符串。"""
    variables = extract_variables(prompt_template)
    return "\n".join(variables)

# `construct_variables_block` 函数接收变量列表,并构造一个“变量块”
# 如果变量是“animal”和“topic”,变量块可能看起来像这样:
"""
<animal>
[为变量“animal”提供的完整、完整的价值]
</animal>
<topic>
[为变量“topic”提供的完整、完整的价值]
</topic>
"""
def construct_variables_block(prompt_template):
    """为合成测试数据提示构造变量块。"""
    variables = extract_variables(prompt_template)
    output = ""
    for v in variables:
        output += f"<{v}>\n"
        output += f"[为变量“{v}”提供的完整、完整的价值。(您无需在标签内重复变量名。)]\n"
        output += f"</{v}>\n"
    return output.strip()

# `construct_examples` 接收一个 {variable: value} 字典并构造一个 XML 格式的示例。
# 例如,如果字典是
# {'animal': 'cat', 'topic': 'movement patterns'},那么示例将是
"""
<example>
<variables>
<animal>
cat
</animal>
<topic>
movement patterns
</topic>
</variables>
</example>
"""
def construct_example_block(variable_dict):
    """从变量字典构造示例块。"""
    output = "<example>\n<variables>\n"
    for k, v in variable_dict.items():
        output += f"<{k}>\n{v}\n</{k}>\n"
    output = output.strip()
    output += "\n</variables>\n</example>"
    return output

用于生成数据的提示模板

这些提示模板的总体思想是采用用户提交的带有变量的提示模板,并构造一些值来填充模板。

实际上,下面有两个提示模板;一个格式化为假设用户已提供示例变量值,另一个则不假设。

它们的共同点是,两个模板都首先为Claude提供有关情况的上下文,并指示Claude在输出测试用例之前仔细考虑每个变量的规范以及用户提供的整个提示模板。

# 为合成评估格式化提示模板

# 此函数准备用于生成合成测试数据的提示模板。

def format_prompt_template_for_synth_evals(prompt_template, examples=None):
    """为合成评估格式化提示模板。"""
    synth_test_data_prompt_template_with_example = """<Prompt Template>
{{PROMPT_TEMPLATE}}
</Prompt Template>

您的工作是为上面的提示模板构造一个测试用例。此模板包含“变量”,这些变量是稍后要填写的占位符。在这种情况下,变量是:

<variables>
{{CONSTRUCT_VARIABLES_NAMES}}
</variables>

这是用户提供的示例测试用例。
<examples>
{{EXAMPLES}}
</examples>

首先,在<planning>标签中,执行以下操作:

1. 总结提示模板。创建它的用户的目标是什么?
2. 对于<variables>中的每个变量,仔细考虑该变量的范例性、现实性示例是什么样的。您需要注意“生产环境”中谁将负责提供值。由人类“最终用户”编写?从网站下载?从数据库提取?考虑长度、格式和语气等因素,以及语义内容。使用用户提供的示例来指导此练习。目标是获得示例正在从中提取的统计分布的感觉。您编写的示例应来自同一分布,但与提供的示例足够不同,可以提供额外的信号。这是一个棘手的平衡,但我对你有信心。

完成后,输出一个测试用例,为该提示模板的每个变量提供一个完整、完整的价值。输出格式应由每个变量的标记块组成,值为块内,如下所示:

<variables>
{{CONSTRUCT_VARIABLES_BLOCK}}
</variables>"""

    synth_test_data_prompt_template_without_example = """<Prompt Template>
{{PROMPT_TEMPLATE}}
</Prompt Template>

您的工作是为上面的提示模板构造一个测试用例。此模板包含“变量”,这些变量是稍后要填写的占位符。在这种情况下,变量是:

<variables>
{{CONSTRUCT_VARIABLES_NAMES}}
</variables>

首先,在<planning>标签中,执行以下操作:

1. 总结提示模板。创建它的用户的目标是什么?
2. 对于<variables>中的每个变量,仔细考虑该变量的范例性、现实性示例是什么样的。您需要注意“生产环境”中谁将负责提供值。由人类“最终用户”编写?从网站下载?从数据库提取?考虑长度、格式和语气等因素,以及语义内容。

然后,输出一个测试用例,为该提示模板的每个变量提供一个完整、完整的价值。输出格式应由每个变量的标记块组成,值为块内,如下所示:
<variables>
{{CONSTRUCT_VARIABLES_BLOCK}}
</variables>"""

    if examples:
        examples_block = "\n".join([construct_example_block(example) for example in examples])
        return synth_test_data_prompt_template_with_example.replace(
            "{{PROMPT_TEMPLATE}}", prompt_template
        ).replace(
            "{{CONSTRUCT_VARIABLES_NAMES}}", construct_variables_names(prompt_template)
        ).replace(
            "{{CONSTRUCT_VARIABLES_BLOCK}}", construct_variables_block(prompt_template)
        ).replace(
            "{{EXAMPLES}}", examples_block
        )
    else:
        return synth_test_data_prompt_template_without_example.replace(
            "{{PROMPT_TEMPLATE}}", prompt_template
        ).replace(
            "{{CONSTRUCT_VARIABLES_NAMES}}", construct_variables_names(prompt_template)
        ).replace(
            "{{CONSTRUCT_VARIABLES_BLOCK}}", construct_variables_block(prompt_template)
        )

接下来,另一个用于填充适当提示模板并调用Claude的快速辅助函数。

def get_test_data(prompt_template, examples, custom_planning=None):
    """使用Anthropic API生成测试数据。"""
    synth_eval_prompt_ready = format_prompt_template_for_synth_evals(prompt_template, examples)

    messages = [
        {
            "role": "user",
            "content": synth_eval_prompt_ready,
        }
    ]
    if custom_planning:
        messages.append({
            "role": "assistant",
            "content": custom_planning,
        })

    message = CLIENT.messages.create(
        max_tokens=4000,
        messages=messages,
        model=MODEL_NAME,
        temperature=1,
    ).content[0].text

    return message
# 我们将使用此函数来采样Claude对填充模板的响应,
# 一旦我们有了示例值/测试用例。

def call_claude_with_template(prompt_template, variables):
    """使用填充的提示模板调用Claude。"""
    filled_template = prompt_template
    for var, value in variables.items():
        filled_template = filled_template.replace(f"{{{{{var}}}}}", value)

    message = CLIENT.messages.create(
        max_tokens=4000,
        messages=[
            {
                "role": "user",
                "content": filled_template,
            }
        ],
        model=MODEL_NAME,
        temperature=0.7,
    ).content[0].text

    return message

现在我们可以开始将这些部分组合起来了。首先,在此处输入您的提示模板。

# 用您的提示模板替换此内容!
# 使用双括号表示变量
# 这是一个例子:
prompt_template = """您是Acme公司的客户支持机器人。
这是FAQ,其中包含Acme的相关政策:

<documents>
{{DOCUMENTS}}
</documents>

请根据政策详情回复此客户支持问题:

<question>
{{QUESTION}}
</question>"""

variables = extract_variables(prompt_template)
print("\n已识别变量:")
for var in variables:
    print(f"- {var}")
已识别变量:

- DOCUMENTS
- QUESTION

接下来,如果您有任何“黄金示例”的输入和理想输出,您可以输入它们。代码目前被注释掉了。

planning_text = None
USER_EXAMPLES = []

# if input("\nDo you want to provide an example value for your variables? (y/n): ").lower() == 'y':
#     example = {}
#     for var in variables:
#         example[var] = input(f"Enter an example value for {var}: ")
#     USER_EXAMPLES.append(example)

接下来,我们可以获取测试用例生成提示模板,并用此信息填充,然后获取一个测试用例!

result = get_test_data(prompt_template, USER_EXAMPLES, planning_text)

现在,让我们看看测试用例和用于生成它的规划。

planning_match = re.search(r'<planning>(.*?)</planning>', result, re.DOTALL)
if planning_match and not planning_text:
    planning_text = "<planning>\n" + planning_match.group(1).strip() + "\n</planning>"

extracted_variables = {}
for var in variables:
    var_match = re.search(f'<{var}>(.*?)</{var}>', result[result.index("<variables>"):], re.DOTALL)
    if var_match:
        extracted_variables[var] = var_match.group(1).strip()

USER_EXAMPLES.append(extracted_variables)

print("~~~~~~~~~~~\n生成的测试用例:\n~~~~~~~~~~~")
for var, value in extracted_variables.items():
    print(f"{var}:\n{value}\n")

print("~~~~~~~~~~~\n规划:\n~~~~~~~~~~~")
print(planning_text)
~~~~~~~~~~~
生成的测试用例:
~~~~~~~~~~~
DOCUMENTS:
退货政策

- 商品可在购买后30天内凭原始收据退货
- 商品必须未使用且包装完好
- 运费不予退还
- 礼品卡不可退货

送货信息

- 标准送货(5-7个工作日):订单满50美元免费
- 加急送货(2-3个工作日):12.99美元
- 次日送达(下一个工作日):24.99美元
- 我们仅运送至美国大陆
- 阿拉斯加和夏威夷的订单需额外支付15美元费用

付款方式

- 我们接受Visa、Mastercard、American Express和PayPal
- 付款在下单时处理
- 礼品卡不能用于部分付款

QUESTION:
你好,我上周买了一件毛衣,但尺寸不合适。我可以退货吗?我支付的运费会退还给我吗?谢谢!

~~~~~~~~~~~
规划:
~~~~~~~~~~~
<planning>

1. 提示模板摘要:
此模板创建一个Acme公司的客户服务聊天机器人,该机器人根据公司官方政策/FAQ文档回答客户问题。目标是确保对客户咨询的响应一致且符合政策。

2. 变量分析:

DOCUMENTS:

- 可能由Acme的政策/法律团队维护
- 存储在知识库或内容管理系统中
- 格式为结构化的FAQ条目或政策声明
- 专业、正式的语气
- 包含不同主题的多个段落
- 清晰的标题和类别
- 长度:几个段落(300-500字)

QUESTION:

- 由最终用户/客户编写
- 非正式、对话式的语气
- 通常为1-2句话
- 通常包含有关其具体情况的背景信息
- 可能包含拼写错误或随意用语
- 长度:20-50字
</planning>

从这里开始,有几种方法可以进行。我们可以生成更多测试用例,或者我们可以编辑Claude的规划逻辑。让我们稍微编辑一下Claude的规划逻辑。也许我们知道ACME的文档使用编号行。其他一些实际的更改可能是:

  • 让Claude告诉自己使文档更长、更详细。
  • 让Claude告诉自己使客户支持查询更正式或不那么正式。
planning_text = planning_text.replace("each with a question and answer format", "each with a question and answer format and associated number.")
# 您可能需要稍微修改规划文本,因此需要重写replace。

让我们重置我们的示例,但使用此规划文本作为预填充。(这可以节省一些采样时间。)

USER_EXAMPLES = []
result = get_test_data(prompt_template, USER_EXAMPLES, planning_text)

现在让我们看看新的结果。

# 从上面的单元格复制粘贴。
planning_match = re.search(r'<planning>(.*?)</planning>', result, re.DOTALL)
if planning_match and not planning_text:
    planning_text = "<planning>\n" + planning_match.group(1).strip() + "\n</planning>"

extracted_variables = {}
for var in variables:
    var_match = re.search(f'<{var}>(.*?)</{var}>', result[result.index("<variables>"):], re.DOTALL)
    if var_match:
        extracted_variables[var] = var_match.group(1).strip()

USER_EXAMPLES.append(extracted_variables)

print("~~~~~~~~~~~\n生成的测试用例:\n~~~~~~~~~~~")
for var, value in extracted_variables.items():
    print(f"{var}:\n{value}\n")

print("~~~~~~~~~~~\n规划:\n~~~~~~~~~~~")
print(planning_text)
~~~~~~~~~~~
生成的测试用例:
~~~~~~~~~~~
DOCUMENTS:
退货政策

- 商品可在购买后30天内凭原始收据退货
- 商品必须未使用且包装完好
- 运费不予退还
- 无收据退货将获得商店积分

送货信息

- 标准送货(5-7个工作日):5.99美元
- 加急送货(2-3个工作日):12.99美元
- 订单满50美元可享受免费标准送货
- 我们目前仅运送至美国大陆
- 阿拉斯加和夏威夷的订单需支付额外费用

付款方式

- 我们接受Visa、Mastercard、American Express和PayPal
- 礼品卡不能用于在线购买
- 付款在下单时处理
- 所有价格均为美元

QUESTION:
你好,我上周买了一件毛衣,但尺寸不合适。我可以退货吗?我仍然保留着标签,但我扔掉了收据。谢谢!

~~~~~~~~~~~
规划:
~~~~~~~~~~~
<planning>

1. 提示模板摘要:
此模板创建一个Acme公司的客户服务聊天机器人,该机器人根据公司官方政策/FAQ文档回答客户问题。目标是确保对客户咨询的响应一致且符合政策。

2. 变量分析:

DOCUMENTS:

- 可能由Acme的政策/法律团队维护
- 存储在知识库或内容管理系统中
- 格式为结构化的FAQ条目或政策声明
- 专业、正式的语气
- 包含不同主题的多个段落
- 清晰的标题和类别
- 长度:几个段落(300-500字)

QUESTION:

- 由最终用户/客户编写
- 非正式、对话式的语气
- 通常为1-2句话
- 通常包含有关其具体情况的背景信息
- 可能包含拼写错误或随意用语
- 长度:20-50字
</planning>

太好了,它完成了编号的问答!

让我们再做一个示例。这个示例将使用我们已经拥有的示例,所以希望它会有有趣的差异。

result = get_test_data(prompt_template, USER_EXAMPLES, planning_text)
# 从上面的单元格复制粘贴。
planning_match = re.search(r'<planning>(.*?)</planning>', result, re.DOTALL)
if planning_match and not planning_text:
    planning_text = "<planning>\n" + planning_match.group(1).strip() + "\n</planning>"

extracted_variables = {}
for var in variables:
    var_match = re.search(f'<{var}>(.*?)</{var}>', result[result.index("<variables>"):], re.DOTALL)
    if var_match:
        extracted_variables[var] = var_match.group(1).strip()

USER_EXAMPLES.append(extracted_variables)

print("~~~~~~~~~~~\n生成的测试用例:\n~~~~~~~~~~~")
for var, value in extracted_variables.items():
    print(f"{var}:\n{value}\n")

print("~~~~~~~~~~~\n规划:\n~~~~~~~~~~~")
print(planning_text)
~~~~~~~~~~~
生成的测试用例:
~~~~~~~~~~~
DOCUMENTS:
产品保修

- 所有电子产品均享有一年有限制造商保修
- 保修涵盖材料和工艺缺陷
- 保修不包括意外损坏或误用
- 可在30天内购买延长保修

价格匹配政策

- 我们匹配授权零售商的价格
- 商品必须是相同的型号/颜色/规格
- 必须在竞争对手的商店有货
- 在线零售商不参与价格匹配
- 价格匹配请求必须在购买时提出

订单取消

- 订单可在下单后2小时内取消
- 一旦发货,则无法取消订单
- 取消的订单将退还至原始付款方式
- 退款处理时间:3-5个工作日
- 如需取消订单,请联系客服

QUESTION:
您好,我三周前在你们店里买了一台笔记本电脑,它总是随机关机。它还在保修期内,对吧?我需要做什么才能修好它?提前谢谢!

~~~~~~~~~~~
规划:
~~~~~~~~~~~
<planning>

1. 提示模板摘要:
此模板创建一个Acme公司的客户服务聊天机器人,该机器人根据公司官方政策/FAQ文档回答客户问题。目标是确保对客户咨询的响应一致且符合政策。

2. 变量分析:

DOCUMENTS:

- 可能由Acme的政策/法律团队维护
- 存储在知识库或内容管理系统中
- 格式为结构化的FAQ条目或政策声明
- 专业、正式的语气
- 包含不同主题的多个段落
- 清晰的标题和类别
- 长度:几个段落(300-500字)

QUESTION:

- 由最终用户/客户编写
- 非正式、对话式的语气
- 通常为1-2句话
- 通常包含有关其具体情况的背景信息
- 可能包含拼写错误或随意用语
- 长度:20-50字
</planning>

仍然是关于ACME公司,但问题不同,知识库也不同。

从这里开始,世界是你的牡蛎——你可以通过循环运行代码来生成更多测试用例,编辑更多规划,在这些测试用例上评估Claude,并将你制作的测试用例与黄金答案一起作为多示例放入你的提示中以提高性能。

要获取黄金答案,您可以自己从头开始编写,或者让Claude编写一个答案,然后根据需要进行编辑。随着提示缓存的出现,现在是添加大量示例以提高性能的最佳时机。