Serverless Framework 内建 Agent Skills 的目录规范、版本治理与自动安装机制
【免费下载链接】serverless⚡ Serverless Framework – Effortlessly build apps that auto-scale, incur zero costs when idle, and require minimal maintenance using AWS Lambda and other managed cloud services.项目地址: https://gitcode.com/GitHub_Trending/se/serverless
导读
本文面向希望在 Serverless Framework 中贡献或定制 Agent Skills 的开发者,围绕仓库 skills 目录讲解其完整机制:技能以何种目录与 Frontmatter 格式随 CLI 分发、serverless agent skills install如何把技能写入服务目录、CI 如何通过 lint-skills.js 强制校验 Frontmatter 契约与版本号,以及“自动更新”是如何做到幂等、静默且永不删除用户文件的。读完你会掌握技能包的目录规范、版本迭代流程与底层同步引擎的设计约束,可直接在本仓库为 Agent 技能做贡献或理解其运行原理。
一、Skills 目录的定位与分发链路
skills/目录是整个 Agent Skills 体系的唯一内容源(source of truth)。根据 skills/README.md 的说明:
- 目录内的每个 Skill 都随 Framework CLI 一起打包发布;
- 用户执行
serverless agent skills install后,技能会被安装进用户的服务目录; - 安装后的技能通过框架的 auto-update 机制持续保持与内置版本一致;
- 技能格式遵循 agentskills.io 规范:每个技能一个目录,目录内必须有
SKILL.md,可附带辅助文件(如references/参考文档目录)。
当前仓库内置两个技能,均带完整的SKILL.md与references/辅助文档:
- serverless-mcp:在 AWS 上以 Serverless Framework 内建 MCP(Model Context Protocol)支持托管 MCP 服务器,覆盖
mcp:配置块、网关鉴权、打包与部署调试等场景; - serverless-sandboxes:面向 AWS Lambda MicroVMs 的沙箱能力,覆盖
sandboxes:配置、serverless dev --sandbox、invoke --sandbox、logs --sandbox等操作。
skills/manifest.json 记录每个技能当前发布的版本号与内容哈希(见下文版本治理)。
打包与运行时读取的双通道设计
技能的读取逻辑在 manifest.js,它采用与框架核心版本号相同的双通道模式:
- 生产环境:构建期由 esbuild 通过
define将__SF_SKILLS_MANIFEST__常量注入打包产物,运行期直接读取该内建清单; - 源码运行 / 测试:检测不到该全局常量时,通过
readSkillsFromDir()直接读取仓库根目录 skills/ 下的真实文件。
这种设计(注释中明确“one function, so the two paths cannot drift”)保证构建期嵌入与运行期回退读取共用同一套解析代码,避免两条路径的行为漂移。配套的 verify-skills-packaging.js 还提供了对打包产物中技能清单的独立校验入口。
二、SKILL.md 的 Frontmatter 契约(CI 强制)
每个技能的SKILL.md必须以 YAML Frontmatter 开头,契约在 skills/README.md 中定义,并由 lint-skills.js 在 CI 中强制执行:
--- name: <必须等于目录名> description: <非空,长度 ≤ 1024 字符> metadata: managed-by: serverless-framework # 必填 —— 更新/所有权标记 version: "1" # 整数字符串;每次内容变更都必须递增 author: Serverless Inc. # 可选 ---各字段规则:
| 字段 | 是否必填 | 约束说明 |
|---|---|---|
name | 必填 | 必须与所在目录名完全一致 |
description | 必填 | 非空,且不超过 1024 字符 |
metadata.managed-by | 必填 | 必须为serverless-framework,作为“受框架托管”的所有权/更新标记 |
metadata.version | 必填 | 整数字符串,如"1";任何内容变更都必须递增 |
metadata.author | 可选 | 例如Serverless Inc. |
严格模式下的逐项校验
Frontmatter 的严格解析在 read-skills.js 中实现,strict: true时逐项校验:
- 技能目录必须存在
SKILL.md,否则直接抛错(missing SKILL.md); - Frontmatter 必须是合法的 YAML 且以
---包围,否则报invalid frontmatter; frontmatter name必须等于目录名;description必填;metadata.managed-by必须为serverless-framework;metadata.version必须是整数字符串(正则/^\d+$/)。
一个值得注意的安全细节:解析 Frontmatter 时显式使用yaml.JSON_SCHEMA(见 read-skills.js 的注释)——因为被安装的SKILL.md属于不可信输入(克隆的仓库可能夹带任意技能),限定为纯 JSON 数据(字符串/数字/对象),禁止任何自定义或带类型的 YAML 标签。
三、内容变更流程:升级 version 并同步 manifest
skills/README.md 明确了内容变更的强制工作流:
- 修改技能内容后必须递增
metadata.version(如"1"→"2"); - 在仓库根目录执行
node packages/sf-core/scripts/lint-skills.js --update; - 将更新后的 skills/manifest.json 一起提交。
不做这三步,CI 会失败。这一约束由 lint-skills.js 落地:
- 对每个技能的全部文件(递归收集、按键名排序后拼接)计算 SHA-256 内容哈希
contentHash; - 与
manifest.json中记录的上一版本哈希对比,若内容变了但metadata.version未变,报错:content changed but metadata.version is still "…" — bump metadata.version and rerun with --update; - 若
metadata.version倒退(如2 → 1),同样报错; - 带
--update运行时,只有在无错误的前提下才会把新哈希与版本写回skills/manifest.json;不带--update时,若某技能尚不在 manifest 中,也会提示需--update并提交清单。
所以skills/manifest.json实际上是一份版本 + 内容指纹的发布台账。以当前仓库为例:
{ "serverless-mcp": { "version": 1, "hash": "6740dff61b9a73008b51d4145ba63c0721d4f402f844967bb628354507e8da42" }, "serverless-sandboxes": { "version": 1, "hash": "f4771bfa41a73fb52fe266f129409019c2633045ee8b443efb29967ade35b492" } }hash由lint-skills.js依据技能目录内全部文件内容生成(任何references/改动都会改变哈希),因此它既能识别“内容变更但未升版本”,也能在合并冲突或手动改动时给出可复现的校验依据。
四、安装与自动更新的同步引擎
同步引擎的三个不变式
核心同步逻辑在 engine.js(syncSkills),它对每个目标目录 × 每个内置技能执行收敛,严格遵循注释中声明的三条不变式:
- 永不删除(never delete)——所以技能目录里“改名而非复用旧名”的规则本质上是为这条不变式兜底:不删除旧文件,只是新增文件覆盖旧引用;
- 只覆盖带
metadata.managed-by: serverless-framework且版本更低的文件; - 幂等地报告每次操作(
added/upgraded/skipped)。
syncSkills对每个技能的决策树为:
- 目标目录中不存在该技能 → 全量写入(
added); - 已存在但
managed-by不是serverless-framework→跳过(ejected,即用户主动“接管/弹出”的技能不受框架干扰); - 存在且受托管,但内置版本更高 → 全量覆盖写入(
upgraded,记录 fromVersion → toVersion); - 已是最新 → 跳过(
up-to-date); - 写入失败(权限等)→ 跳过并标记
unwritable,不影响其他技能。
写入采用原子写(writeFileAtomic:先写.tmp-sf临时文件再rename覆盖,见 engine.js),避免中途失败留下半个技能。版本比较依赖 Frontmatter 中的metadata.version,且只对带托管标记的目录生效——这正是 Frontmatter 契约里managed-by与version两个字段存在的根本原因。
目标目录的探测阶梯
安装与自动更新要写入哪些目录,由 resolve-targets.js 决定,采用“最高优先级优先”的探测阶梯:
| 优先级 | 判定依据 | 写入目录 |
|---|---|---|
| 1 | 显式--dir覆盖(仅 install 模式) | 精确按 flag 映射,例如claude→.claude/skills、agents→.agents/skills |
| 2 | 已存在受托管技能(managed presence) | 收敛到技能实际所在目录 |
| 3 | 服务级 Agent 目录(团队偏好) | 服务目录下存在.claude/或.agents/ |
| 4 | 主目录探测(开发者用什么 Agent) | ~/.claude→ claude;~/.agents、~/.codex、~/.cursor→ agents |
| 5 | 安全默认 | 同时写入.claude/skills与.agents/skills |
注意auto(自动更新)模式只用第 2 层“managed presence”作为 opt-in 门槛:自动更新永远不会主动引导安装(never bootstraps),只有当某个服务目录已经出现过框架托管的技能,后续命令才会去收敛它。--dir的合法值由DIR_MAP({ claude: '.claude/skills', agents: '.agents/skills' })限定,传入未知值会抛出明确错误。
自动更新的四条守卫
安装在命令执行后通过 auto-update.js 的autoUpdateAgentSkills()静默触发,其守卫逻辑(源码注释逐条列出)为:
- 绝不在 CI 中运行(
isCICDEnvironment()直接返回); - 绝不对
agent命令本身运行(避免递归安装自身); - 必须有服务配置文件(
configFilePath为空即返回); - 绝不抛异常——技能更新失败最多输出一条 debug 日志,绝不影响用户正在执行的业务命令。
其中有实现细节值得注意:homeDir不放在默认参数里求值,而是在try块内解析,因为os.homedir()在极简容器(无家目录)中可能抛错,默认参数会在进入try前就被求值,从而破坏“never throws”保证。
当有变化发生时,框架会输出一行提示,例如:
Serverless agent skills updated: +serverless-mcp(新技能以+name表示,升级则以name v1→v2表示,见 auto-update.js。)
五、辅助文件的生命周期规则与维护建议
skills/README.md 还有一条直接影响升级设计的原则:
Aux files are never deleted from user installs — add/rename files rather than repurposing existing names. (辅助文件永远不会从用户安装中删除——请新增或重命名文件,而不要复用既有文件名。)
结合不变式解读,它的意义是:升级技能时,若旧的references/*.md被新版本“改名”,框架只能新增新文件,无法清理旧文件;因此为引用文件规划命名时要预留演化空间,用新增引用文件承载新内容,而不是就地改写同名文件以表达不同含义,以免用户端残留含义不同的同名文件造成混淆。
建议的贡献流程总结为:
- 阅读本仓库 skills/serverless-mcp/SKILL.md 或 skills/serverless-sandboxes/SKILL.md,以现有技能为范本,体会
description如何覆盖“即使未点名 MCP / Sandbox 也能命中”的触发场景; - 新建技能目录并编写符合 Frontmatter 契约的
SKILL.md,把长篇细节拆入references/; - 内容变更时递增
metadata.version; - 在仓库根目录运行
node packages/sf-core/scripts/lint-skills.js --update校验并刷新 skills/manifest.json; - 提交技能目录 + manifest,交给 CI 复核。
若需验证技能在用户侧的实际安装/收敛行为,可继续阅读 auto-update.js 与 engine.js 的决策逻辑,或参考两个技能各自references/下的config.md、testing.md等文档了解其面向的真实业务场景。
六、小结
skills/目录与配套实现共同构成了一个“契约约束 + 哈希校验 + 幂等收敛”的技能分发闭环:Frontmatter 契约保证了技能可被识别、可被托管、可被升级;lint-skills.js+manifest.json用内容哈希杜绝“改了内容却忘升版本”的人为失误;安装/自动更新引擎则以“不删除、只覆盖托管且低版本、CI 外静默、绝不抛错”四条铁律,保证用户服务目录在任何命令序列下都能安全收敛到最新技能集。
【免费下载链接】serverless⚡ Serverless Framework – Effortlessly build apps that auto-scale, incur zero costs when idle, and require minimal maintenance using AWS Lambda and other managed cloud services.项目地址: https://gitcode.com/GitHub_Trending/se/serverless
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考