案例研读 — Perplexity
Perplexity 内部 skill 工程方法研读:上下文工程视角、四组件模型、三层成本、eval-first 流程、gotchas 飞轮
- 原文: Perplexity 内部文章 Designing, refining, and maintaining agent skills at Perplexity,基于其 agent 产品 Computer 的实战经验。
- 核心主张: 写 skill 不是写软件,而是为模型和它的执行环境构建上下文。
- 本页: 按原文章节顺序逐节研读,关键概念配图,文末单独一节给出我们的总结。
Opening:写 skill 是上下文工程,不是软件工程
文章开篇就立起的基础主张:写 skill 不是在写普通软件,而是在为模型和它的执行环境构建上下文。Skill 有自己的约束条件和自己的设计原则,大多数代码 review 的反射在这里都不适用。
把这条主张落地的方式,是把 PEP 20 做并列反转:
| PEP 20 | Skill 工程版本 |
|---|---|
| Simple is better than complex | Complexity is the feature |
| Explicit is better than implicit | Activation is implicit pattern matching |
| Sparse is better than dense | Context is expensive; maximum signal per token |
| Special cases aren’t special enough | Gotchas ARE the special cases |
| If it’s easy to explain, it’s a good idea | If it’s easy to explain, the model already knows it; delete it |
紧跟着的观察:工程师按写代码的反射写 skill,提交的 PR 读起来像内部文档,reviewer 的大部分精力花在把它砍短。
Skill 是什么?
文章把 skill 拆为四个正交的性质。
Skill 是一个目录
不是一个文件。典型布局:
SKILL.md:frontmatter + 指令scripts/:可执行代码,让 agent 直接调用而不是重新构造references/:条件性加载的重型文档assets/:模板、schema、数据config.json:首次运行的用户配置(例如团队特有的 Slack 频道映射)
目录的内部组织本身就是一个 skill 工程问题。文章给出的案例:
Perplexity 为 Computer 做了一份覆盖 Internal Revenue Code 全部 1,945 节的税法 skill。第一版把所有条款扔进单一目录,表现比根本不加载这个 skill 还差。修复方案是三级层次结构:约 20 个大类,每类下约 15 个 topic cluster,cluster 下是具体条款,配套自定义搜索工具和快速参考指南。每深一层,路由可靠性都有明显提升。对密集参考材料而言,进入这份材料的索引本身就是 skill 工程问题。
Skill 是一种格式
Frontmatter 必含 name(小写,可用连字符)和
description。文章的核心提法:description 是路由触发器,不是文档,措辞应当是 Load when ... 而不是
This skill does ...。可选字段包括 depends:(层级依赖)和 metadata:
块。送入模型前是否剥掉 frontmatter 由实现决定,Computer 选择剥掉。
Skill 是可调用的
Computer 的流程:agent 调用 load_skill(name="..."),skill 目录被复制到隔离的 sandbox,递归自动加载 depends:
声明的依赖,剥掉 frontmatter 后再送入模型。
调用过程跨越三层成本:
- Index 层:约 100 tokens / skill。所有非隐藏 skill 的 name + description 在对话开始时注入 system prompt。每个 session、每个用户、永远在付。
- Load 层:约 5,000 tokens。完整的 SKILL.md body。
load_skill调用后注入,持续到下一个 compaction 边界。多个 skill 累加;报告的稳态是每个 thread 加载 3–5 个 skill。 - Runtime 层:无上限。
scripts/、特殊情况文件、子 skill、格式化指南。仅当 agent 主动打开具体文件时才付费。
这三层是所有下游设计取舍的物理基础:description 的每个字节在每次调用都付费;body 的每个字节在加载后的整个 thread 里挂着;只有 runtime 层是真正按需的。
Skill 是渐进式的
昂贵内容的加载推迟到真正需要的那一刻。这是为什么三层模型在几十个 skill 的目录里在经济上仍然成立。
什么时候需要做 skill?
该做 skill 的场景
文章给出的判据:任务需要的行为改变超出了单条 prompt 指令能覆盖的范围;agent 在缺少专门上下文时会失败或表现不稳定;知识是稳定的,但在训练数据里缺席(截止日之后的内容、企业内部 workflow、品味和判断这类内容)。
文章给出的例子:Perplexity 的 Henry Modisett 撰写的 design skill,规定字体选择、视觉美学、设计原则。这是基于品味与判断的知识,模型从训练数据里推不出来,属于“必须做成 skill”的典型例子。
不该做 skill 的场景
三种情况:
- 模型本来就会。常见命令序列是经典反例。文中的对照:
- 不好的写法:把
git log; git checkout main; git checkout -b <clean>; git cherry-pick <commit>完整命令序列写进 SKILL.md - 好的写法:“把 commit cherry-pick 到一个干净 branch 上,解决冲突时保留原意”
- 不好的写法:把
- 属于普适指令。应该放进 global system prompt,不该做成有条件加载的 skill。
- 内容变化快于维护节奏。频繁变动的远端 endpoint 是文章给的例子。一份过期的 skill 比“没有这个 skill”更糟,它会自信地路由进去然后给出错误信息。
核心原则:Every skill is a tax
文章给出的核心纪律。问法是:skill 里任意一句话,没有它 agent 会不会答错?如果不会,这句话就不该存在。
简短被刻画为劳动密集的工艺,借用了帕斯卡那句“这封信写得这么长,因为没时间写得更短”的旧典作为背书。
另引一项研究结论:让 LLM 自己写 skill 平均没有正向收益(“no benefit on average”)。模型可以可靠地消费程序性知识,但写不可靠。
如何构建一个 skill
文章给出六步。
Step 0:先写 eval。 来源:真实生产 query;已知失败案例(推动你做这个 skill 的那个 case);以及边界邻近的、应该路由到别的 skill 的 query。负样本权重高于正样本。
Step 1:Description(最难的一步)。 先把路由触发拿对,再写任何 workflow。文章给的四条清单:
- 用
Load when ...开头 - 目标 50 个英文单词以内
- 描述用户意图,措辞来自 2–3 条真实生产 query
- 不要总结 workflow,那是 body 的事
文中的例子:一个 PR Monitoring skill 的 description 用的是真实用户语言:“babysit”、“watch CI”、“make sure this lands”,而不是功能化的 “monitor pull request status”。前者匹配用户真实的措辞,后者只匹配作者脑补的措辞。
Step 2:写 body。 复述开篇的原则:把一个 workflow 传达给 LLM,和把它传达给同事、甚至传达给运行时系统,是完全不同性质的事。跳过模型已会的内容;不要给一串规定动作的命令序列;火力集中在 gotchas 和负样本;条件性、重型的内容下沉到附属文件。
Step 3:用上层次结构。 把内容分布到
scripts/(“给它代码去组合,不是重新构造”)、references/(触发式加载,例如“API 返回非 200 时去读
api-errors.md”)、assets/(输出模板)、config.json(首次运行配置)。
Step 4:在 branch 上迭代。 Description 里的小词替换都会带来明显的路由偏移。提交时是带完整 eval set 的一个完整 changeset,不做反复来回 review。
Step 5:上线。
如何维护一个 skill
Gotchas 飞轮
长期来看,gotchas 是 skill 里价值密度最高的部分。维护的常态是只增不改:
- 生产中 agent 出错,追加一条 gotcha
- 加载了错误的 skill,收紧 description,加一条负 eval
- 该加载的 skill 没加载,补关键词,加一条正 eval
- System prompt 变更,审查与现有 skill 的重复或抢夺
稳态下 SKILL.md 增长缓慢且不对称,累积的是负向知识。
Eval 套件:四类
Perplexity 报告同时跑:
- Skill 加载精度与召回,含“禁止加载”的负样本检查
- 渐进加载 eval:body 指示 agent 去读
FORMATTING.md这类附属文件时,是否真的读了 - 端到端域内完成,由 LLM judge 按 rubric 评分
- 多模型 eval:GPT、Claude Opus、Claude Sonnet 各跑一遍。文中给出的具体观察:“Sonnet 和 GPT 在 skill 这件事上行为差异不小”
Action at a distance(远程副作用)
加一个新 skill 会拖垮已有 skill,因为新的 description 会争抢同样的路由注意力。这是 eval 套件必须目录级(catalog-wide)而非 skill 级(skill-local)的最有力理由:真正在乎的回归是“加了 skill X 之后,skill Y 会不会被误路由”。
我们的总结
读完这篇文章值得带走的六条。这是我们的提炼,不是原文的措辞:
- 写 skill 是为模型和它的执行环境做上下文工程,不是软件工程。PEP 20 的反转、“Every skill is a tax”的纪律、不要写规定动作命令序列的反模式,都是从这一条主张延伸出来的。
- Skill 目录与其说是知识库,不如说是路由系统。成本在 index 上付,信号在 description 上发,失败在 gotchas 里累积,回归在 skill 与 skill 之间冒出来。
- Description 是最难写的部分。一个英文单词的替换都可能改变路由结果。
- Gotchas 飞轮决定 skill 的长期价值。稳态是缓慢累积负向知识,不是前期设计。
- Eval 必须先写、必须覆盖负样本、必须跨模型、必须目录级。
- 不是所有看上去该做的事都该做 skill。模型已知、变化过快、普适指令,都该排除。
原文链接
Designing, refining, and maintaining agent skills at Perplexity — Perplexity Research