当用户直接对 AI 编码代理说出“subagent-driven-development, please”或“use systematic-debugging”时,Superpowers 的测试套件能验证代理是否真正加载了该技能。这通过一系列精心设计的隔离测试脚本实现,它们模拟真实对话、检测过早动作,并验证技能调用的命名空间匹配模式,确保代理不会在没有加载正确技能的情况下就开始编码工作。

显式技能请求测试:如何验证用户直接说出技能名时代理真的加载技能

在使用 Superpowers 技能库时,一个关键的可靠性保证是:当用户明确说出一个技能的名称,代理是否真的会调用该技能并遵循其流程?如果代理忽略了用户的请求,或者在加载技能之前就自行开始写代码、编辑文件,那么整个流程就会失效。为了系统化地验证这一行为,Superpowers 项目在 tests/explicit-skill-requests 目录下提供了一套完整的测试脚本。本指南将解析这些脚本的原理和使用方法。

为什么需要显式技能请求测试?

Superpowers 的核心理念之一是 using-superpowers,即强制代理先发现并加载相关的技能,再开始工作。这类似于要求一个工人先阅读安全操作手册。如果代理(如 Claude)在用户明确请求一个技能(例如 subagent-driven-development)时,不调用 Skill 工具去加载它,而是直接开始读取计划文件或创建子代理,这就破坏了技能提供的质量门禁,可能导致流程不一致或产生错误。

测试需要覆盖多种场景:直接请求、在多轮对话后请求、使用不同能力的模型(如 Haiku)请求,以及代理已经自己“描述”过该技能后的请求。所有这些场景都旨在复现和预防可能导致技能未被触发的边缘情况。

测试脚本的核心设计与验证逻辑

所有测试脚本共享几个关键设计,用于确保结果可靠且可验证:

  1. 隔离环境 (Isolated HOME):脚本通过在 /tmp/superpowers-tests 下创建带有时间戳的目录,并设置 PROJECT_DIR 作为工作目录,避免了用户本地 ~/.claude 中的 CLAUDE.md 或其他上下文干扰测试结果。这保证了测试是在一个干净、可复现的环境中进行。
  2. 流式输出 (stream-json output):使用 claude -p--output-format stream-json 参数,将代理的全部交互过程以 JSON 格式记录到日志文件(如 claude-output.json)。这为后续的精确模式匹配提供了数据基础。
  3. 技能调用模式匹配 (Skill tool invocation pattern matching):脚本不会简单地检查代理是否说到了技能名,而是检查它是否真正调用了 Skill 工具。匹配模式为 '"skill":"([^"]*:)?${SKILL_NAME}"'。这种模式同时匹配 "skill":"subagent-driven-development" 和可能带有命名空间前缀的 "skill":"sp:subagent-driven-development" 形式,确保了兼容性。
  4. 过早动作检测 (Premature action detection):这是测试的核心失败模式检测。脚本会找到日志中第一次出现 "name":"Skill" 调用的行号,然后检查在该行之前是否存在 "type":"tool_use" 的调用,且这些调用不是 Skill 或规划用的 TodoWrite。如果存在(例如 ReadEditBash),则说明代理在请求技能之前就开始了实际操作,这是一个严重的流程违规。

测试场景详解

下面我们拆解几个关键的测试脚本及其验证的目标场景。

场景一:单轮直接请求 (run-test.sh)

这是最基础的测试。通过 run-all.sh,它批量运行了几个子场景,对应的提示文件位于 prompts/ 目录。

  • subagent-driven-development-please.txt:用户直接说“subagent-driven-development, please”。
  • use-systematic-debugging.txt:用户说“use systematic-debugging to figure out what’s wrong”。
  • mid-conversation-execute-plan.txt:用户先说明已有计划,然后请求技能。

run-test.sh 接收技能名和提示文件,创建一个包含预置计划文件的项目目录,然后调用 claude。其核心验证逻辑在检查 SKILL_PATTERNPREMATURE_TOOLS 部分。例如,对于 systematic-debugging,如果日志中没有出现 "skill":"systematic-debugging"Skill 工具调用,测试就会失败。

场景二:多轮对话请求 (run-multiturn-test.sh)

这个测试旨在重现一个更真实的失败场景:在进行了多轮规划对话后,用户才请求技能。脚本模拟了一个三轮对话:

  1. Turn 1:讨论认证系统需求。
  2. Turn 2:确认计划已写好,询问执行选项。
  3. Turn 3:关键测试,发送“subagent-driven-development, please”。

测试只检查 Turn 3 的日志。验证逻辑与单轮测试完全相同,但它能揭示出,即使代理在前几轮“知道”所有上下文,它在被明确要求时是否仍会加载技能。这能检测代理是否因为“自认为已了解”而跳过加载步骤。

场景三:Haiku 模型测试 (run-haiku-test.sh)

更强大的模型(如 Sonnet)可能更可靠地遵循指令,但成本更高。这个测试使用 --model haiku,验证能力相对较弱、价格更低的模型是否也能正确处理显式技能请求。它构建了一个五轮对话(头脑风暴、回答问题、编写计划、确认、请求技能),模拟了完整的开发前期流程,最后在第五轮发出请求。这对于在资源受限环境下使用 Superpowers 的团队至关重要。

场景四:扩展多轮与“Claude 先描述”场景

  • run-extended-multiturn-test.sh:与多轮测试类似,但轮次更多(五轮),试图通过更长的上下文来诱发失败。
  • run-claude-describes-sdd.sh:这是一个非常具体的边缘案例。测试先让代理(用 Haiku)解释 subagent-driven-development 是什么以及如何工作。然后在下一轮,用户才说“subagent-driven-development, please”。这个场景验证的是:即使代理已经用自己的话描述过该技能,当用户明确请求时,它是否会正确地将此视为一个“加载并执行”的指令,而不是继续口头解释。如果测试失败,日志会显示代理直接开始工作而没有调用 Skill 工具。

如何运行与解读测试

运行测试很简单。进入 tests/explicit-skill-requests 目录后:

  1. 运行单个测试./run-test.sh <技能名> <提示文件路径> 例如:./run-test.sh brainstorming ./prompts/please-use-brainstorming.txt
  2. 运行全套测试./run-all.sh

脚本会输出清晰的日志,包括:

  • PASSFAIL 的最终判定。
  • 本次运行中实际触发了哪些技能(从日志中提取)。
  • 过早动作检测的结果:是 OK 还是 WARNING。这是最有价值的信息,它告诉你代理是否在“走捷径”。
  • 第一条助手回复的摘要。
  • 完整日志的存放路径(在 /tmp/superpowers-tests/ 下),你可以用 jq 或文本编辑器深入分析 JSON 日志。

总结

Superpowers 的显式技能请求测试套件是一套严谨的防御性测试。它不依赖于主观判断,而是通过隔离环境、结构化输出和模式匹配,客观地验证 AI 编码代理是否遵守了“先加载,后执行”的契约。作为贡献者或高级用户,理解并运行这些测试,是确保你的技能修改或工作流调整不会破坏核心交互模型的可靠方法。它守护的是整个技能系统的可信度。

相关阅读:想了解 Superpowers 更全面的测试策略,可以参阅测试体系总览。要深入理解 subagent-driven-development 技能本身的工作流程,请阅读详解文章

FAQ

Q: 测试失败了,最可能的原因是什么? A: 最常见的原因是测试脚本中检测到了“过早动作”。这表明代理在调用 Skill 工具之前,就使用了 ReadBashEdit 等工具。你需要检查对应技能的提示词(SKILL.md)或代理的初始指令,确保它们在会话早期就足够强调了“先使用相关技能”的规则。

Q: 为什么测试要使用隔离的 HOME 目录? A: 为了排除用户个人环境的干扰。用户本地的 ~/.claude/CLAUDE.md 可能包含自定义指令,这些指令可能无意中覆盖或影响技能触发行为,导致测试结果无法反映通用场景。隔离环境保证了测试的可重复性和结果的普适性。

Q: “过早动作检测”过滤了 TodoWrite,这是为什么? A: TodoWrite 在 Superpowers 工作流中通常被代理用作规划工具,在决定使用哪个技能之前列出待办事项是合理的规划行为,不应被视为违规的“实际工作”。因此,脚本将它从“过早动作”的检测列表中排除,只关注那些直接操作项目文件或执行命令的工具。