跳至内容
人工智能
大语言模型
生成式 AI

Promptfoo 教程:LLM 评估实操指南

借助 Promptfoo 将临时的提示词检查转化为结构化的 LLM 评估,从本地测试套件到自动化 CI,更快构建可靠的 AI 应用。
Bexruz (Bex) Tuychiev's photo
Bexruz (Bex) Tuychiev
已更新 · 12分钟 阅读

使用 AI 探索

ChatGPTClaudePerplexity

创建您的免费账户

实践并学习路径你的进步。

使用 Google 继续
继续操作即表示您接受我们的《使用条款》和《隐私政策》,并同意您的数据存储在美国。

大多数 LLM 功能的测试方式都一样:试几个输入,目测输出,还行就上线。

您曾用这种方法测试邮件写作器。在聊天窗口里试了五个输入,看起来都不错。一周后,一半“轻松随意”的邮件听起来却像出自公司法务。没人发现,因为根本无从发现。提示词变了,但那五个手动测试并没有跟着变。

Promptfoo 是一个开源 CLI,可用结构化、可复现的评估替代上述流程。您定义“好输出”的标准,选好模型,然后自动运行所有组合。 

OpenAI 已于 2026 年 3 月收购该项目,项目仍以 MIT 许可发布,并支持数十家提供商。超过 35 万名开发者在用,其中包括 25% 以上《财富》500 强公司的团队。

本教程将带您从零开始设置 Promptfoo,并构建您的第一个评估套件。我们将以邮件写作器为贯穿示例,在 GPT-5 与 Claude Sonnet 4.6 上测试,并在最后接入 GitHub Actions。

60 秒看懂 LLM 评估

在开始用工具前,先理解 LLM 测试如何工作。它不同于常规代码测试。

测试函数时的套路是:给定输入,检查输出是否与预期一致。LLM 的输出不是这样。同一个提示词每次运行都可能生成不同文本,因此无法做精确匹配检查。

取而代之,您需要检查输出的“属性”:

  • 是否包含正确信息?
  • 语气是否合适?
  • 响应是否足够快?

这正是LLM 评估要做的事。它用一组输入运行您的提示词,并依据您定义的规则逐一检查输出。把它看作是“提示词”的测试套件,而非“代码”的测试套件。

本文将反复出现四个术语:

  • 提供商(provider):您要测试的模型 API,例如 GPT-5.4 或 Claude Opus 4.6。
  • 测试用例(test case):一个输入与其期望行为的配对。
  • 断言(assertion):输出必须通过的一条规则,例如“包含单词 Friday”或“30 秒内响应”。
  • 评分标准(rubric):当检查过于主观、无法用字符串匹配时,您提供给另一个 LLM 的英文打分指令,例如判断邮件是否“真的”听起来很随意。

没有断言,您只能说“我觉得可以”;有了断言,您就有了可重复运行的“正确性”定义。

Promptfoo 是什么?

了解评估是什么之后,再看 Promptfoo 如何发挥作用。

您需要提供三样东西:

  • 您的提示词模板
  • 要测试的模型
  • 带有断言的测试用例

它会对每个测试用例,将每个提示词在每个模型上运行并打分。一个命令 promptfoo eval 即可跑完整套评估。

假设您有一个邮件写作提示词、两个模型(GPT-5 和 Claude Sonnet 4)以及三个测试用例(随意、正式、紧急)。Promptfoo 会跑完六种组合,并告诉您哪些通过、哪些失败。无需再逐个手动尝试。

How Promptfoo runs every prompt against every model for every test case and scores the results

整个评估都写在一个名为 promptfooconfig.yaml 的 YAML 文件里,和代码一起纳入版本控制。Promptfoo 在本机运行:配置、结果和缓存都保留在本地。唯一的外部调用是到 OpenAI 或 Anthropic 等模型 API,不管有没有 Promptfoo,您都会发这些请求。

该领域还有其他工具,如 DeepEval(原生 Python、pytest 风格)、LangSmith(面向 LangChain 的生产监控)和 Braintrust(团队看板)。Promptfoo 是最佳起点,因为它免费、本地运行,并且能以最快速度让您从零到可用评估。

设置 Promptfoo 环境

全局安装 Promptfoo 并初始化新项目:

npm install -g promptfoo
mkdir email-writer-eval
cd email-writer-eval
promptfoo init

init 命令会引导交互式配置。它会询问您想做什么(选择 "Not sure yet"),以及使用哪个模型提供商(选择 "[OpenAI] GPT 5, GPT 4.1, ...")。

promptfoo init step 1: choosing what you'd like to do

promptfoo init step 2: choosing a model provider

完成后,您将看到两份文件:一个示例 promptfooconfig.yaml 和一个 README.md。

promptfoo init complete: files generated and next steps

接下来设置 API 密钥。至少需要一个才能运行评估(若要完整跟随本教程,建议两者都配置):

export OPENAI_API_KEY=sk-...
export ANTHROPIC_API_KEY=sk-ant-...

如果您只设置了 ANTHROPIC_API_KEY 而未配置 OpenAI,Promptfoo 会在 llm-rubric 等模型辅助断言中自动使用 Claude 作为评分提供商。

打开生成的 promptfooconfig.yaml。每个 Promptfoo 配置都有三大模块:

  • prompts 用于放置提示词模板。像 {{variable}} 这样的双花括号占位符会从各测试用例中填充。

  • providers 列出要测试的模型。

  • tests 定义输入以及判定输出通过/失败的断言。

prompts:
  - \"Your prompt template with {{variable}}\"
...
 
providers:
  - openai:chat:gpt-5
...
 
tests:
  - vars:
      variable: \"test input\"
    assert:
      - type: contains
        value: \"expected substring\"

这个示例配置只是起点。下一节我们将用一个真实评估替换它,并深入讲解 YAML 结构。

构建您的第一个评估

任务如下:给定要点和语气(随意、正式或紧急),写一封邮件。您将构建一个评估,在两个模型上测试这一需求。

如果想一次性查看完整配置,可在 Gist 获取。我们将在下文逐步搭建。

提示词与提供商

删除生成的示例,新建 promptfooconfig.yaml。从提示词与提供商开始:

description: \"Email writer evaluation\"
 
prompts:
  - |
    Draft an email based on these bullet points.
    Match the specified tone throughout the email.
 
    Bullet points:
    {{bullet_points}}
 
    Tone: {{tone}}
 
providers:
  - id: openai:chat:gpt-5
    label: \"GPT-5\"
  - id: anthropic:messages:claude-sonnet-4-6
    label: \"Claude Sonnet 4.6\"

该提示词模板包含两个占位符:{{bullet_points}} 与 {{tone}}。每个测试用例会用不同值填充它们。每个提供商上的 label 字段用于在结果视图中显示可读的列标题,而不是原始模型 ID。

defaultTest 模块

接着添加 defaultTest 模块。放在 defaultTest 中的断言会自动应用到所有测试用例,避免重复:

defaultTest:
  assert:
    - type: latency
      threshold: 30000

该设置会判定任何超过 30 秒的响应为失败。类似 GPT-5 这样的前沿模型可能因推理 token 耗时,每次请求需要 10–20 秒,因此阈值需要留有余量。此处只需设置一次,所有测试都会继承。

测试用例

现在添加测试用例。每个用例提供不同输入与其专属断言:

tests:
  - vars:
      bullet_points: |
        - Recap of the design review decisions
        - Next steps: finalize mockups by Thursday
        - Ask if anyone has questions
      tone: \"casual\"
    assert:
      - type: icontains
        value: \"mockups\"
      - type: llm-rubric
        value: \"The email uses a casual tone with contractions and short sentences\"
 
  - vars:
      bullet_points: |
        - Q1 revenue exceeded targets by 12%
        - New enterprise client onboarded
        - Hiring plan for Q2 approved
      tone: \"formal\"
    assert:
      - type: icontains
        value: \"Q1\"
      - type: llm-rubric
        value: \"The email maintains a formal, professional tone throughout\"
 
  - vars:
      bullet_points: |
        - API migration deadline is Friday at 5pm
        - Three endpoints still need updating
        - Downtime window is Saturday 2-6am
      tone: \"urgent\"
    assert:
      - type: icontains
        value: \"Friday\"
      - type: llm-rubric
        value: \"The email conveys urgency with direct language and clear action items\"

每个测试用例都配有两类断言。

icontains 是简单的字符串检查:输出是否包含“mockups”(不区分大小写)?它快速、免费,也不调用任何 API。

llm-rubric 会将输出发送给另一个 LLM,请它依据您的评分标准对响应打分。它需要消耗 token,但能捕捉字符串匹配无法涵盖的内容,比如邮件是否真的听起来很随意。

运行评估

运行评估:

promptfoo eval

然后在浏览器中打开结果:

promptfoo view

promptfoo view results matrix showing email writer eval results across GPT-5 and Claude Sonnet 4

Web 界面以列展示提供商、以行展示测试用例。每个单元格显示各项断言的通过/失败,您可点击任意单元格查看完整输出与评分细节。

Promptfoo 默认将 API 响应缓存到磁盘(TTL 14 天),因此重复运行相同评估不再产生费用。需要全新响应时使用 --no-cache。

编写断言

第一次评估用了 icontains 和 llm-rubric。它们只是 Promptfoo 支持的众多断言类型中的两个。本节将介绍主要类别,并在此过程中持续为邮件写作器配置添加断言。

确定性断言

这些断言本地运行、无需费用且即时返回结果。

类型

检查内容

contains / icontains

输出是否包含某个子串(区分/不区分大小写)

regex

输出是否匹配某个模式(可用 \\{.*?\\} 捕捉未填充的 {{placeholders}})

not-contains

输出不应包含的内容(模板残留、拒答、占位文本等)

latency

响应是否在 N 毫秒内返回

cost

响应成本是否低于 $X

您已使用过 icontains 与 latency。现在为“随意”用例添加 not-contains。若模型默认为正式语气,可能会用 “Dear” 开头,而非 “Hey” 这类随意用法。只需一行即可捕捉:

- type: not-contains
  value: \"Dear\"

把这行加入“随意”用例的 assert 列表并重跑。两个模型都应通过:GPT-5 常以 “Hey team,” 开头,Claude Sonnet 4 也是如此。若任一模型以 “Dear Colleagues,” 开头,该断言会立刻标红。

每种断言类型都支持 not- 前缀:如 not-regex、not-equals 等。

模型辅助断言

这些断言会消耗 token,但能判断字符串匹配无法判断的内容。您已在首次评估中使用了 llm-rubric。评分标准写得好坏,至关重要。

像“邮件听起来很专业”这样的笼统标准对评分毫无帮助。具体明确的标准才能提供可衡量依据:

- type: llm-rubric
  value: \"The email uses a casual tone: contractions like 'we'll' and 'don't',
    sentences under 20 words, no corporate jargon like 'synergy' or 'circle back',
    and opens with a greeting like 'Hey' or 'Hi team'\"

使用该评分标准重跑评估后,评分者的输出也会更具体:

  • Claude Sonnet 4.6:“邮件采用随意语气(‘Hey team,’ ‘shoot over’),使用缩写(‘we're,’ ‘let's,’ ‘don't’)。”
  • GPT-5:“随意语气(例如 ‘Hey team,’ ‘Just shout.’),使用缩写(‘We're,’ ‘I'll’)。”

评分标准越具体,评分就越一致、越有参考价值。

Promptfoo 还提供 answer-relevance(输出是否回答了问题?)与 similar(通过嵌入与参考答案做余弦相似度),两者都对 RAG 与搜索类应用很有用。

自定义 Python 断言

当内置类型无法覆盖您的逻辑时,可自行编写。对于邮件写作器,您可能想检查输出是否保持在合理长度。下面是一个内联断言:当邮件字数在 50 到 200 之间时通过:

- type: python
  value: \"50 <= len(output.split()) <= 200\"

将其添加到“随意”用例并重跑。我这次运行中,两个模型都通过:GPT-5 为 54 个词,Claude Sonnet 4 为 187 个词。但请记住该断言,下一节它并非处处都通过。

若需更复杂的检查,将逻辑写入单独文件:

# assert_length.py
def get_assert(output, context):
    word_count = len(output.split())
    in_range = 50 <= word_count <= 200
    return {
        \"pass\": in_range,
        \"score\": 1.0 if in_range else 0.0,
        \"reason\": f\"Word count: {word_count} (target: 50-200)\"
    }

在配置中用 type: python 与 value: file://assert_length.py 引用它。reason 字段会显示在结果 UI 中,便于查看测试通过或失败的具体原因。

何时应该失败

下面是一个真实的失败示例。

将 Python 字数断言添加到三个用例并在两个模型上跑完整评估。在我的运行中,六个组合里有五个通过。Claude Sonnet 4.6 未通过“紧急”用例(由于 LLM 的非确定性,在您机器上可能不会复现)。

那次运行中,输出为 207 个词,超出上限 7 个。llm-rubric 其实是通过的,确认了邮件使用了“紧急、直接的语言”,并给出“清晰的行动项”。icontains 也通过了,因为输出包含 “Friday”。

但 Python 字数断言失败了。Claude 在正文前加了元说明(“Here is a draft email based on your bullet points with an urgent tone:”),把字数推高到了 200 以上。

这类问题靠目测很难抓。邮件本身读起来没问题:语气对、内容对。但因为模型额外添加了不属于正文的文字,输出过长。一个断言就能捕捉到,评估也会标记该用例失败。

修复有两种思路:调整提示词,明确禁止前言;或提高字数上限。无论哪种方式,都要重跑评估并确认通过。

加权评分

此时,“随意”用例已有四个断言: icontains、not-contains、llm-rubric 与 Python 字数检查。它们的重要性并不相同。可用 weight 字段表达权重:

assert:
  - type: icontains
    value: \"mockups\"
    weight: 1
  - type: not-contains
    value: \"Dear\"
    weight: 1
  - type: llm-rubric
    value: \"The email uses a casual tone with contractions and short sentences\"
    weight: 2
  - type: python
    value: \"50 <= len(output.split()) <= 200\"
    weight: 0.5
threshold: 0.7

每个断言的得分会乘以其权重,测试计算加权平均分。threshold 设置通过所需的最低分。此处语气评分(rubric)的权重是关键词检查的两倍,而字数检查是它们的一半。

用这些权重重跑“随意”用例,两个模型都得分 1.0 并通过。但如果 llm-rubric 失败(权重 2),而字数检查通过(权重 0.5),加权得分就会低于 0.7 的阈值,测试将失败。通过权重,您可以告诉 Promptfoo 哪些指标对您更重要。

并排对比模型

配置里已经有两个提供商,因此 promptfoo eval 会在一次运行中同时测试 GPT-5 与 Claude Sonnet 4。打开 promptfoo view,您会看到它们在结果中分别以列显示,每个模型都对所有用例独立打分。

在我这次评估中,GPT-5 在三个用例的六项断言上全部通过。Claude Sonnet 4 通过了五项,但在“紧急”邮件的字数检查上失败。

这类差异用手动试几个提示词很难发现,但在结果矩阵中一目了然。您是在同一输入、同一断言下比较分数,而不是凭记忆判断哪个模型“感觉更好”。

不过 LLM 输出并非确定性。相同提示词在连续运行中可能产生不同结果,一次运行无法判断模型是稳定优秀还是偶然发挥。可用 --repeat 参数来应对:

promptfoo eval --repeat 3

这会让每个提供商对每个用例运行三次。如果某模型在两次中通过语气断言、第三次却失败,那就是一次性运行难以察觉的稳定性信号。

从本地测试到 CI/CD

开发期间在本地运行评估没问题,但这取决于修改提示词的人是否记得去跑。Promptfoo 提供官方的 GitHub Action,可在每个涉及提示词变更的拉取请求上自动运行评估套件,并将结果以评论形式发布。

设置方式:在仓库中创建工作流文件:

mkdir -p .github/workflows

然后创建 .github/workflows/prompt-eval.yml,内容如下:

name: 'Prompt Evaluation'
on:
  pull_request:
    paths:
      - 'prompts/**'
jobs:
  evaluate:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write
    steps:
      - uses: actions/checkout@v4
 
      - name: Set up promptfoo cache
        uses: actions/cache@v4
        with:
          path: |
            ~/.promptfoo/cache
            .promptfoo-cache
          key: ${{ runner.os }}-promptfoo-${{ hashFiles('prompts/**') }}-${{ github.sha }}
          restore-keys: |
            ${{ runner.os }}-promptfoo-${{ hashFiles('prompts/**') }}-
            ${{ runner.os }}-promptfoo-
 
      - name: Run promptfoo evaluation
        uses: promptfoo/promptfoo-action@v1
        with:
          openai-api-key: ${{ secrets.OPENAI_API_KEY }}
          github-token: ${{ secrets.GITHUB_TOKEN }}
          config: 'promptfooconfig.yaml'
          cache-path: '.promptfoo-cache'

在此之前,您需要在 GitHub 仓库的 Settings > Secrets and variables > Actions 中添加 OPENAI_API_KEY(可选添加 ANTHROPIC_API_KEY)为仓库密钥。

paths 过滤器意味着仅当 prompts/ 下的文件发生变更时才触发。checkout 步骤必需,因为该 Action 会在内部用 git 比较分支间的提示词文件差异。它将运行完整评估套件,并在 PR 中发布包含结果与 Web 查看器链接的评论。

若需更灵活的通过/失败逻辑,可直接解析 JSON 输出:

promptfoo eval -c config.yaml -o results.json
FAILURES=$(jq '.results.stats.failures' results.json)
if [ \"$FAILURES\" -gt 0 ]; then exit 1; fi

从这里开始的工作流:

  1. 修改提示词
  2. 发起 PR
  3. CI 运行评估
  4. 结果以 PR 评论呈现
  5. 若有失败则修复
  6. 全部通过后合并

提示词的变更将与代码变更一样,合并前先测试。

结语

开头提到的邮件写作器仍有语气问题,但现在有测试能在用户发现前将其拦下。您从一份空白 YAML 文件出发,最终搭建了跨两个模型并接入 CI 的评估套件。

同样的方法适用于您构建的任何 LLM 功能,无论是聊天机器人、摘要器,还是分类流水线。只要您能为“好输出”写出断言,就能自动化测试,而不是靠目测。

准备更进一步时,Promptfoo 文档覆盖了多个值得关注的方向:

  • 红队对抗:promptfoo redteam run 可借助数十种攻击插件扫描提示注入与越狱

  • 自定义 Python 提供商:用 file://my_provider.py 封装任意内部模型或微调端点

  • CSV 测试数据:当内联 YAML 难以维护时,使用 file://tests.csv 扩展测试套件规模

如果您想改进正在测试的提示词,我们的 Prompt Engineering with the OpenAI API 课程涵盖了许多可应用于各类 AI 开发的重要技巧。

Promptfoo 常见问题

什么是 Promptfoo,它解决了什么问题?

Promptfoo 是一个开源 CLI,用于在 LLM 输出抵达用户之前进行测试。它不再依赖手动尝试少量输入并目测结果,而是让您为“好输出”定义断言、选择模型,并自动运行所有组合。它以结构化、可复现的评估替代“看起来还行”的测试方式。

如何设置并运行您的首个 Promptfoo 评估?

使用 npm install -g promptfoo 安装 Promptfoo,运行 promptfoo init 脚手架项目,设置 API 密钥(如 OPENAI_API_KEY 或 ANTHROPIC_API_KEY),然后编写包含提示词、提供商和测试用例的 promptfooconfig.yaml。执行 promptfoo eval 运行评估,使用 promptfoo view 在浏览器中查看结果。

Promptfoo 支持哪些断言类型,分别在何时使用?

Promptfoo 有三类断言。确定性断言(如 contains、regex、not-contains、latency、cost)免费且即时。像 llm-rubric 这样的模型辅助断言会将输出发送给另一模型,以判断语气等主观特性。自定义 Python 断言允许您用内联代码或独立文件实现任意检查。优先使用确定性断言,对主观判断补充模型辅助断言,对领域逻辑使用 Python 断言。

如何在同一测试套件上比较多个模型?

在 promptfooconfig.yaml 中列出多个提供商,然后运行一次 promptfoo eval。Promptfoo 会将每个模型跑遍所有测试用例,并在结果矩阵中以不同列展示。使用 --repeat(如 promptfoo eval --repeat 3)可让每个提供商对每个测试运行多次,从而捕捉非确定性输出带来的不一致。

Promptfoo 如何融入 CI/CD 流水线?

Promptfoo 提供官方 GitHub Action(promptfoo/promptfoo-action),可在每个涉及提示词文件的拉取请求上运行评估套件。它会将结果以 PR 评论形式发布,并附上 Web 查看器链接。您需要将 API 密钥添加为仓库密钥,并配置路径过滤器,使评估仅在提示词变更时触发。这样,提示词变更也能像代码变更一样在合并前完成测试。


Bexruz (Bex) Tuychiev's photo
Author
Bexruz (Bex) Tuychiev
LinkedIn

我是一名数据科学内容创作者,拥有超过 2 年的经验,并在 Medium 上拥有较大的读者群。我喜欢用略带讽刺的笔调撰写有关 AI 和机器学习的深度文章——毕竟总得想点办法让这些内容不那么枯燥。我已发表 130 多篇文章,并制作了一门 DataCamp 课程,另有一门正在筹备中。我的内容已被超过 500 万人次阅读,其中有 2 万人在 Medium 和 LinkedIn 上成为关注者。 

主题
人工智能
大语言模型
生成式 AI

提示工程课程

课程

理解 Prompt Engineering

1 小时
239.8K
学习如何用 ChatGPT 编写高效提示词,立即应用到你的工作流程中。
查看详情开始课程

创建您的免费账户

实践并学习路径你的进步。

使用 Google 继续
继续操作即表示您接受我们的《使用条款》和《隐私政策》,并同意您的数据存储在美国。
查看更多