文档大纲

Skill 不是文档,是上下文:Perplexity 内部手册给 Claude 用户的 5 条反直觉经验

最近 Perplexity 放出了一份内部 Agent Skills 设计指南,原本只在他们 Agents 团队内部流通。这份手册反复强调一件事:写 Skill 不是写代码,更不是写文档,而是在给模型"喂"上下文。而他们的很多结论,和工程师的本能正好相反。
不管你用的是 Claude Code 自定义 Skill、Plugin、还是 superpowers 体系的 Skill,这 5 条反直觉经验都值得抄进自己的备忘录。

你可能已经在用 Claude 的 Skill 体系了——无论是通过 superpowers 框架的 Skill、Claude Code 的 Slash Command,还是自己封装的 Plugin。你大概也踩过这些坑:

  • 写了一个超详细的 Skill,描述写了一整段,结果模型从来不调用它
  • Skill 内容塞了几千 tokens,模型用了之后反而变笨了
  • 明明已经写过类似场景的指引,模型还是踩同一个坑
  • 加了一个新 Skill,发现老 Skill 突然不灵了

如果你点头了,那 Perplexity 这份手册里写的东西,就是你的解药。

它的核心观点就一句话,全文都围绕这一句展开:

一个 Skill 是一个目录,不是文件;是一段路由触发器,不是一段文档;是条件性加载的上下文,不是塞进 context 的说明书。

下面 5 条经验,每一条都和大多数人的本能相反。

经验 1:Description 不是"自我介绍",是"路由触发器"

反直觉点:大多数人写 Skill 的 description,会习惯性地写"这个 Skill 能做什么、用来干什么"。这是错的。

Perplexity 的原话是:"A bad description describes what the Skill does. A good description says when the agent should load the Skill."

description 不进 Skill 内部文档,它是 system prompt 级别的索引。模型看 description 决定要不要 load_skill()。所以写法应该是:

  • ❌ "This Skill is used to monitor pull requests and report CI status."
  • ✅ "Load when the user says 'babysit this PR', 'watch CI', 'make sure this lands'."

实操清单:

  • 以 "Load when…" 开头
  • 目标 50 词以内——description 会被所有 session、所有用户支付
  • 写用户的真实意图,不是写 Skill 的功能
  • 不要概括工作流——那是 body 的事

对 Claude 用户的实操意义:你写 superpowers Skill 的 frontmatter,或者 Claude Code 的 Slash Command 描述时,请把"我是谁、我能干啥"全部删掉,只留"何时召唤我"。判断标准很简单——把 description 单独抽出来,模型看到它能否正确判断是否需要加载?能,就合格;不能,就改到能为止。

经验 2:Skill 是文件夹,不是文件

反直觉点:新手总以为"Skill = SKILL.md"。Perplexity 说:"Complexity is the feature."

一个生产级 Skill 长这样:

my-skill/
├── SKILL.md           # frontmatter + 核心指令
├── scripts/           # agent 跑、而不是每次重新发明的代码
├── references/        # 重量级文档,按需加载
├── assets/            # 模板、schema、数据
└── config.json        # 首次运行用户配置

这就是"中心辐射"(hub-and-spoke)模式。SKILL.md 是 hub,scripts / references / assets 是 spokes。

为什么要这么拆?因为 Skill 是渐进式(progressive)的。在 Perplexity Computer 里,Skill 加载分三层成本:

层级加载内容预算何时付出
Index每个 Skill 的 name + description~100 tokens/Skill每个 session、每个用户都付
Load完整 SKILL.md body~5,000 tokens运行时
Filesscripts / references / assets无上限仅当 agent 真正读取时

注意第二层:"在 5,000 tokens 之内"。 超过这个量,模型开始"挤掉"其他上下文。Perplexity 的原话是:*"Skills with a lot of fluff will almost certainly degrade other Skills as well as overall agentic capabilities."*

对 Claude 用户的实操意义:

  • 你如果有一个 Skill 的核心 SKILL.md 超过 5,000 tokens,就把它拆。把条件性内容挪到 references/ 里的子文件,告诉模型"如果 X 发生,才读 Y"
  • scripts/ 里的代码不是给模型"学习"的,是给模型"调用"的。如果一段逻辑每次都会重写一遍,直接给代码让模型 compose,不要让模型 reconstruct
  • 报税季 Perplexity 用了三级主题嵌套去组织 1,945 条税法,因为 1,945 个同级选项对模型太难选了。300 个主题拆成 20 个领域,模型先选 20 选 1,再在领域内选 15 选 1,准确率立刻上来——这是非常普适的分层思想

经验 3:Gotcha 才是黄金内容

反直觉点:大多数工程师写 Skill,会本能地写"正确步骤"——第一步、第二步、第三步。Perplexity 让你反过来写:Gotcha,也就是"不要踩的坑",才是信号量最高的内容。

原话:

Gotchas are extremely high-value content. Start thin, grow as the agent fails.

他们的更新循环长这样:

  • Agent 失败在某件事 → 加一条 gotcha
  • Agent 加载错目标 → 收紧 description + 加负例 eval
  • Agent 该加载没加载 → 加关键词 + 加正例 eval
  • System prompt 改了 → 检查有没有冲突或重复

技能是"以追加为主"(append-mostly)的。正文里那些"标准步骤"在模型基础能力上早就内化了,写一遍反而是冗余。但每个 gotcha 都对应一个真实失败案例——这才是模型不知道的。

文章里给了一个绝佳的反例示范:

❌ "git log # 找到 commit;git checkout main;git checkout -b \;git cherry-pick \;"

✅ "Cherry-pick the commit onto a clean branch. Resolve conflicts preserving intent. If it can't land cleanly, explain why."

前者是"对人类好的文档",后者是"对模型好的指令"。你给模型一串命令,它在出错时就会卡死;你给模型一个目标和约束,它能自己想办法。

对 Claude 用户的实操意义:

  • 你的 Skill 里至少应该有一节叫 "Gotchas" 或 "Common pitfalls"。开始可以只有 2-3 条,但每次 Claude 踩坑,就往里加
  • 不要写"系列命令"。把目标写出来,把约束写出来,让模型自己规划
  • 一旦你发现某个特定错误反复出现,这就是 gotcha 候选。用一句话写明"不要做 X,因为 Y"

经验 4:先写 evals,再写 Skill

反直觉点:你接到任务"加一个 X 场景的 Skill",本能是立刻打开 SKILL.md 开始写。Perplexity 让你先停 30 分钟写评估。

原话:

Write evals before the Skill. Include negative examples and forbidden loads for adjacent but distinct skills.

评估用例从三个地方来:

  1. 真实用户查询——从生产日志、用户反馈里捞
  2. 已知失败——上次 agent 失败,正是因为没这个 Skill
  3. 邻域混淆——和你的领域贴近、但应该路由到别的 Skill 的那些 query

反例比正例更强大。这呼应了前面 gotcha 的逻辑——告诉模型"别做什么",比"做什么"更能纠偏。

而且 evals 不是写完一次就丢。每改一次 description 都要跑一遍 eval,因为 description 的小词改动对路由的影响是"溢出"的:你改了 Skill A 的 description,Skill B 的召回率可能突然下降。Perplexity 称之为"远距离作用"(action at a distance):

Remember that it is easy to break other pre-existing Skills by adding a new Skill, even though you didn't touch it.

对 Claude 用户的实操意义:

  • 建一个 evals/ 目录,每个 Skill 配 5-10 条 hero query,包含正例和反例
  • 每次改 description 之前先跑 eval。如果你跑不过,先别发 PR
  • 维护一个"反例池"——专门收集那些"看着像 X 但其实是 Y"的 query。这是判断你 description 是否精准的最直接工具
  • 改 description 时使用 diff 风格的小步迭代,每次只改 1-2 个词,跑 eval 通过再继续

经验 5:每个 Skill 都在"收税"

反直觉点:写 Skill 不是"免费"的礼物。Perplexity 用了一个非常强硬的隐喻——Every Skill is a tax。每一个 session、每一个用户都在为你的 Skill 付 token 税。哪怕他们根本没用上你的 Skill。

原话是引用 Pascal 的那句法语:

« Je n'ai fait celle-ci plus longue que parce que je n'ai pas eu le loisir de la faire plus courte. » —— Blaise Pascal, Lettres Provinciales, 1657

("我之所以把这封信写得更长,是因为我还没有时间把它写得更短。")

写短 Skill 比写长 Skill 难得多。如果你的 Skill 写起来很轻松,那它八成太长或者根本不该存在。

文章里那张著名的对照表,每一行都和工程师本能对着干:

Zen of PythonZen of Skills
Simple is better than complexA Skill is a folder, not a file. Complexity is the feature.
Explicit is better than implicitActivation is implicit pattern matching. Progressive disclosure.
Sparse is better than denseContext is expensive. Maximum signal per token.
Special cases aren't special enough to break the rulesGotchas ARE the special cases (they're the highest-value content).
If the implementation is easy to explain, it may be a good ideaIf it's easy to explain, the model already knows it. Delete it.

最后一行最扎心:"If it's easy to explain, the model already knows it. Delete it." 你觉得是"良心教程"的内容,模型可能早就会了。写进去就是浪费所有人的 context。

对 Claude 用户的实操意义:

  • 每个 Sentence 都接受 Pascal 测试:"没有这句话,agent 会出错吗?" 不会,就删
  • 警惕"教学冲动"。你熟悉的领域你本能想讲清楚,但模型要的是"决策信号"不是"知识科普"
  • 每个 Skill 上线前先自问:真的有 80% 的 session 会用上吗?如果只是 5% 的偶发场景,别做成 Skill,做成 reference 文档让模型按需查
  • 持续修剪。每跑一段时间,回头看你的 Skill,能删的句子就删

给你的一份立即可用的清单

把上面的经验压成一份 checklist,下次写 / 改 Claude Skill 时过一遍:

写之前

  • 明确这个 Skill 的 description 写成 "Load when…" 而不是 "This Skill does…"
  • description 控制在 50 词以内,只描述用户意图
  • 准备好 5-10 条 hero query(含正反例)作为 eval 起点
  • 准备好"邻域反例"——容易混淆的 query 应该路由到别的 Skill

写 body 时

  • 跳过模型显然知道的内容
  • 不要写"命令序列",写"目标 + 约束"
  • 至少留一节 "Gotchas" / "Common pitfalls"
  • 核心 SKILL.md 控制在 5,000 tokens 以内
  • 条件性 / 重量级内容挪到 references/ 子文件
  • 决定性的代码挪到 scripts/,让模型直接调用

发布前

  • 跑一遍 eval,正例全过、反例不误召回
  • 改动 description 时做小步迭代 + 每步跑 eval
  • 自问:每个句子都通过 Pascal 测试了吗?("没有这句 agent 会错吗?")

维护期

  • 每次 agent 踩坑,往 gotchas 加一条
  • 不要重写 description(除非有 eval 支撑)
  • Skills 是"以追加为主"——加 gotcha 远比改主体安全
  • 定期修剪:能删的句子就删

写在最后

Perplexity 这份指南最有价值的地方,不是给了你 5 条技巧,而是给了一种新的视角——你不是在写文档,也不是在写代码,你是在给模型喂上下文。

这个视角一旦切换,很多事就自然了:

  • 为什么 description 要短?因为 system prompt 是公共空间
  • 为什么 body 不要写命令?因为模型不需要被指挥,需要被引导
  • 为什么 gotchas 最重要?因为模型已经知道所有 happy path,它缺的是 edge case 的记忆
  • 为什么每个 Skill 都是税?因为 token 是有限资源,你不为别人省 context,就是在加税

如果你正在用 Claude、正在写 Skill、正在维护团队的 Skill 库——今天就花 30 分钟,按上面的清单自查一遍你手头的 Skill。你会发现,有些"良心内容"该删,有些"教程"该拆成 gotcha,有些 description 写反了。

少即是多。Skill 越短越好,越"碎"越香。 这话听着反直觉,但用一次你就回不去了。

附录:Perplexity 原文完整中文翻译

在 Perplexity 设计、迭代与维护 Agent Skills

Perplexity 的前沿 agent 产品,建立在一套以模块化 Agent Skills 形式封装的专业知识与领域 know-how 之上。我们在各类技术环境中维护着一个经过精心筛选的 Skills 库。这些 Skills 既包含支撑 Perplexity Computer 的大量通用工具,也包含金融、法律、健康等垂直领域的专业能力,还包含数量庞大、用于响应各类用户需求的长尾模块。其中一些 Skills 虽不常被调用,但在被调用时至关重要。为了始终如一地提供卓越的用户体验,Perplexity 的 Agents 团队对 Skill 质量的重视程度与对代码质量的重视程度等同。

开发高质量 Skill 所需的直觉与最佳实践,与构建传统软件所需的截然不同。Agents 团队评审过来自优秀工程师的许多 PR,他们都是在工作中开发 Skills 的。结果几乎总是不胜枚举的修改意见。这是因为许多在写代码时很有用的模式,到了 Skill 开发中反而成了反模式。

例如,把 PEP 20——《Python 之禅》中的一些格言拿出来一看,会立刻发现,写好 Python 代码和写好 Skills 完全是两码事。20 条格言中,至少有一半在写 Skills 时是完全错误甚至严重误导的。下面是其中五条:

Zen of PythonZen of Skills
Simple is better than complexA Skill is a folder, not a file. Complexity is the feature.
Explicit is better than implicitActivation is implicit pattern matching. Progressive disclosure.
Sparse is better than denseContext is expensive. Maximum signal per token.
Special cases aren't special enough to break the rulesGotchas ARE the special cases (they're the highest-value content).
If the implementation is easy to explain, it may be a good ideaIf it's easy to explain, the model already knows it. Delete it.
Zen of PythonZen of Skills
简洁胜于复杂Skill 是一个文件夹,而非一个文件。复杂度本身就是特性。
显式胜于隐式激活是隐式的模式匹配。渐进式披露。
稀疏胜于稠密上下文是昂贵的。每个 token 必须承载最大信息量。
特殊 cases 不足以打破规则Gotcha 本身就是特殊 cases(它们是最高价值的内容)。
实现易解释,也许是个好主意如果容易解释,模型本来就会。删掉。

这份指南是 Perplexity 内部工程师在开发和评审 Skills 时共同使用的文档。我们也面向公众发布它,希望我们的发现与经验能惠及更广泛的社区。无论你是日常工作中设计生产级 Skills 的工程师,是想在自己最擅长的领域开发 Skill 的 Computer 用户,还是两者兼有,这份指南都适合你。

当你写一个 Skill 时,你写的并不是普通的软件(即便 Skills 如今已经是 agent 系统主要逻辑引擎的一部分)。你是在为模型及其运行环境构建上下文。Skill 有不同的约束和不同的设计原则。如果你用写代码的方式写 Skill,你一定会失败。

一个 Skill 至少是四样东西,尤其是在我们 Perplexity 的构建语境下。

Skill 是一个目录

Skill 不仅仅是一个 SKILL.md 文件。在很多情况下,一个 Skill 包含多个文件。在以你的 Skill 命名的目录下,通常会有:

  • SKILL.md:frontmatter 与指令
  • scripts/:agent 运行、而非每次重新发明的代码
  • references/:按需加载的重量级文档
  • assets/:模板、schema、数据
  • config.json:首次运行的用户配置

这种 hub-and-spoke(中心辐射)模式让你能把 Skills 写得非常聚焦、非常紧凑,而目录结构本身也可以用得非常有创意。有时,特别复杂的 Skills 会受益于多层级的目录结构,以帮助模型更好地导航。假设一个 Skill 涉及 300 个主题、可以归并为 20 个领域。可靠地从 300 个主题中选对那一个,即便是今天最好的前沿模型也尚未解决。但让模型先从 20 个领域中聚焦到一个,再在该领域下的 15 个主题中挑选,就要容易得多。

作为多层目录如何创造价值的一个例子:在上一个报税季,我们团队在支撑 Computer 美国所得税能力的 Skills 中,采用了三级主题嵌套。在税法这种复杂度下,这种层级结构是绝对必要的:在早期测试中,把《美国国内收入法典》全部 1,945 条一次性放进同一个文件夹,模型的表现甚至比完全不加载 Skill 还差。把信息按合理方式划分,对于保证读操作的高精度不可或缺。

然而这种层级也不是免费的。层级越深,跨信息架构所需的策展(curation)工作就越多,以管理随之而来的间接性。我们设计了快速参考指南、自定义搜索工具以及其他工具,来帮助模型以尽可能少的间接性定位信息。在这个案例中,付出策展的苦功最终得到了正向结果:这样一个 Skill 让模型在税务相关任务上的表现,远远超过单靠通用工具时的水平。

Skill 是一种格式

A Skill 是一种格式。核心根文件 SKILL.md 必须同时包含 name 和 description。此外,Skill 的名字必须精确匹配其所在目录的名字。名字必须全部小写、不带空格,可使用连字符。description 是路由触发器。这是一个常见的失败点:description 不是 Skill 用途的内部文档,而是给模型的指令,指示它何时加载该 Skill。因此,你经常会看到 "Load when…",而不是 "This Skill does…"。这一点很重要,因为大多数实现都会把 description 注入到模型的上下文中。

在 frontmatter 中还有 depends:,允许你创建 Skill 之间的层级依赖关系;以及 metadata:,用于评审与评估。不同的 agent 系统甚至可以定义自己的 frontmatter 字段,以适配该系统的特殊用法。作为替代方案,Skill 专属的元数据可以打包在辅助的 JSON 或 YAML 配置文件中。当你构建的 agent 系统需要为每个 Skill 提供不同类型的运行时行为、又不想把这些细节灌进模型上下文时,这种做法是更合适的。最后,类似的行为也可以通过在读取时剥离 Skill frontmatter 来实现。Computer 就采用了这种方法,从而让配置能够保留在根 SKILL.md 中。解析逻辑需要非常细致;如果某些字段确实适合留在模型上下文中,你可能希望实现条件剥离。

Skill 是可调用的

A Skill 是可调用的。agent 在运行时加载 Skill。重要的是,Skill 并不总是预先打包进上下文中。默认情况下,大多数 agent 系统会在确有需要时,按需渐进展开 Skill。

在我们 Computer 实现的 Skills 中,运行时上下文至少有三层成本。流程如下:

  1. Computer 调用 load_skill(name="…")
  2. Computer 把 Skill 目录复制到隔离的执行沙箱中
  3. Computer 递归自动加载 depends: 标签中的依赖
  4. Computer 然后剥离 frontmatter,agent 因此只看到正文以及附加文件

不同的 agent 系统可以选择以不同方式暴露 Skill 内容。举例来说,有些系统可能选择完全不暴露文件层级,让模型通过文件系统操作自行发现;其他系统可能选择把整棵文件树(按某个截断与/或深度限制)的映射交给模型。为了保持上下文干净,Computer 在调用上下文中省略了完整的文件层级;不过这可以在每个 Skill 粒度上覆盖。

Skill 是渐进式的

Skills 是渐进式的。在 Computer 中,运行时上下文有三种不同的成本层级,我们在不同阶段分别承担它们:

TierWhat loadsBudgetWhen you pay
Indexname: description for every non-hidden Skill~100 tokens per SkillEvery session, every user, always paid
LoadFull SKILL.md body~5,000 tokensRuntime
Filesscripts/、references/、assets/、subskills、FORMATTING.md、SPECIAL_CASES.md 等UnboundedOnly when the agent reads them
Tier加载内容预算何时付出
Index每个非隐藏 Skill 的 name: description每个 Skill 约 100 tokens每个会话、每个用户、始终付出
Load完整的 SKILL.md 正文约 5,000 tokens运行时
Filesscripts/、references/、assets/、subskills、FORMATTING.md、SPECIAL_CASES.md 等无上限仅在 agent 真正读取时

Computer 会构建一个 Skill 索引,包含每个可用 Skill 的 name 和 description。这部分预算大约是每个 Skill 100 tokens(更短更好)。它必须这么紧,因为每个会话、每个用户都在支付这个代价。该索引会在对话开始时注入 system prompt。模型由此知道一组命名 Skills 及其 description,从而决定是否调用 load_skill()。能够进入这个索引的门槛极高。你的 Skill 必须极其有用,description 必须极其致密、极其精炼,因为所有人、所有时刻都在为之付费。

agent 系统加载 Skill 之后,是完整的 SKILL.md 正文。理想情况下,正文文本不应超过 5,000 tokens。即便如此,你也要让每一个句子都"值"——因为一旦你加载了一个 Skill,对话剩下的部分就要一直为它买单,直到触及压缩边界为止。许多线程会同时加载 3 到 5 个不同的 Skills,这个成本是相乘的。充满冗余的 Skill,几乎必然会拖垮其他 Skills 以及整体的 agent 能力。简而言之,如果你的 Skill 被加载后却没有"做正确的事",那就是在浪费上下文。

渐进式的最后一级是 scripts 或特殊 cases,例如 subskills、formatting 等。这才是你应该放置无上限的条件分支逻辑的地方。agent 只会在需要时才会使用它,所以这里的门槛要低得多。

Index 中,每个 token 都至关重要。加载的 Skill 正文稍微宽松一点,运行时则最宽松。运行时可能多达 20,000 tokens,也可能是 0 tokens。这一层级正是你可以渐进式扩展模型上下文的地方。

何时需要一个 Skill?

Agents 团队经常被问及:在某个领域或用例下,是否真的需要一个 Skill。仅凭第一性原理,我们很少能给出一个明确的答案。要真正搞清楚这件事,唯一的办法是先把你的 agent 不带 Skill 跑起来,跑若干条 hero query,再看它是否做得好。

需要 Skill 的场景

有许多任务对训练好的模型来说是"在分布内"的。你只有在想要以某种特定方式改变其行为、而一句话 prompt 又不够时,才需要 Skill。所以,当你不带 Skill 时 agent 会出错,或者你需要让结果在多次运行之间高度一致而非依赖运气时,你才需要一个 Skill。

你的知识可能很稳定、却不在训练数据里。可能是存在 cutoff,或企业专属工作流,也可能只是品味问题。例如,Computer 中有几个设计相关的 Skills 是由 Henry Modisett(我们的设计负责人)写的。那些 Skills 中每一个 token 存在的原因,都是因为 Henry 在设计网站和 PDF 上有非常好的品味。Henry 明确指定使用哪些字体、不使用哪些字体、这些字体给人的感觉如何,以及其他模型无法仅从训练数据中学到的判断。

不需要 Skill 的场景

我们见过很多 Skills,里面是工程师把一系列 git 命令按顺序写好让模型执行。这是没必要的——模型本来就会。这意味着它是好文档,但不是好 Skill。

我们也见过一些 Skills 在复述 system prompt 里的指令。你不需要为此写一个 Skill。对绝大多数请求都相关的知识,应当放进全局上下文,而不是放进条件加载的 Skill。

如果某件事的变化速度比你维护它的速度还快,你也不需要 Skill。例如,如果你要调用某个远端 MCP 端点,而它的工具或工具版本频繁变化,你就不该把这些塞进一个 Skill。如果硬塞进去,最终只会产生漂移,模型就会犯错。

这里有一个有用的检验办法,可以套用到你的 Skill 中的每一个句子上:"没有这条指令,agent 会出错吗?" 如果这句话不是必需的,那它就负担不起存在——因为所有人、所有时刻都在为它付出代价。当你判断要不要加一个 Skill 时,请记住这个"税":每个会话、每个用户都在支付 token。

每个 Skill 都是一种税负

下面这段广为流传的名言(法语原版更妙)大意为:"我之所以把这封信写得更长,是因为我还没有时间把它写得更短。"

« Je n'ai fait celle-ci plus longue que parce que je n'ai pas eu le loisir de la faire plus courte. » —— Blaise Pascal, *Lettres Provinciales*, 1657

正如 Pascal 所言,你需要为每一个 Skill 投入时间。写一个短的 Skill 是困难的。如果你的 Skill 很容易写出来,那它很可能写得太长,或者本不该存在。一个好的 Skill 应当尽可能短。

如果你试图"一气呵成"地生成 Skill、五分钟就发一个 PR,那结果几乎一定是不尽人意的。事实上,早期研究已经表明,如果你用 LLM 来写 Skills,LLM 自己很可能并不从中受益:"Self-generated Skills provide no benefit on average, showing that models cannot reliably author the procedural knowledge they benefit from consuming."

换句话说,你需要把自己的判断注入到你写的每一个 Skill 中。请遵循以下步骤。

如何构建一个 Skill

Step 0:编写 Eval

先写一些 evals。评估用例可以来自:

  • 真实用户查询:从生产环境或你的"智囊团"里采样
  • 已知失败:agent 失败正是因为 Skill 不存在
  • 邻域混淆:贴近你的领域边界、却路由到另一个 Skill

最起码,你应当验证 Skill 会在需要时被加载。理想情况下,你从生产环境中采样这些用例。你也可以考虑一些已知的错误 case:也许你开始写这个 Skill 的全部原因就是某个具体失败;也许你在重构一个 Skill,而它与相邻领域产生了混淆。

从一组类似的正例和反例开始。反例极其强大,往往比正例更重要。

Step 1:Description 字段

这是 Skill 中最难写的一行。它是路由触发器,而不是文档。要把 name 和 description 写对,你关心的不是 Skill 的内容,而是 Skill 是否在正确的时机被加载、注入,并且没有跑偏——这才是头号失败模式。每多增加一个 Skill,你都在让所有其他 Skill 稍微变差一点,所以你必须把"回归"控制到最低。

再次强调,糟糕的 description 描述 Skill 是做什么的、为什么有用;好的 description 说明 agent 何时应该加载这个 Skill。举个例子,假设你有一个用于监控 PR 的东西。不要写这个 Skill 是做什么的。写工程师在抓狂时说的话,以及他们希望你确保 PR 顺利合并的方式——比如 "babysit"、"watch CI"、"make sure this lands"。

这里有一个快速清单:

  • 以 "Load when…" 开头
  • 目标在 50 词以内
  • 描述用户的意图,最好来自真实查询
  • 不要概括工作流
  • 真实查询足以覆盖 80/20 的情况。通常 2–3 个例子就很好。要做到"不多不少"并不容易。

Step 2:编写正文

接下来,写 Skill 本身的内容。注意,这不是 Step 0,也不是 Step 1。

把工作流传达给 LLM 和把它传达给同事、甚至你的运行时系统,是完全不同的。在学习一个新的软件工具时,工程师可能需要读文档、找有经验的人带一遍、慢慢学会使用它。而对几乎任何一个存在至少一年的软件工具来说,你只要提到它的名字,LLM 就已经具备了所需的一切信息。

在写正文时,跳过那些显而易见的东西。许多工程师写 README.md 的经验都很丰富:他们会列出别人需要运行的每一条命令。当你写 Skill 时,很容易退回这种习惯,因为感觉就像在写文档——但如果你真这么做了,你的 Skill 就会是垃圾。所以,不要写一串命令。

例如,你不需要写:

"git log # 找到 commit;git checkout main;git checkout -b \;git cherry-pick \;"

而应该写:

"把这个 commit cherry-pick 到一个干净的分支。解决冲突时保留原本意图。如果它无法干净落地,请说明原因。"

模型在面对后者这种灵活表述时,做得远好过面对前者那种过于规定化的命令序列——尤其是在事情出错时。不要把路修得太窄、不要过度规定——那是脆弱的;而在多种方法都能奏效的地方,保留灵活性。再次强调,对人类好的文档,往往对模型是糟糕的文档。

接下来,重点写 gotchas 或反例。它们是信号量极高的内容,因为它们经常能引导模型"不要做什么"。每次 agent 踩坑时加一条,随着运行它,你会看到 gotchas 自然生长。

最后,如果某段内容是条件性的、或者内容特别重,把它从作为 hub 的 SKILL.md 中拿出来,放到某一条 spoke 里。放进一个可渐进加载的附属文件中——我们下一步会展开讲。

Step 3:利用层级结构

当你要用脚本、references 或某个特定工具时,请利用 Skill 的层级结构:

路径用途说明
scripts/每个 run 都要重写的确定性逻辑给模型可以拿来组合的代码,而不是让它重新构造
references/仅当条件满足时加载的重量级文档例如 "Read api-errors.md if API returns non-200"
assets/agent 复制、填空的输出模板例如 report-template.md、输出 schema
config.json首次运行的用户配置询问 Slack 频道并保存,下次复用

对任何条件性或与主 Skill 存在分支的内容,请把它拆到一个文件夹里。记住,对于特别复杂的 Skill,也可以使用多层级目录结构。对这些 Skill,你需要仔细思考:功能应当是单体实现,还是拆成一组 Skills(也许通过 depends: 形成加载关系)。

Step 4:迭代

接下来,在分支上做大量迭代。从 main 分支开始、没有 Skill,做几轮迭代,构建你的 hero query 集,跑一大批 evals。任何评审你 Skill 代码的人都会感谢你提交一个单一变更集、并附带一份评估集合。除非加了新的 gotcha,否则评审一连串递增式的小改动非常困难,所以请尽量减少。

你很可能会做大量小词级别的修改。description 中的小词改动会对路由产生巨大影响(包括对其他 Skills 的溢出效应),所以这些工作请在 Step 5 之前完成。

Step 5:发布

发布它。

如何维护一个 Skill

现在你已经写好了一个 Skill,接下来你必须维护它。

Gotchas 飞轮

从这一刻起,你的 gotchas 列表往往会大量增长或频繁变化。我们经常看到工程师提一些没经过 eval 的 PR——例如,改 description。如果你的 Skill 合并后你还在改 description,那你就跑偏了。如果你在改的是决定 Skill 是否被路由的那一块东西,你需要写一些支持这些改动的 evals。

Skills 是以追加为主的。Gotchas 段会随时间积累出最大的价值:

  • Agent 在某件事上失败 → 加一条 gotcha
  • Agent 把 Skill 加载错了目标 → 收紧 description,并增加负例 eval
  • Agent 在应该加载时没加载 → 增加关键词与正例 eval
  • system prompt 改动 → 检查冲突或重复

在内部测试或生产中注意到单个失败 case、然后加一条 gotcha,是很自然的做法。它是反例,所以并没有真正改变既有指引,但它让模型知道:"嘿,这里有一个已知失败。"

当你从 80/20 推进到 99.9% 甚至 99.99% 的成功率时,扩展这个 gotcha 列表是很容易的。当看到这些反例时,你应当主要往 gotcha 段追加。你不应该再加更长的指令或改 description。

Eval 套件

在 Perplexity,我们跑许多 eval 套件来检查不同事情。有针对 Skill 加载与Skill 文件读取的,用于检查 Skill 加载本身的 precision、recall 和 forbidden checks:agent 是否会在该加载时路由到你的 Skill?这能保证新 Skill 不会破坏既有的边界。

也有 eval 可以检查渐进式加载是否正确。agent 可能会加载 Skill,但它读了附属文件吗?比如,你有一个用于金融查询的 finance Skill,它读了那个特殊的 FORMATTING.md 吗?

还有针对 Skills 的 eval,会测试端到端任务完成度。我们会跑完整的 agent loop,让一个 LLM judge 按照一份定义良好的评分细则来给结果打分。

最后,重要的是把这些 eval 跑在不同的模型上。Computer 至少支持三类不同的编排模型家族:GPT、Claude Opus 和 Claude Sonnet。你要把 Skill 加载与领域 Skills 跑在不同的 agent 编排器上,确保不会出现不同的行为。Sonnet 和 GPT 在 Skills 上的表现相当不同。

总结与要点

你写的 Skills 越多,你就越擅长写它们。如果你还没有把每天、每周在做的那些任务自动化或用 Skills 提升可复现性,请立刻开始。

写 Skills 的过程会让你更擅长写 Skills;同时,它们也极其擅长自动化业务流程。如果你每周站会前、每个 sprint 末、或者你作为工程师每天/每周/每季度例行做的任何事情,能被你描述出来,你就应该写一个 Skill 来买回你的时间。

能自动化 postmortem 吗?能评审 PR 吗?任何你能做的事情,至少可以让第一遍由一个 Agent Skill 接手。这会为你节省大量时间。

话虽如此,请记住:Skill 不容易,也并不总是必要。少即是多。其他几点要点:

  • 先写 evals,再写 Skill。包含反例与对相邻但不同 Skill 的 forbidden loads。
  • Description 是最难的部分。"Load when…",每个词都在争夺注意力。
  • Gotchas 是极高价值的内容。先写得稀薄,随着 agent 失败再增长。
  • 记住,一个新 Skill 即便你没碰它,也很容易破坏既有的其他 Skills(警惕"远距离作用")。
  • 每次写或维护 Skill 时,请用上所有可用的工具。如果想了解更多,Agent Skills 网站上有很多好例子;我们的内部仓库与公共生态系统中也有大量设计良好的 Skills 可供参考。
阅读量: 274