mcpField
M
maplibre-running-evals

maplibre-running-evals

github.com/maplibre测试与质量15579 次安装今天更新
代码仓库

关于 maplibre-running-evals

该 Skill 指导贡献者使用 Promptfoo 评估技能,涵盖测试设计、预算检查、结果诊断和记录。通过比较未注入与已注入技能的测试来验证效果。

主要功能

  1. 01检查 API 密钥和提供方限制
  2. 02设计基线与注入技能测试
  3. 03按顺序运行评分评估
  4. 04诊断提供方与评审模型错误
  5. 05以 CSV 和 JSON 记录结果
  6. 06根据结果设置验证状态

适用场景

  1. 01衡量技能是否弥补模型行为差距
  2. 02技能或评分标准修改后重新测试
  3. 03排查评估停滞或触发速率限制
  4. 04记录技能评估结果

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/maplibre/maplibre-agent-skills --skill maplibre-running-evals

安装后在 Claude Code 中输入/maplibre-running-evals确认已加载。

详细介绍

运行 Promptfoo 评测

这里的每项评测都会通过提供方 API 使用两个角色:生成器(待测试的模型)和评审模型(为每个 llm-rubric 断言打分的模型)。evals/README.md 介绍了测试内容,以及编写提示词和评分标准的方法;本文说明运行评测的流程。请按顺序完成各阶段——不要因为已有内容就直接从第 5 阶段开始。

1. 设置运行预算

先做好预算,再做其他事。这些服务商使用免费套餐,而无法完成的运行毫无意义。

  • 确认生成器密钥和评审器密钥已设置——仅检查是否存在,绝不查看密钥值: echo "generator: ${GROQ_API_KEY:+set}" 和 echo "judge: ${GOOGLE_API_KEY:+set}"。如果缺少任何一个,就停止并报告;不要根据未实际运行的测试推断结果。
  • **两个角色都已固定:**生成器配置在 evals/prompts/lib/providers.yaml 中,评审器配置在 package.json 的 eval:graded 脚本中。默认使用固定的模型组合。唯一的硬性要求是两者必须是不同模型——绝不能用生成器评审自身;报告的任何结果都要注明这两个模型。
  • **每次记录在案的运行都必须通过 npm run eval:graded 执行,**这是唯一设置 --grader、--delay 和 -j 的地方。不要手动输入这些参数,也不要从旧结果文档中复制值:该值记录的是那次运行,而不是这次运行。也不要传入 --providers:它会用未固定的生成器替换固定生成器,使运行不再固定,结果也会被错误标记——详情见 evals/README.md,“编写评测提示词”。--providers echo 是探测模式:它会返回渲染后的提示词,而不是生成器的回答,因此你能准确看到将发送给生成器的内容——但每个 llm-rubric 仍会调用评审器,所以即使是探测也会消耗评审器调用额度。
  • **启动前了解服务商的限制:**阅读固定模型所在套餐的速率限制文档,然后向端点发送一个最小请求并显示响应头。对于兼容 OpenAI 的端点,x-ratelimit-* 响应头会提供每分钟令牌窗口(限制、剩余量、重置时间)和每日请求数;每日令牌上限没有对应的响应头,因此只有在收到 429 时才会显现——retry-after 会提供等待时间,响应正文会说明触发了哪项限制。响应头名称及其统计内容因服务商而异,也会发生变化,因此请以服务商自己的文档为准进行确认。
# Example against the current generator's OpenAI-compatible endpoint; the URL and model id come from providers.yaml.
curl -s -X POST https://api.groq.com/openai/v1/chat/completions \
  -H "Authorization: Bearer $GROQ_API_KEY" -H "Content-Type: application/json" \
  --data '{"model":"openai/gpt-oss-120b","messages":[{"role":"user","content":"hi"}],"max_tokens":1}' \
  -D - | grep -iE 'ratelimit|retry-after|"error"'
  • **启动前先算清楚。**令牌数 ≈ 字节数 / 4,因此 wc -c skills/<skill>/SKILL.md 可得每项测试的注入成本;启用技能的运行成本约为测试数 ×(该成本 + 生成器配置的 max_tokens),另外每项评审标准还需要一次评审器调用,所有消耗都要计入当天的剩余额度。基线运行不包含注入内容。如果总量超出剩余预算,就不要一次性运行全部测试。
  • 按顺序运行,每条命令只用一个配置。-j 1 是固定设置;绝不要同时运行两个分级评测,无论是在不同终端还是由不同队友运行。限制按账户计算,而不是按进程计算,因此任一角色收到 429 都会导致两项运行失败;请改为串联运行,并在两项之间留出完整的每分钟窗口:<run one> && sleep 60 && <run two>。每个配置都已接入 lib/skill-prompt.mjs 和 lib/providers.yaml,无需手动组装;如果你自行编写 Promptfoo 命令,也只能指定一个 --config——指定多个配置时,Promptfoo 会合并它们的 defaultTest 区块,并以最后一个 skillFile 为准。
  • **--delay 用于控制请求间隔,不会增加预算。**如果在正确控制请求间隔且没有并发的情况下,速率限制仍然导致失败,就说明可用容量已耗尽——立即停止,采用前面直接请求所查到的重置时间,不要循环重试。
  • 当剩余时间窗口或当天剩余额度不足以完成整次运行时,每次调用就减少测试数量——绝不要延长延迟。--filter-pattern '<匹配测试描述的正则表达式>' 只运行匹配的测试(请加引号;npm run 会通过 shell 传递该参数),也可用于在执行整个配置前,先对某种运行类型中的一项测试进行探测。--filter-failing <the run's .json output> 只会重新运行失败或出错的测试——第 8 阶段会写入该文件。

2. 先编写测试,再编写内容

3. 不注入技能,运行基线测试

npm run eval:graded -- --config evals/prompts/<skill>.yaml --var injectSkill=false

  • 在基线测试中通过的显式、隐式或反模式测试没有区分能力。 重新设计它。绝不要将其作为证据保留,也绝不要为了让运行结果变绿而删除测试。负向测试可以在基线测试中通过——这正是它的用途。
  • 阅读每一份基线回答,而非只看判定列。 如果裁判放过了幻觉,那说明测试设计失败,而非存在知识缺口。
  • 一次运行只是一个样本。 即使 temperature: 0,生成结果仍具有非确定性,因此一项主张必须基于你读过的 FAIL,而绝不能只看判定结果。

4. 只为基线测试中未通过的内容编写技能内容

  • 模型已经能正确处理的内容只会增加成本,不会带来收益——CONTRIBUTING.md,“精简模型已经能正确处理的内容”。
  • 一项主张应放在负责它的技能中,而不是你恰好打开的配置文件中。测试的归属决定内容的归属;否则,同一项主张就会出现在三份彼此逐渐不一致的副本中。

5. 注入技能后运行测试

npm run eval:graded -- --config evals/prompts/<skill>.yaml——默认会注入技能。经过验证的知识缺口,意味着基线测试为 FAIL,注入技能后为 PASS。 两次都通过、两次都未通过,或根本没有运行注入技能后的测试,都意味着目前还没有可成立的主张。

6. 重试前先诊断

Promptfoo 只有在运行结束后才会打印表格并写入 --output 文件,而进度条需要 stderr 上有 TTY,因此在 Agent 下,正常运行和卡死看起来一模一样。切勿根据耗时判断进度——先确认到底是哪一种情况。

pgrep -fl promptfoo                       # take the node pid, not the sh wrapper — the wrapper holds no sockets
lsof -nP -p <node-pid> -i                 # ESTABLISHED = still waiting on a provider
ls -l ~/.promptfoo/promptfoo.db*          # finished rows land in the local db; the -wal file's mtime stops advancing on a stall
  • 连接仍然打开才是信号;CPU 不是——等待提供方响应的运行会停在 0%。只有连接已断开且修改时间戳(mtime)已冻结时,才终止运行。
  • 用第 1 阶段的直接调用确认触发了哪项限制。 耗时一分钟和耗时一天的情况报告完全相同:队列超时,或正在重试的请求触发了速率限制错误。超过每分钟窗口上限的请求则不同——提供方会直接拒绝,并由 Promptfoo 记录一条带有请求正文的错误行。
  • **0 passed / 0 failed 且同时出现 “There were some errors” 表示没有数据,而不是测试结果,**空行也不代表 FAIL——调用根本没有到达模型。两者都应记录为无数据;重试超过一次只会消耗预算,却得不到证据。
  • **被终止的运行不会写入任何 --output 文件,**并会保留已有文件,因此读取前要检查 mtime 和行数。已完成的行保存在本地数据库中:npx promptfoo export eval latest -o <path>.json 会以与运行自身 JSON 输出相同的格式写出这些行,且不会调用提供方。
  • 读取 JSON 输出,不要只看 CSV。 CSV 会将每个单元格压平为判定结果、回答和顶层评分器原因;JSON 则会保留每条断言各自的结果和原因,以及 ERROR 行背后的完整错误信息。应根据 testCase.description 匹配,查看其中的 results.results[].response.output 和 results.results[].gradingResult.componentResults[].reason——不能只凭原因判断判定结果;如果原因是错误消息或为空,那是判定失败,不是判定结果。切勿修改已记录在 CSV 中的评分器原因。
  • 检查每个 FAIL 的末尾是否被截断,并检查每个 Must NOT PASS 的末尾是否提前结束。 被截断的回答可能会丢失评分标准要求的部分,而提前结束则可能轻易满足禁止项。

7. 何时需要重新测试,何时不需要

需要:

  • 添加、更正或删除了一项声明——用该技能运行覆盖它的测试,并在写入任何新声明前建立基线。
  • 改写了评分标准或提示词——该测试的基线现在已失效,因此应使用 --filter-pattern 重新运行该测试,而不是运行整个配置。反过来也一样:更改技能的措辞时,应在同一提交中搜索其评分标准,查找已删除的短语,因为过时的评分标准会宽松地评分,导致结果仍然通过。
  • 固定版本的生成器或判定器发生变化——重新运行每个配置。

每周 CI 漂移检查(eval.yml)会按自己的周期,针对默认分支重新运行整个集合;贡献者无需手动重复该运行。

不需要:

  • 不改变含义的措辞调整、格式修改、链接修正和错别字修正。
  • 在技能之间移动一项声明——语料库没有变化;应将测试与该声明一同移至所属技能的配置中。
  • 删除生成器未为通过测试所需的内容——基线 PASS 就是证据。将删除原因记录在结果文档中。
  • status: process 技能——不纳入 eval,通过对照其所描述的文件进行阅读审核。

8. 简洁记录结果

  • 每次运行都写入两个文件,CSV 和 JSON: --output evals/results/local/<skill>-<baseline|with-skill>.csv evals/results/local/<skill>-<baseline|with-skill>.json — 此标志接受多个路径,且该目录已加入 gitignore。CSV 是记录;JSON 用于诊断(阶段 6)并作为 --filter-failing 的输入。scripts/run-evals.js 在 CI 中也会写入同一对文件。仅提升你引用的运行结果:将其 CSV 提交为 evals/results/latest/<skill>-<baseline|with-skill>_<YYYY-MM-DD>.csv。
  • evals/results/<skill>.md 只包含一个表格 — 每个测试占一行,列出其类型、baseline 结果、with-skill 结果,以及压缩在单元格中的失败模式。表格前写一行 Run:,注明日期、生成器模型和评判模型;末尾用粗体写出通过/失败统计。除非某个判定需要人工解读,否则不要添加其他内容;如需添加,请说明是哪个判定,以及原始输出的位置。
  • status: 与运行结果保持一致: 仅当固定的运行结果通过时标记为 verified,否则标记为 provisional。

相关技能

参考资料

  • evals/README.md — 提示词、评分标准、baseline 探测方法、提供商设置
  • package.json 中的 eval:graded 脚本 — 评分器、延迟和并发设置的唯一事实来源
  • 当前生成器的速率限制页面 — 层级限制和响应标头
  • Promptfoo 0.122 — package.json 固定使用 ^0.122.0;上文提及的每个标志和行为都已根据该版本的 CLI 帮助和源代码核查,因此固定版本变更时请重新核查

此技能是一个快照。 如果主要来源与之矛盾——上述参考资料、MapLibre 当前文档,或运行 MapLibre 时的实际行为——则以该来源为准。遵循该来源,然后报告不一致之处,并注明来源和你的 MapLibre 版本:编辑你安装的副本对其他人毫无帮助,而且下次更新时会被覆盖。

评论

支持 Markdown