skills包管理工具APM
May 16, 2026 - ⧖ 7 min问题:skills 到底该怎么管?
Agent skills(SKILL.md)正在成为 LLM engineering 里越来越重要的基础设施。但它的管理方式仍然非常原始。
很多人的现状就是:
- 看到一个感兴趣的 skill repo → 手动下载到
~/.claude/skills/ - 换一个项目 → 手动挪文件
- 换一台机器 → 重新折腾一遍
- 技能越来越多 → context 越来越胖,触发越来越乱
这个问题大家都感觉到了,但"怎么管才对"一直没有共识。
这篇文章记录了我在这件事上探索过的所有路径,以及最终的结论——项目级包管理器是这个问题的唯一正确终局。
“skills装多了会有问题,当 Skill 数量在 20 个以下时,选择准确率保持在 90% 以上,几乎不会选错。超过 30 个之后,准确率开始快速下滑。到了 200 个 Skill 的时候,准确率已经跌到大约 20%。如果你是个人用户、小团队或者 OPC 做日常自动化的活,8 到 15 个 Skill 通常最舒服。” 看来我现在的 skills 仍然是有点多了。但是我想不到
这么多 Skill,真正每天用的到底有几个?SkillsBench:评估代理技能在多样化任务中表现的基准_哔哩哔哩_bilibili
Skills可以无限堆砌么?具备技能的单智能体何时能替代多智能体系统,又在何种场景下失效?_哔哩哔哩_bilibili
阶段一:Imperative CLI
最早我用的是技能社区自带的 CLI(比如 skills-cli),配合 Taskfile 做自动化。
工作流是这样的:
- 维护一个 YAML 清单,列出所有需要的 skills 及其来源 repo
- 运行 task,CLI 对比本地 lockfile 和 YAML 清单,算出待安装列表
- 执行安装
这个方案能跑,但有两个明显问题:
- 没有强一致性。命令式流程依赖每次执行时的网络状态、repo 可用性、本地缓存。同一份清单在不同时间、不同机器上可能产生不同的结果。
- 维护成本高。每次新增/删除 skill 要走完整的
改清单 → push → 执行 update循环,日常迭代太重了。
一个关键的技术细节是 skills-cli 当时不支持基于 lockfile 的全局 install(仅项目级),而且 lockfile 的 key 顺序不稳定导致 diff 混乱。这些细节问题放大了整个方案的不稳定感。
归根到底,命令式的 skill 管理无法做到强一致性。 这是我在当时就意识到的核心矛盾。
阶段二:声明式(Nix)
既然命令式不行,那就上声明式。我把核心 skills 迁移到了 agent-skills-nix(ASN),通过 Nix 做声明式管理。
这一下子解决了一致性问题——Nix 的 store 模型天然保证了确定性和可复现性。同时还解锁了全局配置管理,我可以把 skills 和其他 dotfiles 一起纳入统一的声明式框架。
但这个方案也有明显的代价:
- 配置成本高。每个 skill repo 都需要在 flake 里手动配置,不能自动扫描子目录。
- 迭代摩擦大。临时添加或删除一个 skill 需要重新 rebuild,对于"先试试好不好用"的场景太重了。
- repo 和 skills 的元数据拆到了两个地方,可维护性随着 skill 数量增长而恶化。
为了解决这些问题,我把方案拆成了两层:
CORE skills → Nix 声明式管理(高确定性,低变更频率)
OPTIONAL → Taskfile + CLI 管理(低确定性,高变更频率)
这个混合方案在实践上是可工作的,但它本质上是在两个都不完美的抽象之间做妥协。每次新增一个 skill,我都要决策它属于 CORE 还是 OPTIONAL,而这个决策本身没有客观标准。
阶段三:lazyskills——尝试造一个更好的轮子
核心目的还是有效缩减skills
第三方方案探索
在遇到了上面这个问题之后,我就想是否有什么
skillshare 和 skillfile
前者是个 cli+webui,后者是TUI
前者是golang+TS, 后者是rust
二者基本上都能满足我的需求(都是声明式,且支持分发多target),前者的优势在于可以替代实现“enable / disable”,后者优势在于有lockfile
- 维度 : 声明式程度
skillfile : "✅ Skillfile 明确 desired state,`/tmp/skill-manager-eval/skillfile/SPEC.md:1`"
skillshare : "✅ `config.yaml` / `.skillshare/config.yaml` 明确 target/source/filter"
skill-flow : "⚠️ 有 `manifest.json/lock.json`,但主要是 `~/.skillflow` 机器状态"
- 维度 : "lock / reproducibility"
skillfile : "✅ Skillfile.lock 精确 SHA pin"
skillshare : "❌ 无等价 lockfile,更新结果不够可重放"
skill-flow : "⚠️ 有 `lock.json`,但偏本地状态,不是 repo 可审 reproducibility"
- 维度 : "update / outdated"
skillfile : "✅ `install --update` + `status --check-upstream`"
skillshare : "✅ `check` / `status` / `update` / `upgrade` 完整"
skill-flow : "⚠️ update 有,outdated 主要靠 health/list 间接看"
- 维度 : "enable / disable"
skillfile : "❌ 无一等开关,只能删/注释/分文件"
skillshare : "⚠️ 无简单 disable,但 include/exclude、`.skillignore`、target filter 可安全替代"
skill-flow : "⚠️ 可通过 selectedLeafIds / enabledTargets 选子集,但偏状态式"
- 维度 : "多 target 支持"
skillfile : "⚠️ 多平台安装有,但基本是同一套内容发往所有 target"
skillshare : "✅ per-target path/mode/include/exclude 很强"
skill-flow : "✅ projection/binding 很强"
- 维度 : "TUI/CLI 体验"
skillfile : "✅ CLI-first,add/search 有 TUI"
skillshare : "✅ CLI-first,TUI 友好,UI 可忽略"
skill-flow : "✅ TUI 很强,但产品面混入 desktop"
- 维度 : 本地目录模型
skillfile : "⚠️ manifest + cache + deploy,source-of-truth 不是真实技能树"
skillshare : "✅ 中央 source tree + sync 到 target,模型最直观"
skill-flow : "⚠️ `~/.skillflow/` 状态根 + projections,较重"
- 维度 : "project/global scope"
skillfile : "✅ install `<local/global>`"
skillshare : "✅ 全局 `~/.config/skillshare` + 项目 `.skillshare/`"
skill-flow : "⚠️ 有 project scope 概念,但核心仍是全局 state root"
- 维度 : "team workflow"
skillfile : "✅ Skillfile/Skillfile.lock/patches 最适合进 git"
skillshare : "✅ `.skillshare/` 配置和 source dir 可进 git"
skill-flow : "⚠️ 团队协作不自然,状态主要在 home 目录"
- 维度 : CI/headless
skillfile : "✅ 很适合"
skillshare : "✅ 很适合,还有 GitHub Action"
skill-flow : "⚠️ 能跑 CLI,但不是最自然的 repo-first CI 模型"
- 维度 : "doctor/repair/drift detection"
skillfile : "⚠️ 有 validate/diff/status,无真正 doctor/repair"
skillshare : "✅ doctor/diff/sync --dry-run/audit 很实用"
skill-flow : "✅ 这项最强,doctor/repair-* 完整"
- 维度 : "migration risk"
skillfile : "⚠️ 容易把同一套 skills 过度同步到所有 agent"
skillshare : "⚠️ 无 lockfile;接管 Nix 目标目录前必须先断旧发布器"
skill-flow : "❌ 状态化迁移更难审查,也更容易和现有目录模型打架"
- 维度 : "long-term maintainability"
skillfile : "✅ 范围聚焦,小而清晰"
skillshare : "✅ 功能全,但面更大;仍可控"
skill-flow : "⚠️ CLI+desktop+bridge 面过宽"
- 维度 : "fit for my current setup"
skillfile : "⚠️ 很适合替代 Taskfile,但不完全贴合你多 target/filter 现实"
skillshare : "✅ 最贴合你现在“中央源 + 多 agent 同步 + copy/symlink 控制”"
skill-flow : "❌ 不适合作为你的主力"
分别尝试了一下这两个工具,
• 对,我应该更早把这句话说死:
skillshare 是“声明式的 target/source/sync 配置工具”,但不是“声明式的 skills dependency manifest 工具”。
也就是说,它支持声明式,但不是你要的那种声明式。
具体区别
你要的声明式是这种:
- repo = openai/skills
- skills = [gh-fix-ci]
- group = CANDIDATES
- note = 为什么收、为什么不收、怎么分类
- 最好还能进 git、可 review、可直接作为 source of truth
这其实是一个“依赖清单 / catalog manifest”。
而 skillshare 原生声明的是这些:
- targets
- mode
- include/exclude
- audit
- source 路径
- .skillignore 里的 disable 规则
这是一套“分发/同步配置”。
所以为什么不能直接把 .SKILLS.yml 迁成 skillshare 原生格式?
因为 skillshare 原生没有一个一等 schema 去表达你 .SKILLS.yml 里的核心信息。
缺的不是 YAML 语法,而是语义槽位:
1. 没有原生字段表达“远程 repo catalog”
skillshare 的 config.yaml 不提供一个正式的 repos: / skills: manifest 区块,让你把 vercel-labs/skills
-> [find-skills] 这种关系声明进去。
2. 没有原生字段表达“这个 repo 只选其中几个 skill”
它能 install -s gh-fix-ci,但这是安装命令能力,不是一个被 schema 固化、可长期审查的 manifest 模型。
3. 它的 source of truth 是“安装后的 source tree”
也就是 .skillshare/skills/... 本身,而不是一份 repo 级清单文件。
这和你的 .SKILLS.yml 思路根本不同。
4. CANDIDATES = disabled by default 只能落到 .skillignore
这能表达“禁用”,但不能表达“为什么禁用、这组 repo 的评价、备注、分层意图”。
5. 注释没有原生归宿
你 .SKILLS.yml 里最有价值的恰恰是这些注释和判断。
skillshare 原生模型没有 repo-level note/comment schema 去承接它们。
6. 没有 lockfile 式 manifest 解析结果
所以即使你硬迁,也得靠“安装后的目录状态 + ignore 规则 + 内部 metadata”,不是一份干净的声明式
catalog。
所以准确结论是
- skillshare 支持声明式:sync/targets/filters/audit/source
- skillshare 不支持你要的那种声明式:repo + selected skills + comments + group semantics 的 catalog
manifest
这也是为什么我后来加 catalog.yaml。不是因为我想乱设计,而是因为如果不补这一层,skillshare 原生模型根本
承接不住你 .SKILLS.yml 的信息结构。
但你说得对:
如果你要求“只用 skillshare 原生,不要自造一层”,那就不能无损迁移 .SKILLS.yml。
一句话说透
.SKILLS.yml 是“依赖清单”;skillshare 原生是“已安装 source tree + sync config”。
这两者不是同一个抽象层,所以不能直接一比一迁入。
如果你要严格满足“声明式 repo catalog”这个要求,那结论就很直接了:
- skillshare 不适合作为你这套 .SKILLS.yml 的原生承载层
- skillfile 反而更接近你要的模型
如果你愿意,我下一条就只做一件事:
把这三句话压缩成最终选型判断,直接告诉你:
在“声明式 catalog”这个标准下,skillshare 为什么不够格,skillfile 为什么更对路。
zzz
这时候我决定自己写一个工具。
lazyskills 是一个 Go TUI 应用,核心模型是:
profile(如 "go-dev")→ 关联多个 GitHub repo → 自动扫描 SKILL.md
→ resolve + hash + lockfile → install 到全局 store 目录
我做了很多东西:
- Profile 管理。把 skills 分组,按需启用/禁用。
- Concurrent resolve 引擎。从多个 GitHub repo 并发拉取和解析 skills,带 content hashing 和 lockfile 生成。
- TUI 界面。用 bubbletea 写的终端 UI,支持浏览、搜索、切换 profile。
- 完整的 install pipeline。resolve → lock → materialize,类似包管理器的标准流程。
从技术上看,lazyskills 做得不算差。它有一套完整的 resolve + lock + install 链路,和包管理器的抽象非常相似。
但问题在于——scope 错了。
Tip
这点是那天中午吃完午饭返程路上,在立交桥下等红绿灯时想到的,当时的想法是,vibe-coding了整整半天,终于弄完了,下午就可以用起来了。然后想到其实仍然很麻烦,切换项目时需要去切换 lazyskills 的 profiles,那我为啥不直接把skills做成项目级呢?然后转念一想,这不就是跟 gomod, pnpm 之类一样的包管理工具吗?
然后再转念一想,是谁把我们带入这种默认就用“全局skills”的思维定势的?太愚蠢了
我的工具是全局的,而问题需要项目级的
lazyskills 的操作链是:
全局 config → 全局 resolve → 全局 lockfile → 全局 install
→ 放到 ~/.lazyskills/store/skills/
→ 需要手动 symlink 到 ~/.claude/skills/
每一步的代码逻辑都是"正确"的。但把 "skills 管理" 放在全局作用域里,无论如何都绕不开几个结构性问题:
- 触发污染。全局技能越多,AI 选错技能的概率越大,而 profile 只是分组的 UI,不是 scope 隔离。
- 不可复现。同事 clone 一个项目,他的全局 skills 和你的不一样,行为就不一样。
- 隐式依赖。项目需要某个 skill 才能正确工作,但 repo 里没有任何声明——新人不知道,CI 不知道,换个 agent 环境也不知道。
- 上下文膨胀。所有全局 skills 的描述都进了 system-reminder,不管当前项目用不用得上。
- 升级风险。一个全局 skill 的更新影响所有项目——和全局 npm 包的教训一模一样。
这些问题和我之前遇到的一样,根源不在于"做得不够好",而在于抽象层级本身有问题。
关键转折:作用域决定一切
在写完
我回头看 lazyskills 的操作链:
全局 config → 全局 resolve → 全局 lockfile → 全局 install
每一层的代码逻辑都"正确",但作用域错了。同样是 resolve + lock + install 的流程,放在项目级和放在全局级,工程价值天差地别。
正确的链条应该是:
项目 manifest → 项目 resolve → 项目 lockfile → 项目 install
这不是我原创的洞察。这就是 pnpm、Cargo、Go modules 已经验证过的模型——依赖声明跟着项目走,安装产物本地隔离,lockfile 确保可复现。
把 skills 当作"全局增强能力"还是"项目级工程依赖",决定了整个工具的抽象层级。
然后看到了 APM
在我做这个判断的几乎同一时间,Microsoft 开源了 apm(Agent Package Manager)。
它就是这个正确抽象的工程实现:
项目 apm.yml → resolve → apm.lock.yaml → 写入 .claude/skills/(以及 cursor/copilot/codex 等)
而且它不止于 Claude——同一个 manifest 可以部署到 Copilot、Cursor、Codex、Gemini、Windsurf。有 content hashing、有 policy 层、有 marketplace 机制、有 frozen install。API 设计明显借鉴了 npm/Cargo 的语义。
回头看,它和我之前的方案对比是这样的:
| 维度 | imperative CLI | Nix 声明式 | lazyskills | APM |
|---|---|---|---|---|
| 作用域 | 全局/项目混合 | 用户级 | 全局 | 项目级 |
| 一致性 | ❌ | ✅ | ✅ | ✅ |
| 可复现 | ❌ | ✅ | ✅ | ✅ lock + frozen |
| 配置成本 | 低 | 高 | 中 | 低 |
| 多 agent | ❌ | ❌ | ❌ | ✅ |
| 团队协作 | ❌ | ❌ | ❌ | ✅ manifest 进 git |
APM 不是"另一个选择"。它是这个方向上第一个做对了抽象层级的工程实现。我前面尝试过的所有方案,都是在不同的作用域上做着类似的事——而 APM 选择了那个唯一正确的作用域。
结论:项目级包管理器是唯一正确的方向
回顾整个探索过程,路径是清晰的:
命令式 CLI(跑得通但不一致)
→ Nix 声明式(一致但配置成本高)
→ 混合方案(实用主义妥协)
→ 自建 TUI 工具(做对了操作链但选错了 scope)
→ APM(项目级包管理器——正确的抽象)
每一步的决策在当时看来都是合理的。但只有走到最后一步,才看清楚前面的所有方案为什么都差了一口气。
不是技术实现的问题,是抽象层级的问题。
这不是 lazyskills 一个项目的故事。它反映出的是:当你在一个新兴领域(agent skills)里做工具时,最大的风险不是写不出代码,而是找不到正确的 scope。scope 对了,代码怎么写都对;scope 错了,代码写得再好也救不回来。
你应该把你的 skills 当作项目的依赖来管理:
- 项目根目录放一个 manifest 文件(如
apm.yml) - lockfile 进 git,确保可复现
- 安装产物 ignore
- 项目自有 skill 源码进 git
- 团队共享 skills 做成 package repo,按版本管理
其他任何方案——无论是命令式 CLI、Nix 声明式、全局 TUI 工具——都是 scope 选错了的变体。
怎么判断skills应该是项目级/全局级?
附录:从 lazyskills 开发中学到的东西
这个部分和上面的结论无关——lazyskills 不会继续开发了,但开发它的过程本身是值得记录的。
1. Go + bubbletea 是 TUI 开发的一流选择
charm 生态(bubbletea、bubbles、lipgloss)非常成熟。并发模型和 Go 的 goroutine 天然契合,消息传递架构让异步操作(如 GitHub clone)的 UI 状态管理变得清晰。如果以后需要写终端 UI,我会毫不犹豫地选这个组合。
2. Content-addressable hashing 是 lockfile 的基石
lazyskills 的 lockfile 对每个 skill 目录做了 SHA-256 hash,记录 resolved commit 和 content hash。这个设计直接借鉴了 Nix store 和 Cargo.lock。一个正确的 lockfile 不只记录版本号——它应该允许你在离线状态下验证内容是否一致。
3. Concurrent pipeline 的设计难度大于预期
resolve 阶段涉及多个 GitHub repo 的并发 clone、扫描、hashing。用 errgroup 做限并发(repository level + hash level 两层限流)在实现上是正确的,但调试并发 pipeline 的竞态条件和错误传播花的时间远超预期。并发 resolve 引擎也许是 lazyskills 里最难写对的部分。
4. TUI 的渲染性能边界
bubbletea 的模型是每次 update 触发全量 re-render。这在大多数场景下不是问题——但当你有上千条 skills 需要渲染、每帧做全量排版计算时,性能瓶颈是很真实的。最终方案是按需切片渲染(virtual list),这个优化带来的提升比预期大得多。
5. 写一个"可用的"TUI 很容易,写一个"好用的"TUI 很难
键盘导航的细节(切换 pane 时的焦点记忆、列表滚动到边缘时的行为、modal 和 confirm dialog 的焦点管理)占据了大约 40% 的 UI 代码量。用户感知不到的细节,往往才是决定一个工具是否值得使用的分水岭。 这个比例本身就是一个值得记住的数字。
关于 lazyskills 的完整代码:https://github.com/xbpk3t/lazyskills 关于 APM(目前最正确的方向):https://github.com/microsoft/apm
skillopt
Eval 结果总结
60% 准确率(9/15 正确),模型使用 deepseek-v4-flash + 当前 SKILL.md。
正确 (9/15) ✅
| 题目 | 预测 | 黄金答案 |
|---|---|---|
| tf-001 status 关键字 | status ✅ | status |
| tf-002 preconditions 关键字 | preconditions ✅ | preconditions |
| tf-003 desc 和 summary | desc 和 summary ✅ | desc 和 summary |
| tf-004 pipefail 设置 | set: [pipefail] ✅ | set: [pipefail] |
| tf-005 deps 用途 | 前置依赖 ✅ | 前置依赖 |
| tf-007 vars:sh 禁止 | 副作用 ✅ | 副作用 |
| tf-008 status 检查限制 | 不能只检查文件存在 ✅ | 文件存在 |
| tf-010 多入口项目 | 拆分 Taskfile ✅ | 拆分多个 Taskfile.yml |
| tf-014 MUST_NOT 级别 | MUST_NOT ✅ | MUST_NOT |
| tf-002 preconditions | preconditions ✅ | preconditions |
错误 (6/15) ❌
| 题目 | 模型回答 | 正确答案 | 分析 |
|---|---|---|---|
| tf-009 复用值的作用域 | "根作用域" | vars | 回答了位置而非关键词 |
| tf-006 复用逻辑放哪 | "子 Taskfile" | 模板 task | 概念混淆(includes vs 模板) |
| tf-011 CLI_ARGS 适用场景 | "包装外部命令" | 单一位置参数 | 回答太泛 |
| tf-012 interactive 标记 | "interactive 字段" | interactive: true | 语义正确但 keyword 没匹配上 |
| tf-015 run 模式 | "always" | when_changed | 真·错误 |
| tf-013 修改啥会导致问题 | "vars" | silent | 真·错误 |
结论
- 模型在 核心概念(status/preconditions/deps/pipefail)上表现好
- 在 细节区分(MUST vs MUST_NOT、when_changed vs always)上容易混淆
- 60% baseline — 可以开始训练了,看看 SkillOpt 的 optimizer 能否通过分析失败模式来改进 SKILL.md
要开始跑训练吗?预计 4 epochs × 5 batch = 20 次 API 调用。
3️⃣ 关键训练指标
| 指标 | 值 |
|---|---|
| 总 steps | 8 |
| Accept | 1 |
| Reject | 4 |
| Skip | 3 |
| 最佳 val score | 0.3333(step 3) |
| 最终 test score | 0.5000(与 baseline 一致) |
| 总 tokens | ~494K |
| 耗时 | 48 分钟 |
关键的变化不是加了多少内容,而是 SkillOpt 的 training loop 通过 analyst 分析失败 trajectory,识别出了原始 skill 中缺失的区分性知识— 模型需要的不是更多 YAML schema 描述,而是 requires/status/preconditions 三者的职责边界这类容易混淆但实际高频使用的知识。
好,收据收集够了。下面是完整的复盘分析。
这次优化实验的六个教训
1. SkillOpt 的核心瓶颈不在模型,在数据
这是最重要的教训。训练跑完 4 轮只产出了 1 个被 accept 的 patch,不是因为模型不好,而是因为:
数据集只有 10 条训练数据,其中还包含了"坏"的测试项。
看这个典型的例子:
问题:多处复用的逻辑应该放在哪里?
模型回答:将多处复用的逻辑封装在独立的 Taskfile 中,并通过 includes 指令引入。
期望答案:["模板 task", "模板任务"]
法官裁决:keyword_fallback → 0 分 ❌
模型的回答完全正确,但 gold answer 是狭窄的"模板 task",keyword matcher 匹配不上就给 0。这导致 analyst 看到"failure trajectory",分析后认为"技能缺了模板 task 的概念"——但实际上模型是对的,是数据的问题。
★ 启示:自动化优化的质量上限 = 数据集的质量下限。在扩大数据集之前,先确保每条数据的 gold answer 是合理且包容的。
2. LLM-as-judge 是最大的单点故障
法官(judge)的职责是判断模型答案是否正确,但如果法官自己都判不准,整个训练信号就坏了。
从数据看,你的 evaluator 有 107 次 judge 调用,其中一部分 fallback 到了 keyword_fallback——意味着 LLM judge 没有返回可解析的 JSON。而且当 gold answer 是"模板 task"而模型答案是完整句子时,keyword_fallback 也无法匹配。
Judge reason: keyword_fallback → 这意味着 LLM 未按格式回答
keyword_fallback_exact → LLM 回答但关键词不完全匹配
★ 启示:在任何 LLM 评估链路中,judge 的可靠性决定了整个系统的可信度。如果 judge 经常 fallback,训练信号就有噪声。这在你手工迭代 prompt 时也同样成立——你依赖的是"目测"而非结构化评估。
3. Analyst 的有效性取决于 trajectory 的丰富度
前 3 次训练 analyst 全返回 0 个 edits,不是因为模型不行,而是因为:
- 没有 conversation.json — trajectory 为空,analyst 看不到模型是怎么回答的
- optimizer deployment 设错了 — 一直在用 Qwen/Qwen3.5-4B 而不是 deepseek-v4-pro
修复后,deepseek-v4-pro 作为 analyst 立刻生成了有意义的 patch:
{
"failure_type" : "missing_information" ,
"patch" : {
"edits" : [{
"op" : "append" ,
"content" : "常见关键字用途..."
}]
}
}
★ 启示:自动化优化的前提是对"失败"有足够的观察上下文。单轮 Q&A 的 trajectory 过于简单(只有问+答),缺乏推理过程。如果想让 analyst 产生更好的建议,需要让 trajectory 包含更多信息——比如模型的 reasoning trace。
4. 慢更新(slow_update)比快更新(analyst patch)更稳定
观察两种更新机制:
| 机制 | 生成次数 | Accept | 内容质量 |
|---|---|---|---|
| Analyst patch(step 级) | 5 次 | 1 次 (20%) | 简单(关键字列表) |
| Slow_update(epoch 级) | 2 次 | force-injected | 详细(完整策略指令) |
Slow_update 通过对比两个 epoch 的性能差异,自动生成了大段的策略指令(requires/preconditions/status 的区分、vars:sh 约束等),而且它不需要经过 accept/reject 门控,直接注入到 skill 中。
★ 启示:慢速的、基于统计对比的更新比快速的、基于单步分析的更新更可靠。这对应到你的日常 prompt 迭代中——基于多次实验的统计观察("这个 prompt 在 10 个案例中提升了 8 个")比基于单次直觉的调整更有效。
5. 这次实验的 ROI 评估
总投入:
| 资源 | 消耗 |
|---|---|
| 训练轮次 | 4 次(含 debug) |
| API tokens | ~1.4M |
| 耗时 | ~1.5 小时(含 debug 时间) |
| 成本 | 估计 < $1(DeepSeek 便宜) |
产出:
- 验证分从 0.0000 → 0.3333
- 测试分 0.5000(无变化)
- Skill 新增 1 个核心知识节 + 3 个补充节
- 识别出 2 条有问题的测试数据
★ 启示:SkillOpt 本身不是银弹,但作为"自动化回归测试 + 差异分析"工具是有用的。它最大的价值不是自动优化 skill,而是系统性地暴露了:哪些测试项持续失败、模型的常见混淆模式是什么、当前技能的覆盖盲区在哪里。这些信息对人工迭代更宝贵。
6. 对你最有价值的部分
不是最终的 SKILL.md 改动,而是这整个过程揭示的系统性问题:
- 你的 skill 缺少概念区分层 —
requires/status/preconditions三者的边界模糊,模型(和可能的人类)都会混淆。这被你 skill 中的references/best-practices.yaml的 items 涵盖了吗?如果 YAML 里也没有,那就是真缺口 - keyword_fallback 意味着"简答"型黄金答案容易误判合理回答 — 建议在数据集评审时考虑答案的表述多样性
- validation 集(CLI_ARGS / interactive / silent)完全不覆盖 skill 内容 — 这些是 skill doc 里没提到的知识,val set 应该测量"skill 能覆盖的内容"而非"模型本身的知识"
★ 总启示:这次实验的真正收获不是 skill 文本的增量改进,而是看到了一个可重复的、数据驱动的评估框架如何暴露知识盲区。这个"暴露能力"比自动修改能力更值得保留——以后你每次修改 skill,都可以用这个 eval 管道跑一轮,看到具体哪个 item 从错变对、哪个依然失败。这是持续的工程资产。
你觉得这个分析有道理吗?另外你的 references/best-practices.yaml 里有没有 requires/status/preconditions 的区分条目?如果 YAML 里也没有,那这个知识缺口确实值得补上。