没想到吧!Skill 也可以测试 — 小白都看得懂的 Skill 教程

0 阅读13分钟

1-LB_9-xxFxl3gVVdGE-ROSg-zh.png

读完这篇文章,你会知道应该如何测试上一篇文章中创建的 Skill。

没错,Skill 也是可以测试的!

同时,我们还会得到一个真正可以运行的脚本。以后每次修改 Skill,都可以用它重新检查一遍,确认原来的能力没有被破坏。

前面的文章,我们已经创建了一个可以工作的 Skill:meeting-notes-formatter。它可以把杂乱的会议记录,整理成一份结构清晰、方便团队分享的会议纪要。

不过,创建一个可以工作的 Skill,和真正信任它、使用它,并推荐给你的同事,是两回事。

一个 Skill 只成功运行一次,可能只是这次提示词刚好合适。只有它在不同输入和多次运行中,仍然能够满足同一套标准,才算真正成为了一套可靠的基础设施。

我们使用 Skill,本来就是为了提高一致性。而测试,就是我们获得这种一致性的方式。

所以,还是准备一杯咖啡吧。☕

接下来看看,怎样让这个 Skill 成为“可以真正信任、使用,并愿意推荐给同事”的 Skill。

为什么测试总是最容易被跳过

这种情况其实很常见:

我们写完 Skill,粘贴一段杂乱的会议记录,它很快生成了一份漂亮的摘要。看起来没有什么问题,于是就会觉得:这个 Skill 完成了。

但当你推荐给你的同事,一周之后,同事换了一种写法,Skill 可能根本没有触发;或者虽然触发了,却凭空添加了一个从来没有人确认过的行动项。甚至同样的输入,周一和周五得到的结果也完全不一样(稍微不一样是可以接受的,毕竟这就是 AI)。

这并不一定说明 Skill 已经坏了。更准确地说,它从一开始就没有经过测试。

测试一个 Skill,需要回答三个问题,而且三个都不能少:

  • 该触发的时候,它能否正确触发? 不该触发时,又能否保持安静?
  • 它的输出是否正确? 结构是否符合要求,有没有虚构信息?
  • 它是否足够一致? 多次运行时,结果是否保持相同的结构?

少了其中任何一项,这个 Skill 都仍然带有很强的偶然性。

1-kRdY8EHwC7yVUiWzB2d-pA-zh.png

不过,在开始测试之前,我们还要先确定:这个 Skill 究竟需要测试到什么程度。

三种测试强度

并不是所有 Skill 都需要一套完整的测试系统。测试投入应该和它的重要程度相匹配。

  • 手动测试:直接向 Agent 输入请求,观察它是否触发,以及最终输出是否正确。速度快,不需要额外准备,很适合第一次检查。
  • 脚本测试:把测试用例自动化。以后每次修改 Skill,都可以重新运行相同的测试。这也是本文后面会用到的方式。
  • 程序化评估:针对固定测试集系统运行完整的 Eval,通过具体宿主或模型平台提供的 API、运行记录和评分器,对结果进行衡量。

自己使用的 Skill,测试要求显然不需要和面向几千名用户发布的 Skill 一样。

可以先记住一个简单的判断方法:

个人 Skill,手动测试通常已经够用;团队 Skill,最好加入脚本;产品级 Skill,则需要程序化评估。

我们先从手动测试开始,再把后面那些重复工作交给脚本。

不过,在正式开始之前,还有一个很实用的经验,可以节省不少时间。

先解决一个真正困难的样本

真正擅长创建 Skill 的人,通常不会一开始就准备二十个测试用例。

他们会先拿出一个真正难处理的输入,例如能找到的最混乱的一份会议记录,然后不断修改 Skill,直到 Agent 可以正确处理它。接下来,再把这次有效的经验整理出来,扩展到更多类型的测试用例。

这种方式得到反馈的速度更快。解决一个真正困难的失败案例,通常比观察十个简单用例全部通过更有价值。

所以,可以先让一个很糟糕的样本稳定工作,然后再扩大测试覆盖。

1-Ur08hIWgJBeSVh-RK7gMbQ-zh.png

下面继续使用 meeting-notes-formatter,依次完成三种测试。

测试一:它会在正确的时机出现吗

前两篇提到过,Agent 在决定是否加载一个 Skill 时,首先看到的是 Frontmatter 中的 namedescription。其中,自动匹配主要依赖 description

因此,触发测试本质上是在检查我们的 description:它是否覆盖了真实用户可能使用的表达,同时又没有把范围写得过于宽泛。

首先,需要准备两个列表。

应该触发: 真实用户可能输入的内容。

- “整理一下这些会议记录”
- “把电话会议的笔记清理一下”
- “根据下面的内容编写会议纪要”
- “总结一下我们刚才的会议”
- 直接粘贴一段杂乱的会议记录,不附带任何命令

不应该触发: 看起来有些接近,但实际没有关系的请求。

- “旧金山今天的天气怎么样?”
- “帮我写一个 Python 函数”
- “创建一份支出统计表”

第二个问题列表和第一个问题列表同样重要。一个几乎遇到什么请求都会触发的 Skill,和一个永远不会触发的 Skill 一样有问题。

这里可以先做一个快速检查,直接问 Agent:

“你会在什么情况下使用 meeting-notes-formatter Skill?”

Agent 的回答通常会重新解释一遍 description。如果它的回答根本没有覆盖“应该触发”列表中的表达,那么问题多半出在描述上,而不是 Agent 本身。

不过,这种方式只能检查 Agent 如何理解描述,不能代替真正的触发测试。正式测试时,仍然要把前面的正向和负向请求逐条输入,并观察 Skill 是否真的被调用。

1-jE0J2NFDCmt1LeM9p4RvAA-zh.png

能够正确触发只是第一步。接下来还要看看,它真正生成的内容是否正确。

测试二:输出正确吗

这时,我们在上一篇文章中定义的成功标准就派上用场了。

我们要求这个 Skill:

  • 始终生成四个章节:参会者、已做出的决策、行动项、待解决问题
  • 不虚构原始记录中不存在的信息;
  • 为每个行动项提供负责人,无法确认时标记为“未分配”;
  • 最终结果比原始会议记录更短。

所谓功能测试,就是运行一个已知输入,然后按照这些规则检查输出。

例如,可以使用下面这段杂乱的会议记录:

“和 Sarah、Tom 讨论了产品发布。我们决定推迟到 3 月 15 日。Sarah 会在下周五之前完成新闻稿。Tom 需要和法务沟通,但还不知道什么时候完成。我们仍在讨论是否把移动端功能放进 v1。”

正确的输出应该包含全部四个章节,将 Sarah 和 Tom 列为参会者,记录 3 月 15 日这个决定,并把新闻稿的负责人分配给 Sarah。

还有一个非常关键的要求:不能凭空补充会议日期,因为原始记录中根本没有提供。

最后这一项很容易被忽略。

Skill 最常见的问题,往往不是少了一个章节,而是非常肯定地补出一条原文里不存在的信息。

豆包胡编乱造的情况还是很多的。我问过它一些关于 Compose 的问题,它甚至会捏造一些函数来糊弄我。

所以,测试集中至少应该加入一个“正确答案就是未说明”的用例。

1-ZeVO7c3MRsPc3xt5hPpj9w-zh.png

前两项测试通过之后,还剩最后一项。它决定了我们得到的究竟是一个演示,还是一个可以真正使用的工具。

测试三:它每次都能工作吗

使用同一份输入,连续运行五次。

然后看看,五次输出是否都采用相同的结构。

如果第一次运行时生成了全部四个章节,第三次却丢掉了“待解决问题”,那么即使每次输出单独看起来都没有明显错误,这个 Skill 仍然不能算可靠。

这里检查的并不是完全相同的文字。具体措辞可以变化,我们关心的是相同的结果形状:章节相同、格式规则相同、缺失信息的处理方式相同。

如果结构在多次运行中发生漂移,通常说明指令还不够严格。可以把关键规则移动到更靠前的位置,并更明确地写出什么才算真正完成。

这也可以看作 Agent Harness 的一部分。当然,这不是 Agent Harness 的全部,但在 Agent 开发中,我们确实会花很多时间编写成功标准、反复运行样本并检查回归。

一致性本来就是 Skill 存在的意义。如果只能保留一组测试,就优先保留包含明确成功标准的重复回归测试,避免只验证“每次都一样”,却没有验证“每次都正确”。

不过,手动重复这些操作很快就会让人厌烦。下面把它们自动化。

使用一个脚本完成三种测试

手动测试很适合最开始的检查。

但 Skill 后面还会继续修改,我们也希望每次改动之后,可以在几秒钟内重新检查全部内容,而不是再次输入十几条请求。

因此,可以在 Skill 目录中加入一个小型但真正能够运行的测试 Harness。它会读取实际使用的 SKILL.md,而不是单独复制一份,然后完成前面的三种测试。

meeting-notes-formatter/
├── SKILL.md
├── references/
│   └── formatting-rules.md
├── assets/
│   └── meeting-template.md
└── tests/
    └── test_skill.py   ← 测试 Harness

完整脚本可以在这里获取:test_skill.py

这份脚本只使用 Python 标准库。设置 API Key 后即可运行:

export ANTHROPIC_API_KEY="sk-ant-..."
python tests/test_skill.py --mode all

这里使用的是基于 Anthropic API 编写的测试脚本。如果使用 Codex、OpenAI API 或其他 Agent 宿主,需要替换对应的模型调用与运行观测方式,但测试思路本身没有变化。

运行之后,会得到类似下面的结果:

=== 1. TRIGGERING ===
  [PASS] (expected: trigger    ) Format these meeting notes for me
  [PASS] (expected: stay quiet ) What's the weather in San Francisco?
  ...
  -> 9/9 trigger cases correct

=== 2. FUNCTIONAL ===
  [PASS] happy_path_with_owners: required sections present
  [PASS] happy_path_with_owners: no invented date
  ...
  -> 6/6 functional checks passed

=== 3. CONSISTENCY (5 runs) ===
  run 1: sections = ['Action Items', 'Attendees', 'Decisions', 'Open Questions']
  ...
  -> PASS: all runs produced the same structure

==================================================
TOTAL: 16/16 checks passed

1-jN0I7hF6UdFbC_mhK90oxA.png

关于它的实现方式,注意几个地方:

  • 触发测试 只把 description 作为路由信息发送给模型,然后询问“这个 Skill 是否应该触发”。因此,它直接测试了自动匹配中最重要的那段文字;
  • 功能测试和一致性测试 会把 SKILL.md 的正文作为指令,再使用普通代码,根据部分成功标准检查输出,例如章节是否完整、内容是否过长,以及在未提供日期时是否出现了 ISO 8601 格式的日期;
  • 脚本读取的是真实 SKILL.md,因此不会另外维护一份容易与实际 Skill 脱节的指令副本。

它并不是一套完整的生产级 Skill Eval,更不是所有平台通用的 Skills API。它只是一个可以在每次修改之后快速运行的测试 Harness。例如,它对“虚构日期”的检查主要依赖 YYYY-MM-DD 格式与少量“未说明”类关键词,仍可能漏掉“3 月 10 日”这类自然语言中的虚构日期;如果这是关键风险,还需要补充更严格的检查或人工复核。

同时,它的触发测试是在模拟路由逻辑,不能完全替代真实 Agent 宿主中的端到端测试。不同宿主对 Skill 的发现、上下文注入、工具调用和权限处理可能存在差异。

不过,它已经足够适合快速回归。测试用例只是脚本顶部的一些列表,后续扩展也很简单:增加一条表达,补充一个检查,然后重新运行。

测试失败时,它也会告诉我们应该调整什么地方。

怎样理解不同的失败结果

测试不是为了让我们看到一排绿色的 PASS,而是为了指出问题所在。

应该触发,却没有触发,也就是漏触发(undertriggering)。 通常应该先检查 description 是否太模糊,或者缺少真实用户会使用的表达。可以补充更具体的触发用语;对于技术类 Skill,还可以加入准确的关键词和文件类型。

不相关的请求也会触发,也就是误触发(overtriggering)。 这时可以先检查 description 的范围是否太宽。需要进一步明确任务范围,并说明哪些相邻场景不应该使用它。

能够正确触发,但输出错误或者不一致。 这时问题通常不在 description,而在具体指令。需要收紧操作步骤,把关键规则移动到更靠前的位置,并明确说明正确结果应该是什么样。

1-Oq8LAbwGyEzB0Y--c18ATA-zh.png

测试并不是为了第一次运行就全部通过。真正重要的是:一旦某个地方发生退化,我们可以在几秒钟内发现,并且知道应该从哪里开始检查。

Skill 本来就是一份会持续变化的文档。发布之后,真实输入会带来我们没有预料到的情况,我们也会重新回来完善指令。测试套件可以让这个循环变得更快,也更安全。

未完待续

现在,我们已经创建了一个 Skill,也初步验证了它可以正确触发、生成符合要求的结果,并在多次运行中保持结构一致。

接下来只剩最后一步:怎样把它从自己的目录中拿出来,交给其他人使用。

接下来我们会继续讨论:

  • 怎样正确分享 Skill;
  • 如何进行版本管理,避免更新破坏其他人的工作流;
  • 怎样在团队中部署;
  • 如何避开那些会在长期使用中悄悄降低 Skill 可靠性的问题。

实际上这个短系列最开始的问题,是我们总要在每次对话中重复相同的要求。现在,我们已经可以创建一套工作流,证明它足够可靠,并且有信心把它交给其他人。

这就是我们最终想做的事情。