优化技能描述
如何改进你的技能描述,使其可靠地触发相关提示。
一个技能只有在被激活时才有用。你的 SKILL.md 中的描述字段是智能体用来决定是否为给定任务加载技能的主要机制。描述不足意味着该技能在应该触发时不会触发;描述过于宽泛意味着它在不应该触发的时候触发了。
本指南介绍了如何系统地测试和改进你的技能描述,以达到触发准确性。
(1)
技能触发的工作原理
智能体使用渐进式披露来管理上下文。在启动时,它们只加载每个可用 skill.md 的名称和描述——仅足以确定某个 skill 何时可能相关。当用户的任务与描述相匹配时,智能体会在上下文中读取完整的 SKILL.md 并遵循其指示。
这意味着描述承担了触发的全部责任。如果描述没有传达技能何时有用,智能体就不知道如何获取它。
一个重要的细微差别是:智能体通常只为那些需要超出其自身能力范围的知识或能力的任务咨询技能。即使描述完全匹配,像 “阅读这个 PDF” 这样简单的一步请求也可能不会触发 PDF 技能,因为智能体可以使用基本工具来处理它。对于涉及专业知识的任务,如不熟悉的 API、特定领域的工作流程或不寻常的格式,一个精心编写的描述可能会产生影响。
(1)
编写有效的描述
在测试之前,了解一个好的描述应该是什么样子很有帮助。以下是一些原则:
使用命令式措辞。将描述设置为对特工的指示:“当……” 时使用这个技能,而不是 “这个技能可以……” 特工正在决定是否采取行动,所以告诉它何时采取行动。
关注用户意图,而非实施。描述用户试图实现的目标,而非技能的内部机制。代理与用户请求的内容相匹配。
错误在于咄咄逼人。明确列出适用该技能的上下文,包括用户没有直接命名域名的情况:“即使他们没有明确提到 ‘CSV’ 或 ‘analysis’。”
保持简洁。短段落中的几句话通常是正确的——长度足以涵盖技能的范围,短度不会影响智能体在多个技能中的语境。规范强制实施了 1024 个字符的严格限制。
(1)
设计触发器评估查询
为了测试触发,你需要一组 eval 查询——真实的用户提示符,并标注它们是否应该触发你的技能。
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 },/下载/report_final_v2.xlsx)
{ “query”: “whats the quickest way to convert this json file to yaml”, “should_trigger”: false }
]
目标约 20 个查询:8-10 个应该触发,8-10 个不应该触发。
(1)
应该触发的查询
这些测试描述是否捕捉了技能的范围。沿着几个轴变化它们:
措辞:有些正式,有些随意,有些带有打印或缩写。
明确性:有些人直接命名技能的领域 (“分析这个 CSV”),其他人则不命名地描述需求 (“我的老板想要这个数据文件的图表”)。
详细信息:将简短的提示语与上下文密集的提示语结合起来——一个简短的 “分析我的销售 CSV 并制作图表”,以及一个包含文件路径、列名和背景故事的较长信息。
复杂性:改变步骤的数量和决策点。将单步任务与多步工作流结合使用,以测试智能体是否能够识别出当它所处理的任务被埋没在更大的链条中时是否相关的技能。
最有用的 “应该触发” 查询是那些技能会有所帮助,但仅凭查询本身并不能明显看出联系的查询。在这些情况下,描述性措辞会产生影响——如果查询已经询问了技能的确切功能,任何合理的描述都会触发。
(1)
不应触发的查询
最有价值的负面测试案例是差点失败——与你的技能共享关键词或概念,但实际上需要不同内容的查询。这些测试检验描述是否准确,而不仅仅是宽泛。
对于 CSV 分析技能,弱负面例子可能包括:
“写一个斐波那契函数”——显然无关紧要,不需要测试任何东西。
“今天天气怎么样?”——没有关键词重叠,太容易了。
强烈的负面例子:
“我需要更新 Excel 预算电子表格中的公式”——共享 “电子表格” 和 “数据” 的概念,但需要 Excel 编辑,而不是 CSV 分析。
“你能否编写一个 python 脚本,读取一个 csv 并将每一行上传到我们的 postgres 数据库”——这涉及 CSV,但任务是数据库 ETL,而不是分析。
(1)
现实主义小贴士
真实的用户提示包含通用测试查询所缺乏的上下文。包括:
文件路径 (
个人背景 (“我的经理让我……”)
特定详细信息 (列名、公司名称、数据值)
随意语言、缩写和偶尔出现的字体
(1)
测试描述是否触发
基本方法:在安装了技能的情况下,通过代理运行每个查询,并观察代理是否调用该技能。确保技能已注册并可由代理发现——具体工作方式因客户端而异 (例如,技能目录、配置文件或 CLI 标志)。
大多数代理客户端提供某种形式的可观测性——执行日志、工具调用历史记录或冗长的输出——这些信息可以让你了解在运行过程中使用了哪些技能。请查看客户端的文档以了解详细信息。如果代理加载了你的技能的 SKILL.md,则触发该技能;如果代理在未咨询该技能的情况下继续运行,则不会触发该技能。
查询在以下情况下 “通过”:
should_trigger 为真且技能已被调用,或
should_trigger 为假,技能未被调用。
(1)
多次运行
模型行为是非确定的——相同的查询可能在一次运行中触发技能,但在下一次运行中不会。多次运行每个查询 (3 是合理的起点),并计算触发率:技能被调用的运行次数的百分比。
如果触发率高于阈值 (0.5 是一个合理的默认值),则应该触发查询通过。如果触发率低于阈值,则不应该触发查询通过。
如果每次运行 3 次,每次运行 20 个查询,那么总共需要 60 次调用。你需要编写这个代码。以下是一般结构——将 check_triggered 中的子句调用和检测逻辑替换为代理客户端提供的任何内容:
#!/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 ‘.’
如果你的代理客户端支持它,你可以在结果明确后提前停止运行——代理要么咨询技能,要么在没有技能的情况下开始工作。这可以显著减少运行完整 eval 集的时间和成本。
(1)
避免列车/验证分割的过拟合
如果你针对所有查询优化描述,你可能会冒着过拟合的风险——构建一个适用于这些特定短语但在新短语上失败的描述。
解决方案是拆分你的查询集:
训练集 (约 60%):用于识别故障和指导改进的查询。
验证集 (约 40%):将查询放在一边,仅用于检查改进是否具有普遍性。
确保两个集合都包含 “应该触发” 和 “不应该触发” 查询的比例混合——不要意外地将所有正面查询都放在同一个集合中。随机洗牌,并在迭代中保持分配固定,这样你就是在比较苹果和苹果。
如果你使用的脚本类似于上面的脚本,可以将查询分成两个文件——train_queries.json 和 validation_queries.json,然后分别针对每个文件运行脚本。
(1)
优化循环
评估列车和验证集上的当前描述。列车结果指导你的变更;验证结果告诉你这些变更是否具有普遍性。
识别故障在列车集哪些应该触发的查询没有触发?哪些不应该触发的查询触发了?
只使用训练集故障来指导你的变更——无论你是自己修改描述还是提示进行 LLM,都要将验证集结果排除在过程之外。
修改描述。专注于泛化:
如果应该触发查询失败,描述可能过于狭窄。请扩展范围或添加关于技能何时有用的上下文。
如果 “不应该触发” 的查询是虚假触发,那么描述可能过于宽泛。需要增加技能不能做什么的具体说明,或者明确该技能与相邻能力之间的界限。
不要从失败的查询中添加特定的关键字——那样做过度拟合。相反,找到这些查询所代表的一般类别或概念,并解决它们。
如果你在几次迭代后遇到了困难,可以尝试一种结构上不同的描述方法,而不是逐步调整。不同的框架或句子结构可能会突破精炼无法实现的地方。
请确保描述保持在 1024 个字符的限制以下——描述在优化过程中往往会增长。
重复步骤 1-3,直到所有训练集查询都通过,或者你不再看到有意义的改进。
根据验证通过率 (即验证集中通过的查询的比例) 选择最佳迭代。请注意,最佳描述可能不是你生成的最后一个描述;早期迭代的验证通过率可能高于后续过拟合到训练集的迭代。
通常五次迭代就足够了。如果性能没有提高,问题可能在于查询 (太容易、太难或标记不好),而不是描述。
技能创建器 Skill 将这个循环从头到尾自动化:它划分 eval 集,并行评估触发率,使用 Claude 提出描述性改进建议,并生成一个实时 HTML 报告,你可以在运行时观看。
(1)
应用结果
选择最佳描述后:
更新你的 SKILL.md 前言中的描述栏。
验证描述是否低于 1024 个字符的限制。
按预期验证描述触发器。手动尝试几个提示符,作为快速的合理性检查。为了进行更严格的测试,编写 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.”
改进后的描述更加具体地描述了该技能的功能 (摘要统计数据、衍生列、图表、清理),并更广泛地描述了它的应用时间 (CSV、TSV、Excel;甚至没有显式的关键字)。
(1)
下一步
一旦你的技能可靠地触发,你就需要评估它是否产生良好的输出。请参阅评估技能输出质量,了解如何设置测试用例、评分结果和迭代。
最后编辑:Ddd4j 更新时间:2026-05-14 23:26