AI Coding Skills 体系:把经验沉淀成可复用能力
AI Coding Skills 体系:把经验沉淀成可复用能力
反复说明同一件事,是最贵的开发成本
上个月我在一次代码审查里第三次看到 AI 把测试文件放到 apps/src/ 下面。项目规范明确写着「测试放 apps/tests/」,CLAUDE.md 里也写了,但每次新会话,Agent 还是会犯同样的错。问题不在 Agent 不听话,在于上下文窗口里塞了太多事实性规则,真正要执行的流程反而被稀释了。
后来我把「如何决定测试放哪」从 CLAUDE.md 搬到 Skill 里,写清楚触发条件、判断步骤和输出格式,问题消失了。这不是加了一条更严的规则,而是把一条散落的规则,变成了可被按需加载的流程。
这就是 Skills 体系的核心价值:把团队反复说明的经验,沉淀成 Agent 可复用、可维护、可按需加载的能力单元。
上下文工程:Skills 的理论基础
Martin Fowler 在 2026 年初的「Context Engineering for Coding Agents」一文中指出:上下文工程的本质是「curating what the model sees so that you get a better result」——整理模型看到的内容,让它产出更好的结果[1]。开发者通常把上下文分成两类:可执行的行为指令和通用的项目约定,但这两者在实际文档中经常混在一起,导致模型分不清「这是背景知识」还是「这是现在要做的事」。
CLAUDE.md 的默认行为是把所有内容在会话开始时全量注入。当项目规则少的时候这没问题;当规则膨胀到几百行,模型需要在一堆事实性约束里找到当前任务需要的流程,注意力被稀释是必然结果。arXiv 上关于终端 AI 编码 Agent 的论文(2603.05344)描述了一种三层技能体系——内置技能、用户全局技能、项目本地技能——通过懒加载机制只在需要时注入领域相关的提示模板[2]。这和 Claude Code 的 Skills 设计高度一致:SKILL.md 的内容只在技能被触发时才加载,长参考材料的成本在不需要时接近零[3]。
从提示工程到上下文工程的转变,核心在于从「把所有东西塞进去祈祷模型找到重点」变成「给模型一个按需取用的知识抽屉」。Skills 就是这个抽屉的具体实现。
Skills 是什么:一个被低估的抽象层
一个 Skill 不是一个提示词片段,它是一个有触发条件、操作流程、参考材料和输出格式的完整能力单元。和 CLAUDE.md 的静态规则不同,Skill 有生命周期——可以被自动触发、手动调用、动态注入上下文、按作用域覆盖。
这张图展示了 Skill 从触发到执行的基本路径。关键设计在于:description 字段决定了 Agent 是否认为当前任务需要这个 Skill,而 SKILL.md 的正文只在触发后才进入上下文。这把「加载成本」和「使用价值」解耦了。
案例一:把「请认真调试」变成系统化调试流程
场景
团队的 AI 工具在遇到测试失败时,习惯直接猜原因然后改代码。改对了算运气好,改错了就在错误方向上越跑越远,经常一个 bug 修三轮还没好。
翻车
有一次一个 API 返回 500,Agent 直接改了 handler 的返回格式。实际上问题是数据库迁移没跑,测试夹具里的字段和 schema 不匹配。Agent 花了四次对话才定位到真正的根因,因为它没有「先收集证据再下结论」的流程。
修复
我写了一个 systematic-debugging Skill,把调试过程固化成可执行步骤:
---
name: systematic-debugging
description: 遇到测试失败、构建失败、异常行为或修复多次无效时使用。
当同一问题修复两次仍失败时,必须触发此技能。
---
# 系统化调试
## 触发条件
- 测试失败(单元 / 集成 / E2E)
- 构建或 typecheck 失败
- 用户反馈功能异常
- 同一问题修复两次仍未解决
## 流程
1. 复述现象和影响范围,不要猜测原因。
2. 收集错误日志、最近改动和环境信息。
3. 区分四层:环境 / 配置 / 测试夹具 / 实现逻辑。
4. 做最小复现,确认复现成功再往下走。
5. 提出一个可证伪的假设,用最小改动验证。
6. 修复后运行与改动匹配的验证命令。
7. 输出根因、修改范围、验证结果和剩余风险。
## 禁止行为
- 不要在没有复现的情况下直接改代码。
- 不要一次修改多个变量。
- 不要把「可能是」当结论输出。效果对比
// ❌ 没有 Skill 时的典型行为
// Agent 看到测试失败 → 读测试代码 → 猜是 handler 返回格式不对 → 直接改 handler
async function handleRequest(req: Request) {
// Agent 把 response 格式改了,但问题其实在数据库
return Response.json({ data: transform(req.body) })
}
// ✅ 有 systematic-debugging Skill 后的行为
// Agent 先收集证据:
// 1. 读错误日志 → 发现 500 来自 db.query
// 2. 查最近改动 → 发现 migration 文件存在但未执行
// 3. 检查测试夹具 → 夹具字段和新 schema 不匹配
// 4. 假设:跑完迁移 + 更新夹具后测试通过
// 5. 验证:pnpm db:migrate && pnpm test → 通过
// 6. 输出根因和修改范围,不再猜测这个 Skill 的价值不在于教 Agent 「要调试」,而在于给它一个不会跳步的执行流程。人经过多年训练能做到的「先收集证据再下结论」,对 Agent 来说需要显式写成步骤。
一个 Skill 应该包含什么
触发条件:越具体越好
触发条件写在 frontmatter 的 description 字段里。它决定了 Agent 是否认为当前任务需要这个 Skill。「任何开发任务都使用」会让 Skill 在每次对话都被加载,失去按需加载的意义。「修复测试失败时使用」则精确匹配特定场景。
---
description: 在审查代码、准备 Pull Request 或判断代码是否可合并时使用。
---
# 好的:场景具体,Agent 能判断是否匹配
description: 用于所有编程任务。
# 坏的:等于没有触发条件,每次都加载
---操作流程:写成步骤,不是原则
「请写出高质量代码」是原则,模型有自己的理解。「先定义验收条件,再写实现,再跑验证」是步骤,模型可以执行。两者的差异在于输出稳定性——步骤产生的结果更可预测。
---
name: test-driven-development
description: 实现新功能或修复 bug 时使用,在写实现代码之前触发。
---
# 测试驱动开发
## 流程
1. 用一句话描述要完成的行为。
2. 写一个失败的测试,只覆盖这一个行为。
3. 运行测试,确认它因为预期中的原因失败。
4. 写刚好让测试通过的最少代码。
5. 重构,保持测试始终通过。
6. 重复 1-5,直到功能完整。
## 检查点
- 每个红灯到绿灯的循环不超过 5 分钟。
- 不要在没有失败测试的情况下写实现代码。
- 不要一次写多个测试再补实现。参考材料:按需加载,不要全塞
复杂 Skill 可以引用项目规范、API 文档或示例文件。关键是让 Agent 知道什么时候去读什么,而不是把所有材料平铺在 SKILL.md 里。
## 参考材料
- 项目代码风格见 `docs/style-guide.md`,在写新组件前阅读。
- API 错误码定义见 `apps/src/api/error-codes.ts`,在写错误处理时查阅。
- 不要在这里复制规范内容,直接引用路径让 Agent 按需读取。输出格式:让结果可消费
代码审查、测试诊断、技术写作和交付报告,都应该有不同的输出结构。没有格式约束的 Skill,输出质量完全取决于模型当次的「心情」。
## 输出格式
每次审查输出必须包含以下部分:
### 总结
一段话描述整体评价和是否建议合并。
### 必须修改
逐条列出阻塞合并的问题,每条附带文件路径和行号。
### 建议修改
逐条列出非阻塞但值得改进的点。
### 风险
列出修改可能引入的副作用或未覆盖的边界。案例二:代码审查从「随便看看」到结构化输出
场景
团队用 AI 做代码审查,每次 Agent 都输出一大段笼统建议——「建议加错误处理」「命名可以更清晰」。看起来有道理,但 reviewer 看完还是要自己重新审一遍,因为 Agent 没有给出可以操作的结论。
翻车
一次 PR 里,Agent 说「代码看起来没问题」,但合并后发现了环境变量直接读 process.env 而没有走项目封装的 apps/src/config/env/ 模块。这种项目特有的约束,Agent 在泛泛审查时根本不会注意到。
修复
写一个 code-review Skill,把项目容易出错的边界直接嵌进审查流程:
---
name: code-review
description: 审查代码、处理 Pull Request 反馈或判断代码是否可合并时使用。
---
# 代码审查
## 审查清单
### 项目特有边界(高优先级)
1. 环境变量是否通过 `apps/src/config/env/` 读取?直接 `process.env` 是错误做法。
2. 测试文件是否在 `apps/tests/` 下?放进 `apps/src/` 违反项目规范。
3. API 请求是否通过 `apps/src/api/` 的 core 模块?feature 层不应直接 fetch。
4. 页面文件是否只做路由装配?复杂 UI 应拆到 `_components/` 或 `features/`。
### 通用质量
5. 是否有未处理的错误路径?
6. 是否有硬编码的魔法值?
7. 新增公开路由是否同步更新 sitemap 和 SEO 配置?
8. 单文件是否超过 500 行?超过需要说明是否应该拆分。
## 输出格式
- **结论**:建议合并 / 需要修改 / 需要重构
- **阻塞项**:必须修改才能合并的问题,附文件路径和行号
- **建议项**:非阻塞但值得改进的点
- **风险**:修改可能引入的副作用效果对比
// ❌ 没有 Skill 时的审查输出
// 「代码整体质量不错,建议在 handleUser 函数中增加错误处理。
// 命名方面,data 变量可以考虑更有意义的名称。」
// → reviewer 看完还是要自己重新审
// ✅ 有 code-review Skill 后的审查输出
// 结论:需要修改
//
// 阻塞项:
// 1. apps/src/features/user/page.tsx:42 — 直接读取 process.env.DATABASE_URL,
// 应改为通过 apps/src/config/env/database.ts 的封装读取。
// 2. apps/src/features/user/_components/UserList.tsx:1 — 单文件 620 行,
// 超出项目 500 行限制,建议拆分 UserListHeader 和 UserListBody。
//
// 建议项:
// 1. apps/src/features/user/api.ts:18 — fetch 调用建议迁移到 core 模块。
//
// 风险:
// 1. 环境变量封装改动可能影响其他 feature 的测试夹具。
// → reviewer 可以直接根据结论行动案例三:技术写作从「AI 味」到可控风格
场景
我用 AI 辅助写技术文章,每次生成的内容都有明显的 AI 套路——「凌晨两点被 oncall 叫醒」「你有没有遇到过这种情况」「想象一下完美的架构」。这些开头在营销号里或许管用,但在工程团队内部读起来像噪音。
翻车
一篇架构复盘文章,Agent 用了「周五下午 CI 全红,团队陷入沉默」开头。实际上那次事故的原因很平淡——一个配置项拼写错误。用戏剧化开头反而让文章失去了可信度。更麻烦的是每篇文章都要重复提醒「不要用这个开头」「不要用第二人称」「中文引号用「」不用 ""」。
修复
写一个 technical-writing Skill,把写作约束固化:
---
name: technical-writing
description: 起草、改写或审查中文技术文章、架构复盘、工程实践文章时使用。
---
# 技术写作规范
## 禁止清单
- 禁止以下开头:「凌晨两点被 oncall 叫醒」「你有没有遇到过」
「想象一下」「周五下午 CI 全红」
- 禁止过场句:「接下来我们看看」「聊到这里该说说」
- 禁止第二人称「你」,使用第一人称「我」
- 禁止英文引号 "" ,中文语境使用「」
## 语气要求
像一个有经验的同事在分享经验,不是主持人在串场。
章节过渡靠逻辑关系,不靠过渡句。
结论必须有证据支撑,不能「显然」「不言而喻」。
## 结构要求
技术文章必须包含:
1. 问题背景(具体场景,不是虚构故事)
2. 分析过程(为什么这么判断,不是直接给结论)
3. 解决方案(可操作的步骤,不是抽象原则)
4. 验证结果(实际效果,不是「相信这会有帮助」)效果对比
<!-- ❌ 没有 Skill 时的开头 -->
周五下午,CI 全红。团队陷入沉默。你有没有遇到过这种情况——
一个小小的改动,却引发了连锁反应?今天我们来聊聊如何在微服务架构中
避免这类问题。
<!-- ✅ 有 technical-writing Skill 后的开头 -->
上个月我在一次发布后遇到了一个配置项拼写错误导致的级联失败。
A 服务的数据库连接字符串指向了 staging 环境,但因为 B 服务的健康检查
没有校验数据库可用性,问题在发布后 40 分钟才被下游服务的数据异常暴露出来。
这篇文章复盘这次事故的定位过程和后续修复。两种开头描述的是同一个事件,但第一种读完不知道文章要讲什么,第二种读完已经知道问题、影响范围和文章方向。
Skills 的颗粒度和作用域
颗粒度判断
Skill 不宜过大,也不宜过细。判断标准是:这个 Skill 是否能在多次真实任务中复用,且不会和其他 Skill 的职责混淆。
| 维度 | 颗粒度过粗 | 合适的颗粒度 | 颗粒度过细 |
|---|---|---|---|
| 触发频率 | 每次会话都加载 | 特定任务类型触发 | 几乎不触发 |
| 上下文成本 | 加载大量无关规则 | 只加载当前需要的 | 路由成本高于加载成本 |
| 维护成本 | 改一处影响多个场景 | 职责清晰,独立维护 | 文件多,管理成本高 |
| 输出质量 | 模型在大量规则中迷失 | 指令聚焦,输出稳定 | 流程被拆得太碎,缺少上下文 |
作用域管理
Skills 按作用域分四层,优先级从高到低:
| 作用域 | 路径 | 适用对象 | 典型内容 |
|---|---|---|---|
| 企业级 | 由管理员配置 | 组织内所有用户 | 安全策略、合规审查 |
| 个人级 | ~/.claude/skills/<name>/SKILL.md | 你的所有项目 | 个人写作偏好、通用调试流程 |
| 项目级 | .claude/skills/<name>/SKILL.md | 当前项目 | 项目特有的审查规则、部署流程 |
| 插件级 | <plugin>/skills/<name>/SKILL.md | 启用插件的项目 | 第三方工具集成 |
同名 Skill 在不同作用域会产生覆盖。我在项目级写了一个 code-review Skill,它会覆盖个人级的同名 Skill。这个设计让团队可以在项目层面定制行为,而不需要每个开发者手动调整个人配置。
Monorepo 场景还有一个实用细节:嵌套目录下的 .claude/skills/ 会被自动发现。当你在 packages/frontend/ 里工作时,这个目录下的 Skills 也会生效,适合给不同子包定义各自的构建和测试规则[3]。
Skill 与项目规范的边界
CLAUDE.md 回答「这个仓库有什么约束」,Skill 回答「遇到这类任务怎么做」。把仓库事实写进通用 Skill,换项目时会误导 Agent。
# ❌ 错误做法:把项目规范写进 Skill
---
name: testing
description: 编写测试时使用
---
测试文件放在 apps/tests/ 目录下,不能放进 apps/src/。
# 这是项目特有的约束,不是通用测试知识。
# 换一个测试文件放 src/ 旁边的项目,这条规则就是错的。
# ✅ 正确做法:Skill 引导 Agent 去读项目规范
---
name: testing
description: 编写测试时使用
---
## 流程
1. 先读 CLAUDE.md 和 AGENTS.md 中的测试策略章节。
2. 确认测试文件的放置位置。
3. 确认测试类型(单元 / 集成 / E2E)和对应目录。
4. 写测试前确认是否有现成的测试工具函数可以复用。项目规范是事实,Skill 是流程。事实放在全局配置里,流程放在 Skill 里。两者配合而不是混在一起。
动态上下文注入:让 Skill 接地气的关键
Skill 不只是静态提示词。Claude Code 支持在 SKILL.md 里用 !command`` 语法执行命令,把输出内联到提示中[3]。这意味着 Skill 拿到的不是过时的文档,而是当前工作区的真实状态。
## 当前变更
!`git diff HEAD --stat`
## 详细 Diff
!`git diff HEAD`
## 指令
根据上面的 diff 内容,检查是否有以下问题:
1. 是否包含敏感信息(密钥、token、密码)
2. 是否有未提交的调试代码(console.log、debugger)
3. 修改是否和 commit message 描述一致当 Claude 处理这个 Skill 时,!`git diff HEAD` 会被替换成实际的 diff 输出。Agent 看到的是当前工作区的真实状态,而不是一个「请假设用户做了某些改动」的抽象提示。这把 Skill 从「通用指南」变成了「基于当前事实的操作手册」。
Skills 维护:当作工程资产,不是一次性配置
Skills 会过期。工具升级了、项目规范变了、之前有效的流程不再适用——如果不维护,Skills 会从资产变成噪音。
定期评估清单
| 阶段 | 检查项 | 通过标准 |
|---|---|---|
| 使用频率 | 最近 30 天是否被触发过 | 至少触发 3 次以上 |
| 重复说明 | 是否减少了重复的口头交代 | 团队成员不再手动补充该 Skill 覆盖的规则 |
| 准确性 | 最近一次触发是否产生正确输出 | 无误导性建议 |
| 替代性 | 是否有更好的自动化手段替代 | 无 linter / CI / 项目规范可替代 |
| 颗粒度 | 是否需要拆分或合并 | 职责单一,不和其他 Skill 重叠 |
| 触发条件 | description 是否仍然准确 | 该触发时触发,不该触发时不触发 |
| 参考材料 | 引用的文档路径是否仍然有效 | 所有路径可访问,内容未过期 |
| 输出格式 | 输出结构是否仍然符合团队需求 | 输出可直接消费,不需要二次加工 |
| 项目适配 | 是否包含已废弃的项目约束 | 不包含过时的路径、模块名或工具名 |
| 跨工具兼容 | 是否遵循 Agent Skills 开放标准 | 可在多个 AI 工具中复用[4] |
维护节奏
我在团队里定的规则是:每个迭代末尾花 15 分钟过一遍项目级 Skills。出现重复返工时补充规则,规则失效时删除,工具升级后更新说明。一个长期有效的 Skill 通常来自多次真实任务复盘,而不是一次性写完。
常见反模式和修复方式
| 反模式 | 症状 | 修复方式 |
|---|---|---|
| 万能 Skill | 一个 Skill 覆盖所有开发场景 | 按任务类型拆分为调试、审查、写作、部署等独立 Skill |
| 事实型 Skill | Skill 里写满项目特有路径和约束 | 把事实移回 CLAUDE.md / AGENTS.md,Skill 只留流程 |
| 无触发条件 | description 写「用于所有任务」 | 重写为具体的触发场景和前置条件 |
| 无输出格式 | Skill 只有指令没有结构 | 添加明确的输出分段和必填字段 |
| 静态上下文 | Skill 不读取当前工作区状态 | 用 !command`` 注入 diff、日志、配置等动态信息 |
| 只建不管 | Skill 创建后从未更新 | 建立定期评估机制,过期即删 |
写在最后
Skills 体系的本质不是「给 AI 写更多提示词」,而是把团队的经验从口口相传变成可版本化、可复用、可按需加载的工程资产。一个好的 Skill 来自真实任务的反复复盘——哪些地方 Agent 会犯错、哪些流程不能跳步、哪些输出格式对人有用。
从 CLAUDE.md 到 Skills 的迁移,是从「把所有规则塞进一个文件」到「让正确的流程在正确的时候被加载」。当你的 Agent 不再重复犯同一个错误、不再需要每次交代同样的规则,你就知道 Skills 体系起作用了。
参考资料
[1] Martin Fowler. Context Engineering for Coding Agents. 2026. https://martinfowler.com/articles/exploring-gen-ai/context-engineering-coding-agents.html
[2] Building AI Coding Agents for the Terminal: Scaffolding, Harness, and Skills. arXiv:2603.05344. 2026. https://arxiv.org/html/2603.05344v1
[3] Anthropic. Extend Claude with Skills — Official Claude Code Docs. 2026. https://code.claude.com/docs/en/skills
[4] Agent Skills Open Standard. Agent Skills Specification. https://agentskills.io
[5] Mikhail. Inside Claude Code Skills: Structure, Prompts, Invocation. 2025. https://mikhail.io/2025/10/claude-code-skills/
[6] AlexOP. Claude Code Customization Guide: CLAUDE.md vs Skills vs Subagents. 2025. https://alexop.dev/posts/claude-code-customization-guide-claudemd-skills-subagents/
[7] Addo Zhang. Building a Reusable Skills Ecosystem for AI Agents. Medium, 2026. https://addozhang.medium.com/agent-skills-deep-dive-building-a-reusable-skills-ecosystem-for-ai-agents-ccb1507b2c0f