- 软件开发行业的 AI 范式演进:从辅助工具到工程智能体
- Claude Code 全景入门:装好、看懂、跑起来
- 上下文注入:用 @ 和 ! 精准喂给 Claude 它需要的信息
- 记忆系统:用 CLAUDE.md 告别每次对话都要"重新认识你"
- 斜杠命令:把你的重复操作变成一个单词
- Hooks 机制:让 AI 的每一步都在你的规则里
- MCP:给 Claude Code 接上"外设"
- Skills:让 Claude 按需加载你的领域知识
- Sub-agents:给 Claude 分身,让专家各司其职
- Agent Teams:组建你的 AI 开发小队
- 安全与回退:给 AI 戴上"安全带"
- SDK 与 Headless:把 Claude 变成你的自动化引擎
- 上下文工程:从记忆文件到分层知识架构
- 模型选择与成本控制:把每一分钱花在刀刃上
有一段时间我觉得 CLAUDE.md 越来越不对劲。
项目刚开始那个文件只有三十来行——技术栈、目录结构、几条明确的禁令。用起来效果很好,Claude 对项目的背景理解很准确。
然后慢慢往里面加内容:数据库访问的规范、企微接口的调用约定、并发编程的注意事项、gRPC 接口设计约定、测试覆盖策略……两个月后,那个文件快两百行了。
然后 Claude 开始"忘事"了。写数据库查询时不按连接池规范来,goroutine 启动时忘了传 context,企微 access_token 的刷新机制搞错了。
不是 Claude 变笨了——是那两百行里的信息互相稀释。当它处理一个具体任务时,和这个任务不直接相关的内容也占着上下文窗口,真正相关的规则反而没被充分关注到。上下文窗口虽然很大(200K tokens),但它也是有限的稀缺资源——而且存在注意力稀释效应:窗口里信息越多,模型对每条信息的关注度越低。
Skills 是解法。不是把所有知识都堆进 CLAUDE.md,而是把不同领域的知识封装成独立的"能力包",Claude 处理相关任务时按需加载对应的包,不相关时完全不加载。
Skills 的定位:可操作知识
在理解 Skills 之前,先搞清楚它在 Claude Code 能力体系里的位置。Claude Code 有几种核心扩展机制,各自回答不同的问题:
- Tools 回答"能做什么"——读文件、改代码、跑命令,是操作层面的能力
- Hooks 回答"什么时候检查"——在关键节点自动触发验证或约束
- Skills 回答"怎么做,以及何时做"——它不是工具,也不是检查点,而是可操作的领域知识
(还有一种是 Sub-agents,回答"谁来做"——下篇会详细讲。)
Skills 最重要的特征:它不是一份被动的参考文档等人去查,而是一段具备语义入口的标准操作程序。它通过 description 告诉 Claude 在什么情况下应该加载这项能力,通过正文定义执行步骤和规范,在需要时自动加载到 Claude 的认知空间中。
一份 API 设计指南放在 Wiki 上是静态文本——它等着人去阅读。封装成 Skill 之后,它变成了 Claude 在写 API 时自动到场的行为规范。从"人去查文档"变成"知识主动找到 Claude"。
Skills 和斜杠命令的关系
第三篇讲斜杠命令时已经提到了 Skills 格式。这里说清楚它们的关系。
在新版 Claude Code 中,Commands 已经合并到 Skills。.claude/commands/review.md 和 .claude/skills/review/SKILL.md 都会创建 /review,行为一样。你之前写的 .claude/commands/ 文件继续有效,不需要迁移。新建的话推荐用 Skills 目录,因为它支持辅助文件和更完整的 frontmatter 配置。如果同名共存,Skill 优先。
但 Skills 比斜杠命令多了一个关键特性:可以由 Claude 自动识别和加载。
斜杠命令只能被人触发——你输入 /pre-check,它才跑。Claude 不会自己判断"现在应该跑一下 pre-check"然后主动执行。
Skills 不一样。你描述任务,Claude 扫描所有 Skill 的 description,通过语义推理判断当前任务是否需要某个 Skill。如果判断需要,它会主动加载那个 Skill 里的知识,按照 Skill 定义的规范做事。不用每次都提醒它"记得遵守数据库访问规范"——Claude 自己会判断。
这是从"指令"到"技能"的范式转变:斜杠命令是祈使句("去做这件事"),Skill 是陈述句("我是一种能做某件事的能力")。前者由人调度,后者由模型自主调度。
渐进式加载:Skills 的核心机制
这个机制是理解 Skills 价值的关键,值得单独说。
Claude Code 不会一启动就把所有 Skill 的完整内容全加载到上下文里。它用的是三层渐进式加载:
第一层:description 常驻(目录页)。 所有 Skill 的 description 始终在上下文中,Claude 随时能看到"有哪些能力可用"。这一层很轻,每个 Skill 只占几十个字。就像图书馆的目录索引——你不需要读完所有的书,只需要扫一眼目录就知道哪些书和你当前的研究相关。
第二层:按需加载正文(章节)。 当 Claude 判断某个 Skill 和当前任务相关(或者你手动输入 /skill-name),才会加载那个 SKILL.md 的完整内容。这是"即时加载"——需要成为某个领域的专家时才去"读那本书"。
第三层:辅助文件按需读取(附录)。 SKILL.md 里可以引用同目录下的其他文件(详细参考文档、示例代码、脚本),Claude 只在确实需要时才去读取。
举个直观的例子:你有 5 个 Skill,每个完整内容 1000 tokens。如果全部常驻上下文,每次对话都要花 5000 tokens 在"可能用不到"的知识上。用渐进式加载,只有 description 常驻(大约 250 tokens),实际用到的那一个才加载完整内容——token 节省 78% 到 98%。 大多数请求只需要部分资源,渐进式加载的优势是累积的。
这就是 Skills 和"全塞进 CLAUDE.md"的本质区别。不是信息量变少了,是加载时机变精确了。
两类 Skill:参考型和任务型
从工程角度,Skill 分为两类:
参考型 Skill 提供领域知识,Claude 自动判断什么时候需要加载。没有执行步骤,没有输出模板——它影响的是 Claude "怎么做",而不是"做什么"。比如数据库访问规范、API 设计约定。
任务型 Skill 执行有副作用的具体任务——跑测试、生成报告、提交代码。加了 disable-model-invocation: true,只能由人手动 /skill-name 触发。Claude 不会自作主张执行它们。
简单判断:如果这个 Skill 是"遵循这些规范",用参考型;如果是"执行这个操作",用任务型。
参考型 Skill 实战
Skills 放在 .claude/skills/<skill-name>/SKILL.md(项目级)或 ~/.claude/skills/<skill-name>/SKILL.md(用户级)。最简单的结构:一个目录,一个文件。
拿我们 Go 项目举例。数据库访问有一套团队规范——连接池怎么管理、事务怎么用、查询超时怎么设、错误怎么包装。这些规则以前塞在 CLAUDE.md 里,现在提取成 Skill。
.claude/skills/db-access/SKILL.md:
---
name: db-access
description: 数据库访问规范:定义连接池管理、事务使用模式、查询超时设置和错误处理方式。
当编写数据库查询、使用事务、管理数据库连接、处理 SQL 错误时自动应用。
allowed-tools: Read, Grep, Glob
---
# 数据库访问规范
## 连接池
所有数据库连接通过 `pgxpool.Pool` 管理,禁止手动创建单连接:
- 连接池在 `main()` 初始化,通过依赖注入传递
- 设置 `MaxConns`、`MinConns`、`MaxConnLifetime`
- 每个请求从池中获取连接,用完归还,不要长期持有
## 事务使用
- 只有涉及多条写操作时才开事务
- 事务函数签名统一用回调模式:
```go
func WithTx(ctx context.Context, pool *pgxpool.Pool, fn func(pgx.Tx) error) error {
tx, err := pool.Begin(ctx)
if err != nil {
return fmt.Errorf("begin tx: %w", err)
}
defer tx.Rollback(ctx)
if err := fn(tx); err != nil {
return err
}
return tx.Commit(ctx)
}
- 不要在事务内做外部 HTTP 调用
- 事务超时不超过 5 秒
查询规范
- 所有查询必须通过 context 传递超时:
ctx, cancel := context.WithTimeout(ctx, 3*time.Second) - 批量查询用
pgx.Batch,不要循环单条执行 - 查询结果用
pgx.CollectRows收集,不要手动for rows.Next()
错误处理
- 数据库错误统一用
fmt.Errorf("操作描述: %w", err)包装 - 检查特定错误码用
pgconn.PgError,不要匹配错误字符串 pgx.ErrNoRows在业务层判断,不要在 repository 层吞掉
文件分两部分:**frontmatter**(`---` 包裹)是配置元数据,**Markdown 正文**是 Claude 加载后需要遵循的知识。
这是一个**参考型 Skill**——它提供领域知识,Claude 在处理相关任务时自动加载、自动遵守。没有执行步骤,没有输出模板,不是"先做 A 再做 B",而是"遵循这些规范"。没有设 `disable-model-invocation`,所以 Claude 可以自动判断何时需要。工具只给了 `Read, Grep, Glob`——查阅规范不需要改代码。
## description:Skill 的灵魂
`description` 是 Skill 里最关键的字段。**它不是给人看的文档说明——它是给 Claude 看的触发器。**
Claude 接到任务时,扫描所有 Skill 的 description,通过语义推理判断该不该加载。这不是关键词精确匹配,而是语义理解——用户说"帮我查一下订单数据",Claude 能理解这和"数据库查询"相关。
**写法公式:[做什么] + [怎么做] + [什么时候用]**
```markdown
# 太模糊——Claude 什么时候都不确定要不要加载
description: 数据库相关规范
# 精准——Claude 能准确判断触发时机
description: 数据库访问规范:定义连接池管理、事务使用模式、查询超时设置和错误处理方式。
当编写数据库查询、使用事务、管理数据库连接、处理 SQL 错误时自动应用。
如果你有多个 Skill 功能有重叠,description 要明确区分:
# 区分清楚,Claude 才能选对
name: db-access
description: 数据库访问规范:连接池、事务、查询超时。
当编写 SQL 查询、管理数据库连接时使用。
name: db-migration
description: 数据库迁移规范:migration 文件命名、字段类型选择、索引策略。
当创建新的 migration 文件、修改表结构时使用。
description 的字符预算:所有 Skill 的 description 加在一起有总量限制——大约 15,000 字符。如果 Skill 很多导致 description 被截断,可以在 Claude Code 里运行 /context 查看警告,通过环境变量 SLASH_COMMAND_TOOL_CHAR_BUDGET 调大预算。
任务型 Skill 实战
参考型 Skill 提供知识,Claude 自动触发。任务型 Skill 执行有副作用的具体操作——跑测试、生成报告、清理缓存。这类操作必须由人手动触发,加一个字段:disable-model-invocation: true。
拿 Go 的基准测试举例。我们项目每次优化性能之后要跑 benchmark、分析结果、生成对比报告。这个流程固定,但不应该自动触发。
.claude/skills/go-bench/SKILL.md:
---
name: go-bench
description: Go 基准测试分析:运行 benchmark 并生成性能报告。
argument-hint: [可选:指定包路径,如 ./internal/service/...]
disable-model-invocation: true
allowed-tools: Bash(go test:*), Read, Grep, Glob
---
运行 Go benchmark 并分析性能数据。
## 当前基准数据
项目模块:
!`find . -name '*_test.go' -exec grep -l 'func Benchmark' {} \; 2>/dev/null | head -10`
运行 benchmark:
!`cd $CLAUDE_PROJECT_DIR && go test -bench=. -benchmem -count=3 $ARGUMENTS ./... 2>&1 | tail -40`
## 分析要求
1. 识别每个 benchmark 的 ns/op、B/op、allocs/op
2. 标注分配次数异常高的用例(可能有优化空间)
3. 如果存在历史 benchmark 数据文件,做前后对比
4. 关注 hot path 上的内存分配
## 输出格式
### 性能概览
- 总共 N 个 benchmark,M 个有优化空间
### 需要关注
- `BenchmarkXxx` — allocs/op 偏高(当前 N,建议排查 M 处分配)
### 性能良好
- `BenchmarkYyy` — ns/op 和内存分配均在合理范围
### 优化建议(如有)
[具体建议,指向代码位置]
几个关键点:
disable-model-invocation: true:只能用 /go-bench 手动触发。Claude 不会自己跑它。加了这个字段之后,这个 Skill 的 description 不会加载到上下文——Claude 完全看不到它,也不占 description 预算。
allowed-tools:精确控制权限。Bash(go test:*) 只允许 go test 开头的命令,其他 shell 操作一律禁止。权限最小化是任务型 Skill 的铁律——不要随便用 Bash(*)。
!command``:这是动态上下文注入——Skill 内容发给 Claude 之前,这些 shell 命令先在本地执行,输出替换进提示词。Claude 启动时就拿到了完整的 benchmark 数据,不需要再花几轮对话自己去跑。这个机制和斜杠命令里的一样,但在 Skill 中配合 $ARGUMENTS 特别好用。
$ARGUMENTS:接收调用时传的参数。/go-bench ./internal/handler/... 会把包路径传进去。注意 $ARGUMENTS 参数会先被替换,再执行 !command``,也就是用户输入会进入 shell 命令——所以 allowed-tools 必须严格限制。
如果需要多个独立参数,用 $1、$2:
将 $1 模块从 $2 迁移到 $3,保留所有现有功能和测试。
/migrate payment-service Express Fastify → $1 = payment-service,$2 = Express,$3 = Fastify。
多文件 Skill 与契约式引用
当规范比较复杂时,可以拆分成多个文件。SKILL.md 作为入口和路由,辅助文件按需加载——这就是渐进式加载的第三层。
.claude/skills/db-access/
├── SKILL.md ← 入口(控制在 500 行以内)
├── query-patterns.md ← 详细查询模式和示例
├── migration-rules.md ← 迁移操作规范
└── scripts/
└── check-slow-query.sh ← 辅助脚本
关键是引用的写法。不要只写一个路径——要写一个契约,告诉 Claude 什么时候该加载、加载后能得到什么:
# 弱引用——Claude 不知道何时该加载
详细内容见 `query-patterns.md`。
# 契约式引用——Claude 清楚加载条件和预期内容
## 查询模式
当需要编写复杂查询(JOIN、子查询、CTE)或优化现有查询时:
→ 加载 `query-patterns.md` 获取标准查询模式和性能注意事项
契约式引用三要素:触发条件(什么情况下加载)、文件路径(去哪找)、内容预期(加载后能得到什么)。
Claude 只在确实需要查看详细内容时才去读取辅助文件。好处是 Skill 主体保持简洁,细节按需加载。官方建议 SKILL.md 控制在 500 行以内——超过 500 行意味着你可能把"参考资料"混进了"路由指令",该拆出去了。
${CLAUDE_SKILL_DIR} 变量指向 Skill 所在目录,可以在 !command`` 里引用辅助脚本:
!`bash ${CLAUDE_SKILL_DIR}/scripts/check-slow-query.sh`
更多 frontmatter 字段
除了上面用到的字段,还有几个在特定场景下很有用:
context: fork:在隔离的上下文中执行,Skill 的输出不会污染主对话。适合输出量大的任务——比如全量代码审查、大规模 benchmark 分析。
user-invocable: false:对用户隐藏,用户在 / 菜单里看不到它,但 Claude 可以自动触发。适合"背景知识"类的 Skill——比如一个描述遗留系统架构的 Skill,Claude 在改相关代码时需要了解它,但用户不需要手动调用。
model:指定执行模型。简单的格式化任务用 haiku 更快更省;复杂的分析任务用 sonnet。
hooks:Skill 级别的 Hooks,只在这个 Skill 的生命周期内生效,随 Skill 一起分发。比如在 Skill 执行时每次写操作之后自动跑 go vet:
hooks:
PostToolUse:
- matcher: Edit
hooks:
- type: command
command: "cd $CLAUDE_PROJECT_DIR && go vet ./... 2>&1 | tail -5"
两个字段组合起来控制"谁能触发":
| 配置 | 用户可触发 | Claude 可触发 |
|---|---|---|
| 默认(都不设) | 是 | 是 |
disable-model-invocation: true | 是 | 否 |
user-invocable: false | 否 | 是 |
哪些知识放 CLAUDE.md,哪些放 Skill
判断标准:
放 CLAUDE.md:每个任务都需要知道的基础信息。技术栈、目录结构、绝对禁令——无论 Claude 在做什么都需要。控制在 50-100 行以内。
放 Skill:特定场景才需要的详细知识。数据库规范只在写数据库代码时有用;并发规范只在写 goroutine 时有用——有明确的触发场景,适合提取成 Skill。
拿不准的时候,往 Skill 里放。 在 CLAUDE.md 里保持一行简短引用("数据库规范见 db-access Skill"),具体内容在 Skill 里。这是官方文档也推荐的做法。
设计 Skill 的几条原则
单一职责。 一个 Skill 做一件事。与其做一个宽泛的"代码质量检查",不如拆成"静态分析"、"安全审计"、"性能分析"三个专注的 Skill。职责单一时 description 写得更精准,Claude 匹配更准确,上下文也更干净。
权限最小化。 参考型只给 Read, Grep, Glob;任务型精确到命令级(Bash(go test:*) 而不是 Bash(*))。权限越小越安全,也帮助 Claude 更好地聚焦。
description 当广告写。 它不是给人看的文档——它是给 Claude 看的触发器。包含用户可能说的关键词,明确说明触发场景。模糊的 description 导致该触发时不触发。
任务型 Skill 的设计清单。 设计一个任务型 Skill 时,按顺序回答这几个问题:
- 动作是什么?→ 命名(commit、deploy、review)
- 谁能触发?→
disable-model-invocation: true - 需要什么权限?→
allowed-tools精确到命令级 - 启动时需要什么上下文?→
!command`` 预注入 - 执行过程需要什么安全网?→
hooks - 输出量大不大?→ 大则
context: fork - 用什么模型?→ 简单 haiku,复杂 sonnet
提交到 Git。 项目级 Skill(.claude/skills/)是团队资产。团队里的人 clone 下来就能用同一套规范。有人发现 description 不够精准、prompt 可以改进,发个 PR。
内置 Skills
Claude Code 自带了几个开箱即用的 Skill,不需要配置:
/simplify:改完代码后跑一遍,它会并行启动三个审查代理(代码复用、代码质量、效率),汇总发现的问题并自动修复/batch:大规模并行改动。描述要做的变更,它自动拆分成独立单元,每个单元在隔离的 git worktree 里执行/debug:排查 Claude Code 本身的问题,读取会话调试日志进行分析
这些是 prompt 驱动的 Skill,不是硬编码的命令——它们和你自己写的 Skill 用的是同一套机制。
实际用起来是什么效果
把 db-access 这个 Skill 配好之后,我把 CLAUDE.md 里的数据库部分删掉了。
现在当我说:
帮我实现客户订单查询接口,需要支持按状态筛选和分页,
数据从 PostgreSQL 的 orders 表查。
Claude 识别到"数据库查询"这个场景,自动加载 db-access Skill,然后按照团队规范写代码——用 pgxpool.Pool 而不是单连接、查询带 context.WithTimeout、用 pgx.CollectRows 收集结果、错误用 fmt.Errorf 包装——我一句话都不用提醒。
CLAUDE.md 现在保持在四十行左右,都是真正每次都需要的基础信息。Claude 的注意力更集中,按规范输出的准确率明显提高了。
几个实际的坑
Skill 没触发? 先检查 description 里有没有包含用户自然会说的关键词。可以问 Claude "What skills are available?" 确认它能看到你的 Skill。如果确认能看到但没触发,试着调整 description 让它和你的请求更匹配。也可以随时 /skill-name 手动触发。
触发太频繁? 说明 description 写得太宽泛。把触发条件收窄,或者干脆加 disable-model-invocation: true 改成手动触发。
Skill 太多导致部分被截断? 运行 /context 看有没有警告。description 总预算大约 15,000 字符。解决办法:精简 description、合并功能相近的 Skill、或者通过 SLASH_COMMAND_TOOL_CHAR_BUDGET 环境变量调大预算。
monorepo 的 Skill 发现:如果你在 packages/frontend/ 目录下工作,Claude Code 会自动发现 packages/frontend/.claude/skills/ 下的 Skill。每个 package 可以有自己的 Skill,不需要全放在根目录。
Skills 的优先级:当多个作用域存在同名 Skill 时,优先级从高到低:Enterprise(企业级)> Personal(用户级)> Project(项目级)。Plugin Skills 使用 plugin-name:skill-name 命名空间,不与其他级别冲突。
先让 Claude 帮你写。 你不需要从零开始手写 SKILL.md。描述你想要的能力,让 Claude 生成初稿,在这个基础上迭代调优。团队使用中不断根据实际效果打磨 description 和正文——好用的 Skill 是在真实使用中迭代出来的。
到这里,Claude Code 的几个核心扩展机制都覆盖到了:CLAUDE.md 管记忆,斜杠命令管流程复用,Hooks 管硬约束,MCP 管能力扩展,Skills 管按需知识加载。
这些机制不是要你全部用上。按实际痛点选——如果 Claude 老是"忘规矩",先把 Skills 建起来;如果需要跨越工具边界,再考虑 MCP;如果有操作绝对不能碰,才需要 Hooks。