目录

Promptfoo:LLM 评测与 Red Teaming 实战指南

目标读者:AI 应用开发者、Prompt 工程师、测试工程师 核心问题:如何用测试驱动开发替代"调 Prompt 靠猜"? 难度:⭐⭐⭐(中级) 事实边界:本文所有配置示例和 API 均来自 promptfoo 官方文档和 GitHub 仓库,未经验证的功能不做展开。

一、项目概述

1.1 什么是 Promptfoo

Promptfoo(promptfoo/promptfoo)是一个开源的 LLM 评测和 Red Teaming 工具。它帮助开发者通过测试套件评估 Prompts、Models 和 RAG 系统,并扫描 LLM 应用的安全漏洞。

核心定位:从"调 Prompt 靠猜"到"用测试驱动开发"。

2026 年 Promptfoo 已加入 OpenAI,但项目仍保持 MIT 开源协议。

1.2 关键特性

特性说明
本地运行LLM 评测 100% 在本地执行,Prompt 不会离开你的机器
60+ 模型OpenAI、Anthropic、Google、Azure、AWS Bedrock、Ollama 等
YAML 配置声明式配置,高度可定制
Red Teaming自动化对抗测试,扫描安全漏洞
Web UI可视化查看评测结果
CI/CD集成 GitHub Actions 等持续集成流程
缓存自动缓存结果,加速迭代
扩展支持 JavaScript/Python 自定义 Provider 和断言

1.3 解决的问题

传统痛点Promptfoo 方案
Prompt 调优靠猜测试驱动开发,量化评估
模型对比困难统一配置,多模型并排对比
RAG 效果难测内置 RAG 评测指标(事实性、相关性、忠实度)
上线后才发现问题CI/CD 集成,自动化测试
安全漏洞难发现Red Teaming 自动化扫描

二、快速开始

2.1 安装

# 使用 npx(无需安装)
npx promptfoo@latest eval

# 或全局安装
npm install -g promptfoo

# 或使用 Homebrew
brew install promptfoo

# 或使用 pip
pip install promptfoo

2.2 运行示例

# 初始化示例项目
npx promptfoo@latest init --example getting-started

# 进入目录,运行评测
cd getting-started
npx promptfoo@latest eval

# 查看结果
npx promptfoo@latest view

2.3 交互式配置

# 通过 CLI 向导创建配置
npx promptfoo@latest init

# 或通过 Web UI 配置
npx promptfoo@latest eval setup

2.4 环境变量

大多数 Provider 需要 API Key:

export OPENAI_API_KEY=sk-abc123
export ANTHROPIC_API_KEY=sk-ant-xxx

三、配置详解

Promptfoo 使用 promptfooconfig.yaml 作为核心配置文件。

3.1 最小配置

# promptfooconfig.yaml
prompts:
  - 'Convert the following English text to {{language}}: {{input}}'

providers:
  - openai:gpt-4o

tests:
  - vars:
      language: French
      input: Hello world
    assert:
      - type: contains
        value: 'Bonjour le monde'

3.2 配置结构

字段类型必填说明
promptsstring[]Prompt 列表,支持 {{variable}} 变量
providersstring[]LLM Provider 列表
testsTest[]测试用例列表
defaultTestTest默认测试配置(如全局断言)
outputPathstring输出文件路径
evaluateOptionsobject评测选项(并发数、超时等)
extensionsstring[]扩展钩子文件列表
envobject环境变量覆盖

3.3 Provider 格式

Provider 使用简洁的字符串标识符:

providers:
  # OpenAI
  - openai:gpt-4o
  - openai:gpt-4o-mini

  # Anthropic
  - anthropic:messages:claude-sonnet-4-20250514

  # Google
  - google:gemini-2.5-pro

  # 本地模型
  - ollama:chat:llama3

  # 自定义 Provider(JS/Python 文件)
  - file://path/to/custom_provider.py

也支持带配置的对象格式:

providers:
  - id: openai:gpt-4o
    config:
      temperature: 0.7
      max_tokens: 1024

3.4 Prompt 格式

Prompt 支持多种写法:

prompts:
  # 内联字符串
  - '翻译以下文本为{{language}}:{{input}}'

  # 外部文件
  - file://prompts/translation.txt

  # 带标签的对象
  - id: file://prompts/v1.txt
    label: 简洁版
  - id: file://prompts/v2.txt
    label: 详细版

四、评测场景

4.1 Prompt 对比评测

对比不同 Prompt 变体的效果:

prompts:
  - '翻译:{{input}}'
  - '你是一个专业翻译。请将以下{{language}}文本翻译成{{target_language}},保持原意并符合目标语言习惯:{{input}}'

providers:
  - openai:gpt-4o

tests:
  - vars:
      input: "The quick brown fox"
      language: English
      target_language: 中文
    assert:
      - type: contains
        value: 快速

运行后,Web UI 会并排展示两个 Prompt 在同一组测试上的输出,方便对比。

4.2 模型对比评测

对比不同模型的表现:

prompts:
  - '解释量子计算的基本原理。'

providers:
  - openai:gpt-4o
  - anthropic:messages:claude-sonnet-4-20250514
  - google:gemini-2.5-pro

tests:
  - assert:
      - type: contains-any
        value: ["量子", "叠加", "纠缠"]

也可以通过命令行临时替换 Provider:

npx promptfoo@latest eval -r google:gemini-2.5-pro google:gemini-2.5-flash

4.3 RAG 评测

评估检索增强生成系统的质量:

providers:
  - id: 'http://localhost:3000/api/rag'
    config:
      transformResponse: 'json.data'

prompts:
  - '根据以下上下文回答问题。\n\n上下文:{{context}}\n\n问题:{{question}}'

tests:
  - vars:
      question: 什么是向量数据库?
      context: 向量数据库是一种专门存储和检索向量表示的数据库系统...
    assert:
      - type: contains
        value: 向量
      - type: context-faithfulness
        contextTransform: 'output.sources.map(s => s.content).join("\n")'
        threshold: 0.9

RAG 评测的关键指标包括:事实性(factuality)、答案相关性(answer relevance)、上下文召回(context recall)、上下文相关性(context relevance)、上下文忠实度(context faithfulness)。

4.4 Agent 评测

评测 Agent 工作流:

npx promptfoo@latest init --example openai-agents-basic

Agent 评测关注任务完成度和轨迹正确性(trajectory)。


五、断言系统

断言是 Promptfoo 的核心机制,用于自动检查 LLM 输出是否满足预期。

5.1 常用断言类型

类型说明示例
contains包含指定文本(区分大小写)value: "风险"
icontains包含指定文本(不区分大小写)value: "risk"
contains-any包含任一关键词value: ["风险", "问题"]
contains-all包含所有关键词value: ["优点", "缺点"]
not-contains不包含文本value: "错误"
regex正则匹配value: "^\\d+$"
equals完全相等value: "expected"
is-json输出是合法 JSON
similar语义相似度(需 Provider)value: "预期输出", threshold: 0.8
llm-rubricLLM 评分(需 Provider)value: "评分标准"
javascript自定义 JS 逻辑value: "output.length > 100"
python自定义 Python 逻辑value: "len(output) > 100"
cost检查 API 调用成本threshold: 0.01
perplexity困惑度检查threshold: 10
context-faithfulness上下文忠实度threshold: 0.9
context-relevance上下文相关性threshold: 0.8

5.2 断言配置示例

tests:
  - vars:
      question: 什么是机器学习?
    assert:
      - type: contains
        value: 算法
      - type: not-contains
        value: 我不知道
      - type: similar
        value: 机器学习是人工智能的一个分支,通过数据训练模型
        threshold: 0.7
        provider: openai:gpt-4o-mini
      - type: javascript
        value: output.length >= 50 && output.length <= 500

5.3 自定义 JavaScript 断言

tests:
  - vars:
      input: 请列出三种编程语言
    assert:
      - type: javascript
        value: |
          const languages = ['python', 'javascript', 'java', 'go', 'rust', 'c++', 'typescript'];
          const output_lower = output.toLowerCase();
          const count = languages.filter(l => output_lower.includes(l)).length;
          return { pass: count >= 3, score: count / 3 };

5.4 自定义 Python 断言

tests:
  - vars:
      input: 计算斐波那契数列第10项
    assert:
      - type: python
        value: |
          import re
          numbers = re.findall(r'\d+', output)
          pass_test = '55' in numbers
          return {"pass": pass_test, "score": 1.0 if pass_test else 0.0}

5.5 默认断言与阈值

defaultTest:
  assert:
    - type: not-contains
      value: "Error"
    - type: not-contains
      value: "我无法"

tests:
  - vars:
      question: 你好
    assert:
      - type: contains
        value: 你好
    threshold: 0.5

threshold 设置测试的最低通过分数,低于此值则测试失败。


六、Red Teaming

Promptfoo 内置 Red Teaming 功能,用于扫描 LLM 应用的安全漏洞。

6.1 快速开始

# 初始化 Red Team 配置
npx promptfoo@latest redteam

# 运行 Red Team 扫描
npx promptfoo@latest redteam eval

Red Teaming 会自动生成各种对抗性输入,测试你的 LLM 应用是否存在安全漏洞。

6.2 扫描范围

Red Teaming 覆盖的安全类别包括:

类别说明
Prompt 注入尝试覆盖系统指令
越狱(Jailbreak)尝试绕过安全限制
数据泄露尝试提取训练数据或系统提示
PII 泄露检测是否泄露个人信息
有害内容测试是否生成有害输出

6.3 Red Team 配置

Red Team 通过交互式向导生成配置,也可手动编辑 promptfooconfig.yaml 中的 redteam 部分。配置完成后,运行 npx promptfoo@latest redteam eval 即可启动扫描。

扫描结果会在 Web UI 中以安全报告形式展示,标注每个漏洞的严重程度。


七、Web UI

7.1 启动与使用

# 查看最近一次评测结果
npx promptfoo@latest view

# 通过 Web UI 配置评测
npx promptfoo@latest eval setup

7.2 核心功能

功能说明
结果概览测试通过率、评分分布
对比视图多模型/多 Prompt 并排对比
详情查看完整输入/输出/断言结果
导出CSV、JSON、YAML、HTML 格式

八、扩展机制

8.1 自定义 Provider

通过 JavaScript 或 Python 文件实现自定义 Provider:

// custom_provider.js
async function callApi(prompt, context) {
  const response = await fetch('https://your-api.com/generate', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ prompt }),
  });
  const data = await response.json();
  return {
    output: data.text,
    tokenUsage: { total: data.tokens },
  };
}

module.exports = { callApi };

在配置中引用:

providers:
  - file://custom_provider.js

8.2 扩展钩子

Promptfoo 支持在评测生命周期的特定节点执行自定义代码:

钩子触发时机用途
beforeAll评测开始前初始化、添加测试用例
afterAll评测结束后汇总报告、清理
beforeEach每个测试前修改变量、创建会话
afterEach每个测试后日志记录、清理会话
// extension.js
async function extensionHook(hookName, context) {
  if (hookName === 'beforeAll') {
    console.log('评测开始');
  } else if (hookName === 'afterEach') {
    console.log(`测试完成: ${context.test.description}, 通过: ${context.result.success}`);
  }
}

module.exports = extensionHook;

配置中引用:

extensions:
  - file://extension.js:extensionHook

8.3 输出转换

对于返回非标准格式的 API,可以通过 transformResponse 转换:

providers:
  - id: 'http://localhost:3000/api'
    config:
      transformResponse: 'json.data.answer'

测试级别也可以转换输出:

tests:
  - vars:
      query: 退款政策是什么?
    options:
      transform: 'output.answer'
    assert:
      - type: contains
        value: 30天

九、CI/CD 集成

9.1 GitHub Actions

# .github/workflows/llm-eval.yml
name: LLM Evaluation

on:
  push:
    paths:
      - 'prompts/**'
      - 'promptfooconfig.yaml'
  pull_request:

jobs:
  evaluate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Install Promptfoo
        run: npm install -g promptfoo

      - name: Run Evaluation
        run: promptfoo eval --config promptfooconfig.yaml --output results.json
        env:
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}

      - name: Upload Results
        uses: actions/upload-artifact@v4
        with:
          name: promptfoo-results
          path: results.json

9.2 命令行选项

# 运行评测
promptfoo eval

# 指定配置文件
promptfoo eval -c path/to/config.yaml

# 覆盖 Provider
promptfoo eval -r openai:gpt-4o openai:gpt-4o-mini

# 过滤测试
promptfoo eval --filter-pattern "auth.*"

# 控制并发
promptfoo eval --max-concurrency 10

# 禁用缓存
promptfoo eval --no-cache

# 输出格式
promptfoo eval --output results.json
promptfoo eval --output results.csv
promptfoo eval --output results.html

十、适用场景与边界

10.1 适合的场景

场景说明
Prompt 开发对比多个 Prompt 变体,量化评估效果
模型选型在真实任务上评测多个模型,数据驱动决策
RAG 优化评测检索策略、块大小、向量搜索参数
安全加固Red Teaming 扫描 Prompt 注入、越狱等漏洞
回归测试CI/CD 集成,确保 Prompt 变更不引入退化

10.2 边界与注意事项

边界说明
不是模型训练工具Promptfoo 评测和测试 LLM 输出,不训练模型
评测依赖断言质量断言设计不合理会导致误判,需持续优化
语义类断言有成本similarllm-rubric 等需要额外 LLM 调用
本地运行为主评测在本地执行,大规模并发需注意 API 限流
Red Teaming 不是渗透测试它是自动化扫描,不能替代专业安全审计

十一、常见问题

Q: 支持本地模型吗?

支持。配置 Ollama、LM Studio 或 LocalAI:

providers:
  - ollama:chat:llama3

Q: 如何减少 API 成本?

  1. 使用缓存(默认开启):promptfoo eval 自动缓存已运行的测试
  2. 使用 --filter-pattern 只运行相关测试
  3. 语义类断言使用更小的模型:provider: openai:gpt-4o-mini
  4. 设置 --max-concurrency 控制并发

Q: 如何在测试间共享变量?

使用 defaultTest 设置全局断言和变量:

defaultTest:
  assert:
    - type: not-contains
      value: "Error"

tests:
  - vars:
      input: 你好

Q: 支持多轮对话测试吗?

支持。通过 options.storeOutputAs 将前一轮输出存储为变量,供后续测试使用。


相关链接