SkillPkg Logo
如何优化 Agent Skill 描述:提升技能触发率的系统方法指南

如何优化 Agent Skill 描述:提升技能触发率的系统方法指南

Ironben/

只有在被触发时,技能才有用。SKILL.md frontmatter 里的 description 字段,是智能体判断某个任务是否要加载某个技能的主要依据。如果说明写得太模糊,该触发时不会触发;写得太宽泛,又会在不该触发时被触发。本文介绍如何系统性地测试并改进技能说明,使其触发更准确。

技能触发的工作方式

智能体使用「渐进披露(progressive disclosure)」来管理上下文。启动时,它们只会加载每个技能的 namedescription——刚好够判断一个技能是否可能相关。当用户的任务与某个技能的说明匹配时,智能体才会把该技能完整的 SKILL.md 读入上下文并按其中说明执行。

这意味着,描述文案几乎承担了全部的触发责任。如果说明里没有表达清楚技能在什么情况下有用,智能体就不知道什么时候该用它。

有个重要细节:智能体通常只会在任务需要超出它自身能力范围的知识或能力时,才去查技能。像「帮我读一下这个 PDF」这种简单的一步操作,即使和 PDF 技能的描述完全匹配,也可能不会触发该技能,因为智能体自己就能用基础工具处理。涉及专业知识的任务——比如不熟悉的 API、特定领域的工作流、不常见的格式——才是说明写得好坏真正拉开差距的地方。

如何写出有效的说明

在开始测试之前,先了解一下一条好的说明应该是什么样。几个原则:

  • 使用祈使句式。 把说明写成对智能体的指令:“当……时使用这个技能”,而不是“这个技能会做……”。智能体是在做“要不要用”的决策,所以你要告诉它“什么时候要用”。
  • 聚焦用户意图,而不是实现细节。 描述用户想达成什么,而不是技能内部怎么实现。智能体是根据用户的提问去匹配说明的。
  • 宁可写得稍微“激进”一点。 明确列出适用场景,包括用户没有直接提到领域名的情况:比如“即使用户没有显式提到『CSV』或『分析』”。
  • 保持简洁。 通常几句到一小段就够了——既要足够长,能覆盖技能的功能边界,又不能太长,否则在有很多技能时会把智能体的上下文撑爆。规范 里有 1024 字符的硬性上限。

设计触发评测用的查询

要测试触发效果,你需要一组评测查询——即一批接近真实用户的提示,并为每条标注“应不应该触发”你的技能。

eval_queries.json

1[
2 { "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 },
3 { "query": "whats the quickest way to convert this json file to yaml", "should_trigger": false }
4]

目标大约是 20 条查询:8–10 条应该触发,8–10 条不应该触发。

应该触发的查询

这些用来测试说明有没有覆盖技能的功能范围。可以从几个维度去做变化:

  • 说法:有的正式、有的口语化,有的带错别字或缩写。
  • 显性程度:有的直接点出技能领域(“分析这个 CSV”),有的只描述需求(“我老板要我从这个数据文件里做一张图表”),但不说 CSV。
  • 细节量:短促的提示(“分析我的销售 CSV 并画一张图”)和信息量很大的长提示混合出现,长提示里可以有路径、列名、背景故事等。
  • 复杂度:步骤和决策点数量要有变化。既包括一步就能完成的任务,也包括多步工作流,看智能体在任务被埋在长流程里时,能不能发现技能是相关的。

最有价值的 should-trigger 查询,是那些技能能帮上大忙,但从字面看不那么明显的场景。对于那些一眼就说出“正好是这个技能干的事”的情况,任何合理的说明都能触发,区分度不大。

不该触发的查询

最有价值的负例是“擦边球”——和你的技能共享一些关键词或概念,但实际上需要的是别的能力。这些用来测试说明是不是既覆盖广,又足够精确。

对于一个 CSV 分析技能,一些较差的负例是:

  • "编写斐波那契函数" —— 完全不相关,测不出什么。
  • "今天天气怎么样?" —— 没有任何关键词重叠,太简单。

更好的负例例子:

  • "我需要更新我的 Excel 预算电子表格中的公式。" —— 有“表格”和“数据”等概念,但需要的是 Excel 编辑,不是 CSV 分析。
  • "你能编写一个 Python 脚本,读取 CSV 文件并将每一行上传到我们的 PostgreSQL 数据库吗?" —— 虽然涉及 CSV,但任务是数据库 ETL,而不是分析。

让查询更贴近真实用户

真实的用户提示往往会带一些通用测试里没有的上下文。可以加入:

  • 文件路径(~/Downloads/report_final_v2.xlsx
  • 个人背景(“我经理让我……”)
  • 具体细节(列名、公司名、数据值)
  • 口语化的语言、缩写和偶尔的错别字

如何测试说明是否被触发

基本思路是:在安装好技能的前提下,用你的智能体跑每条查询,然后观察智能体是否调用了该技能。先确认技能已经在你的智能体中注册并可被发现——不同客户端方式不同(比如技能目录、配置文件或 CLI 参数)。

大多数智能体客户端都会提供某种可观测性——执行日志、工具调用记录或详细输出——可以用来查看一次运行中智能体用了哪些技能。你可以查看客户端文档了解细节。如果智能体加载了你技能的 SKILL.md,说明触发了;如果没有读取就直接继续工作,说明没有触发。

一条查询在以下情况下视为“通过”:

  • should_triggertrue 且技能被调用,或者
  • should_triggerfalse 且技能没有被调用。

多次运行

模型是随机的——同一条查询有时会触发技能,有时不会。可以每条查询跑多次(3 次是个不错的起点),然后计算一个触发率:即该技能在这几次中被调用的比例。

一条 should-trigger 查询,如果它的触发率高于某个阈值(0.5 是合理默认),就算通过。一条 should-not-trigger 查询,如果它的触发率低于这个阈值,就算通过。

假设你有 20 条查询,每条跑 3 次,就是 60 次调用。你会想写个脚本来自动跑。下面是一个通用结构示例——把其中 claude 的调用和 check_triggered 里的检测逻辑替换成你自身智能体客户端的实现即可:

1#!/bin/bash
2QUERIES_FILE="${1:?Usage: $0 <queries.json>}"
3SKILL_NAME="my-skill"
4RUNS=3
5
6# 这个例子使用 Claude Code 的 JSON 输出检查 Skill 调用。
7# 把这个函数替换成你自己客户端的检测逻辑。
8# 如果技能被调用则返回 0(成功),否则返回 1。
9check_triggered() {
10 local query="$1"
11 claude -p "$query" --output-format json 2>/dev/null \
12   | jq -e --arg skill "$SKILL_NAME" \
13      'any(.messages[].content[]; .type == "tool_use" and .name == "Skill" and .input.skill == $skill)' \
14     > /dev/null 2>&1
15}
16
17count=$(jq length "$QUERIES_FILE")
18for i in $(seq 0 $((count - 1))); do
19  query=$(jq -r ".[$i].query" "$QUERIES_FILE")
20  should_trigger=$(jq -r ".[$i].should_trigger" "$QUERIES_FILE")
21  triggers=0
22
23  for run in $(seq 1 $RUNS); do
24   check_triggered "$query" && triggers=$((triggers + 1))
25  done
26
27 jq -n \
28    --arg query "$query" \
29    --argjson should_trigger "$should_trigger" \
30    --argjson triggers "$triggers" \
31    --argjson runs "$RUNS" \
32    '{query: $query, should_trigger: $should_trigger, triggers: $triggers, runs: $runs, trigger_rate: ($triggers / $runs)}'
33done | jq -s '.'

如果你的智能体客户端支持,可以在结果已经明确时提前结束一次运行——比如一旦智能体已经查过技能,或已经开始在没查技能的情况下执行任务。这可以显著降低整套评测跑完的时间和成本。

使用训练集 / 验证集划分避免过拟合

如果你用所有查询来优化说明,就有过拟合的风险:说明可能会刚好适配这些特定的说法,但对新说法表现很差。

解决办法是把查询集拆成两部分:

  • 训练集(约 60%):你用来定位问题和指导改进的那部分查询。
  • 验证集(约 40%):提前留出来,只在检查“改进是否能泛化”时使用。

确保两部分中 should-trigger 和 should-not-trigger 的比例大致相同——不要不小心把所有正例都放进同一边。随机打乱后再划分,并在多轮迭代中固定这个划分,这样才有可比性。

如果你使用类似上面那段脚本 above,可以把查询拆成两个文件——train_queries.jsonvalidation_queries.json——分别跑。

优化循环

  1. 评估 当前说明在训练集和验证集上的表现。训练集结果用来指导修改;验证集结果用来判断修改是否泛化。
  2. 训练集找问题:哪些 should-trigger 查询没有触发?哪些 should-not-trigger 查询被误触发了?
    • 只用训练集的失败案例来指导修改——无论是你自己改,还是让 LLM 帮你改,都不要把验证集结果带入过程。
  3. 修改说明。 重点是泛化:
    • 如果 should-trigger 查询没有触发,说明可能太窄了。可以适当扩大适用范围,或增加技能在什么情况下有用的描述。
    • 如果 should-not-trigger 查询频繁误触发,说明可能太宽了。可以明确写出技能做什么,或者澄清本技能和相邻能力之间的边界。
    • 避免只是简单把失败查询里的关键词一个个抄到说明里——那是在对训练集“投机取巧”,而不是泛化。要找到这些查询背后更一般的类别或概念,并针对这一层去调整。
    • 如果改了好几轮还卡住,不妨换一种完全不同的写法,而不是在同一版本上做微调。有时换一种结构或表述方式比小修小补更有效。
    • 记住说明要保持在 1024 字符限制之内——在优化过程中文本往往会越写越长。
  4. 重复 步骤 1–3,直到训练集上的所有查询都通过,或者你感觉已经很难再有明显提升。
  5. 选择最优版本,依据是验证集的通过率——即验证集中通过的查询比例。注意,最好的说明未必是你最后一版;有时早期版本的验证通过率,可能比后面那些对训练集过拟合的版本更高。

通常 5 轮迭代就够了。如果表现一直不提升,问题可能不在说明,而是在查询本身(太简单、太难或标注不准确)。

skill-creator 这个技能可以把整个循环自动化:会拆分评测集、并行评估触发率、用 Claude 提出说明改进建议,并生成一个你可以实时查看的 HTML 报告。

应用最终结果

选定最佳说明之后:

  1. SKILL.md frontmatter 里更新 description 字段。
  2. 确认说明长度在 1024 字符限制 之内。
  3. 确认说明能按预期触发。可以手动试几条提示做快速检查。若想更严谨一点,可以额外写 5–10 条全新的查询(同样包含 should-trigger 和 should-not-trigger),用评测脚本跑一遍——因为这些查询从未参与过优化过程,可以更诚实地反映说明是否真的能泛化。

前后对比:

1# Before
2description: 处理 CSV 文件.
3
4# After
5description: >
6 分析 CSV 和表格数据文件——计算汇总统计信息,
7 添加派生列,生成图表,并清理杂乱数据。
8 当用户拥有 CSV、TSV 或 Excel 文件并希望探索、转换或可视化数据时,
9 即使他们没有明确提及“CSV”或“分析”,也可以使用此技能。"

改进后的说明在两个维度上都更清晰:一方面更具体地写明了技能能做什么(统计汇总、派生列、图表、数据清洗),另一方面又更全面地说明了适用场景(CSV、TSV、Excel;即使用户没说“CSV”或“分析”)。

后续步骤

当你的技能已经可以稳定被正确触发,下一步就该评估它的输出质量了。可以参考文档 “如何评估Skill的输出质量”,了解如何设置测试用例、给结果打分并迭代改进。

相关技能包📦

用于指导如何创建高效的 Skills。当用户希望创建新的 Skill(或更新已有 Skill),以通过专业知识、工作流程或工具集成来扩展 Agent 的能力时,应使用此 Skill。

🛠️skill-creator

生产力

Anthropic

如何优化 Agent Skill 描述:提升技能触发率的系统方法指南 | 技能包 SkillPkg