
1. 120 技能到底哪一步先崩先说清楚我讲的“Agent Skill”指什么不是几十行的提示词片段而是包含SKILL.md描述、入口脚本、示例参数和配套资产的一整套能力目录。早期我只有十几个技能放在一个文件夹里完全没有压力随手就能定位。后来数量爬过五十个我开始试图分类爬过一百个尤其是超过 120 个之后我发现自己每次新增技能之前都要做一轮心理建设这个技能会不会和旧的重复旧的现在还能不能跑如果我改了某个入口哪些调用链会跟着断真正把我逼到动手造“包管理器”的不是那次随手数数而是连续踩中的三个实坑。1.1 先出问题的不是文件乱是“同名不同行为”开始互相打架第一个坑是命名冲突。听起来很小儿科实际相当恶心。我的技能库里出现过两个都叫web_extract的技能一个是从普通网页抽正文另一个是给特定结构化接口做爬虫封装。它们在运行时被注册成同一个名字调用结果完全不可预期。当你只有十几个技能时靠人脑记这种差异还不算离谱当你有上百个技能时靠记忆维护“同名但语义不同”的映射就是在给自己埋雷。名字问题背后其实是身份问题。技能不只是“一堆代码”它应该拥有一个不可变身份知道它是什么版本、从哪里来、被谁维护。没有身份技能和流浪代码没有分别短期能跑长期就是定时炸弹。1.2 手动分类和“前缀洁癖”撑不过三个月我也尝试过不少土办法结果各有各的尴尬。按业务功能建目录pdf/、web/、email/、data/……听起来合理但技能往往横跨多个领域一个“导出网页正文为 PDF”的技能你放哪个目录都会觉得不对劲。用命名前缀做约束category__name__vX这种格式一开始挺清爽但版本一多就退化它只是把版本号塞进了名字里并没有真正暴露版本关系。写内部文档登记天真的做法。文档更新的速度永远赶不上代码变更的速度一周之后就没人再看了。这三个方案共同的缺陷是它们只解决了“物理摆放”和“目视管理”没有解决“源”和“同步”。目录再整齐如果不知道技能的正式版本在哪、如何更新、如何回溯那它依然是一堆躺在磁盘里的未知文件。说句难听的它们只是在给混乱做装饰没有真正引入秩序。1.3 真正致命的三个代价上下文、信任和技能发现到了 120 这个量级比目录乱更严重的是三个系统性问题。上下文失控。我当时的架构会把技能的“描述头”作为上下文注入模型。当技能数量超过一百个哪怕每个只占 300 token总开销也有几万 token这还不算为了选出合适技能而进行的额外推理成本。结果就是模型被淹没在大量“可能有用但当前用不上”的描述里真正该突出的意图理解反而被削弱。信任崩塌。技能多了之后我没法保证“这个技能是最近一次验证过的版本”。有几次我明明修好了一个 bug换到另一个项目环境却发现旧行为又出现了因为那一份技能副本还是老的。这种“看似能跑但不敢依赖”的状态比技能本身坏掉更消耗精力。技能发现困难。技能库大到一定程度想找一件“我记得我做过”的能力非常耗时。明明是自己的资产却像在旧仓库里翻箱子而且大多数箱子没有标签。搜索、定位、判断是否适合自己的需求这些动作的耗时已经超过写一个新技能的成本于是你会本能地选择重写然后制造出第 121 个重复轮子。到这个阶段我意识到我需要的不再是“再整理一次文件夹”而是一个类似操作系统包管理器的东西能回答四个基本问题技能从哪里来、装到哪去、怎么升级、怎么删除。答案拆开就是标题里那两句话——仓库是源链接是安装。2. 从 Linux 包管理器和 Maven 生态里偷师的三件事包管理器不是什么新鲜概念。Linux 下的 apt/yum、Java 生态的 Maven、Python 的 pip、前端世界的 npm都是已经运行了十几二十年的成熟模式。动手之前我专门把它们的共同点拆出来看了一眼发现真正核心的其实只有三条。2.1 仓库是“唯一事实源”包管理器的第一性原理是让所有客户端面对同一个事实源。apt 的源仓库、Maven 中央仓库、npm registry、Docker Hub本质都是同一个角色定义“什么东西存在、什么版本可用、什么内容被正式认可”。Skill 生态缺的正是这一层。大多数人的技能散落在个人目录、GitHub 仓库、公司共享盘里每个人拿到的都是某个时间点的副本。副本之间没有同步机制也就谈不上“正式版”。当我决定把“仓库”作为基础设施时我给自己定了一条规则任何技能只要没有被提交到仓库就不算存在。所有安装、更新行为都以仓库里的内容为准。这条规则听起来简单但它把“我这台机器上有什么”和“系统里应该存在什么”明确分开了。前者是本地状态后者是仓库权威。很多所谓的版本混乱本质上就是这两个集合长期不一致造成的。2.2 链接是“安装的最小可寻址单元”我喜欢 Maven 的一个细节依赖坐标是groupId:artifactId:version一个三元组就能唯一定位一个制品。它把“从哪来”和“怎么装”解耦了。链接作为安装手柄是同样轻量的思路。所谓“链接是安装”实际含义是安装动作的参数是一个 URL而不是一个复制命令。这个 URL 指向仓库里某个技能的唯一入口。安装程序通过解析 URL获得技能的清单、文件清单、版本信息和来源信息然后把技能落地到本地并在本地索引里记录“这个技能从哪个 URL 安装”。相比“手工把技能目录拷贝到某个位置”链接的价值有三点可追溯URL 本身就是溯源记录随时能翻回去看安装时拿的是什么内容。可更新当仓库里更新了同一 URL 下的内容可以显式升级。可验证有效链接证明资源仍然存在于权威源中拷贝出来的目录则没有任何证明。2.3 我刻意没有做的事传递依赖解析Maven 的依赖树解析确实强大但对 Agent Skill 来说一上来就做传递依赖管理我认为是错的。原因一技能的“依赖”在 Agent 上下文里更多是“推荐配套使用某个工具”而不是编译期的 class 依赖。一个 PDF 提取技能可能希望 Agent 同时具备 OCR 能力但这不应该是强制安装的。如果把它做成运行时强依赖安装链会变得非常脆弱。原因二依赖解析会引入大量重复检查和版本冲突问题早期版本如果卡在这一步整个项目根本走不到“能用”的阶段。我采用的折中方案是在SKILL.md元数据里声明recommended_skills和environment安装器只展示提示不自动解析安装。从实际效果看这已经覆盖了绝大多数场景。提示先做最小可用版本把“包管理器”跑起来比设计一个完美的依赖解析器重要得多。依赖问题等真遇到了再补不要提前自嗨。3. 一个周末跑通最小可用版仓库、清单文件和安装器这个项目的实施时间比预想短很多核心原因是我没有把“包管理器”做成一枚火箭而是用了最朴素的“Git 仓库 JSON 索引 一个 CLI”组合。3.1 SKILL.md 清单规范7 个字段就够每个技能在仓库里占一个目录目录内至少有一个SKILL.md作为描述文件。字段我规定为字段必填用途name是技能唯一 IDversion是semver 版本号description是给 Agent/模型看的调用说明entry是入口文件与主函数定位author否维护者方便追溯license否使用时的约束recommended_skills否建议搭配的能力列表不自动安装我最想提醒后来人的一点description是写给你电脑里的模型看的不是写给人看的。它是模型判断“这个技能适不适合当前任务”的关键依据。描述里应当说明“它做什么、什么时候不应该用、需要哪些输入”。不少技能被我拒绝进仓库就是因为描述写得太抽象比如“对文本进行深度处理”——这句话对模型毫无帮助。一个合格的描述大概长这样Extract main text content from an HTML page and convert it to clean Markdown. Useful for article reading, content archiving, or building datasets. Do NOT use for image-based PDF files; those need the ocr-pdf skill. Input: a URL or local HTML file path.后半段“什么时候不该用”特别重要它能让 Agent 在调用前做出更好的筛选。3.2 仓库结构一个 Git 仓库加一个索引文件仓库目录结构长这样skillhub/ ├── index.json ├── skills/ │ ├── web/ │ │ ├── pdf-extractor/ │ │ │ ├── SKILL.md │ │ │ ├── main.py │ │ │ └── assets/ │ │ └── page-reader/ │ └── email/ │ └── summary/ │ ├── SKILL.md │ └── summary.py └── releases/ └── skillhub-v1.0.0.jsonindex.json是给搜索用的快速索引内容是一个简单数组[ { name: pdf-extractor, version: 1.2.0, path: skills/web/pdf-extractor, tags: [pdf, extract, text] } ]为什么要有index.json而不是直接让 CLI 去遍历仓库因为遍历一个完整 Git 历史很慢尤其在仓库变大之后。一个轻量索引可以把“搜索”这个高频操作降到毫秒级而索引生成完全可以交给 pre-push 钩子或 CI 脚本。3.3 安装协议一个 URL 从解析到落地的完整链路这是“链接是安装”最直接的体现。安装命令的调用长这样skillhub install https://gitee.com/myorg/skillhub/raw/main/skills/web/pdf-extractor/SKILL.mdCLI 拿到 URL 之后执行五步操作下载清单获取SKILL.md原文。字段校验检查name、version、entry三个必备字段是否存在且格式正确。本地落盘把技能目录完整放到本地技能区local_skills_dir/category/name/保留元数据。写入溯源记录在本地索引中记录{name, version, source_url, installed_at}。输出加载信息打印技能入口路径和推荐描述方便接入 Agent 时直接引用。我建议安装协议适配 Git raw 地址 release 标签而不是把 URL 指向分支。分支是可变的今天装的是这个版本明天可能就变。release 标签才具有不可变性。这也是我后来控制“链接漂移”的关键设计。3.4 CLI 提供五个动词足够了CLI 最终只暴露了五个命令基本对标 npm 和 apt 的用户直觉skillhub search keyword搜索公共仓库里的技能。skillhub info name查看技能描述和版本。skillhub install urlURL 安装。skillhub remove name移除本地技能并同步索引。skillhub verify检查本地索引和实际文件目录是否一致。实现上也不复杂我用 Node.js 写了大概两百行的 CLI用原生fetch下载列表和文件用本地 JSON 文件维护索引整个过程没有引入任何重量级依赖。如果你想在团队里复刻用 Python 也一样核心逻辑是相通的。4. 把 120 个散装技能迁移进“仓库”的完整过程方案在技术上不难真正的难点是迁移。下面我把迁移过程完整复盘一遍尽量保留细节。4.1 先盘点不要急着建仓库我第一天没有建任何仓库而是先写了一个扫描脚本把现有技能内容、入口、依赖、大小全部扒了出来。这一步的价值远超预期。扫描完全量目录后我拿到了第一组准确数据。原本我预期有 120 个技能实际共 137 个目录。其中约 40 个明显是重复或废弃副本。最后真正有维护价值、需要保留的大约占一半。没有第一步盘点后面所有“整理”都是盲目的。盘点时的关键动作扫描全量技能对每个技能目录生成 SHA-256。后面用来比对重复版本。按名称聚类找出功能雷同的候选。列出“最近半年没有调用记录”的技能作为僵尸技能候选。4.2 清洗与补元数据合并重复补写描述盘点完成后进入清洗阶段前后花了三天。我总结了三个原则重复内容合并。最典型的是网页提取不同时期写了好几个变体。最终合并成一个稳定版本废弃其他。无描述技能一律补写。凡是描述少于 20 个字的技能必须补上“做什么、什么时候不用”才允许入库。入口无法验证的技能标记为待定。不确定能否跑通的在清单里标成status: pending。它不会进入正式索引但会保留在档案区。这比直接删除更容易让人接受。最后确认 15 个僵尸技能彻底废弃其余确认保留。4.3 入库与索引生成分类只是主域标签承载跨域清洗完之后才是“建仓”。我按业务域做了分类web/、pdf/、email/、data/、prompt/、misc/。分类过程被“技能横跨多个领域”这个现实打脸了好几次。解决办法是允许一个技能归属多个标签但目录只选择主域。一份tags字段承担跨域标记比如 PDF 提取技能放在web/但 tags 同时带上pdf、extract、ocr。在每个技能目录里SKILL.md的格式和现有 Agent 工具生态保持一致。这意味着迁移后Agent 侧加载模块不需要做额外适配老调用方式依然工作。4.4 首次安装演练一次性发现两个实际问题仓库搭建完成后我在一个干净临时目录里从头执行了一次安装流程第一次就暴露了两个问题。案例 A只复制主脚本忘了配套资源。有些技能入口调用了同目录下的辅助文件但迁移时我只拷贝了主脚本没有拷贝assets/目录。安装产物在资源加载时直接缺失。修正很快但它让我意识到入库校验必须检查入口引用的本地文件是否都在目录里。后来我加了一段静态分析脚本扫描入口源文件中的所有相对引用。案例 B链接失效。一个技能清单里记录的原始 URL 是旧位置迁移后这个 URL 已经 404。这提醒我安装 URL 是硬约束每次更新仓库中的技能都要同步更新source_url并把技能文件放到仓库的 release 附件中。后来我还加了一条 CI 任务定期检查所有链接的有效性发现死链直接告警。5. 四个最容易翻车的环节以及我踩过的坑迁移上线只是开始后续维护才真正考验设计。下面按四个月的实际使用把最容易翻车的环节讲清楚。5.1 链接漂移把 URL 指向分支是最隐蔽的坑如果你把安装 URL 指向 Git 分支比如raw/main/skills/...在某个时间点它是有效的。但分支内容是可变的。当我修改技能并推送到 main 之后所有之前“装过这个版本”的机器表面没有变化但再重装拿到的是另一个内容。想要诊断到底装的哪个版本全靠source_url溯源记录。解决办法为每个技能发布版本时打tag安装 URL 统一指向 release 标签对应的 raw 路径。这样安装动作就是可重复的分支只承担开发流不承担安装流。5.2 安装天然带代码执行风险“链接是安装”听起来方便但外部链接拉下来直接执行等同于把一个陌生人请进家。我对此做了四件事建议所有的接入方都照做默认安装器不执行技能里的任何安装脚本只负责把文件复制到本地。manifest 只允许白名单入口类型比如python: main.py或node: index.js对 shell 等高危入口直接拒绝。公共仓库里的技能必须经过审阅没有 review 不允许合入。安装时完整记录source_url出事以后第一时间回溯源头。提示凡是来自公开链接的技能默认按“不可信代码”处理。先审阅再使用不要嫌麻烦。5.3 团队协作时提交纪律比工具本身更决定成败如果你只是一个人在仓库里操作你只需要解决自己那部分问题。团队协作时剩下 40% 的成败取决于 merge 纪律。我见过最典型的反面案例有人直接 push 一个技能进仓库没有描述没有入口验证结果别人安装完根本跑不起来。后来我在仓库里加了 PR 模板要求必须包含变更说明一条可执行的示例对应SKILL.md的前端描述同步。同时在 CI 里加了 schema 校验字段不齐或版本号不合法直接阻止合并请求。这一步看起来死板但实际把“入库即可用”这条标准落到了机器层面。5.4 离线环境和内网部署依然可以跑起来这套模型不要求必须连公共网络。因为“仓库是源”你只要把 Git 仓库同步到内网机器用 Nginx 或任何静态服务器托管 skills 目录安装 URL 改成内网地址即可。客户端侧的本地索引完全不变。用到私有化交付时甚至可以做一个skillhub bundle命令把仓库和索引导出成一个 tar 包拿到离线环境里直接安装。这比每次到客户现场都要重新配一套网关舒服得多。6. 四个月后我重新理解了 Agent Skill 治理6.1 实际感受到的差异这套体系跑通之后变化最明显的有三点。第一是维护成本。以前调整一个技能要担心有没有同步到每个环境现在统一提交到仓库更新策略非常清晰。第二是技能发现效率。我直接通过 CLI 搜索从整个库存里筛出某个域内的可用技能不靠印象也不靠翻 Notion。第三是上下文控制。技能打上标签后Agent 加载技能以需求为维度而不是把全部技能塞进上下文。我自己的记录里新增一个技能从“半天也没定下来”缩短到十几分钟技能召回的准确率也明显提升。这种改善不是因为某个功能特别炫而是因为“先搜索、再安装、再加载”的链路把心智负担从人脑转移到了工具上。6.2 什么时候不需要自己造这套东西如果你手头只有十个以内的技能且只给自己一个人用建议不要造。包管理器是治理型工具只有当维护成本显著高于工具本身的建设成本时才值得动手。市面上也已经有一些现成的 skill marketplace 和 MCP 生态能覆盖一部分场景。但如果你和我当时的情况很像——几十甚至上百个技能需要在多个环境同步、需要多人协作、需要不断做版本迭代——那么一个最小可用的包管理器成本完全值得。6.3 下一步要补的能力指纹校验、自动更新与分级权限现有版本还剩几个想补的功能。第一个是签名校验在 manifest 里引入 sha256 指纹安装时对比下载文件哈希防止内容被篡改。第二个是自动更新类似apt upgrade把仓库的 index 与本地索引做 diff列出可升级项由用户决定是否升级。第三个是分级权限把“可匿名安装”和“需要审核安装”分开尤其在团队内给敏感技能的入口加一道审批。最后说一点个人体会。这次折腾让我明白很多看似“管理混乱”的问题本质不是人的问题而是缺了一个抽象层。把技能当作包把仓库当作源把链接当作安装句柄——这个简单的映射比任何“再整理一次文件夹”的努力都要稳妥。工具不必一开始就完美先把“仓库是源链接是安装”这两条原则落地后面的问题会自然消解一大片。