提示 A/B 测试:量化对比
提示 A/B 测试指对两版(或多版)提示在同一批样本上各跑一遍,用统一指标比较谁更优,把「哪版更好」从主观感受变成可统计的结论。它与产品 A/B 的区别在于:产品的 A/B 通常面向真实用户、随机分流、看线上指标;而提示 A/B 往往用固定的离线评测集,因为 LLM 的输出对温度等参数敏感,需要可控、可复现的对比环境,才能把变量锁定在「提示」本身。它本质上是一种受控实验:唯一的自变量是提示,因变量是选定的质量指标。
是什么
与产品 A/B 类似:A 为基线提示(baseline),B 为新提示(variant)。在相同的测试集上各跑一遍,对比自动指标(如准确率、合规率、JSON 解析成功率)或人工评分。关键不在「跑一次」,而在「用同一批样本、同一套打分逻辑」,保证观察到的差异只来自提示本身,而不是样本或模型抖动。当存在多版候选时,可扩展为 A/B/C 多臂对比,但分析复杂度也随之上升。
为什么做
LLM 的输出具有随机性:同一个提示、同一个样本,换一次采样结果可能不同。一个看似更好的提示,可能只是在你手头几个例子上碰巧表现好,换个分布就翻车。只有批量对比并看聚合指标与置信区间,才能区分「真实提升」与「偶然波动」。此外,A/B 还能暴露 trade-off:B 版在准确率上略升,但合规率却下降,或平均得分涨了但某类失败更频繁——这种取舍必须靠对照数据才能看见,单看一个维度容易被误导。
怎么做
用评测框架批量打分,再算指标差与样本量。一个最小可运行的思路:
from openai import OpenAI
def run_and_score(prompt, test_set, client):
scores = []
for case in test_set:
resp = client.chat.completions.create(
model="gpt-4o-mini",
temperature=0, # 控制随机性,固定变量
messages=[{"role": "user", "content": prompt + "\n" + case["input"]}],
)
scores.append(grade(resp.choices[0].message.content, case["expected"]))
return sum(scores) / len(scores)
score_a = run_and_score(prompt_a, test_set, client)
score_b = run_and_score(prompt_b, test_set, client)
print(score_a, score_b)
要判定「B 是否真的优于 A」,不能只看平均分,还需统计检验。两版提示在同一组样本上产生的是配对数据(同一输入既被 A 又被 B 评过),常用 McNemar 检验(针对分类对错)或 bootstrap 自举法估计指标差的置信区间。例如对准确率差异做 bootstrap:
import numpy as np
rng = np.random.default_rng(0)
diffs = []
for _ in range(2000):
idx = rng.integers(0, len(test_set), len(test_set)) # 有放回重采样
diffs.append(correct_b[idx].mean() - correct_a[idx].mean())
lo, hi = np.percentile(diffs, [2.5, 97.5])
print(lo, hi) # 若区间含 0,则差异不显著
指标设计要区分两类:一类是可自动判定的(格式正确、JSON 可解析、正则命中关键词、与标准答案精确匹配),另一类是需要裁判的——开放式回答的质量常用「模型裁判(LLM-as-judge)」按 rubric 打分,但要注意校准:先拿人工评分标几十条,看裁判模型与你的一致性(如 Cohen’s kappa),避免裁判本身有偏。评测集本身也要有代表性,最好覆盖边界与失败场景,而不是只挑「能答对的」例子。工程上可用 LangSmith 或 Promptfoo 管理评测集、跑批量对比并留存每次结果,方便长期追踪提示版本的涨跌。
实验设计要点
- 样本量:差异越小,需要的样本量越大。上线前可先做功效分析(power analysis)估算所需样本,避免「测了 30 条就下结论」。
- 多次采样:即便 temperature=0,不同模型版本或系统层仍可能有波动;对关键结论,可对每样本跑多次取稳健统计。
- 顺序与混杂:离线评测不需要随机分流用户,但要确保 A、B 跑的是完全相同的样本与参数,避免把模型版本差异误认为提示差异。
- 多重比较:若同时比较多版,需对显著性做校正(如 Bonferroni),否则假阳性率会随比较次数上升。
注意点
- 样本要足够多:样本不足时容易被噪声误导,给出「显著」的假象。
- 固定变量:模型版本、温度、随机种子、系统提示都要锁定。
- 看分布而非只看均值:平均分涨了,但失败案例可能集中在某一类输入上,务必拆解失败案例的分布。
- 指标要可复现:自动评分要记录评分逻辑;人工评分要给出 rubric 与多人一致性。
- 注意成本:大批量跑强模型成本高,可先用小集合快速筛选,再在胜出者之间做更大规模对比。
- 别偷看后改:实验进行中根据中间结果反复调整提示会引入偏差,应预先定好方案再跑。
指标怎么打分
自动指标虽省事,但很多任务「对不对」无法用字符串匹配判断。常见做法是设计 rubric 让模型裁判或人工按维度打分。例如对「客服回复」可从相关性、礼貌、合规三个维度各打 1–5 分,再加权平均:
rubric:
- 相关性(0-5): 是否直接解决用户问题
- 礼貌(0-5): 语气是否得体
- 合规(0-5): 是否泄露禁答信息、是否过度承诺
最终分 = 0.5*相关性 + 0.2*礼貌 + 0.3*合规
用模型裁判时务必做校准:抽 30–50 条由人工打分,计算裁判与人工的 Cohen’s kappa,低于 0.6 说明裁判不可信,需要先改进裁判提示或退回人工。评测集也要覆盖边界与失败场景,而非只挑「能答对」的例子。
效应量与显著性
统计显著(p 值小)不等于业务重要。以下为示意数字:一个 2000 条样本上准确率从 82.0% 涨到 82.4% 可能显著,但 0.4 个百分点的提升未必值得为此增加提示长度与成本。判读时要同时看效应量(指标差的绝对值)与置信区间宽度:区间窄且不含 0,且差值达到你预设的最小可观测量(MDE),才建议上线。反之,若区间很宽,说明样本仍不足,结论应暂缓。
上线决策流程
- 先定「最小可观测量」:比如准确率至少涨 1 个百分点才算赢。
- 跑足样本,算出指标差与 95% 置信区间。
- 若区间不含 0 且差值 ≥ MDE:B 胜出,记录版本与证据后上线。
- 若区间不含 0 但差值 < MDE:统计赢了但业务无意义,维持 A。
- 若区间含 0:无结论,扩大样本或放弃该改动。
用 Promptfoo 管理实验
Promptfoo 这类开源工具把「提示 + 测试用例 + 评分器」固化为配置,可批量跑多版对比并生成报告,适合把 A/B 测试沉淀为团队流程:
prompts:
- prompt_a.yaml
- prompt_b.yaml
providers:
- openai:gpt-4o-mini
tests: ./test_set.json
常见失败模式
- 偷看后改:中途看了一眼结果就回头改 B,等于用测试集调参,结论会乐观失真。
- 只在happy path上测:评测集全是「能答对」的例子,差异自然不明显,也发现不了回归。
- 忽略成本维度:B 略优但 token 翻倍、延迟翻倍,综合未必划算。
- 多重比较不校正:同时比 5 版,按 0.05 阈值会大概率出现假阳性。
小结
提示 A/B 测试用同批样本与统一指标对比版本差异,把优化建立在统计证据上。需保证样本量、严格控制变量、结合失败案例分布,并用置信区间或统计检验判断差异是否可信,而非凭平均分下结论;认清多重比较与偷看偏差,结论才站得住脚。
参考与延伸阅读
- OpenAI Evals 评测框架仓库。已核验。https://github.com/openai/evals
- LangSmith 文档(支持提示对比与评估数据集)。已核验。https://docs.smith.langchain.com/
- Promptfoo(开源提示评测与 A/B 对比工具)。已核验。https://www.promptfoo.dev/
- scikit-learn 指标模块(含配对检验相关工具)。待核实最新接口。https://scikit-learn.org/stable/modules/classes.html#module-sklearn.metrics