先承认一个事实:大多数「调优」没有基线
改一句提示词、换一个模型版本,功能看起来更好了——但「更好」是拿什么比的?如果只是随手试了两条输入,那既说明不了变好,也发现不了变差。模型行为是分布,不是开关;没有一组固定用例,「感觉变差了」就永远只能是感觉,团队也只能靠回滚和争论来收场。
promptfoo 解决什么
promptfoo 把评测写成声明式配置:一份 YAML 里列出用例(输入与期望)、断言(怎么判通过)和待测对象(不同提示词、不同模型)。一条命令跑完,输出一张横向对照表,可以直接看逐条结果,也能接进 CI;红队与漏洞扫描作为另一类用例一起跑,用它检查提示注入、越权之类的边界。
# 快速起手
npx promptfoo@latest init
npx promptfoo@latest eval
npx promptfoo@latest view # 打开本地结果面板,逐条对照
# 在 CI 里跑,通过率不达标就失败
npx promptfoo@latest eval --ci --output results.json第一步:把散落的用例收成一份配置
prompts:
- file://prompts/support.txt
providers:
- openai:gpt-6
- anthropic:claude-opus-4.5
tests:
- vars:
question: "订单 12345 什么时候到?"
assert:
- type: contains
value: "物流"
- type: not-contains
value: "我无法"
- type: is-json用例不用多,先覆盖真实业务里最容易出错的三四类:格式被破坏、长尾事实答错、该拒绝时没拒绝、以及多语言或术语。把这些写成 vars 与 assert,跑得起来比写得全重要;后续再按线上真实失败样本往里加。
第二步:跑一次基线,把「现在的水平」固定下来
第一次跑的目的不是变好,是留下一个可对照的起点:记录通过率、逐条失败项、耗时与 token 成本。这份基线和配置一起进版本管理,之后每次改动都与它比,而不是与记忆比。凡是「我觉得更好了」,都要能落到这张表上。
该断言什么,不该断言什么
- 优先用规则断言:包含 / 不包含、正则、JSON Schema、耗时上限,确定性高、可复现
- 主观质量别只看总分:如果要用模型评分,就固定评分模型与评分提示词的版本,并保留逐条理由
- 别只测顺风用例:空输入、超长输入、恶意输入、多语言都要进集合
- 用例要能公开复现:脱敏后放进仓库,否则换个人就跑不了
- 红队扫描当成一类用例:不是一次性的安全审计,而是每次发布前都跑一遍
边界与几个坑
- 它评的是你写的用例,不等于「功能好不好」:用例设计偏了,结论就偏
- 模型评分本身有噪声:同一份输出跑两次可能不同,多次取平均并记录波动
- 别拿评测集当训练集:反复针对同一批用例调优会过拟合,要留一批没参与调优的样本做最终验收
- 密钥不要写进配置:用环境变量或 provider 的密钥管理
一句话:把验收从「点几下试试」换成「跑一条命令」,团队才有资格说这个功能是变好了,还是变差了。