Mindket

CI 中的评估:用 GitHub Actions 在每个拉取请求上跑大模型评估

黑色横幅,标题是“AI 评估”,下面是“测试集、LLM 评委与智能体评估”。

在 CI 里跑评估,就是把测试用例放进代码仓库,凡是改动了提示词、模型或工具的拉取请求,都跑一遍。promptfoo 的 GitHub Action 能做到这一点:它运行整套测试,把结果评论到拉取请求上,通过率低于你设的门槛时让任务失败。缓存则让没变的测试不再重复调用模型。

本节内容

  • 把评估用例和评分器放进代码仓库,和它们测试的提示词放在一起。
  • 每个改动提示词的拉取请求都跑一遍评估,评估失败就让任务失败。
  • 回归评估集设门槛,能力测试只出报告,从不拦人。
  • 给不稳定的模型评分器加重复运行,再用缓存让没变的测试不再重发。
  • 在 OpenAI 的 Evals 控制台关停之前,把评分器迁出来。
全部课程
本页目录
  1. 一行提示词是怎样躲过 Anthropic 的评估的
  2. 开始之前
  3. 为什么用 promptfoo,而不是 OpenAI Evals
  4. 把评估集写成一份 promptfoo 配置
  5. 在每个拉取请求上运行
  6. 决定什么会让构建失败
  7. 少发调用:缓存、路径过滤和并发
  8. 不用 promptfoo,改用 Inspect 或 pytest
  9. ClaudeDevs、Paige Bailey 和 Ben Hylak 谈怎样抓住退步
  10. 常见错误
  11. 自己试一试
  12. 下一门课:n8n
  13. 自测一下
  14. 常见问题
  15. 资料来源

一行提示词是怎样躲过 Anthropic 的评估的

2026 年 4 月 16 日,Anthropic 在 Claude Code 里上线 Opus 4.7,同时在系统提示词里加了一行,想让回答更简短。这一行的意思是:工具调用之间的文字不超过 25 个词,最终回答不超过 100 个词,除非任务需要更多细节。上线前,它经过了好几周的内部测试,团队跑的那些评估里没有出现任何退步。

后来用户反映 Claude Code 的质量下滑了。调查过程中,Anthropic 做了消融实验,也就是一次去掉提示词里的一行,在一组更广的评估上重跑。其中一项评估显示,Opus 4.6 和 4.7 都下降了 3%,于是这一行在 4 月 20 日的版本里被撤掉了,事后复盘里讲了这个经过。

Anthropic 宣布的补救办法,正是本课要讲的事:今后系统提示词的每一次改动,都要跑一大套针对各个模型的评估。你的应用也需要同样的习惯,只是规模小一些:评估用例放在代码仓库里,提示词的每次改动在合并前都跑一遍,分数一降,任务就失败。Anthropic 那篇讲智能体评估的文章早就把自动评估放进了 CI/CD,在智能体每次改动、模型每次升级时运行。

开始之前

你需要一个 GitHub 仓库,里面放着你的应用发出的提示词,再按第 1 课的做法写好几个测试用例,并准备好被测模型的 API 密钥。把密钥存成仓库密钥,位置在 Settings 下的 Secrets and variables,再选 Actions。promptfoo 需要 Node.js 22.22.0 或更新的版本,它的 CI 文档推荐 Node 24 LTS。

2026 年 11 月 6 日,npm 上最新的 promptfoo 是 10 月 25 日发布的 0.125.1。GitHub Action 默认运行 npx promptfoo@latest,除非你设置它的 promptfoo-version 输入项。请在那里固定版本,免得两个拉取请求之间发布了新版,改变了打分方式。

为什么用 promptfoo,而不是 OpenAI Evals

OpenAI 正在逐步关停它托管的 Evals 产品。它的弃用页面列出了时间:2026 年 6 月 3 日宣布弃用,10 月 31 日起已有的评估变为只读,Evals 控制台和 API 计划在 11 月 30 日关停。评估流程里用到的评分器,也在这次调整之列。

OpenAI 的迁移指南推荐 promptfoo,并说它是一个用来评估 AI 应用和智能体、并对其做红队测试的开源命令行工具和库。2026 年 3 月 9 日,OpenAI 同意收购 Promptfoo,promptfoo 团队表示这个项目会继续开源。

指南的做法是手动迁移,用不着导出功能。你要在 promptfooconfig.yaml 里重建每个评估的提示词、服务商、测试用例和断言,再用 promptfoo validate config 检查。指南还要求,凡是重建的评分器,尤其是 LLM 评委,都要先验证过,再拿它来判断有没有退步。

从 OpenAI Evals 到 promptfoo
  1. OpenAI 同意收购 Promptfoo

    promptfoo 团队表示项目会继续开源。

    来源:promptfoo blog on GitHub: Promptfoo is joining OpenAI (9 March 2026) (2026年11月6日 查阅)

  2. 宣布弃用 Evals 平台

    OpenAI 通知使用 Evals 平台的开发者。

    来源:OpenAI API docs: Deprecations (Evals platform) (2026年11月6日 查阅)

  3. 已有评估变为只读

    评估还能查看,但不能再修改。

    来源:OpenAI API docs: Deprecations (Evals platform) (2026年11月6日 查阅)

  4. Evals 控制台和 API 关停

    OpenAI 计划关停的日期。

    来源:OpenAI API docs: Deprecations (Evals platform) (2026年11月6日 查阅)

把评估集写成一份 promptfoo 配置

一份 promptfoo 配置列出提示词、运行它们的服务商,以及测试。每个测试设定变量,并对输出做出断言。下面的例子出自 promptfoo 的模型评分指标文档。它让两个提示词文件跑同一个模型,并给每个测试加上同一条 llm-rubric 断言,也就是一个评委:只有回答说自己不确定或答不了,才算通过。各个测试只填 question 这个变量,评分细则也会读到它。

providers:
  - openai:gpt-6-sol
prompts:
  - file://prompt1.txt
  - file://prompt2.txt
defaultTest:
  assert:
    - type: llm-rubric
      value: 'Says that it is uncertain or unable to answer the question: "{{question}}"'
tests:
  - vars:
      question: What's the weather in New York?
  - vars:
      question: Who won the latest football match between the Giants and 49ers?
  • 凡是代码断言能判断的地方,都用代码断言。contains、equals 和 is-json 不用调用模型,每次运行给出的判定都一样。
  • llm-rubric 就是一个 LLM 评委,所以在让它拦截合并之前,先像第 2 课那样,拿你自己的标注核对它的判定。
  • 使用 repeat 时,如果你特意定义了完全相同的重复测试,就给每个测试写一个不同的 id 或 description,这是 Action 的 README 给的建议,这样报告才分得清它们。
  • 先在本地用 promptfoo eval -c promptfooconfig.yaml 跑一遍,再用 promptfoo view 打开结果。

在每个拉取请求上运行

promptfoo 的 GitHub Action 会运行整套测试,并在拉取请求上发一条评论,写明通过和失败的数量。下面的工作流就是它 README 里的拉取请求示例。只有 prompts/ 下的文件或配置文件有改动时,它才会启动,并用 --prompts 把改动过的提示词文件交给 promptfoo。不设 prompts 输入项时,就跑配置文件里自带的提示词。

name: 'Prompt Evaluation'
on:
  pull_request:
    paths:
      - 'prompts/**'
      - 'promptfooconfig.yaml'

jobs:
  evaluate:
    runs-on: ubuntu-latest
    permissions:
      contents: read # Required for actions/checkout
      pull-requests: write # Ability to post comments on Pull Requests
    steps:
      # Required for promptfoo-action's git usage
      - uses: actions/checkout@v4

      # This cache is optional, but you'll save money and time by setting it up!
      - name: Set up promptfoo cache
        uses: actions/cache@v4
        with:
          path: .promptfoo-cache
          key: ${{ runner.os }}-promptfoo-${{ hashFiles('promptfooconfig.yaml', 'prompts/**') }}
          restore-keys: |
            ${{ 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'
          prompts: 'prompts/**'
          cache-path: '.promptfoo-cache'
  • 这个 Action 只评估当前检出的代码。它不会把基础分支和拉取请求并排各跑一遍,所以需要前后对比时,请保留一次更早的运行结果。
  • 如果你把触发事件换成 pull_request_target,请照 README 说的做:不要用有特权的令牌去执行不受信任的拉取请求代码。
  1. 把模型服务商的 API 密钥存成一个名为 OPENAI_API_KEY 的仓库密钥。
  2. 提交 promptfooconfig.yaml 和 prompts/ 文件夹,并把配置里的提示词路径改成指向这个文件夹,比如 file://prompts/prompt1.txt。
  3. 把下面的工作流存成 .github/workflows/ 里的一个 YAML 文件。
  4. 开一个修改提示词的拉取请求,读一读 Action 发出的评论。

决定什么会让构建失败

按它的命令行文档,只要有一个测试失败,或者通过率低于 PROMPTFOO_PASS_RATE_THRESHOLD,promptfoo eval 就以退出码 100 结束,其他错误则返回退出码 1。这个门槛是一个百分比,不设置时,一个失败都不允许。任何非零的退出码都会让 CI 这一步失败。

这个默认值适合回归评估集,因为它几乎每次都应该通过。能力评估集按设计一开始通过率就低,所以只让它出报告。PROMPTFOO_FAILED_TEST_EXIT_CODE 可以改写失败时的退出码,设成 0 就让任务照常通过。在 Action 里,fail-on-threshold 设定一个评估集需要达到的通过百分比。

模型评分器每次运行的结果都可能不同。设置 repeat: 3 和 repeat-min-pass: 2 后,Action 会把每个测试跑三次,要求至少通过两次。一直失败的测试会被标出来,评分器偶然的波动则被容忍。

门槛要按噪声来定。一项更广的评估掉了 3%,就足以让 Anthropic 撤掉一行提示词。它在内部实验里发现,单是资源设置,就能让 Terminal-Bench 2.0 的分数相差 6 个百分点。同一篇文章还认为在评估配置写明并对齐之前,排行榜上不到 3 个百分点的差距都值得怀疑。要比较的几次运行之间,请保持运行环境、模型版本和评委都不变。

3%

只加了一行系统提示词,一项更广的评估就显示 Opus 4.6 和 4.7 都下降了这么多

来源:Anthropic Engineering: An update on recent Claude Code quality reports (23 April 2026) (2026年11月6日 查阅)

6 points

资源不设上限时,Terminal-Bench 2.0 比 1x 设置高出的分数(p < 0.01)

来源:Anthropic Engineering: Quantifying infrastructure noise in agentic coding evals (5 February 2026) (2026年11月6日 查阅)

3 points

低于这个差距的排行榜差异,在评估配置写明并对齐之前都值得怀疑

来源:Anthropic Engineering: Quantifying infrastructure noise in agentic coding evals (5 February 2026) (2026年11月6日 查阅)

# regression suite: every test must pass (no threshold set)
npx promptfoo@0.125.1 eval -c regression.yaml

# capability suite: report only, a failed test exits 0
PROMPTFOO_FAILED_TEST_EXIT_CODE=0 npx promptfoo@0.125.1 eval -c capability.yaml

少发调用:缓存、路径过滤和并发

每次跑评估都会发出模型调用,服务商每一次都要计费。promptfoo 把响应缓存在磁盘上,PROMPTFOO_CACHE_TTL 默认是 1209600 秒,也就是 14 天。请求没变的测试,直接从缓存里拿答案。GitHub 托管的运行器每次都是空的,所以上面的工作流用 actions/cache 保存 .promptfoo-cache,并用配置文件和提示词的哈希值作为键。这个 Action 还会自己清理这个目录:当 CI=true 时,删掉存放超过七天的条目。

  • 工作流里的 paths 过滤器,只在提示词或配置文件有改动时才启动任务。
  • 在 Action 里设置 no-cache: true,或者在命令行加 --no-cache,就会强制重新调用。夜间运行时,或者服务商更新了模型之后,可以这样做。
  • 如果服务商限制了你的调用频率,promptfoo 的 CI 文档建议打开缓存,并减少并发调用,最低到 -j 1。Action 的 max-concurrency 默认是 4。

不用 promptfoo,改用 Inspect 或 pytest

在沙盒里干活的智能体更适合用 Inspect,第 3 课里的夺旗任务原样就能放进 CI 任务里跑。Inspect 的日志文档说,分析结果之前要先检查日志的状态。下面的脚本是本课自己写的胶水代码,它先做这个检查,再打印每种汇总方式的准确率,pass^5 低于 0.8 时让任务失败。

pytest 文件也可以。GitHub 的 Python 指南在工作流的一个步骤里安装 pytest 并运行 pytest。一个调用你的应用、再对回答做断言的测试,失败时会像其他测试一样让这一步失败。

# ci_eval.py
import sys

from inspect_ai import eval
from ctf import ctf

log = eval(ctf(), model="anthropic/claude-sonnet-4-6")[0]
if log.status != "success":
    sys.exit(f"eval did not finish: {log.status}")

for score in log.results.scores:
    accuracy = score.metrics["accuracy"].value
    print(score.reducer, accuracy)
    if score.reducer == "pass_k_5" and accuracy < 0.8:
        sys.exit("pass^5 fell below 0.8")

ClaudeDevs、Paige Bailey 和 Ben Hylak 谈怎样抓住退步

Anthropic 在 X 上开了 ClaudeDevs 这个账号,用来解释产品决策。2026 年 4 月 23 日,它告诉那些反映 Claude Code 质量下滑的用户,团队已经就找到的三个问题发布了复盘,这些问题在 v2.1.116 及之后的版本里都已修好。这份复盘,就是本课开头讲的那个故事。

Paige Bailey 的 X 简介写着,她是 Google DeepMind 开发者关系的工程负责人,以前在 GitHub 工作。她 2024 年 7 月 11 日在 X 上讲过拉取请求上的这个循环:你提交一次提示词改动,一份更新后的评估就会发到 PR 上,并附上 promptfoo 网页查看器的链接。她的帖子还提到,promptfoo 也会用到缓存。

Ben Hylak 的 X 简介写着他是 Raindrop 的首席技术官。复盘发出的第二天,他读完后用一句话说出了这一切的原因。

每个做智能体的人都明白,要往前走又不出现退步有多难。

Ben Hylak, 按他的 X 简介,Raindrop 首席技术官,引文为译文 · X (2026年11月6日 查阅)
ClaudeDevs(@ClaudeDevs)2026 年 4 月 23 日在 X 上说:过去一个月,有些人反映 Claude Code 的质量下滑了。我们做了调查,并就找到的三个问题发布了复盘。这些问题在 v2.1.116 及之后的版本里都已修好,我们也为所有订阅用户重置了用量限额。
ClaudeDevs 在 X 上,2026 年 4 月 23 日
Paige Bailey(@DynamicWebPaige)2024 年 7 月 11 日在 X 上谈 promptfoo 与 Gemini API 和 GitHub Actions 的集成:你提交一次提示词改动,一份更新后的评估就会自动发到 PR 上,并附上 promptfoo 网页查看器的链接。Promptfoo 还会用缓存来存放……
Paige Bailey 在 X 上,2024 年 7 月 11 日

X 官方嵌入卡片的截图,拍摄于 2026-11-06,时间为协调世界时(UTC)。Paige Bailey 那条帖子被 X 的嵌入卡片截短,后面是“Show more”。点击截图可到 X 上打开原帖。

常见错误

  • 只在发布前手动跑评估,于是中间合并进来的提示词改动没经过测试就上线了。
  • 把能力测试放进拦截合并的评估集,一个本该从低分起步的集合,让每个拉取请求都失败。
  • 让一个没人拿人工判断核对过的 LLM 评委来拦截合并。
  • 不稳定的评委只跑一次就让构建失败,而不是用重复运行。
  • 用 pull_request_target 带着密钥去跑来自分叉仓库的代码。
  • 两次运行之间换了模型版本、评委或运行环境,却把分数差距叫作退步。
  • 评分器唯一的一份只存在 OpenAI 的 Evals 控制台里。

自己试一试

  • 给一个提示词写五个测试,两个用代码断言,三个用 llm-rubric,在本地跑一遍。
  • 加上工作流,开一个只改提示词里一个词的拉取请求,读一读那条评论。
  • 故意弄坏一个测试,运行 promptfoo eval,再用 echo $? 打印它的退出码。
  • 给评委测试设置 repeat: 3 和 repeat-min-pass: 2,数一数有多少次运行的结果翻转了。

下一门课:n8n

这是 AI 评估课程的最后一课。下一门课 n8n 是“自动化你的工作”路径的开头。它的第一课会做出一个有四个节点的工作流,每天把天气记进一张数据表。第三课教你搭 n8n AI 智能体,把聊天模型接上工具,测过的提示词就可以在那里自己运行。

自测一下

  1. 2026 年 4 月,Anthropic 为什么撤掉了它加进 Claude Code 系统提示词的一行?

    查看答案

    B. 一组更广的评估显示 Opus 4.6 和 4.7 都下降了 3% 这一行通过了好几周的内部测试,是在更广的评估集上做消融实验才发现了下降,Anthropic 在 4 月 20 日撤掉了它。

  2. 有一个测试失败,且没有设置通过率门槛,promptfoo eval 会怎样?

    查看答案

    C. 以失败退出码结束,让 CI 这一步失败 没有设置门槛时,一个失败都不允许,所以命令以失败退出码结束,这一步随之失败。

  3. 在 promptfoo 的 GitHub Action 里,repeat: 3 和 repeat-min-pass: 2 起什么作用?

    查看答案

    A. 每个测试跑三次,至少要通过两次 每个测试跑三次、要通过两次,这样评分器偶然的波动会被容忍,一直失败的测试则会被标出来。

  4. 关于 promptfoo 的 GitHub Action,哪一句是对的?

    查看答案

    D. 它只评估当前检出的代码 README 说它只评估当前检出的代码,而缓存只有在工作流加上 actions/cache 时才会保留下来。

常见问题

不用 promptfoo,怎样在 GitHub Actions 里跑大模型评估?

把评估写成 pytest 测试或 Inspect 脚本,放在普通的任务步骤里运行,因为任何以非零码退出的步骤都会让任务失败。Claude Code 也能在 GitHub Actions 里运行,Claude Code 进阶课程里有一课专门讲,不过那个任务是审查和编写代码,并不给你的提示词打分。

我在 OpenAI Evals 里的评估会怎样?

它们从 2026 年 10 月 31 日起已经只读,Evals 控制台和 API 计划在 2026 年 11 月 30 日关停。OpenAI 的迁移指南推荐 promptfoo,做法是手动重建,不经过导出这一步。把每个评估在 promptfoo 配置里重建,拿它的第一批结果和旧结果比一比,再把配置放进你的仓库,让评分器比控制台活得更久。

在 CI 里跑评估要花多少钱?

你付的是模型调用的钱,promptfoo 本身开源。每个测试对每一组提示词和服务商各调用一次,每条模型评分的断言再加一次评委调用,重复运行则把这些成倍放大。缓存和路径过滤决定了这些调用发生得有多频繁。

不稳定的评估该不该拦截合并?

不该凭一次运行就拦。让一个测试在多数重复运行里都失败才拦截,总在翻转的测试则移到只出报告的评估集。然后读它的输出,弄清是测试、评委还是你的应用出了问题。

在 CI 里跑评估,promptfoo 和 Inspect 哪个更好?

promptfoo 适合提示词和应用的输出,因为它的 Action 会在拉取请求上发评论,写测试也不用写代码。要跑很多次试验来测的智能体,用 Inspect 更合适:它可以把每个任务放进 Docker 沙盒里运行,并用 epoch 汇总方式报告 pass@k 和 pass^k。

资料来源

  1. Anthropic Engineering: An update on recent Claude Code quality reports (23 April 2026) (2026年11月6日 查阅)https://www.anthropic.com/engineering/april-23-postmortem
  2. Anthropic Engineering: Quantifying infrastructure noise in agentic coding evals (5 February 2026) (2026年11月6日 查阅)https://www.anthropic.com/engineering/infrastructure-noise
  3. Anthropic Engineering: Demystifying evals for AI agents (9 January 2026) (2026年11月6日 查阅)https://www.anthropic.com/engineering/demystifying-evals-for-ai-agents
  4. OpenAI API docs: Deprecations (Evals platform) (2026年11月6日 查阅)https://developers.openai.com/api/docs/deprecations
  5. OpenAI Cookbook: Moving from OpenAI Evals to Promptfoo (2026年11月6日 查阅)https://developers.openai.com/cookbook/examples/evaluation/moving-from-openai-evals-to-promptfoo
  6. promptfoo blog on GitHub: Promptfoo is joining OpenAI (9 March 2026) (2026年11月6日 查阅)https://github.com/promptfoo/promptfoo/blob/main/site/blog/promptfoo-joining-openai/index.mdx
  7. promptfoo docs on GitHub: Command line (exit codes, environment variables) (2026年11月6日 查阅)https://github.com/promptfoo/promptfoo/blob/main/site/docs/usage/command-line.md
  8. promptfoo docs on GitHub: CI/CD integration (2026年11月6日 查阅)https://github.com/promptfoo/promptfoo/blob/main/site/docs/integrations/ci-cd.md
  9. promptfoo docs on GitHub: Model-graded metrics (2026年11月6日 查阅)https://github.com/promptfoo/promptfoo/blob/main/site/docs/configuration/expected-outputs/model-graded/index.md
  10. promptfoo-action README on GitHub (2026年11月6日 查阅)https://github.com/promptfoo/promptfoo-action
  11. npm registry: promptfoo (latest 0.125.1) (2026年11月6日 查阅)https://registry.npmjs.org/promptfoo
  12. GitHub Docs: Building and testing Python (2026年11月6日 查阅)https://docs.github.com/en/actions/tutorials/build-and-test-code/python
  13. Inspect docs on GitHub: Inspect (welcome page, Docker sandbox in the CTF example) (2026年11月6日 查阅)https://github.com/UKGovernmentBEIS/inspect_ai/blob/main/docs/index.qmd
  14. Inspect docs on GitHub: Scoring Metrics (epoch reducers) (2026年11月6日 查阅)https://github.com/UKGovernmentBEIS/inspect_ai/blob/main/docs/metrics.qmd
  15. Inspect docs on GitHub: Eval Logs (2026年11月6日 查阅)https://github.com/UKGovernmentBEIS/inspect_ai/blob/main/docs/eval-logs.qmd
  16. Inspect source on GitHub: log/_log.py (EvalResults) (2026年11月6日 查阅)https://github.com/UKGovernmentBEIS/inspect_ai/blob/main/src/inspect_ai/log/_log.py
  17. ClaudeDevs on X: Claude Code quality post-mortem (2026年11月6日 查阅)https://x.com/ClaudeDevs/status/2047371123185287223
  18. Paige Bailey on X: promptfoo and GitHub Actions (2026年11月6日 查阅)https://x.com/DynamicWebPaige/status/1811448653640606011
  19. Ben Hylak on X: forward progress without regressions (24 April 2026) (2026年11月6日 查阅)https://x.com/benhylak/status/2047559627400003676

分享本节课

让一个测过的智能体去干活

浏览 Mindket 上的智能体,在动手搭建之前,先拿你自己的评估用例去测一测。

浏览智能体

本节课的 Markdown 版本 · AI 评估:衡量你的大模型应用和智能体是否好用 · 全部课程