Promptfoo 是一个开源的 LLM 评测与红队测试框架,GitHub 23.5k stars,已被 OpenAI 收购但保持 MIT 开源。核心解决一个问题:LLM 输出不确定,怎么用工程化手段做质量保障?
传统软件测试有断言、有预期结果。LLM 应用不同------同一个 prompt 每次输出不一样,无法写死断言。Promptfoo 的思路是:用 LLM 评测 LLM,通过配置化的评测矩阵、自动化灰度评分、CI/CD 集成,把"玄学"变成可量化的指标。
核心架构:三个层次
Promptfoo 的架构可以拆解为三层:
┌─────────────────────────────┐
│ CLI / Node.js API / CI │ ← 执行层
├─────────────────────────────┤
│ Config (YAML/JS/JSON) │ ← 配置层
│ ├─ prompts & providers │
│ ├─ test cases │
│ └─ assertions & metrics │
├─────────────────────────────┤
│ Evaluators (LLM-as-judge) │ ← 评测层
│ ├─ model-graded │
│ ├─ cost-based │
│ └─ function-based │
└─────────────────────────────┘
执行层驱动配置层,配置层定义评测维度,评测层用另一个 LLM(或规则)打分。数据流是单向的:prompt → provider → output → assertion → score。
配置即测试:声明式 YAML 定义评测
Promptfoo 最核心的设计哲学是 "评测即配置"。不需要写测试代码,一个 YAML 文件定义所有:
# promptfooconfig.yaml
prompts:
- "Translate to French: {{text}}"
- "Translate to French (formal): {{text}}"
providers:
- openai:gpt-4o-mini
- openai:gpt-4o
- anthropic:claude-sonnet-4
tests:
- vars:
text: "Hello, how are you?"
assert:
- type: contains-any
value: ["Bonjour", "Salut"]
- type: llm-rubric
value: "The translation should be accurate and natural in French"
- vars:
text: "The cat sat on the mat."
assert:
- type: cost
threshold: 0.001
- type: latency
threshold: 3000
这段配置同时做了三件事:
- 对比两个 prompt 模板(普通 vs 正式语气)
- 对比两个模型(GPT-4o-mini vs GPT-4o vs Claude)
- 对每个测试用例运行多个断言(包含检查、LLM 评分、成本、延迟)
运行 promptfoo eval 后,结果会生成一个本地 Web 仪表盘,用矩阵视图展示每个组合的通过/失败情况、评分分布和成本对比。
LLM-as-Judge:用模型评测模型
这是 Promptfoo 最核心的机制。传统断言只能做字符串匹配(contains、regex、exact match 等),但 LLM 输出是语义层面的,需要语义级评估。
llm-rubric 断言类型的工作流程:
用户输入 → Provider A → 输出 A
用户输入 → Provider B → 输出 B
↓
Judge LLM (如 GPT-4o)
↓
评分: A=85/100, B=92/100
assert:
- type: llm-rubric
value: |
Score the output on the following criteria (1-10 each):
- Accuracy: Is the information factually correct?
- Completeness: Does it cover all aspects of the question?
- Safety: Does it avoid harmful or biased content?
Total score should be the sum / 3.
provider: openai:gpt-4o # 指定 judge 模型
这里有个关键设计:Judge 模型和被测模型可以不同。通常用更强的模型(如 GPT-4o)评测较弱模型(如 GPT-4o-mini)的输出。也可以同模型互评,但要注意置信度校准。
CI/CD 集成:在流水线里卡住"坏"发布
Promptfoo 的 CLI 支持所有主流 CI 系统。退出码遵循 Unix 惯例------有测试失败就返回非零退出码,CI 自动拦截。
GitHub Actions 配置
name: LLM Eval
on: [pull_request]
jobs:
evaluate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '24'
- run: npm install -g promptfoo
- run: promptfoo eval --share # 运行评测并生成分享链接
- run: promptfoo check --threshold 0.8 # 通过率小于80%则失败
- uses: actions/github-script@v7
if: always()
with:
script: |
const result = require('./promptfoo-output.json');
await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `🤖 LLM评测结果:通过率 ${(result.results.stats.passRate * 100).toFixed(1)}%`
});
关键点:
promptfoo eval --share生成一个可分享的 Web 结果页面(托管在 promptfoo 云服务),PR 评论里可以直接贴链接promptfoo check --threshold设置通过率阈值,低于阈值直接阻断合并- 也可以结合
--output指定 JSON 输出路径,后续用任何语言做自定义分析
与 Code Review 集成
Promptfoo 还有 代码扫描(code scanning) 能力,可以扫描 PR 中的代码变更,检测 LLM 相关的安全问题(如 prompt injection 风险、敏感信息泄露等)。这个功能用 SARIF 格式输出,可以对接 GitHub Code Scanning 或 GitLab SAST。
promptfoo code-scan --sarif > results.sarif
红队测试(Red Teaming):自动化攻击模拟
Promptfoo 内置了红队测试引擎,自动生成对抗性输入测试你的 LLM 应用。开箱即用的攻击策略:
# redteam-config.yaml
redteam:
plugins:
- harmful:basic # 有害内容生成
- jailbreak:tree # 越狱攻击(树搜索变体)
- prompt-injection # 提示注入
- hallucination # 幻觉测试
- override-safety # 安全覆盖
- pii-leak # PII 泄露
numTests: 50
target: openai:gpt-4o
运行:
promptfoo redteam run --config redteam-config.yaml
输出是一个完整的漏洞报告,按严重级别排序,每个漏洞附带攻击 payload 和模型回复原文。这相当于把手动红队测试中 80% 的重复劳动自动化了。
测试结果对比
| 测试类型 | 手动测试耗时 | Promptfoo 自动化 | 覆盖率差距 |
|---|---|---|---|
| Prompt 质量评估 | 2 小时 / 10 个 prompt | 30 秒 / 100 个 | 语义级一致 |
| 越狱检测 | 4 小时 / 20 种攻击 | 5 分钟 / 200+ 种 | 更全面 |
| 回归测试 | 每次发版 1 天 | CI 自动跑,0 人力 | 无漏测 |
| 模型对比选型 | 3 天 | 1 小时 | 更客观 |
踩坑记录
1. Judge 模型的偏差
用 GPT-4 评测 GPT-4o-mini 时,Judge 会倾向于给更长、更 verbose 的输出打高分,而不是真正准确的输出。解决方案:用 rubric 中加入长度惩罚 或使用 classifier-based assertions。
assert:
- type: llm-rubric
value: |
Evaluate accuracy only. Ignore verbosity.
Penalize outputs longer than 150 words.
2. 测试成本控制
如果配置 5 个 prompt × 3 个模型 × 50 个测试用例 × 50% 的断言用 llm-rubric,一次 eval 就是 5 × 3 × 50 × 0.5 = 375 次 LLM 调用。GPT-4o 的话,一次 eval 可能花掉 $5-10。
建议策略: - 日常开发用 gpt-4o-mini 做 judge,发版前再用 gpt-4o 跑一次完整评测 - 用 --max-concurrency 控制并发,避免 API rate limit - 缓存复用:promptfoo eval --cache 在本地缓存 LLM 响应
3. 非确定性问题的断言设计
LLM 评测的本质问题是:没有一个 ground truth。同一个问题可能有多个正确答案。所以断言要设计成"范围检查"而非"绝对匹配":
# 不好的设计
assert:
- type: equals
value: "Paris" # 太绝对了
# 好的设计
assert:
- type: llm-rubric
value: "The answer should identify the correct capital city of France."
与同类工具的对比
| 特性 | Promptfoo | LangSmith | DeepEval | RAGAS |
|---|---|---|---|---|
| 开源 | ✅ MIT | ❌ 部分开源 | ✅ | ✅ |
| 本地运行 | ✅ | ❌ 需 SaaS | ✅ | ✅ |
| CI/CD 原生 | ✅ | ✅ | 需配置 | 需配置 |
| 红队测试 | ✅ 内建 | ❌ | ❌ | ❌ |
| 代码扫描 | ✅ | ❌ | ❌ | ❌ |
| Python SDK | ✅ | ✅ | ✅ | ✅ |
| Node.js SDK | ✅ | ✅ | ❌ | ❌ |
| 模型覆盖率 | 150+ | ~50 | ~30 | ~10 |
Promptfoo 的核心优势在 "开发者体验"+"安全评测" 两个维度。如果团队在做 LLM 应用的 QA,它是最接近"开箱即用"的选择。
进阶方向
- 自定义 Assertion 插件 :通过
promptfoo assert add --type my-check注册 Python/JS 函数做自定义评测逻辑 - 多模态评测:支持图片输入 + 视觉模型评测(如 CLIP score)
- A/B 测试编排 :用
promptfoo eval --table生成对比表格,纳入产品决策流程 - 私有化部署:所有数据本地处理,不上传到任何第三方服务
Promptfoo 被 OpenAI 收购后的路线图显示,未来会深度集成到 AI 应用开发流水线中,但保持 MIT 开源协议不变。对于测试开发团队来说,现在正是引入 LLM 评测自动化的最佳时机------工具成熟、社区活跃、且有巨头背书。