Skip to main content
一个 skill 只有在被激活时才有帮助。SKILL.md frontmatter 中的 description 字段,是 agent 判断某个任务是否应该加载该 skill 的主要依据。描述过于模糊,skill 就会在该触发时不触发;描述过于宽泛,skill 就会在不该触发时误触发。 这份指南介绍如何系统地测试并改进 skill 的 description,从而提升触发准确性。

skill 触发机制是如何工作的

agent 使用渐进式披露来管理上下文。启动时,它们只会加载每个可用 skill 的 namedescription,信息量刚好足以判断某个 skill 何时可能相关。当用户任务与某个描述匹配时,agent 才会把完整的 SKILL.md 读入上下文,并按其中说明执行。 这意味着 description 承担了整个触发判断的重任。如果描述没有清楚传达这个 skill 什么时候有用,agent 就不知道应该调用它。 还有一个重要细节:agent 通常只会在任务需要超出自身基础能力的知识或能力时,才会去 consult skills。像“读一下这个 PDF”这样简单的一步请求,即使和某个 PDF skill 的描述完全匹配,也未必会触发,因为 agent 用基础工具就能完成。真正能体现高质量描述价值的,往往是那些涉及专业知识的任务,例如陌生 API、领域特定工作流,或者不常见的数据格式。

编写有效的描述

在测试之前,先明确一条好的描述应该长什么样,会很有帮助。这里有几个原则:
  • 使用祈使式表达。 把描述写成对 agent 的指令,例如“在……情况下使用这个 skill”,而不是“这个 skill 会做……”。agent 正在决定是否采取行动,所以你应该直接告诉它什么时候行动。
  • 关注用户意图,而不是实现细节。 描述用户想达成什么,而不是 skill 内部如何实现。agent 匹配的是用户提出的需求。
  • 宁可写得主动一点。 明确列出 skill 适用的上下文,包括用户没有直接点出领域名称的情况,例如“即使用户没有显式提到 ‘CSV’ 或 ‘analysis’ 也应使用。”
  • 保持简洁。 一般来说,几句话到一个短段落最合适:既足够覆盖 skill 的边界,又不会因为描述过长,让 agent 在加载大量 skills 时上下文膨胀。规范对它也有 1024 个字符的硬限制。

设计触发 eval 查询

要测试触发效果,你需要准备一组 eval 查询,也就是一批真实风格的用户提示词,并标注它们是否应该触发你的 skill。
eval_queries.json
[
  { "query": "I've got a spreadsheet in ~/data/q4_results.xlsx with revenue in col C and expenses in col D — can you add a profit margin column and highlight anything under 10%?", "should_trigger": true },
  { "query": "whats the quickest way to convert this json file to yaml", "should_trigger": false }
]
建议准备大约 20 条查询:其中 8 到 10 条应该触发,8 到 10 条不应该触发。

应触发的查询

这类查询用于测试描述是否准确覆盖了 skill 的边界。设计时可以沿几个维度变化:
  • 表达方式:有些正式,有些口语化,有些带错别字或缩写。
  • 显式程度:有些直接点出 skill 所属领域(例如“分析这个 CSV”),有些只描述需求而不点名(例如“我老板想要这份数据文件生成一张图表”)。
  • 细节丰富度:混合简短提示词和上下文丰富的提示词,例如短句“分析我的销售 CSV 并做一张图”,以及包含文件路径、列名和背景说明的长消息。
  • 复杂度:变化步骤数量和决策点。既要有单步骤任务,也要有多步骤工作流,用来测试当 skill 所解决的问题被埋在更长任务链中时,agent 是否仍能识别它相关。
最有价值的“应触发”查询,是那些 skill 明明能帮上忙,但从字面上看并不明显相关的情况。正是这些场景,最能体现描述措辞的差异。如果用户本来就精确说出了 skill 的用途,那只要描述写得不是太差,通常都会触发。

不应触发的查询

最有价值的负样本是近似但不匹配的查询,也就是那些和你的 skill 共享某些关键词或概念,但真正需要的却是别的东西。它们测试的是描述是否足够精确,而不只是够宽泛。 对于一个 CSV 分析 skill,较弱的负样本会是:
  • "Write a fibonacci function" — obviously irrelevant, tests nothing.
  • "What's the weather today?" — no keyword overlap, too easy.
更强的负样本示例:
  • "I need to update the formulas in my Excel budget spreadsheet" — shares “spreadsheet” and “data” concepts, but needs Excel editing, not CSV analysis.
  • "can you write a python script that reads a csv and uploads each row to our postgres database" — involves CSV, but the task is database ETL, not analysis.

提高真实性的小建议

真实用户提示词通常会带有很多泛化测试查询里没有的上下文。你可以加入:
  • File paths (~/Downloads/report_final_v2.xlsx)
  • Personal context ("my manager asked me to...")
  • Specific details (column names, company names, data values)
  • Casual language, abbreviations, and occasional typos

测试描述是否会触发

基本做法是:在已安装该 skill 的前提下,把每条查询交给你的 agent 运行,然后观察 agent 是否调用了它。前提是这个 skill 已经被注册,并且 agent 可以发现它。具体怎么做会因客户端不同而有所差异,例如可能通过 skills 目录、配置文件,或者 CLI 参数来实现。 大多数 agent 客户端都会提供某种可观测性能力,比如执行日志、工具调用历史或详细输出,让你能看到一次运行中 consult 了哪些 skills。具体细节要看你的客户端文档。如果 agent 读入了这个 skill 的 SKILL.md,就算触发;如果它直接继续执行而没有 consult 这个 skill,就算未触发。 一条查询在以下情况下算“通过”:
  • should_trigger is true and the skill was invoked, or
  • should_trigger is false and the skill was not invoked.

多次运行

模型行为具有非确定性。同一条查询,这次可能触发 skill,下次可能不触发。因此每条查询都应运行多次(3 次是一个不错的起点),并计算触发率,也就是触发 skill 的运行次数占总运行次数的比例。 如果一条“应触发”查询的触发率高于某个阈值(0.5 是一个合理默认值),它就算通过;反过来,“不应触发”查询的触发率若低于该阈值,则算通过。 如果有 20 条查询、每条跑 3 次,那就是 60 次调用。你大概率会希望把这个流程脚本化。下面是一种通用结构,你可以把其中的 claude 调用和 check_triggered 中的检测逻辑替换成你自己的 agent 客户端实现:
#!/bin/bash
QUERIES_FILE="${1:?Usage: $0 <queries.json>}"
SKILL_NAME="my-skill"
RUNS=3

# This example uses Claude Code's JSON output to check for Skill tool calls.
# Replace this function with detection logic for your agent client.
# Should return 0 (success) if the skill was invoked, 1 otherwise.
check_triggered() {
  local query="$1"
  claude -p "$query" --output-format json 2>/dev/null \
    | jq -e --arg skill "$SKILL_NAME" \
      'any(.messages[].content[]; .type == "tool_use" and .name == "Skill" and .input.skill == $skill)' \
      > /dev/null 2>&1
}

count=$(jq length "$QUERIES_FILE")
for i in $(seq 0 $((count - 1))); do
  query=$(jq -r ".[$i].query" "$QUERIES_FILE")
  should_trigger=$(jq -r ".[$i].should_trigger" "$QUERIES_FILE")
  triggers=0

  for run in $(seq 1 $RUNS); do
    check_triggered "$query" && triggers=$((triggers + 1))
  done

  jq -n \
    --arg query "$query" \
    --argjson should_trigger "$should_trigger" \
    --argjson triggers "$triggers" \
    --argjson runs "$RUNS" \
    '{query: $query, should_trigger: $should_trigger, triggers: $triggers, runs: $runs, trigger_rate: ($triggers / $runs)}'
done | jq -s '.'
如果你的 agent 客户端支持,一旦结果已经明确,就可以提前终止这一轮运行。也就是说,只要 agent 已经 consult 了 skill,或者已经在未 consult 的情况下开始工作,这一轮就没必要继续跑完。这样能显著降低整套 eval 的时间和成本。

用 train / validation 切分避免过拟合

如果你用全部查询一起优化描述,就有过拟合风险,也就是写出了一个只对这些特定表达有效、但一换新表达就失效的描述。 解决方案是把查询集拆分成两部分:
  • Train set(约 60%):用来发现问题并指导你改进的查询。
  • Validation set(约 40%):提前留出来,只用来检查改进是否真的具备泛化能力的查询。
要确保两个集合里都按比例混合“应触发”和“不应触发”的查询,不要不小心把所有正样本都放进同一个集合里。先随机打乱,然后在多轮迭代中保持这次切分不变,这样你比较的才是同一基准上的变化。 如果你使用的是像上面那样的脚本方式,可以把查询拆成两个文件:train_queries.jsonvalidation_queries.json,再分别运行脚本。

优化闭环

  1. 评估当前描述在 train 和 validation 两个集合 上的表现。train 结果用于指导改动,validation 结果用来判断这些改动是否具备泛化性。
  2. train set识别失败样本:哪些“应触发”的查询没有触发?哪些“不应触发”的查询却触发了?
    • 只有 train set 中的失败样本可以用来指导改动。无论是你自己改描述,还是让 LLM 帮你优化,都不要把 validation set 的结果带入这个过程。
  3. 修订描述。 重点是做泛化而不是死记:
    • 如果“应触发”的查询没有触发,说明描述可能太窄。你需要适度扩大范围,或者补充说明这个 skill 在什么情况下有帮助。
    • 如果“不应触发”的查询被误触发,说明描述可能太宽。你需要补充这个 skill 不做什么,或者更清楚地划定它和相邻能力之间的边界。
    • 不要把失败查询里的特定关键词直接塞进描述里,那就是过拟合。应该去提炼这些查询背后的通用类别或概念,并针对那个层面做修订。
    • 如果几轮下来都卡住了,不要只做微调,可以尝试从结构上重写描述。换一种 framing 或句式,有时比局部修补更有效。
    • 同时检查描述是否仍然低于 1024 字符限制。因为在优化过程中,描述很容易越写越长。
  4. 重复步骤 1 到 3,直到 train set 全部通过,或者你已经看不到有意义的提升。
  5. 根据 validation pass rate,也就是 validation set 中通过查询的比例,选出最佳版本。注意,最好的描述未必是最后一版;有时中间某一版的 validation 表现反而优于后续那些过拟合到 train set 的版本。
通常迭代五轮左右就足够了。如果效果一直不提升,问题可能不在描述,而在于查询本身设计得有问题,比如过于简单、过于困难,或者标注不准确。
skill-creator 这个 Skill 可以把这套闭环完整自动化:它会拆分 eval 集、并行评估触发率、使用 Claude 提出描述改进建议,并生成一个可实时查看的 HTML 报告。

应用优化结果

选定最佳描述后:
  1. 更新 SKILL.md frontmatter 中的 description 字段。
  2. 确认描述仍低于1024 字符限制
  3. 验证这个描述是否按预期触发。你可以先手动试几条 prompt 做快速 sanity check。若要更严格地测试,可以另外再写 5 到 10 条全新的查询(混合“应触发”和“不应触发”),然后跑一次 eval 脚本。因为这些查询从未参与过优化过程,所以它们能更真实地检验描述是否具备泛化能力。
优化前后对比:
# Before
description: Process CSV files.

# After
description: >
  Analyze CSV and tabular data files — compute summary statistics,
  add derived columns, generate charts, and clean messy data. Use this
  skill when the user has a CSV, TSV, or Excel file and wants to
  explore, transform, or visualize the data, even if they don't
  explicitly mention "CSV" or "analysis."
改进后的描述对这个 skill 做什么 说得更具体了(汇总统计、派生列、图表、清洗),同时也对 什么时候适用 说得更完整了(CSV、TSV、Excel,甚至在没有明确关键词时也应触发)。

下一步

当你的 skill 已经能够稳定触发之后,下一步就该评估它的输出质量了。可以参考评估 skill 输出质量,了解如何建立测试用例、给结果打分并继续迭代。