别再靠感觉选模型:用 Promptfoo 把 LLM 应用的评测、红队与 CI 变成可重跑流程
别再靠感觉选模型:用 Promptfoo 把 LLM 应用的评测、红队与 CI 变成可重跑流程
我认为,LLM 应用从 prototype 走到 production 之后,最先暴露的通常不是「模型不够聪明」,而是团队没有一套稳定的方式回答三个问题:这次改动有没有让回答变好?它是不是只在少数案例变好?安全性与成本是否因此变差?如果每次都靠人工打几个 prompt、看几个结果,再凭印象决定要不要合并,这个流程很快就会失去可重现性。
Promptfoo 是一个开源的 CLI 与 library,定位是评估与 red teaming LLM 应用。它把 prompt、provider、测试资料、assertion,以及结果检视整合在同一个工作流中;官方文件也提供 CI/CD、code scanning 与 vulnerability scanning 的入口。对我来说,它最值得注意的地方不是「又一个 prompt 工具」,而是它把原本散落在聊天视窗、试算表与人工审查中的判断,逐步变成可以版本控制、可以重跑、可以在 Pull Request 中检查的工程资产。
先讲结论:Promptfoo 解决的是「变更之后,如何相信结果」
如果你的团队只是偶尔比较两个模型,直接呼叫 API 再人工阅读输出,Promptfoo 可能显得太正式。但只要应用开始有多个 prompt、多个模型、不同供应商、固定业务案例,或每次部署都可能改变输出,就需要把「评测」从一次性实验提升成回归测试。
Promptfoo 的基本单位可以理解成四层:
1. Prompts:要测试的提示词,可以是内嵌文字,也可以放在档案中。
1. Providers:要比较的模型或 LLM API。官方文件展示了 OpenAI 等 provider,也支援更多模型服务与本机模型整合。
1. Tests:输入变数与预期条件,让同一批案例可以重复跑。
1. Assertions:对输出做判断,例如是否包含某段文字、是否符合结构、是否通过模型评分,或是否触发安全性条件。
这四层的价值在于,模型不再是唯一被评估的东西。你可以固定同一批测试案例,比较不同 prompt;也可以固定 prompt,比较不同 provider;更可以把输出品质与安全检查放到同一个变更流程里。当结果变差时,至少有一个可追溯的差异,而不是只剩下「我觉得新版比较怪」。
为什么传统的人工试 prompt 很快会失效
人工测试不是没有价值,问题是它很难成为唯一的品质门槛。第一个问题是案例漂移:今天测的是三个简单问题,下一次换了资料来源、语言或上下文,却还拿今天的印象当作判断。第二个问题是比较不公平:如果不同模型看到的 prompt、参数或输入不完全相同,最后的优劣很可能只是测试设计造成的。
第三个问题是负面案例常被忽略。正常使用者问「请摘要这篇文章」时,所有模型看起来都不错;但当输入混入越权要求、敏感资料、提示注入或不完整条件时,差异才会浮现。第四个问题是部署节奏:人工检查可以帮你发现一次问题,却不会自动在下一次 commit 时重现同一个检查。
因此,我会把 Promptfoo 放在「模型呼叫」与「产品测试」中间。它不是取代产品端的整合测试,也不是保证模型永远正确的魔法,而是提供一个专门处理 LLM 输出不稳定性的评测层。这一层的任务,是让团队能够用同一批案例持续观察品质、差异与风险。
实际开始:先建立一个最小可验证的 eval
官方 Quick Start 要求使用 Node.js >=22.22.0,并建议使用 Node.js 24 LTS。可以先用 npx,不必把 CLI 安装成全域工具;若团队已经固定使用全域安装,也可以用 npm 或 Homebrew 安装。下面的流程刻意不把任何真实凭证写进档案,API key 应该由环境变数或 CI secret 管理。
npx promptfoo@latest init --example getting-started
cd getting-started
export OPENAI_API_KEY="$YOUR_PROVIDER_API_KEY"
npx promptfoo@latest eval
npx promptfoo@latest view
这四个步骤各自有清楚的责任。init 建立可修改的范例目录;设定环境变数让 provider 能够呼叫模型;eval 执行评测;view 开启结果检视介面。第一个成功标准不是「模型回答得像人」,而是你可以在另一台干净环境,以相同设定重跑并得到一份可比较的结果。
接着可以把设定收敛成一个很小的 promptfooconfig.yaml。以下范例使用官方文件中的设定概念,让两个 prompt 对同一个输入进行比较:
description: customer support answer evaluation
prompts:
- file://prompt1.txt
- file://prompt2.txt
providers:
- openai:gpt-5-mini
tests:
- vars:
question: "退款需要哪些资料?"
assert:
- type: contains
value: "退款"
实际使用时,provider 名称应依官方 provider 文件与你目前可用的模型调整;不要把范例中的模型名称视为永远不变的产品承诺。测试案例也不该只用一题。我的建议是先建立三组资料:一组代表最常见的正常输入,一组代表容易出错的边界输入,另一组则代表你不希望系统泄漏或执行的请求。这样即使第一版只有几十笔,也比单次手动示范更接近可以持续维护的品质基线。
Assertion 不只是「有没有包含关键字」
最简单的 assertion 是检查输出是否包含或不包含某段文字,适合验证明确的产品规则,例如回答必须提到退款期限、不得出现内部栏位名称。然而,关键字只适合处理表面条件,不代表内容真的正确。像「必须给出三个步骤」这种要求,即使文字包含三个序号,也不代表步骤顺序、资讯完整度与事实都合格。
所以我会把 assertion 分成三个层次。第一层是 deterministic checks:字串、JSON 结构、正规表达式、长度与禁止内容。它们快速、便宜、容易在 CI 中稳定执行。第二层是语意评分:把输出交给评分器,检查相关性、完整性、语气或与参考答案的相似程度。它比较能处理开放式回答,但需要留意评分器本身的偏差与不稳定性。第三层是 domain review:由熟悉业务的人定期检视测试案例与失败样本,确认指标仍然对产品有意义。
这三层不能互相取代。只做关键字检查,容易让团队得到虚假的安全感;只做 LLM judge,则可能把不稳定的判断再交给另一个模型。比较务实的做法,是把便宜且确定的规则放前面,把昂贵的语意检查留给真正需要的案例,再用人工抽样校准结果。
从 eval 走向模型比较:先定义成本与风险,再看分数
Promptfoo 很适合做 side-by-side model comparison,但「分数最高」不应该直接等于「最适合上线」。模型选择至少要同时看四件事:品质、延迟、成本与失败型态。
品质可以由测试通过率、评分器分数与人工抽样组成;延迟要看平均值之外的尾端表现,因为使用者通常会感受到慢的那几次;成本不能只比较单价,还要看 prompt 长度、重试次数与输出长度;失败型态则要看模型是在不知道答案时诚实拒答,还是用流畅文字补出错误内容。
我会把这些资讯放进版本控制的评测报告,而不是只截一张 dashboard 图。每次改 prompt 或换 provider 时,至少记下测试资料版本、设定档、执行日期与通过/失败案例。当某个模型在平均分数上略胜,但在敏感案例中有更高的错误率时,这个差异才不会被漂亮的总分掩盖。
还有一个常被忽略的细节:快取与非决定性。官方 README 把 caching 与 live reload 列为 developer-first 特性,但快取会影响你看到的结果,随机性也会让同一案例多次执行产生不同输出。因此,在评测规格中要明确记录哪些结果可以重用、哪些测试需要重跑,以及如何处理边界分数。可重跑不代表每次输出逐字相同,而是代表条件、案例与判断方法一致。
Red teaming:把安全检查放进同一条管线
品质评测回答「正常情况下好不好」,red teaming 则是在问「被刻意挑战时会不会失守」。Promptfoo 官方提供 red teaming 与 vulnerability scanning 文件入口,也能在 CLI 中执行 redteam run。这使安全测试不必停留在一次性的人工攻击演示,而可以成为定期执行的检查。
npx promptfoo@latest redteam run --config promptfooconfig.yaml
实际红队测试要先定义范围。哪些资料不可被泄漏?哪些工具呼叫必须拒绝?哪些角色权限不可被输入内容改写?哪些输出格式若破坏就会造成下游系统误动作?如果没有先写出这些政策,扫描结果很容易变成一串看似严重、却无法排序的 finding。
我会把结果分成至少三类:可接受的拒绝、需要人工检视的模糊案例、以及应该阻挡部署的高风险案例。对每个高风险案例,都应保留最小化的重现输入与修正后的回归测试。这里要特别注意资料治理:测试用的敏感内容应使用去识别化或合成资料,报告与 CI log 也不能把 API key、个人资料或完整机密 prompt 原样印出。
另外,红队工具不是安全保证书。它能帮你扩大攻击面与测试覆盖率,但不会自动知道你的授权模型、资料保留规则或业务风险。安全团队仍需要审查测试政策、确认 finding 的严重度,并检查模型之外的 API、资料库与工具执行层。
接到 CI/CD:让 Pull Request 先回答「这次变更有没有退步」
当评测设定与测试资料进入 repository,下一步就是在 CI 中执行。官方 CI/CD 文件展示了使用 npx promptfoo@latest eval 输出结果,以及执行 redteam run 的方式。最小化的 pipeline 可以长这样:
npx promptfoo@latest eval -c promptfooconfig.yaml -o results.json
npx promptfoo@latest redteam run promptfooconfig.yaml
但不要一开始就把所有测试、所有模型、所有红队案例都放进每个 Pull Request。比较稳健的分层方式是:快速 smoke eval 在每个 PR 执行;完整回归测试在合并到主分支或排程执行;成本较高的模型比较与深度红队扫描则在夜间或发布前执行。这样既能维持回馈速度,也不会因为 CI 太慢而被团队绕过。
CI 的失败门槛也应该与测试类型对应。确定性 assertion 失败,可以直接阻挡;语意评分接近门槛时,可以标记为需要人工检视;外部 provider 暂时不可用,则要和真正的产品退化分开记录。否则一个供应商的短暂网路错误,就可能让团队把错误方向当成 prompt 回归问题。
在安全上,CI secret 只应注入执行环境,不应放在 promptfooconfig.yaml、测试输出或 issue comment。结果分享也要先确认是否含有使用者输入、模型输出或内部 system prompt。评测自动化的终点不是「每次都把全部结果公开」,而是让正确的人在正确的权限下看到足够的证据。
哪些地方不要过度期待
第一,Promptfoo 不能替你定义「好回答」。它提供执行与比较能力,但测试资料、assertion、评分门槛与风险分类仍需由产品与工程团队负责。垃圾测试资料只会产生更自动化的错误结论。
第二,LLM judge 不是绝对客观的裁判。评分 prompt、参考答案、模型版本与上下文都可能影响结果。重要功能应保留 deterministic checks 与人工抽样,不要让单一分数成为唯一上线依据。
第三,外部 provider 与本机环境会影响可重现性。模型更新、服务限制、网路错误、速率限制与成本变化,都可能使一次评测失败。报告中应记录 provider、模型识别、设定与执行环境;必要时使用固定版本或建立容错重试策略。
第四,CLI 的快速上手不等于完整治理。正式导入还需要处理测试资料版本、输出保存期限、权限控管、PII 遮罩、secret 管理、失败案例 triage 与变更审批。这些是产品生命周期的一部分,不是安装工具之后自然得到的功能。
我会怎么安排第一周导入
第一天只选一个高价值流程,例如客服摘要或知识库问答,建立十到二十个正常案例与五个边界案例。第二天把 prompt、provider 与测试资料放入设定档,先只做 deterministic assertions,确认团队知道怎么阅读结果。第三天加入一个语意品质检查,并用人工抽样比较评分器与专家判断是否一致。
第四天整理三到五个安全政策,加入最小化的 red-team 测试,不追求一次涵盖所有攻击。第五天把 smoke eval 接到 Pull Request,让团队实际经历一次通过与一次失败;失败之后要能找到案例、理解原因、修正设定,并把回归案例留下来。第一周的成功标准不是测试数量,而是大家开始用同一套证据讨论模型与 prompt 的变更。
之后再逐步加入模型比较、完整 CI、排程扫描与团队报告。当测试套件累积到一定规模,要定期删除失去辨识力的案例,合并重复案例,并重新检查门槛。评测套件和产品程式一样会腐化;只增加测试、不维护测试,最后仍然会变成没人信任的仪表板。
结语:把「感觉变好」改成「证据足够」
Promptfoo 的核心价值,不是替团队选出一个永远最好的模型,而是把模型与 prompt 的变更放进一条可检查的工程流程。你可以从 npx promptfoo@latest init --example getting-started 开始,用一小组案例建立基线,再把 assertion、模型比较、red teaming 与 CI/CD 逐层加上去。
我会把它推荐给已经有 LLM 应用、开始频繁改 prompt 或需要在多个 provider 之间做选择的团队。若目前还没有稳定的测试资料、没有定义产品品质,或只是想找一个按钮保证模型安全,先不要急着导入;先把成功条件与风险政策写清楚,工具才有可以执行的对象。真正成熟的做法不是追求一个漂亮分数,而是每次变更都能说明:我们测了什么、哪里变好、哪里退步,以及为什么现在仍然敢发布。