最近 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 | 运行时 |
| Files | scripts / 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.
评估用例从三个地方来:
- 真实用户查询——从生产日志、用户反馈里捞
- 已知失败——上次 agent 失败,正是因为没这个 Skill
- 邻域混淆——和你的领域贴近、但应该路由到别的 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 Python | Zen of Skills |
|---|---|
| Simple is better than complex | A Skill is a folder, not a file. Complexity is the feature. |
| Explicit is better than implicit | Activation is implicit pattern matching. Progressive disclosure. |
| Sparse is better than dense | Context is expensive. Maximum signal per token. |
| Special cases aren't special enough to break the rules | Gotchas ARE the special cases (they're the highest-value content). |
| If the implementation is easy to explain, it may be a good idea | If 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 原文完整中文翻译
- 原文标题:Designing, Refining, and Maintaining Agent Skills at Perplexity
- 来源:research.perplexity.ai/articles/designing-refining-and-maintaining-agent-skills-at-perplexity
- 发布时间:2026 年 5 月 1 日
在 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 Python | Zen of Skills |
|---|---|
| Simple is better than complex | A Skill is a folder, not a file. Complexity is the feature. |
| Explicit is better than implicit | Activation is implicit pattern matching. Progressive disclosure. |
| Sparse is better than dense | Context is expensive. Maximum signal per token. |
| Special cases aren't special enough to break the rules | Gotchas ARE the special cases (they're the highest-value content). |
| If the implementation is easy to explain, it may be a good idea | If it's easy to explain, the model already knows it. Delete it. |
| Zen of Python | Zen 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 中,运行时上下文至少有三层成本。流程如下:
- Computer 调用
load_skill(name="…") - Computer 把 Skill 目录复制到隔离的执行沙箱中
- Computer 递归自动加载
depends:标签中的依赖 - Computer 然后剥离 frontmatter,agent 因此只看到正文以及附加文件
不同的 agent 系统可以选择以不同方式暴露 Skill 内容。举例来说,有些系统可能选择完全不暴露文件层级,让模型通过文件系统操作自行发现;其他系统可能选择把整棵文件树(按某个截断与/或深度限制)的映射交给模型。为了保持上下文干净,Computer 在调用上下文中省略了完整的文件层级;不过这可以在每个 Skill 粒度上覆盖。
Skill 是渐进式的
Skills 是渐进式的。在 Computer 中,运行时上下文有三种不同的成本层级,我们在不同阶段分别承担它们:
| Tier | What loads | Budget | When you pay |
|---|---|---|---|
| Index | name: description for every non-hidden Skill | ~100 tokens per Skill | Every session, every user, always paid |
| Load | Full SKILL.md body | ~5,000 tokens | Runtime |
| Files | scripts/、references/、assets/、subskills、FORMATTING.md、SPECIAL_CASES.md 等 | Unbounded | Only when the agent reads them |
| Tier | 加载内容 | 预算 | 何时付出 |
| Index | 每个非隐藏 Skill 的 name: description | 每个 Skill 约 100 tokens | 每个会话、每个用户、始终付出 |
| Load | 完整的 SKILL.md 正文 | 约 5,000 tokens | 运行时 |
| Files | scripts/、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 可供参考。