提示词改了一句,客服分类、JSON 格式或拒答行为都可能变。用 promptfoo 做回归测试的关键是固定输入与验收断言,让旧提示词和新提示词在同一套用例上运行,再保存失败输入和输出。下面先用不调用模型的本地模拟器跑通流程,之后再接你的真实应用。
先把任务写成能检查的结果
本例是虚构的客服分类任务:退款诉求输出 refund,发票诉求输出 invoice,其他输出 other。输出必须是只有 category 字段的 JSON。这里能检查两件事:格式合法、分类符合人工标注;它们比“回答质量不错”更明确。

实际验证环境是 Windows、Node.js 24.14.1、promptfoo 0.123.1。该版本要求 Node.js 至少 22.22.0。在新建的练习目录执行:
node --version
npm init -y
npm install --save-dev promptfoo@0.123.1
npx promptfoo --version
本地安装并固定版本,后续复现就不用每次跟随 latest 更新。配置包含 prompts、providers、tests,字段可查官方配置指南。
建立离线模拟器,故意制造一条回归
新建 mock_provider.cjs,内容如下。它按文字返回固定分类,并故意让 VERSION_B 的发票请求分类错误,用于验证失败能否被发现。
module.exports = class MockProvider {
id() { return 'offline-mock'; }
async callApi(prompt, context) {
const text = context.vars.text;
let category = text.includes('退款') ? 'refund' :
text.includes('发票') ? 'invoice' : 'other';
if (prompt.includes('VERSION_B') && text.includes('发票')) {
category = 'other';
}
return { output: JSON.stringify({category}) };
}
};
自定义 provider 至少实现 id 和 callApi,返回 output。接口见官方自定义 API provider 文档。本例的第二版故障由代码模拟,不代表一句真实提示词一定会导致发票分类错误。
保存两个提示词和同一套断言
新建 promptfooconfig.yaml。YAML 缩进用空格。两版提示词引用同一个 {{text}},测试集不能随版本更换:
description: 客服分类提示词离线回归演示
prompts:
- |-
VERSION_A
将文本分为 refund、invoice、other,输出仅包含 category 的 JSON。
文本:{{text}}
- |-
VERSION_B
简短回复,只输出 category JSON。
文本:{{text}}
providers:
- file://mock_provider.cjs
tests:
- description: 退款诉求
vars:
text: 我要申请退款
expected: refund
- description: 发票诉求
vars:
text: 如何开具发票
expected: invoice
- description: 其他诉求
vars:
text: 营业时间是什么
expected: other
defaultTest:
assert:
- type: is-json
- type: javascript
value: |-
const obj = typeof output === 'string' ? JSON.parse(output) : output;
return Object.keys(obj).length === 1 &&
obj.category === context.vars.expected;
is-json 检查 JSON 格式,JavaScript 断言核对字段数与预期类别。若解析时报错,应检查原始输出是否夹带 Markdown 代码围栏,而不是删除格式断言迁就输出。可用断言类型及自定义规则见官方断言文档。不要用阈值 0 绕过全部断言。
运行并保存失败样例
npx promptfoo eval -c promptfooconfig.yaml --no-cache --output regression.json --no-table
npx promptfoo view
先检查终端,再打开本地结果页逐行比较。--no-cache 用于本轮重新运行;后续接真实模型时会增加调用成本。--output 和其他选项可查官方命令行说明。
本地实际运行得到 6 个测试结果:5 个通过、1 个失败、0 个执行错误,退出码为 1。三个输入分别与两版提示词组合,VERSION_A 三个通过,VERSION_B 的“如何开具发票”返回 {"category":"other"},与标注的 invoice 不一致。
只记“通过率 83.33%”不够,失败输入必须留下。若你已安装 Python,可保存下列脚本为 failed_cases.py,运行 python failed_cases.py:
import json
from pathlib import Path
report = json.loads(Path('regression.json').read_text(encoding='utf-8'))
rows = report['results']['results']
failed = [row for row in rows if not row.get('success', False)]
Path('failed-cases.json').write_text(
json.dumps(failed, ensure_ascii=False, indent=2), encoding='utf-8'
)
print('total:', len(rows), 'failed:', len(failed))
for row in failed:
print(row['testCase'].get('description'), row['response'].get('output'))
该脚本按 promptfoo 0.123.1 的实际 JSON 导出结构验证过,得到 total: 6 failed: 1,并提取发票诉求。升级版本后先打开导出文件检查层级,不要假设输出结构永久不变。
接入真实模型后,怎样判断改动可以采用?
离线模拟只验证配置、断言与报告流程,没有调用真实 LLM,也没有测提示词实际效果。接你的应用时,把 provider 换成真实入口或修改 callApi 调用应用,保留两个提示词、相同测试集和相同断言。不要让 provider 为了让测试通过而根据 expected 生成答案;expected 只能交给检查器。
保留模型标识、采样参数、提示词文件、应用版本及运行时间。用例增加真实的失败历史、模糊表达、空输入和多诉求输入,并先人工定义多诉求如何分类。真实模型可能输出不同结果,固定采样参数也不能保证完全确定;重复运行并复核偶发失败,比单次通过率更可靠。
只有旧版通过的关键用例没有新增失败、输出格式符合接口、预算和延迟满足要求,才考虑采用新版。模型报错、超时或凭证缺失应归为执行错误,不能记成模型正常拒答。语义正确性无法用关键词判断时,可补充人工评审或裁判,但要保存裁判依据与限制。回归测试也可与AI 编程任务评估配合,把每次修复发现的失败变成下一轮固定用例。
Ai菜鸟网。发布者:AI小管家,转载请注明出处:https://www.alyyhw.com/30518.html