AgentDepthAgent 工程学习指南
输入概念或问题,检索正文和待编写目录。
第一次学习?从 L0 总览开始 →

提示版本与 A/B 实验

我的学习 →

正在读取学习记录…

Prompt 版本是一次任务说明与输出约定的确定快照。A/B 比较把同一批输入分别交给两个版本,再用相同判据逐条看结果。上一站模型路由决定请求发到哪里;这里先固定目标和数据,只比较 Prompt 的变化。

以客户留言“登录失败”为例,参考答案是技术支持组。假设旧版任务说明得到“综合客服组”,新版得到“技术支持组”,这条案例就有了改善。把同样的比较用于整批客户留言,就能找出哪些改好了、哪些反而分错了。下面的程序使用准备好的两版回答来练习这种比较。

1. 把“版本”说清楚

先把 A、B 当成两份菜谱,工单是同一份原料。比较要用两份菜谱各做一次,再逐盘检查;只改菜谱却拿旧菜比较,看不出新效果。代码中的 A、B 是任务文本,ROWS 是预先写好的两版回答,因此改文本不会重新生成回答。内容哈希是根据文本计算的一串指纹,用来识别版本;稍后在主程序输出中看它。

同一批冻结工单同时进入 A/B 提示,经同一判据形成逐例差异
图1:每行一对,A 和 B 回答同一个问题,再按同一判据比较。
需冻结的项为什么若变化会怎样
输入与参考标签排除样本难度差异无法归因于 Prompt
模型标识、参数、工具排除调用条件差异可能是模型或工具导致
评分规则防止看完结果再改门槛“胜利”变成事后解释
版本正文哈希避免同名文件悄悄改动结果不能重放

2. 先看逐例变化,再看总数

先看源码里的 login:参考标签 expected=Technical,A 给 General,B 给 Technical,因此归入 improved(改进)。billing 两版都给 Billing,归入 same_correct(都对);privacy 从 Unknown 变成 Billing,归入 regressed(退步)。same_wrong 表示两版都错,当前四条夹具里没有这类。标记 critical=True 的是关键风险例,退步时进入 blocking,发布决定为 block。

四格对照:两版都对、B 改进、B 退步、两版都错;退步需逐例复核
图2:同时记录新增正确与新增错误;高风险退步不能被总分掩盖。

Anthropic 的评测工具说明也围绕测试用例比较输出;它不是“跑一次就可上线”的保证。真实模型有波动,抽样、人工复核和更大的代表性样本仍必要。

3. 跟着做:离线核对实验流程

复制下方完整代码,保存为 version_lab.py、test_version_lab.py至自己电脑的同一个新文件夹。在资源管理器打开该文件夹,从地址栏输入 powershell,核对文件并执行:

macOS:先在 Python 篇的学习文件夹中打开终端,执行 source .venv/bin/activate。再输入 cd ,把当前例子的文件夹拖入终端并回车,运行下面的命令。

保存为 version_lab.py:

Python
"""逐条比较两个版本的固定回答,找出进步、退步和需要阻止发布的问题。"""
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
Python
# 核对提示版本与 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()
Bash
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。核心判断是:

Python
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 输出改错:

Bash
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 以迎合预期结果。

真正评测时,用记录表保存每次比较的依据:

字段示例值作用
样例 IDT-004确保 A、B 处理同一输入
输入版本脱敏工单 v1追溯是否改变了问题
Prompt 指纹A: hashA,B: hashB证明比较的是哪两份文本
模型与参数模型标识、温度、长度上限避免把模型变化误当 Prompt 收益
原始回答原文与结束原因让解析错误可回查
参考判据业务裁决与日期解释“正确”的来源
判读A 对、B 错、风险级别找出关键退步

固定输入、模型和参数后,A/B 的差异才主要指向 Prompt;即便如此,非确定性输出还需要预先约定重复次数与评分方法。若用不同模型、不同采样设置,或只给 B 增加检索资料,就不再是单因素 Prompt 对照。费用和延迟也要按实际请求记账,而不是从本地固定输出推断。

4. 从固定回答走向真实 A/B

源码中的 login 从 General 改为 Technical,privacy 从 Unknown 改为 Billing;关键隐私样例退步使比较器阻断
图 3:login 改进,关键 privacy 退步;billing 在原始夹具里两版都对。按相同 ID 核对,再看总数。

回到刚才的 billing 改参实验,你改的是一条 B 的固定输出,所以逐例退步数会变化,而模板哈希和真实模型能力不会神奇改变。真正做 A/B 前,按图保证两边样例 ID、输入和判据相同,再写好阻断规则。

真实运行前,先写清唯一改动、相同输入、参考标签和关键退步的停止规则,再固定模型与参数。用独立保留集检查效果,费用和延迟按实际请求记录。这个提前约定评价方法的动作叫预注册,避免看完结果才改门槛。

团队评测需要补齐的记录

记录样本来源、包含与排除标准、最低门槛、最大成本;开发样例与保留集分开。运行后保存原始输出、请求参数、模型版本、Prompt哈希和判据版本。私有输入脱敏并限制访问;人工判据有分歧时保留分歧,等裁决后再比较。

小结

比较两版 Prompt,就像让两个厨师照同一批订单做菜:题目、判据和条件相同,差异才容易归到改动上。逐条记录 A、B 与参考答案,先检查隐私等关键样例,再汇总改善与退步。试着找出一个“总体改善,但关键样例退步”的例子,说明此时怎样处理新版本。

参考资料

现有练习

课后练习展开习题收起习题

4 道练习 · 客观题规则评分 · 其余题目待批改

正在读取本地记录…

不调用模型,不执行代码。简答、代码和场景题仅保存答案并展示自查标准,不生成评分、徽章或认证。输入内容不会自动保存,请点击提交;不要填写个人信息或密钥。

练习 1 / 4 · v1.0.1

选择题 · 先判断边界

关于“提示版本与 A/B 实验”,哪种做法更稳妥?

对应知识点:L1-prompt-version

选择一个答案
练习 2 / 4 · v1.0.0

简答题 · 解释失败

写出一个失败输入并说明记录字段。

对应知识点:L1-prompt-version

练习 3 / 4 · v1.0.0

代码题 · 边界测试

补充正常、边界、失败三组输入和预期结果。

对应知识点:L1-prompt-version

练习 4 / 4 · v1.0.0

场景题 · 结果未知

证据不足时如何设计状态和人工接管?

对应知识点:L1-prompt-version

图片查看器
100%

文章配图 · 按 Esc 关闭