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 用下来最容易被忽视但最能省钱的功能。

评论

此博客中的热门博文

我写了半年 prompt,这是我发现的最好的技巧