提示版本与 A/B 实验
正在读取学习记录…
Prompt 版本是一次任务说明与输出约定的确定快照。A/B 比较把同一批输入分别交给两个版本,再用相同判据逐条看结果。上一站模型路由决定请求发到哪里;这里先固定目标和数据,只比较 Prompt 的变化。
以客户留言“登录失败”为例,参考答案是技术支持组。假设旧版任务说明得到“综合客服组”,新版得到“技术支持组”,这条案例就有了改善。把同样的比较用于整批客户留言,就能找出哪些改好了、哪些反而分错了。下面的程序使用准备好的两版回答来练习这种比较。
1. 把“版本”说清楚
先把 A、B 当成两份菜谱,工单是同一份原料。比较要用两份菜谱各做一次,再逐盘检查;只改菜谱却拿旧菜比较,看不出新效果。代码中的 A、B 是任务文本,ROWS 是预先写好的两版回答,因此改文本不会重新生成回答。内容哈希是根据文本计算的一串指纹,用来识别版本;稍后在主程序输出中看它。
| 需冻结的项 | 为什么 | 若变化会怎样 |
|---|---|---|
| 输入与参考标签 | 排除样本难度差异 | 无法归因于 Prompt |
| 模型标识、参数、工具 | 排除调用条件差异 | 可能是模型或工具导致 |
| 评分规则 | 防止看完结果再改门槛 | “胜利”变成事后解释 |
| 版本正文哈希 | 避免同名文件悄悄改动 | 结果不能重放 |
2. 先看逐例变化,再看总数
先看源码里的 login:参考标签 expected=Technical,A 给 General,B 给 Technical,因此归入 improved(改进)。billing 两版都给 Billing,归入 same_correct(都对);privacy 从 Unknown 变成 Billing,归入 regressed(退步)。same_wrong 表示两版都错,当前四条夹具里没有这类。标记 critical=True 的是关键风险例,退步时进入 blocking,发布决定为 block。
Anthropic 的评测工具说明也围绕测试用例比较输出;它不是“跑一次就可上线”的保证。真实模型有波动,抽样、人工复核和更大的代表性样本仍必要。
3. 跟着做:离线核对实验流程
复制下方完整代码,保存为 version_lab.py、test_version_lab.py至自己电脑的同一个新文件夹。在资源管理器打开该文件夹,从地址栏输入 powershell,核对文件并执行:
macOS:先在 Python 篇的学习文件夹中打开终端,执行 source .venv/bin/activate。再输入 cd ,把当前例子的文件夹拖入终端并回车,运行下面的命令。
保存为 version_lab.py:
"""逐条比较两个版本的固定回答,找出进步、退步和需要阻止发布的问题。"""
import hashlib
A = "选择团队。"
B = "仅从允许团队中选择;信息不足时拒绝并说明证据。"
# 同一行对应同一个客户问题:expected 为参考答案,a/b 为两个版本的回答。
# Billing 为账单组,Technical 为技术支持组,General 为综合客服组,Unknown 为待确认。
ROWS = [
{"id": "billing", "expected": "Billing", "a": "Billing", "b": "Billing", "critical": False},
{"id": "login", "expected": "Technical", "a": "General", "b": "Technical", "critical": False},
{"id": "unknown", "expected": "Unknown", "a": "General", "b": "Unknown", "critical": False},
{"id": "privacy", "expected": "Unknown", "a": "Unknown", "b": "Billing", "critical": True},
]
def fingerprint(prompt):
return hashlib.sha256(prompt.encode("utf-8")).hexdigest()
def compare(rows):
if not rows:
raise ValueError("empty dataset")
ids = [row.get("id") for row in rows]
if not all(ids) or len(ids) != len(set(ids)):
raise ValueError("missing or duplicate id")
result = {"improved": [], "regressed": [], "same_correct": [], "same_wrong": [],
"blocking": []}
for row in rows:
if not all(key in row for key in ("expected", "a", "b", "critical")):
raise ValueError("missing comparison field")
# 分别核对两个版本,再判断这条案例是进步、退步,还是保持原样。
a_ok = row["a"] == row["expected"]
b_ok = row["b"] == row["expected"]
category = ("same_correct" if a_ok else "same_wrong") if a_ok == b_ok else (
"improved" if b_ok else "regressed")
result[category].append(row["id"])
if a_ok and not b_ok and row["critical"]:
result["blocking"].append(row["id"])
result["decision"] = "block" if result["blocking"] else "review"
return result
if __name__ == "__main__":
print("A_sha256", fingerprint(A))
print("B_sha256", fingerprint(B))
print(compare(ROWS))完整代码:test_version_lab.py
# 核对提示版本与 A/B 实验的正常结果和错误分支;断言失败时会显示对应的测试。
import unittest
from version_lab import A, B, ROWS, compare, fingerprint
class VersionTests(unittest.TestCase):
def test_fingerprints_differ(self):
self.assertNotEqual(fingerprint(A), fingerprint(B))
def test_paired_diff_and_block(self):
result = compare(ROWS)
self.assertEqual(result["improved"], ["login", "unknown"])
self.assertEqual(result["blocking"], ["privacy"])
self.assertEqual(result["decision"], "block")
def test_empty_rejected(self):
with self.assertRaises(ValueError):
compare([])
def test_duplicate_rejected(self):
with self.assertRaises(ValueError):
compare([ROWS[0], ROWS[0]])
def test_missing_reference_rejected(self):
with self.assertRaises(ValueError):
compare([{"id": "x", "a": "x", "b": "x", "critical": False}])
if __name__ == "__main__":
unittest.main()ls
python version_lab.py
python -m unittest -v test_version_lab.py第一步,脚本用 sha256 计算两个固定模板的哈希;这是内容指纹,不是质量分数。第二步,固定样例以 id 对齐 A/B 输出,防止漏样本或重复 id。第三步,逐例输出 improved、regressed、same_correct、same_wrong;对于 critical 样例退步,即使净改进为正也给 block。核心判断是:
a_ok = row["a"] == row["expected"]
b_ok = row["b"] == row["expected"]
if a_ok and not b_ok and row["critical"]:
blocking.append(row["id"])脚本用固定样例检查比较器,预期出现改进、退步和阻断三类信号。若出现 SyntaxError,按报错行号核对引号、括号和代码缩进;若找不到模块,用 dir 确认两个文件在同一目录。
| 输入边界 | 应有行为 | 为什么 |
|---|---|---|
| 空样本集 | 拒绝比较 | 0/0 没有意义 |
| 重复 id | 拒绝比较 | 配对关系不唯一 |
| 参考标签缺失 | 拒绝比较 | 不能计算正确性 |
| 高风险样本退步 | 阻断并列出 id | 总数不能掩盖关键失败 |
亲手做一轮“只改一个变量”的比较
打开保存的 version_lab.py,找出 A、B 两份固定模板:把它们当作两个待比较的 Prompt 版本。运行脚本后,记下两份哈希与逐例状态。接着只在你本地的 B 模板末尾追加“信息不足时返回 Unknown。”,再运行:你应看到 B 的哈希变化,但人工夹具输出未必变化。这一步故意证明“Prompt 文本改变”与“模型效果改变”是两件事;哈希只用来追踪输入版本。
再看每条工单的 expected、a、b。把普通样例 billing 的 b 从 Billing 改为 Technical,运行 python version_lab.py,观察返回字典的 regressed 列表,随后恢复。单测用断言检查函数,并不打印变化统计;演示数据改错也未必使现有测试失败,因此观察入口是主程序或下面的 compare 调用。
想保留原程序,也可以在同一目录只复制内存中的四行夹具,再把 billing 这条的 B 输出改错:
python -c "from version_lab import ROWS,compare; rows=[dict(x) for x in ROWS]; rows[0]['b']='Technical'; print(compare(rows))"运行后,regressed 从只有 privacy 变为 billing 与 privacy;blocking 仍只有高风险的 privacy。这说明“退步数”和“一票阻断”不是同一个指标。若 billing 没有进入退步列表,先检查原样例 a 与 expected 是否同为 Billing,以及是否真的修改了索引 0 的副本;不要直接改原始 ROWS 以迎合预期结果。
真正评测时,用记录表保存每次比较的依据:
| 字段 | 示例值 | 作用 |
|---|---|---|
| 样例 ID | T-004 | 确保 A、B 处理同一输入 |
| 输入版本 | 脱敏工单 v1 | 追溯是否改变了问题 |
| Prompt 指纹 | A: hashA,B: hashB | 证明比较的是哪两份文本 |
| 模型与参数 | 模型标识、温度、长度上限 | 避免把模型变化误当 Prompt 收益 |
| 原始回答 | 原文与结束原因 | 让解析错误可回查 |
| 参考判据 | 业务裁决与日期 | 解释“正确”的来源 |
| 判读 | A 对、B 错、风险级别 | 找出关键退步 |
固定输入、模型和参数后,A/B 的差异才主要指向 Prompt;即便如此,非确定性输出还需要预先约定重复次数与评分方法。若用不同模型、不同采样设置,或只给 B 增加检索资料,就不再是单因素 Prompt 对照。费用和延迟也要按实际请求记账,而不是从本地固定输出推断。
4. 从固定回答走向真实 A/B
回到刚才的 billing 改参实验,你改的是一条 B 的固定输出,所以逐例退步数会变化,而模板哈希和真实模型能力不会神奇改变。真正做 A/B 前,按图保证两边样例 ID、输入和判据相同,再写好阻断规则。
真实运行前,先写清唯一改动、相同输入、参考标签和关键退步的停止规则,再固定模型与参数。用独立保留集检查效果,费用和延迟按实际请求记录。这个提前约定评价方法的动作叫预注册,避免看完结果才改门槛。
团队评测需要补齐的记录
记录样本来源、包含与排除标准、最低门槛、最大成本;开发样例与保留集分开。运行后保存原始输出、请求参数、模型版本、Prompt哈希和判据版本。私有输入脱敏并限制访问;人工判据有分歧时保留分歧,等裁决后再比较。
小结
比较两版 Prompt,就像让两个厨师照同一批订单做菜:题目、判据和条件相同,差异才容易归到改动上。逐条记录 A、B 与参考答案,先检查隐私等关键样例,再汇总改善与退步。试着找出一个“总体改善,但关键样例退步”的例子,说明此时怎样处理新版本。
参考资料
- OpenAI Evals 指南:数据、运行、评分的组织方式。
- Anthropic 评测工具:用测试用例比较提示输出;产品界面可能变化。
现有练习
课后练习展开习题收起习题
正在读取本地记录…
不调用模型,不执行代码。简答、代码和场景题仅保存答案并展示自查标准,不生成评分、徽章或认证。输入内容不会自动保存,请点击提交;不要填写个人信息或密钥。
选择题 · 先判断边界
关于“提示版本与 A/B 实验”,哪种做法更稳妥?
对应知识点:L1-prompt-version
简答题 · 解释失败
写出一个失败输入并说明记录字段。
对应知识点:L1-prompt-version
代码题 · 边界测试
补充正常、边界、失败三组输入和预期结果。
对应知识点:L1-prompt-version
场景题 · 结果未知
证据不足时如何设计状态和人工接管?
对应知识点:L1-prompt-version
描述会发送给本站维护者,用于修订文章,保存90天。请只填写文章问题,省略个人信息、密钥和内部资料。