Communitygithub.com

UiPath/技能激活检查

生成并运行一套coder-eval 激活测试套件,用于检查 Claude Code 技能是否能够正确触发。当用户询问技能是否生效、想要测试技能激活,或担心技能已悄然停止触发时使用。

技能激活检查 是什么?

该技能提供了一套系统化的方法来测试和验证 Claude Code 技能是否按预期激活。它通过构建一组标记好的用户请求数据集(包括正面用例和干扰项),在真实的 Agent 环境中运行每个请求,并评分技能是否被正确触发。该技能支持所有使用 `Skill` 工具的 Agent(如 Claude Code),以及通过文件读取检测技能使用的 Agent(如 Codex),因此具有广泛的适用性。核心工作流程包括六个步骤:定位目标技能并检查其 frontmatter 描述是否被截断或从技能列表中驱逐、确认 coder-eval CLI 已安装、检查现有测试套件以避免重复构建、设计包含正面用例和干扰项的数据行、编写测试套件 YAML 和 JSONL 文件、以及运行并解释结果报告。该技能特别强调了两个关键问题:技能描述可能因字符预算限制而被截断,以及在技能数量较多的仓库中,描述可能因列表溢出而被丢弃,从而导致召回率低的假象。它提供了诊断和解决这些问题的具体方法,帮助开发者准确判断技能描述的有效性。

兼容平台✓Claude Code✓Codex CLI~Cursor
npx skills add https://github.com/UiPath/coder_eval/tree/main/plugins/coder-eval/skills/check-skill

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

技能激活检查 是做什么的?

技能只有在模型在正确时机调用它时才有价值。这个决策几乎完全取决于技能 frontmatter 中的 description(描述)——所以“我的技能会触发吗?”是一个可测量的问题,而这就是你的测量方法:构建一组标记好的用户请求,对每个请求运行真正的 Agent,并评分技能是否被启用。

用户的请求是:$ARGUMENTS

步骤 1 — 定位目标技能

$ARGUMENTS 可能是 SKILL.md 的路径、技能 目录 的路径、技能名称或为空。

  • 为空:使用 glob 查找 .claude/skills/*/SKILL.md 和 **/skills/*/SKILL.md。匹配到一个→使用它。多个→列出它们并询问使用哪个。没有→告知并停止。
  • 目录:使用该目录内的 SKILL.md。
  • 名称:查找匹配的技能目录。

裸技能名称即包含 SKILL.md 的目录名称。读取 frontmatter 中的 description 并将其放在面前:这个字符串是模型匹配的依据,因此它是数据行设计和文本编辑建议的主要输入。

在读取时测量其长度——两个独立的预算会截断它,任何一个都会产生一个看起来像是措辞不佳的低召回率结果。

  1. 单个技能截断。 description 和 when_to_use 被拼接并在一个固定的字符预算处截断——1,536 个字符,可通过 skillListingMaxDescChars 设置配置。超过截断点的触发文本完全无法影响激活,因此它等同于不存在。
  2. 整个列表的预算,这在执行激活测试套件的仓库中更为重要。 列表总是包含每个技能的名称,但其总字符预算大约为 模型上下文窗口的 1%,由用户安装的 每个 技能共享。当溢出时,Claude Code 会从你调用最少的技能开始丢弃描述。

第二个后果值得直言:在一个多技能仓库中,一个技能可能拥有完美的描述但召回率接近零,因为它的描述从未被放入列表中。然后重写措辞也无济于事。而丢弃顺序是最少调用的优先,因此一个新创作的技能——它本身被调用次数极少,且正是有人对此运行测试套件的对象——是最有可能的受害者。这是对被测技能的系统性偏见。

如果问题出在列表上,可用的杠杆有:skillListingBudgetFraction(默认 1%)、SLASH_COMMAND_TOOL_CHAR_BUDGET 环境变量(固定字符数),以及将 skillOverrides 设置为 "name-only" 以从不需要匹配的技能中释放预算。

如果技能在其 frontmatter 中没有 description,则停止并报告该发现——没有描述的技能永远无法被模型调用,因此测试套件会构建出零召回率的结果,且无法提供任何有用信息。

步骤 2 — 确认已安装 coder-eval

运行 coder-eval --version。安装此插件并未安装 CLI,且没有它就无法验证或运行测试套件。

如果缺失,请按照 ${CLAUDE_PLUGIN_ROOT}/reference/cli-setup.md 的说明:提供安装选项,运行前询问,并在安装后使用 coder-eval --version 确认。切勿未经提示安装,如果用户拒绝则不要继续。

该参考文档还涵盖了版本检查的另一半——该项目是否固定了 coder-eval 版本,以及当已安装版本不匹配时该如何处理。

步骤 3 — 检查该技能已有的覆盖

在设计任何内容之前,先查看仓库中已有的内容。根据 ${CLAUDE_PLUGIN_ROOT}/reference/repo-layout.md 找到任务树,并使用 glob 查找带有 skill_triggered 标准的任务。

如果现有套件已命名此技能,报告其覆盖率——包含多少个正面用例和干扰行,及其依赖的 suite_thresholds——并提议扩展它,通过向其数据集追加行,而不是搭建一个并行的套件。一个技能的两个套件会逐渐偏离,用户每次运行都要为两者付费。只有在没有套件覆盖此技能时,或者用户在查看现有内容后选择这样做时,才进行搭建。

有两件事值得报告,而不是默默绕过:

  • 一个没有干扰行的现有套件。 精度按定义为 1.0,因此其一半结果毫无意义。提议添加干扰项——这比搭建一个竞争套件或保持沉默是更实际的改进。
  • 一个命中了不同技能的相邻套件。 这不算是覆盖。继续搭建,并说明为什么相邻套件不算。

然后检查 agent.plugins 应该来自哪里。 如果任务要解析的实验已经提供了该块,则继承它,不要编写一个新的——重新声明实验已提供内容的任务会逐渐偏离,且没有任何机制能捕获这种偏差。只有当没有实验向沙箱暴露该技能时,才编写模板自身的块。如果存在多个实验,且不清楚该任务解析的是哪一个,则询问而非猜测。

步骤 4 — 设计数据行

这一步是整个实验的核心。其余都是机械工作。

正面用例行——真实用户可能提出的、描述声称要覆盖的请求。进行释义;切勿复制描述中的措辞。从描述中直接提取的行测试的是字符串匹配,而非激活。变换词汇,并至少包含一个间接行,其中用户描述的是他们的问题而非技能执行的操作。

干扰行——技能不应处理的相邻请求,尤其是那些与描述共享词汇的请求。这些才是使精度有意义的关键。

兄弟技能所有行 (可选)——合理属于同一仓库中已命名的其他技能的请求,且 expected_skill 设置为该兄弟技能。在多技能仓库中,两个相邻技能之间的错误路由是常见故障,而一个普通的干扰行只能显示发生了误触发,却无法显示误触到了哪里。这些行指明了去向。

当两个技能具有重叠主题,且你想知道哪一个获胜时,添加这些行。它们会消耗额外的行,而每一行都是一次完整的 Agent 运行——因此将其视为有针对性的后续步骤,而非默认操作。

切勿在提示中命名技能。 那测试的是服从性,而非激活:

  • 错误:“使用 pdf-forms 技能填写此申请表。”
  • 正确:“我需要填写此 PDF 申请表上的字段并将其发回。”

此规则不受兄弟技能所有行的影响:expected_skill 是数据集中的标签,由标准读取,且从未向 Agent 展示。该行的 prompt 仍然不得命名任何技能,包括兄弟技能。

规模。 最少 3 个正面用例 + 3 个干扰行。目标是每种 8-12 个以获得可据此行动的信号——3 行上的召回率以 33 个百分点的步长移动,这太粗糙,无法区分真正的回归和噪声。提供的模板包含 6 行,因为那只是演示的最小值,而非目标。任何兄弟技能所有行都是额外的:8-12 个正面用例加 8-12 个干扰行已经是 16-24 次运行,因此在编写套件前说明最终总数。

拒绝生成没有干扰行的套件。 没有负面用例,精度按定义为 1.0,且一半结果毫无意义。说明原因并要求提供相邻案例。

步骤 5 — 编写套件

将两个模板文件复制到用户的任务树中,位置按照 ${CLAUDE_PLUGIN_ROOT}/reference/repo-layout.md 确定(如果仓库中没有,则提议一个位置并询问):

  • ${CLAUDE_PLUGIN_ROOT}/reference/templates/activation.yaml
  • ${CLAUDE_PLUGIN_ROOT}/reference/templates/activation-rows.jsonl

然后进行替换,并保持 JSONL 与 YAML 相邻(dataset.paths 条目相对于任务文件解析):

  • task_id → <技能名称>-activation
  • skill_name → 裸技能名称
  • 每个正面用例行的 expected_skill → 相同的裸名称;干扰行保持 "";兄弟技能所有行的 expected_skill → 该兄弟技能的裸名称
  • 每一行的 prompt → 步骤 4 设计的请求,每行一个 JSON 对象

然后使技能可访问,这是决定套件是否有意义的步骤。 任务在一个全新的沙箱中运行,该沙箱不包含用户的任何文件,因此除非任务指明了技能的位置,否则 Agent 不会获得任何技能。这就是模板中的 agent.plugins 块——并且只有在没有其他东西暴露该技能时才应由模板承担:如果步骤 3 找到了提供该块的实验,则继承它并删除模板的副本,而不是编写第二个声明。

否则,填写它。path 必须是插件根目录:一个包含 skills/ 子目录的目录,这样技能位于 <路径>/skills/<技能名称>/SKILL.md。.claude-plugin/plugin.json 是可选的——没有它时命名空间默认为目录自身的名称。

对于 .claude/skills/pdf-forms/SKILL.md,其根目录是 .claude,而不是 .claude/skills。指向一个只包含技能目录的目录则什么也加载不到。

构建一个最小根目录,而不是直接指向 .claude 本身。 一个插件根目录会加载整个插件,而不仅仅是其技能:位于 skills/ 旁边的 agents/、commands/ 或 hooks/ 目录也会对被评估的 Agent 可见。指向仓库的 .claude 就等于将每个项目的子Agent 和命令都交给了 Agent——而一个可以回答请求的子Agent 是一个混杂因素,而非一个细节。如果在测量 pdf-forms 时 .claude/agents/pdf-expert.md 存在,Agent 可能会委托给它而不是调用技能;skill_triggered 记录为 no,并且召回率因与技能描述无关的原因而下降。这也使得数字依赖于仓库,因此两个套件不再可比。

因此,构建一个只包含被测单元的根目录,并指向它:

SKILL_ROOT="$(mktemp -d)"
mkdir -p "$SKILL_ROOT/skills"
ln -s "$(pwd)/.claude/skills/pdf-forms" "$SKILL_ROOT/skills/pdf-forms"
export SKILL_SOURCE_PATH="$SKILL_ROOT"

在符号链接不方便的地方(Windows、某些 CI 镜像),使用 cp -R 替代 ln -s。如果用户更喜欢单行命令,export SKILL_SOURCE_PATH="$(pwd)/.claude" 仍然有效——但需明确指出这也会暴露 .claude 下的所有其他内容,因此低召回率可能是兄弟技能而非技能本身的问题。

将其保留为环境变量,而不是将绝对路径硬编码到 YAML 中——套件会被提交并在其他机器上重新运行。如果变量未设置,技能就会缺失,每个正面用例行都得 0 分,且结果与一个永不触发的技能无法区分,因此在报告任何低召回率的发现之前,先确认它已被设置。

即使技能来自插件并调用为 plugin:skill,skill_name 也必须是裸名称——检查器在比较前会去除命名空间。这里使用带命名空间的值会悄悄地在每一行上得零召回率,这看起来完全像一个损坏的技能。

因为匹配基于裸名称,它无法处理名称冲突。 两个名为 init 的技能——一个来自插件,一个内置——对标准来说是同一个字符串,因此它会将任何触发的技能视为被测对象。在信任结果之前,检查该名称在所有已安装内容中是否唯一(/context 和 /doctor 会列出活动集)。冲突不会产生错误;它测量的是错误的技能。如果存在冲突,则直接说明,而不是报告数字——重命名技能,或在不存在冲突的环境中测量,才是唯一诚实的修复方法。

对于超出此模板的标准字段,请阅读 ${CLAUDE_PLUGIN_ROOT}/reference/criteria.md。

步骤 6 — 在运行前验证

coder-eval plan <activation.yaml的路径> 必须返回退出码 0。它仅进行架构检查——不会读取数据集文件——因此也请确认 JSONL 文件与 YAML 文件相邻且每行包含一个对象。

步骤 7 — 运行

每一行都是一次完整的 Agent 运行:N 行意味着 N 次运行和真实的令牌成本。说明行数和将要使用的 Agent/模型,然后在开始前询问。

coder-eval run <activation.yaml的路径>

该标准是 Agent 无关的——它通过 Skill 工具检测 Claude 是否使用了技能,并通过从磁盘读取技能文件来检测任何没有该工具的 Agent(例如 Codex)——因此无论任务解析到哪个 Agent,同一套件都有效。

步骤 8 — 报告与解读

呈现召回率、精度、F1 分数和混淆矩阵,然后说明它们的含义:

  • 低召回率(遗漏了本应捕获的行):在断定描述覆盖面不足之前,先排除截断和列表驱逐问题。 两者都会产生与措辞不佳无法区分的低召回率结果,且检查成本都很低廉:/doctor 会估算列表的上下文成本及其最大贡献者,而 /context 中的 Skills 行会报告应用预算后的列表大小——这才是模型实际接收到的内容。只有在描述被确凿证明在列表内且未超过单个技能截断点时,措辞才是罪魁祸首:它没有提到应该触发它的情境、文件类型或措辞方式。
  • 低精度(在干扰行上触发):描述覆盖过度,正在抢占相邻请求。缩小其范围,并明确说明技能不适用于什么。
  • 误触集中在一个兄弟技能上(配合兄弟技能所有行):这是两个描述之间的边界争议,而非一个模糊的描述。单独修复被测技能往往会转移问题而非消除它——明确说明两个技能各自不适用于什么,然后重新运行。
  • 两者都高:报告数字和行数,并指出小套件说明的问题有限——提议扩大套件。

将问题指向 frontmatter 中的 description 作为待修改的内容,引用导致问题的具体行,并在用户重写后提议重新运行套件。

Individual skills in this repo

This repo contains 5 individual skills — each has its own dedicated page.

相关技能