> ## Documentation Index
> Fetch the complete documentation index at: https://skills.developerdoc.cn/llms.txt
> Use this file to discover all available pages before exploring further.

# 优化 skill 描述

> 如何改进 skill 的 description，使它能够在相关提示词上稳定触发。

一个 skill 只有在被激活时才有帮助。`SKILL.md` frontmatter 中的 `description` 字段，是 agent 判断某个任务是否应该加载该 skill 的主要依据。描述过于模糊，skill 就会在该触发时不触发；描述过于宽泛，skill 就会在不该触发时误触发。

这份指南介绍如何系统地测试并改进 skill 的 `description`，从而提升触发准确性。

## skill 触发机制是如何工作的

agent 使用[渐进式披露](/what-are-skills#how-skills-work)来管理上下文。启动时，它们只会加载每个可用 skill 的 `name` 和 `description`，信息量刚好足以判断某个 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 时上下文膨胀。[规范](/specification#description-field)对它也有 1024 个字符的硬限制。

## 设计触发 eval 查询

要测试触发效果，你需要准备一组 eval 查询，也就是一批真实风格的用户提示词，并标注它们是否应该触发你的 skill。

```json eval_queries.json theme={null}
[
  { "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 客户端实现：

```bash theme={null}
#!/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 '.'
```

<Tip>
  如果你的 agent 客户端支持，一旦结果已经明确，就可以提前终止这一轮运行。也就是说，只要 agent 已经 consult 了 skill，或者已经在未 consult 的情况下开始工作，这一轮就没必要继续跑完。这样能显著降低整套 eval 的时间和成本。
</Tip>

## 用 train / validation 切分避免过拟合

如果你用全部查询一起优化描述，就有过拟合风险，也就是写出了一个只对这些特定表达有效、但一换新表达就失效的描述。

解决方案是把查询集拆分成两部分：

* **Train set（约 60%）**：用来发现问题并指导你改进的查询。
* **Validation set（约 40%）**：提前留出来，只用来检查改进是否真的具备泛化能力的查询。

要确保两个集合里都按比例混合“应触发”和“不应触发”的查询，不要不小心把所有正样本都放进同一个集合里。先随机打乱，然后在多轮迭代中保持这次切分不变，这样你比较的才是同一基准上的变化。

如果你使用的是像[上面](#running-multiple-times)那样的脚本方式，可以把查询拆成两个文件：`train_queries.json` 和 `validation_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 的版本。

通常迭代五轮左右就足够了。如果效果一直不提升，问题可能不在描述，而在于查询本身设计得有问题，比如过于简单、过于困难，或者标注不准确。

<Tip>
  [`skill-creator`](https://github.com/anthropics/skills/tree/main/skills/skill-creator) 这个 Skill 可以把这套闭环完整自动化：它会拆分 eval 集、并行评估触发率、使用 Claude 提出描述改进建议，并生成一个可实时查看的 HTML 报告。
</Tip>

## 应用优化结果

选定最佳描述后：

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

优化前后对比：

```yaml theme={null}
# 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 输出质量](/skill-creation/evaluating-skills)，了解如何建立测试用例、给结果打分并继续迭代。
