大多数 Skill 正在让你的 Agent 变差
不是明显地、戏剧性地。轻微地。每次会话。
你不需要更多 prompt。你需要的是一个为成本而非便利设计的 Skill 文件夹结构。一个写成路由器而非摘要的描述。一个完全由模型不知道的东西构成的正文。以及在 Skill 存在之前就写好的 eval。
这是 Avid 综合三个顶级团队经验后的 Skill Playbook。
三个团队的共识
Garry Tan 在 4 月发了帖,精准命中了这个点。他的原话:"当有人问我是怎么'prompt'我的 AI 时,答案是:我不。skills 就是 prompts。"
Anthropic 的 Barry Zhang 和 Mahesh Murag 在 AIE 演讲上说了同样的话:停止构建 Agent,构建 Skill。
Perplexity shortly after 发布了内部 Skill 设计指南。开场白:"如果你像写代码一样写 skill,你会失败。"
AI 领域三个最受尊重的团队。同一个答案。不同的到达路径。
他们趋同的原因是:prompting 不会复利。每次会话你重新输入同样的指令。Agent 从零开始。你稍微更擅长 prompting 了。系统从未变得更聪明。
Skills 复利。你今天写的 skill 下个月还在运行。你在 Agent 失败时追加内容。失败变成指南。随着时间,skill 反映了你遇到的每个边界情况,不只是第一天想象的幸福路径。
文件夹结构:三层上下文成本
典型初学者 skill 是一个长正文的单 markdown 文件。每次相关会话完全加载。每条指令、每个例子、每个边界情况,全部,每次。
有效的文件夹结构:
your-skill/
├── SKILL.md ← 描述 + 约束 only
├── scripts/ ← 确定性代码,Agent 不每次重新发明
├── references/ ← 重文档,只在需要时加载
└── assets/ ← 模板、schema、输出格式
形状重要的原因是上下文成本。
SKILL.md 是唯一默认被 Agent 读取的文件。保持简短。把所有重的东西推到 references/。references/ 里的东西在 Agent 显式读取之前不花一个 token。这个区别是整个优化。
大多数构建者把 skill 文件当 wiki。他们把 API 文档、错误处理表、边界情况列表、完整风格指南全塞进去。文件膨胀到 8,000 tokens。Agent 每次 skill 触发都加载它。八千 tokens 乘以每次会话、每个用户、永远。
把 API 文档推到 references/api.md。Agent 在需要时读取它。不是之前。
scripts/ 存在是因为有些任务是确定性的。如果 skill 总是运行同样的 git 命令、同样的 linter、同样的文件重命名模式,放进脚本。scripts/ 里的确定性代码每次运行方式相同,加载成本为零。skill body 里的非确定性代码每次被模型重新发明,有轻微变化,花完整 token 成本。
描述是路由触发器,不是能力摘要
描述不是 skill 做什么的摘要。它是路由触发器。Agent 在会话开始时读取你 skill 库中的每个描述来决定加载什么。搞错了,错误的 skill 触发。另一个方向搞错了,正确的 skill 永远不加载。两边都稍微搞错,你库中的每个其他 skill 都稍微变差,因为模型在任务旁边加载了不相关的上下文进行推理。
每条描述都以 "Load when" 开头。
不是 "This skill helps with"。不是 "Use for X"。是 Load when。
- "Load when the user is debugging a failed API call or getting a 4xx error in a service they own."
- "Load when the user asks to write, refactor, or review a skill file."
- "Load when the user mentions a deploy, migration, or schema change."
这个短语迫使你用触发器而非能力来思考。能力是你的 skill 做什么。触发器是 Agent 应该决定加载它的时机。这是不同的问题,只有其中一个对描述重要。
描述的另一半是词汇。用工程师沮丧时说的词。不是他们应该说的。是他们实际说的。
- "my build is broken" 不是 "there is a compilation error in my project"
- "this is slow as hell" 不是 "the query is underperforming"
- "why does this keep happening" 不是 "there is a recurring failure pattern"
模型基于与用户实际消息的语义相似度来路由。如果用户说 "my build is broken" 而你的描述说 "compilation error resolution",skill 可能不加载。用用户着火时操作的 register 来写描述。
Avid 曾经花两小时重写一条描述。skill 一周来都在错误的请求上触发,搞不清为什么。修复是把 "handles authentication errors" 改成 "Load when the user is getting 401s or can't log in." 一句话。之前错一半时间。之后几乎每次都对。
Skill Body:模型不知道的东西
Skill body 中的每句话在留下前必须通过一个测试:没有这行,Agent 会搞错吗?
如果不会,删掉。模型已经知道它。
模型知道怎么写 PR 描述。你不需要 "write a clear title and describe the changes"。模型知道 Python。你不需要 "use descriptive variable names"。模型知道 git。除非你有非显而易见的 house style,否则你不需要 commit message 格式。
模型不知道的是你的品味。
你的品味是:"我们永远不在 hotfix 时 force-push 到 main,哪怕在 hotfix 期间。" 你的品味是:"type stubs 放在实现文件里,不是单独的 .pyi。" 你的品味是:"API 错误总是在抛出前记录,不是之后。" 这些才放进 body。
这个测试能抓住大多数人写的约 60%。几乎任何 skill 的初稿有三分之二的东西是模型已经知道的。删掉它们。剩下的才是 skill。
不要写命令。写意图。
Avid 的第一个 PR-review skill 有十二个步骤。检查标题格式。运行 linter。Grep TODO 注释。读 diff。检查测试覆盖。重写后是四句话:"I review PRs against the team's style guide in references/style.md. I flag anything that would fail in production, not just anything that's suboptimal. I never approve without reading the test file. Here are three PRs I thought were well-reviewed and why."
重写现在产出更好的输出。随着模型改进它会继续变好。过程式 skill 会老化。意图式 skill 会复利。
三层上下文成本结构
- Index tier:约 100 tokens,总是加载。那是你的描述。你无法逃避这个成本,所以每个词都必须物有所值。
- Load tier:2,000-8,000 tokens,skill 触发时加载。那是 SKILL.md。这是你优化的。大多数 skill 应该保持在这里低于 1,500 tokens。
- Runtime tier:无界,只在 Agent 请求时加载。那是 references/。需要之前免费。
把所有能推的都推到 Runtime tier。
风格指南放 references/style.md。API 文档放 references/api.md。错误表放 references/errors.md。输出模板放 assets/。Agent 在任务需要时加载它们。不是之前。
Avid 有一个 skill 应用这个之前是 6,000 tokens。把参考材料移到 references/、模板移到 assets/ 后,skill body 是 800 tokens。skill 运行更快。上下文预算去了重要的地方。行为相同。之前一直在为无意义的东西付 5,200 额外 tokens。
Eval:在 Skill 存在前写好
写 skill 前写 10 到 20 个查询。每个标记:"should trigger" 或 "should not trigger"。十个 should-trigger。十个 should-not-trigger。
Should-triggers 告诉你 skill 实际是做什么的。Should-not-triggers 迫使你定义边界。两者都是你写描述前需要的信息。
如果你写不出 eval,你不需要这个 skill。
这听起来显而易见。几乎从来没人做。典型流程是:决定做 skill,写 skill,肉眼测试几个响应,发布。skill 对幸福路径有效。三周后它在不该触发的请求上触发,在应该抓的请求上错过。
Eval 集在你写任何东西前就抓住这个。
发布前运行 eval 集。每次重大变更后再运行。如果变更破坏超过两个 eval,回滚。Eval 是契约。Skill 是实现。
Eval 还给你一个对抗模糊 skill 的强制函数。如果你在写一个叫 "writing-helper" 的 skill,写不出十个具体的 should-trigger 查询,skill 太宽了。拆分它。"writing-helper" 变成 "api-changelog-writer" 和 "error-message-copywriter"。现在 eval 自己写出来了。
Avid 曾经发布了一个没有 eval 的 skill。一周运行良好。然后开始在任何文件相关的请求上触发,因为描述提到了 "file operations" 而 skill 实际是关于 git 操作的。它在一半会话上花上下文预算。事后写 eval 让问题在五分钟内变得明显。本应该在写 skill 之前就花那五分钟。
Gotcha:唯一应该增长的部分
Agent 失败时,不要重写描述。不要重写 body。追加 gotcha。
Gotchas
- If the repo uses pnpm workspaces, the root package.json is not the entrypoint. Start from packages/.
- The staging environment requires VPN. CI runs without VPN. Never test staging from CI.
- API rate limits reset at midnight UTC, not midnight local. Retries before midnight will always fail.
Gotcha 部分是 skill 发布后唯一应该增长的部分。其他一切应该保持稳定。
这不是约定。是设计决策。原因是:如果你每次 Agent 失败后都重写描述,描述会漂移。它积累限定词和特殊情况,直到不再正确路由。
Gotcha 部分不影响路由。它作为 skill body 的一部分加载,在 Agent 已经决定加载 skill 之后。你可以无限追加而不触碰任何对路由重要的东西。
Gotcha 部分也是 skill 的制度记忆。每个条目都是 Agent 在真实任务上做出的真实失败。随着时间它变得比原始 skill body 更有价值,因为原始 body 是从想象写的,gotchas 是从经验写的。
Avid 最老的一个 skill body 600 tokens,gotcha 部分 400 tokens。大多数有用指令在 gotchas 里。这就是三个月真实使用的样子。
Self-Generated Skills 没有平均收益
这是 Avid 读到时最惊讶的规则。Perplexity 直接指出来了。Barry 和 Mahesh 在舞台上暗示了。
模型能起草结构。它能填充明显约束。它不能识别自己训练分布中的缺口。
Skill 有价值是因为它编码了模型没有它就会搞错的东西。模型看不到它会搞错什么。它无法访问自己失败的案例。它不能从内部观察自己的盲点。
能写好 skill 的人是看过 Agent 失败的人。在真实任务上。不止一次。同一个失败模式出现在不同上下文中。
那个人是你。不是模型。
模型能写一个看起来正确的 skill。正确的结构、正确的格式、覆盖明显案例。发布没问题。然后三周后它错过了你环境中实际发生的每个案例,因为那些案例特定于你的技术栈、你的约定、你的用户、你的失败历史。这些都不在模型的训练数据中。
Skill 编写是少数 remaining 的软件任务之一,其中瓶颈是真实的人类判断。
如果你有 /skillify 就用它。但把输出当作初稿。它需要一个看过 Agent 失败的人类来让它真实。
六周进化历程
- Week 1:三天发布九个 skill。感觉很好。Agent 正确处理了一个 PR review,以为搞懂了什么。
- Week 2:Agent 开始在错误的 skill 上触发。debug-api skill 在任何提到 API 的请求上加载,即使是文档请求。PR skill 在与 PR 无关的 code review 期间加载。上下文在 Agent 开始思考前就填满了不相关指令。
- Weeks 3-4:重写了每个描述。从能力语言转到触发语言。"Helps with API debugging" 变成 "Load when the user is getting a 4xx or 5xx from a service they own and are trying to diagnose it." Skill 开始正确路由。噪音下降。
- Week 4:把所有重的东西移到 references/。Skill body 从平均 3,200 tokens 降到 900。会话性能改善。不再在复杂任务上遇到上下文预算问题。
- Week 5: retroactively 为每个已发布的 skill 写了 eval。发现三个 skill 在同一个触发器上重叠。合并了两个。完全删除了一个,因为写不出十个真实的 should-trigger 查询。结果是为一个只做了一次的任务写的。
- Week 6:打开一个三周没碰的 skill,发现 gotcha 部分有一条不记得写过的线。Agent 在一个涉及 monorepo 的任务上失败了,记录了失败,在反思周期中 skill 被更新了。Gotcha 是正确的。它抓住了写原始版本时没想到要预防的东西。
系统不是魔法。Skill 仍然失败。变化是错误停止重复,抓住过一次失败的 skill 继续抓住它们,不需要重新教那课。
常见错误
Description drift:每次失败后重写描述。六次重写后它不再正确路由。修复:发布后永不重写描述。追加 gotchas。只在 skill 整个范围变化时才重写描述。
Token blindness:Skill body 5,000 tokens。每次触发加载。一半内容是模型已经知道的。修复:对每行运行"没有这行 Agent 会搞错吗"测试。把参考材料移到 references/。Skill body 目标低于 1,500 tokens。
Trigger collision:两个 skill 在同一个查询上触发。都加载。上下文填满冲突指令。修复:发布前写 eval。包含覆盖相邻 skill 的 should-not-trigger 查询。如果两个 skill 共享超过三个 should-trigger 查询,合并它们或重新定义边界。
LLM 起草的 skill 没有边界情况:Agent 生成的 skill 看起来正确,发布没问题,错过你环境中实际发生的每个案例。修复:只在看过 Agent 失败的人类审查并添加真实 gotchas 后才发布 skill。把 /skillify 当脚手架,不是终稿。
Stale references:references/api.md 六个月旧了。Agent 自信地读取它并调用不再存在的端点。修复:给 references/ 中每个文件标注最后更新日期。在 skill manifest 中标记任何超过 60 天的东西待审查。
Eval rot:Eval 通过是因为 skill 现在专门在 eval 查询上路由,不是因为路由逻辑正确。修复:每月给 eval 集添加新的真实世界查询。Eval 应该包含来自真实会话的查询,不只是发布前写的那些。
七天上手路径
- Day 1:读三个来源。Garry Tan 的帖。Barry Zhang 和 Mahesh Murag 的 AIE 演讲。Perplexity 的"Designing, Refining, and Maintaining Agent Skills"。一次性读完。它们从不同角度覆盖同一块地,重叠就是共识。
- Day 2:Clone gbrain。添加 gstack。
- Day 3:挑一个你每周都做的工作流。不是新工作流。你已经做的。你知道失败模式的。
- Day 4:在 Agent 里做一次。不要优化。观察 Agent 在哪里搞错。那些错的地方就是 skill body。
- Day 5:输入 /skillify。观察它变成永久的。Skill 现在在库里了。下次 Agent 会找到它。
- Day 6:在真实实例上运行。追加 gotchas。第一次真实运行会发现你没预料到的东西。那些进 gotcha 部分,不是 body。
- Day 7:再来两个工作流。
第 7 天你有三个 skill、三个脚本、三个触发器。你停止写 prompts。上周编码的工作流下个月还在库里。它们相同地运行。每次 Agent 失败你追加 gotcha 时它们改进。你不需要做任何事来让这发生。这就是复利。
结语
现在大多数人在更擅长 prompting。这不等于系统在变得更好。
趋同到 skill 的团队没有协调。他们独立运行实验,落在同一个地方,因为实验指向那里。Prompts 活在你的脑袋和聊天记录里。每次会话重置。它们不累积。它们不抓住上周二的失败并应用到周四的任务。
Skills 活在 git repo 的纯文件里。Gotcha 部分增长。References 更新。Eval 抓住回归。系统下个月知道比今天更多,不需要你重新教任何东西。
最让 Avid 惊讶的是瓶颈不是技术性的。文件夹结构简单。格式平易近人。真正难的部分是看过 Agent 失败足够多次,知道它需要但它还没有的东西。那个知识是你的。不能被生成。只能被积累。
拥有你的 skill。拥有塑造它们的失败。把它们保存在纯文件和 git 里,没人能拿走,每次 Agent 失败时它们都在变得更好。