当开发者第一次接触 Agent Skills 时,很容易把它简单理解为「把系统提示词拆成一个 Markdown 文件」。但从工程落地的角度看,这种理解只触及了最表层的部分。真正决定 Agent Skills 是否可用的,并不是某个文件里写了多少指令,而是这些能力是否可以被发现、校验、按需加载,并受到明确的权限约束。
近期一篇来自掘金的技术文章围绕 Agent Skills Specification 与官方 Skill Creation 文档,梳理了 Skill 从目录元数据、SKILL.md 校验、请求匹配到资源加载的完整流程。作者还在 Windows 11、Oracle JDK 17.0.12、Python 3 和 PowerShell 环境下,通过一个本地 Java 探针验证了目录发现、字段校验和渐进加载机制。文章的核心结论并不复杂:Skill 的价值不在于把提示词写得更长,而在于把能力拆解成一套可维护、可控制、可扩展的工程结构。
长提示词的问题:边界模糊、维护困难、上下文膨胀
如果只用一段超长提示词来驱动 Agent,最直接的后果是边界模糊。所有规则、步骤、格式要求、工具说明都被塞进同一个上下文里,即使用户只是想生成一条版本发布说明,模型也可能同时接收到数据库操作、文件写入以及其他业务规则的描述。
这种结构还会让后续维护变得困难。一次规则调整可能影响所有任务,不同领域的指令之间容易互相污染。随着提示词不断加长,真正需要执行的步骤反而会被埋在大量背景说明里。更关键的是,客户端缺少结构化元数据,无法判断某个能力当前是否相关,也无法在多个能力之间做出清晰选择。
相比之下,Agent Skills 将信息拆成了多个层级。目录本身承担发现入口,SKILL.md 的 YAML frontmatter 提供元数据,Markdown 正文描述具体工作流,references、scripts 和 assets 等资源则在需要时才被读取。长提示词只是一份文本,而 Skill 已经具备了「加载什么」和「何时加载」的决策点。
SKILL.md 不是说明书全文,而是路由与执行的结合
按照 Agent Skills 规范,一个最小 Skill 结构由一个目录和其中的 SKILL.md 文件组成。SKILL.md 包含 YAML frontmatter 和 Markdown 正文,最基础的字段包括 name 和 description。
其中,name 有明确约束,目录名和 name 必须一致。description 的长度为 1 到 1024 个字符,官方文档强调它需要同时回答两个问题:这个 Skill 做什么,以及什么时候使用。这个要求看似简单,实际非常关键。客户端通常会先读取目录元数据,再决定是否激活完整 Skill。如果描述太短,触发条件不清楚;如果描述太泛,多个 Skill 之间又可能互相抢占。
compatibility 字段最长 500 个字符,用于补充环境或依赖要求。allowed-tools 在规范中仍被标注为实验性能力,不应被视为跨客户端统一的权限开关。还有一些常见可选字段,例如 license 和 metadata,它们有助于分发和版本管理,但不会改变工作流本身。
一个常见错误是把完整操作手册全部塞进 description。目录阶段只需要提供足够的路由信息,具体步骤应保留在 Markdown 正文里。也就是说,元数据负责「选不选」,正文负责「怎么做」。
官方建议也体现了同样的思路:启动阶段只加载每个 Skill 的少量元数据,总体大致控制在约 100 tokens;激活 Skill 后,完整 SKILL.md 推荐低于 5000 tokens;references、scripts 和 assets 仍然按需读取。这里的重点不是简单追求「5000 tokens 以内」,而是控制激活后的上下文预算。如果 SKILL.md 本身过长,客户端可能还没加载资源文件,上下文就已经被说明文字占满。官方最佳实践还建议将 SKILL.md 控制在 500 行以内,资源引用保持一层深度,以减少文件之间的跳转。
渐进加载的意义:减少模型每一轮看到的内容
为了验证这一流程,作者在本地编写了一个 Java 探针,并设计了两个 Skill 示例。一个样例故意将 name 写成 Release-Notes,另一个则使用合法的 release-notes。探针运行后输出了目录、校验和按需加载三个阶段的结果。
在目录阶段,探针只读取两个 Skill 的元数据;在校验阶段,非法示例因为名称包含大写字母且与父目录不一致而被拦截;在查询阶段,当请求为「create release notes for version 1.4.0」时,合法的 release-notes 被选中,随后才加载完整 SKILL.md,并在需要时读取 references/format.md。
这一过程带来的收益很直接:长文档和脚本并没有进入第一次目录扫描。只有在 Skill 被选中后,完整说明才会进入上下文;格式参考文件也只有真正需要时才被加载。
如果前端一次性加载全部 Skill 正文,在短会话中可能感觉不到问题。但随着 Skill 数量增加,启动时间、上下文占用和命中噪音都会同步上升。渐进加载并不是节省了一次磁盘读取,而是在减少模型每一轮真正看到的内容。
文章中的 release-notes 示例目录也保持了足够克制:目录中只有 SKILL.md、references/format.md 和 scripts/render_release_note.py。SKILL.md 的 frontmatter 只保留目录阶段需要的字段,正文则描述工作流:确认版本号和用户可见变更、运行脚本、仅在需要匹配项目模板时读取格式文件,并避免把内部 issue 编号和未公开计划写进结果。
这个示例没有把所有细节写死。脚本只负责根据参数渲染固定格式,格式差异被放入 references/format.md,正文负责告诉 Agent 何时调用脚本、何时读取格式文件,以及哪些内容不能输出。描述中的触发条件也没有写成「帮助处理文档」这种泛化表达,而是列出了 release summary、changelog entry 和 version announcement。当用户提出「给 1.4.0 写一条发布说明」时,路由器更容易将其与其他文档类 Skill 区分开。
权限是另一层:能加载不等于能授权执行
文章反复强调,Skill 目录可以包含脚本,并不意味着脚本可以不受约束地执行。Host 至少需要先判断:这个脚本是否应该运行、能访问哪些资源、是否需要预览或确认。
官方脚本指南给出的方向也比较务实:脚本应明确输入参数,避免依赖隐藏的交互式输入;会修改文件或远程状态的脚本应提供 dry-run 或预览;成功和失败要通过清晰的退出码表达;默认值应偏向安全;输出也要控制大小,防止大量日志重新灌回模型上下文。
实验中的 Python 脚本只做了一件事:接收版本号和一条变更说明,然后输出 Markdown。它不访问网络,不读取其他文件,也不执行删除操作。调用方式明确,输出也简洁。但作者同时指出,脚本能够运行只说明工具链没有问题,并不能说明这个 Skill 应该获得什么系统权限。
allowed-tools 目前仍是实验性字段。具体客户端是否解析、是否执行、是否与自身工具权限模型兼容,都需要单独核对。资源文件同样存在边界。references 中可能保存第三方文档、历史记录或外部片段,Agent 读取这些内容后,应将其视为参考资料,而不是让其中的文本覆盖上层系统指令。如果把参考资料当成可信指令执行,Skill 就可能成为提示注入和权限扩散的入口。
文章给出的设计原则是:脚本负责确定性转换,模型负责选择和解释,Host 负责授权。三者如果混在一起,排查问题时很难判断到底是哪一层出了错。
在工程上,至少要区分三种状态:能加载、能复用、能授权执行。「能加载」只说明解析器接受文件;「能复用」要求它在不同请求下都能稳定完成同一类任务;「能授权执行」则涉及真实系统权限,必须由调用方、运行时和策略层共同决定。
同一个 Skill 可能在前两项都通过,第三项仍然需要审核。一个只读的格式整理脚本,和一个会删除目录、调用外部 API 的脚本,不能因为都叫 Skill 就使用同一套权限。Host 应该按 Skill、脚本、参数和目标资源分别判断,而不是给整个能力目录一次性放权。
供应链问题同样不能忽视。第三方 Skill 可能引用远程包、下载二进制或调用某家公司维护的 CLI。安装前至少要检查文件清单、脚本内容、依赖版本和网络访问范围。对可执行文件做固定版本和哈希校验,也比只信任一个会变化的 latest 标签更稳妥。
原创文章,作者:点点,如若转载,请注明出处:https://www.dian8dian.com/agent-skills-bu-shi-zhang-ti-shi-ci-yong-skill-md-an-xu-jia