Skills:CLAUDE.md 塞不下的知识我全部扔到了按需加载包里
CLAUDE.md 我越写越克制之后,确实变好用了。但问题来了——有些知识只有特定场景下才需要,写进 CLAUDE.md 太占空间,不写又要反复解释。Skills 就是干这个的:把大块知识写成按需加载的 SKILL.md,干活的时候 @ 一下就能用。
聊完 CLAUDE.md 之后,有件事我一直没解决。
CLAUDE.md 我缩到了 20 行以内,效果确实好——Claude 听话了,不会再忽略关键指令。但代价是,很多东西我写不进去了。
比如我的项目里有一套自定义的错误处理规范。所有服务层的错误都要包装成 AppError,带 errorCode 和 context。这个规范本身不算复杂,但涉及的文件有七八个依赖。如果要保证 Claude 每次涉及错误处理时都遵循,我要么写进 CLAUDE.md(但太长了),要么每次对话里提一嘴(但总会忘)。
我试过把规则写进 CLAUDE.md 的 "Code Style" 部分。加上去之后,CLAUDE.md 从 20 行变成了 35 行。Claude 开始忽略一些更重要的指令——比如"不要改自动生成的文件"那条被挤出了上下文的关键区域。
这就是 CLAUDE.md 的天花板:它总是加载。任何总是加载的东西,都不可能无限膨胀。
如果说 CLAUDE.md 是全局配置,那 Skills 就是按需加载的插件。
Skills 解决的就是这个问题
Claude Code 的 Skills 机制很简单:你写一个 SKILL.md 文件放到 ~/.claude/skills/ 下,然后在对话里用 @skill-name 引用。用到了才加载,用不到就不占上下文。
这么说吧,我在 CLAUDE.md 里只放那些"99% 的场景下都适用"的规则。而那些"只有写错误处理时才需要"、"只有部署时才需要"、"只有写测试时才需要"的东西——我全部拆成了 Skills。
CLAUDE.md 就像操作系统的内核,一直驻留在内存里。Skills 就像你按需安装的软件包,用到的时候才加载。这个类比其实挺准的。
具体怎么用
创建 Skills 目录
Skills 文件放在 ~/.claude/skills/ 下,每个子目录就是一个 Skill,目录里必须包含一个 SKILL.md 文件:
~/.claude/skills/
├── error-handling/
│ └── SKILL.md
├── deployment/
│ └── SKILL.md
├── testing-strategy/
│ └── SKILL.md
└── api-patterns/
└── SKILL.md
目录名就是 skill name。比如上面的 error-handling,在对话里用 @error-handling 就可以加载。
写一个 SKILL.md
拿我的错误处理规范举例:
# Error Handling Conventions
## AppError 类
所有业务错误必须包装为 AppError,格式:
```typescript
class AppError extends Error {
constructor(
public errorCode: string,
message: string,
public context?: Record<string, unknown>
) {
super(message);
}
}
使用规范
- service 层只抛 AppError,不 catch
- controller 层 catch AppError,转 HTTP 响应
- 数据库/网络错误在 service 层包装后抛出
常见错误码
- AUTH_TOKEN_EXPIRED: token 过期
- AUTH_INSUFFICIENT: 权限不足
- VALIDATION_INPUT: 输入校验失败
- BIZ_RESOURCE_NOT_FOUND: 资源不存在
不要在 controller 层做业务判断后再抛异常
错误:controller 调用 service 后判断返回值再抛异常
正确:service 直接抛 AppError,controller 统一在全局 error handler 里处理
注意看——这份内容五六百字。如果塞进 CLAUDE.md,代价太大。但作为 Skill 只在处理错误时才加载,完全没毛病。
### 在对话里调用
在对话里直接引用:
用 @error-handling 的原则重构 auth/service.ts 的错误处理逻辑
或者更灵活一点,先加载再聊:
加载 @error-handling,然后我们聊聊这个模块的认证流程
Claude 收到 `@skill-name` 之后,会把对应的 `SKILL.md` 内容注入到上下文中。效果跟你在对话里贴了一段知识文档一样。
## Tricks
### Trick 1:用 Skills 管理框架/库的版本特定知识
这是我发现最实用的场景。
我项目里用了一个比较小众的 ORM 库,它的某个版本有个奇怪的限制——关联查询不支持嵌套 where 条件。之前在 CLAUDE.md 里写了,但问题是项目升级 ORM 版本之后这个限制解除了,CLAUDE.md 反而过时了。
用 Skills 之后,我把 ORM 相关的知识写成了 `@orm-patterns`,版本升级了只用改这个 Skill 文件,不影响其他配置。
对于版本依赖型知识,Skills 比 CLAUDE.md 灵活太多了。
### Trick 2:一个 Skill 可以引用另一个 Skill
Claude Code 支持跨 Skill 引用。当 `@deployment` 加载时可以自动触发 `@testing-strategy`,因为部署流程需要先过测试。
在 `SKILL.md` 里加上一行:
Required Skills: @testing-strategy
这样加载 `@deployment` 时,`@testing-strategy` 也会被自动加载。对于有依赖关系的知识块,这个机制很顺手。
不过注意,不要搞出循环引用。Claude 不会死循环,但层级太深的话,你本来想按需加载的结果全都加载了,就失去了 Skills 的意义。
### Trick 3:把常用的命令行组合写成 Skill
我有个 `@git-workflow`,里面写了我团队常用的 Git 流程:
```markdown
# Git Workflow
## 提交流程
1. git pull --rebase origin main
2. 本地测试通过后提交
3. git commit 信息格式: feat/fix/chore(scope): description
## 分支命名
- feat/description
- fix/description
- chore/description
- refactor/description
## 合并规范
- PR 必须 squash merge
- squash 信息要包含所有 commit 的要点
然后我每次要提交就喊一句 "按 @git-workflow 帮我整理这次提交"。Claude 会按我团队的风格生成 commit 信息、确认分支名、检查当前状态。省掉了很多"你 git commit 怎么写"的对话。
Trick 4:在 CLAUDE.md 里声明常用 Skill
我在 CLAUDE.md 末尾加了一行:
## Skills
可用的 Skills 列表,按需加载:
@error-handling: 错误处理规范,涉及错误时考虑加载
@deployment: 部署流程和检查清单
@testing-strategy: 测试策略和 mock 规范
@api-patterns: API 接口风格和参数规范
这行本身很短,不会影响 CLAUDE.md 的密度。但 Claude 看到之后,在涉及错误处理的任务时会主动问我"是否需要加载 @error-handling"。这比我自己记着有哪些 Skill 好用多了。
注意事项 / 踩坑
坑一:Skill 不是用来替代对话的
我见过有人把整个项目文档塞进 SKILL.md,五六千字。结果是 Claude 加载之后上下文塞满了一半,反而回答质量下降。
Skills 的设计意图是"知识包",不是"项目文档"。它应该包含的是:
- 决策规则("这种情况用 A,不要用 B")
- 边界条件("这个库的版本 x.x 不支持功能 Y")
- 约定规范("命名风格、目录结构")
而不应该是:
- 接口文档(应该在你的代码里或专用文档里)
- 全量 API 列表(Claude 会读代码,不需要你写进文本里)
我给自己定的规则:单个 Skill 的 SKILL.md 不超 300 行,最好在 100-200 行。 超过这个量,拆成更细粒度的 Skills。
坑二:Skill 加载后不可卸载
@skill-name 加载之后,内容会混入当前的上下文。它没有 unload 机制。如果同一个 session 里你想做另外一件跟这个 Skill 无关的事,它占用的上下文不会自动释放。
所以如果一个 Skills 特别大(接近 300 行),慎用。加载之后做几件事就关掉 session 重开是比较省心的做法。
坑三:Skill 名不要用空格和特殊字符
目录名决定了引用方式。如果目录名是 My Project Standards,引用的时候要写 @My Project Standards——但 Claude 有时候会把空格后面的部分当成普通文本。我踩过这个坑之后,一律用 kebab-case 命名:my-project-standards。保险,Claude 解析 @my-project-standards 从不出错。
坑四:Skills 不跨 session 继承
CLAUDE.md 每次 session 启动自动加载,这是个硬性保证。但 Skills 不是——你上一个 session 加载了 @error-handling,下一个 session 要重新 @ 一次。
这其实是个特性。如果 Skills 也自动加载,那跟写在 CLAUDE.md 里有什么区别?按需加载就是"用到才说的话",上一个 session 用到的 Skill 不代表这个 session 也需要。
但缺点也很明显:你可能会忘记自己有哪些 Skill。 所以我强烈建议在 CLAUDE.md 里放一个 Skill 清单(就是上面 Trick 4 的做法),至少 Claude 能提醒你。
Skill vs CLAUDE.md 的选择标准
用了一段时间之后,我给自己画了一条很清楚的分界线:
| 场景 | 放哪儿 |
|---|---|
| 这个规则每个 session 都会用到 | CLAUDE.md |
| 这个规则只有做某类任务时用 | Skill |
| 这个规则一条就能说清楚 | CLAUDE.md,一行的事 |
| 这个规则要写几段才能解释明白 | Skill,太长了别占 CLAUDE.md |
| 项目唯一且永不过时 | CLAUDE.md |
| 跟框架/库版本相关,可能会变 | Skill,更新方便 |
用这个表筛一遍之后,CLAUDE.md 稳定在 15-20 行,Skills 变成了八九个。每个 Skill 在需要的时候才出现,不需要的时候就安安静静躺在磁盘里。
好,接下来要聊的 /compact 和上下文管理,可能是整个 Claude Code 用下来最容易被忽视但最能省钱的功能。
评论
发表评论