1. 从"agent-skills"这个标题能读出什么
第一次看到agent-skills这个项目名,我的直觉是:这不是一个普通工具,而是一套给 AI 编码代理(AI coding agents)装"技能包"的规范或框架。为什么这么判断?因为 "skills" 这个词在 AI 代理语境下,指的从来不是模型本身的推理能力,而是外部注入的、可复用的、结构化的操作知识——比如"如何做测试驱动开发""如何写一个规范的 commit""如何排查一个内存泄漏"。这些知识模型本身可能"知道一点",但不知道你团队的具体做法、具体命令、具体约束。
所以agent-skills要解决的核心问题就浮出水面了:AI 编码代理很聪明,但它不知道你的项目规矩。你让它改一个函数,它可能顺手把测试删了;你让它加个功能,它可能不写测试直接提交。这不是模型笨,是你没告诉它"在这个项目里,什么叫做好"。
这个项目适合谁?三类人最该关注:
- 已经在用 Claude Code、Cursor、Copilot 这类 AI 编码代理的开发者,觉得"能用但不够听话",想让代理按自己的工程规范干活;
- 团队技术负责人,想把团队的编码规范、测试要求、提交约定固化成代理能读懂的"技能文件",让每个成员用的代理行为一致;
- 对 AI 代理工作流感兴趣的技术爱好者,想搞清楚"skills CLI"这类工具到底在抽象什么。
关键词里出现的test-driven-development是个强信号——它暗示这套技能体系里,TDD 是一个被重点封装的技能。也就是说,agent-skills很可能提供了一种机制:你把"先写测试再写实现"这个流程写成一个 skill,代理在接到编码任务时会自动加载并遵循它。
下面我会从"为什么需要技能层""技能文件长什么样""怎么落地到 Claude Code 这类工具""TDD 技能的具体拆解""踩过的坑"几个角度,把这件事讲透。内容基于我对 AI 编码代理工作流的实际使用经验,以及对 skills CLI 这类工具设计逻辑的合理推演,具体命令和字段以你本地实际版本为准。
2. 为什么裸奔的 AI 编码代理总是不听话
2.1 代理的"聪明"和"守规矩"是两回事
很多人对 AI 编码代理有个误解:觉得模型越强,写出来的代码就越符合预期。实际用下来你会发现,模型能力和工程规范遵循度是两个独立维度。一个能写出精妙算法的模型,照样可能在你明确说了"不要改公共接口"之后,手贱去重构你的 API。
根本原因在于:代理的默认行为是**"完成任务",而不是"按你的方式完成任务"**。它的训练目标是让代码能跑、能通过测试、能解决当前问题。至于命名风格、目录结构、错误处理约定、提交信息格式,这些"软约束"它只能靠上下文猜。上下文里没写,它就按训练数据里最常见的做法来——而"最常见"往往不是"你想要的"。
我踩过最典型的一个坑:让代理给一个 Python 项目加日志。它直接import logging然后在每个函数里logging.info(...)。问题是这个项目早就统一用了structlog,而且日志字段有严格的命名规范。代理不是不会用 structlog,是它不知道这个项目用 structlog。你每次都得在 prompt 里重复一遍,累不累?
2.2 把规范塞进 prompt 的三个致命问题
有人会说,那我每次把规范写进 prompt 不就行了?我试过,三个问题绕不过去:
第一,token 成本。一份完整的团队编码规范少说两三千字,每次对话都塞进去,长会话里 token 消耗飞快,而且模型对超长上下文中段的注意力会衰减——你写在中间的那条"必须写测试",它可能根本没"看见"。
第二,一致性差。今天你记得写"用 pytest 不用 unittest",明天换个同事忘了写,代理行为就不一样。规范散落在每个人的 prompt 里,等于没有规范。
第三,无法版本化。prompt 是临时的,改了就没了。团队规范应该像代码一样进 Git,能 review、能回滚、能追溯"这条规矩什么时候加的、为什么加"。
agent-skills这类项目的价值就在这里:把规范从"临时 prompt"变成"版本化资产"。技能文件进仓库,代理按需加载,团队共享同一套。
2.3 技能层到底抽象了什么
我理解agent-skills的抽象是这样的:一个 skill 就是一段结构化的、带触发条件的、可被代理按需读取的操作知识。它至少包含三部分:
- 元信息:这个技能叫什么、什么时候该用(触发条件)、适用哪些文件类型;
- 指令正文:具体怎么做,用自然语言写清楚步骤、约束、示例;
- 可选的辅助资源:脚本、模板、检查清单。
关键在"按需加载"。代理不是一上来就把所有技能读一遍,而是根据当前任务判断"我现在要写测试,那我去加载 TDD 技能"。这既省 token,又让代理的注意力集中在当前相关的规范上。
提示:技能文件的设计哲学和"系统提示词"完全不同。系统提示词是"永远生效的背景约束",技能是"特定场景下才激活的操作手册"。混用这两者,要么浪费 token,要么规范失效。
3. 一个 skill 文件到底该写什么
3.1 从"触发条件"倒推内容结构
写 skill 最容易犯的错,是把它写成一篇"编码规范文档"。规范文档是给人读的,skill 是给代理读的,两者的组织逻辑不一样。人读文档是从头到尾,代理读 skill 是"匹配触发条件后精准取用"。
所以写 skill 的第一步不是写内容,是写触发条件。你要回答:代理在什么情况下应该加载这个技能?比如 TDD 技能的触发条件可能是"当任务涉及新增功能或修改业务逻辑时"。触发条件写清楚了,内容结构自然就出来了——你只需要写"在这个场景下,代理必须知道的那几件事"。
我一般按这个结构组织一个 skill:
--- name: test-driven-development description: 当需要新增功能或修改业务逻辑时使用,强制先写测试 trigger: 新增功能、修改业务逻辑、修复 bug --- ## 核心原则 先写失败的测试,再写实现,最后重构。 ## 具体步骤 1. 阅读需求,写出一个会失败的测试 2. 运行测试,确认它确实失败(且失败原因正确) 3. 写最小实现让测试通过 4. 重构,保持测试绿色 5. 重复直到需求完成 ## 硬性约束 - 禁止在没有测试的情况下修改业务逻辑 - 测试必须能在本地独立运行 - 每个测试只验证一个行为 ## 反例 不要这样:先写实现,再补测试。补出来的测试往往在验证"实现做了什么",而不是"需求要求什么"。注意最后那个"反例"部分。给代理写规范,反例比正例更有用。因为代理的默认行为往往就是那个反例,你明确点出来,它才会刻意避开。
3.2 指令要写成"可执行动作"而非"原则口号"
"代码要整洁"这种话对代理毫无意义,因为它无法把"整洁"翻译成具体动作。有效的指令必须是可执行、可验证的。对比一下:
| 无效指令 | 有效指令 |
|---|---|
| 写高质量的测试 | 每个测试函数只包含一个 assert,测试名用test_<行为>_<预期>格式 |
| 保持代码风格一致 | 遵循项目根目录.editorconfig,缩进用 4 空格,行宽 100 |
| 注意错误处理 | 所有外部调用必须包裹 try/except,异常必须记录日志并重新抛出业务异常 |
| 提交信息要规范 | 提交信息格式:<type>(<scope>): <描述>,type 限 feat/fix/refactor/test/docs |
右边这列的每一条,代理都能直接执行,而且你能验证它有没有做到。写 skill 的过程,其实是在逼你把"团队默契"翻译成"机器可执行的规则"——这个过程本身就很有价值,很多团队写完 skill 才发现,原来大家对"规范"的理解根本不一致。
3.3 技能之间的依赖和组合
真实项目里,一个任务往往需要多个技能协同。比如"实现一个新 API 端点"可能同时触发:TDD 技能(先写测试)、API 设计技能(路由命名规范)、错误处理技能(统一异常格式)、文档技能(自动更新 OpenAPI)。
agent-skills这类框架通常会提供技能组合机制——要么在 skill 里声明依赖,要么由代理根据任务自动编排。我的经验是:技能粒度要小,组合要显式。一个 skill 只干一件事,需要组合时在元信息里写清楚"本技能通常与 X、Y 一起使用"。粒度太大,代理加载一堆无关内容;组合不显式,代理可能漏掉关键技能。
注意:技能不是越多越好。我见过一个团队写了 40 多个 skill,结果代理每次任务都要在技能选择上花大量推理,反而变慢变笨。控制在 10 个以内,覆盖最高频的场景,剩下的用项目级配置文件兜底。
4. 把 skills 接进 Claude Code 的实际路径
4.1 先搞清楚 Claude Code 的技能加载机制
Claude Code 本身有一套项目级配置机制,通常通过项目根目录的配置文件(如CLAUDE.md或类似约定文件)来注入项目上下文。agent-skills这类工具的价值,是把散乱的配置升级成结构化的技能库,并提供 CLI 来管理这些技能。
实际接入时,你要搞清楚两件事:
- 技能文件放在哪:通常是项目内一个约定目录,比如
.agent-skills/或.claude/skills/,具体以你用的版本为准; - 代理怎么发现技能:要么通过一个索引文件(列出所有可用技能及其触发条件),要么通过 CLI 生成的配置注入到代理的上下文里。
我建议的做法是:技能文件进 Git,索引文件由 CLI 生成。这样技能内容可 review、可追溯,索引文件不用手动维护,避免"加了技能忘了更新索引"这种低级错误。
4.2 skills CLI 的典型工作流
虽然具体命令以你本地版本为准,但这类 CLI 的工作流大同小异,我按常见实践梳理一遍:
# 1. 初始化技能目录结构 skills init # 2. 创建一个新技能(交互式或从模板) skills create test-driven-development # 3. 校验技能文件格式是否正确 skills validate # 4. 生成代理可读的索引/配置 skills build # 5. 列出当前所有技能及其触发条件 skills listskills validate这一步千万别跳过。技能文件里的元信息格式错了(比如 YAML frontmatter 缩进不对),代理可能静默地加载失败,你还以为是代理不听话。每次改完技能,先 validate 再 build,这是我踩过坑之后养成的习惯。
4.3 验证技能真的生效了
技能写完不等于生效。怎么验证?我的方法是设计一个"陷阱任务":故意给代理一个容易违反规范的场景,看它会不会触发对应技能。
比如验证 TDD 技能,我会说:"给用户模块加一个根据邮箱查用户的方法。"如果技能生效,代理应该先写测试、跑测试看它失败、再写实现。如果它直接甩出一段实现代码,说明技能没加载,或者触发条件没匹配上。
排查顺序:
skills list确认技能在列表里;- 检查触发条件的关键词是否覆盖了当前任务描述;
- 看代理的上下文里有没有技能内容(有些工具支持查看注入的上下文);
- 实在不行,把触发条件放宽一点,或者直接在任务描述里点名"请使用 TDD 技能"。
提示:触发条件匹配是"语义匹配"还是"关键词匹配",不同工具实现不同。如果是关键词匹配,你的触发词要覆盖同义表达,比如"新增功能"和"添加特性"都得写上。
5. TDD 技能:一个值得拆透的样板
5.1 为什么 TDD 最适合做成技能
在agent-skills的关键词里,test-driven-development被单独拎出来,我认为很有道理。TDD 是"代理默认行为"和"工程最佳实践"冲突最激烈的场景。代理的默认行为是"尽快给出能跑的代码",而 TDD 要求"先写一个失败的测试"。这两个目标在代理的推理里是矛盾的——它倾向于跳过"写失败测试"这一步,因为那看起来像"没完成任务"。
把 TDD 做成技能,本质是用显式指令覆盖代理的默认倾向。而且 TDD 的流程高度结构化(红-绿-重构),非常适合写成可执行的步骤。
5.2 红绿重构在技能文件里的落地
红-绿-重构三步,每一步在技能文件里都要写清楚"代理必须做什么"和"代理必须确认什么":
红阶段:代理写出测试后,必须实际运行测试并确认它失败。这一步最容易被跳过。代理经常写完测试就假设它会失败,直接进入实现。但测试可能因为语法错误、导入错误而"失败",这种失败是假失败,不能算数。技能文件里要明确:"运行测试,确认失败原因是'功能未实现',而非语法或导入错误。"
绿阶段:写最小实现让测试通过。注意"最小"两个字。代理倾向于一次写一个完整实现,但 TDD 要求你只写刚好让当前测试通过的量。技能文件里要写:"只实现让当前测试通过所需的最少代码,不要提前实现后续需求。"
重构阶段:测试保持绿色的前提下改进代码。这一步代理也容易忽略,因为它觉得"测试过了就完事了"。技能文件里要写:"每次绿灯后,检查是否有重复代码、过长函数、不清晰命名,有则重构,重构后重跑测试。"
5.3 让代理"先失败"的指令技巧
让代理接受"先写失败测试"这件事,光靠命令不够,得给它一个认知框架。我在技能文件里会加一段"为什么":
先写测试的价值不在于测试本身,而在于它强迫你在写实现前明确"这个功能到底应该做什么"。测试是你对需求的第一次精确表达。如果跳过这一步,你会在实现里做一堆需求没要求的假设,这些假设就是 bug 的来源。
代理读到这段"为什么",遵循度会明显提升。给代理讲道理,比单纯下命令有效——这也是写 skill 和写传统配置文件的区别。
6. 技能库维护中那些没人告诉你的坑
6.1 技能膨胀:从 5 个到 40 个的失控
技能库最容易失控。一开始大家很克制,就写几个核心技能。然后每个人遇到问题就加一个 skill,半年后 40 多个,代理每次任务都要在技能选择上纠结。更糟的是,技能之间开始冲突——A 技能说"用 tabs",B 技能说"用 spaces",代理无所适从。
我的控制策略:
- 定期合并:每月 review 一次技能库,把触发条件重叠的技能合并;
- 设上限:核心技能不超过 10 个,超出的必须合并或降级为项目配置;
- 冲突检测:
skills validate之外,我还会写个脚本检查技能间的硬性约束是否矛盾。
6.2 触发条件写太窄或太宽
触发条件太窄,技能永远不激活,等于没写;太宽,技能到处激活,干扰其他任务。这个度很难一次调准。
我的经验是从窄开始,逐步放宽。先写一个很具体的触发条件,观察代理在哪些本该触发的场景没触发,再针对性放宽。反过来(从宽到窄)很难,因为你不知道哪些激活是"误激活"。
判断误激活的方法:看代理的输出里有没有出现"这个技能不该管的内容"。比如 TDD 技能在"只改文档"的任务里被激活了,那就是触发条件太宽。
6.3 技能和项目配置的边界
有些规范适合做成技能(按需加载),有些适合放进项目级配置(永远生效)。边界在哪?
我的划分标准:"永远不能违反"的放项目配置,"特定场景才适用"的放技能。比如"禁止提交密钥"是永远不能违反的,放项目配置;"新增功能要先写测试"是特定场景的,放技能。搞混了,要么项目配置臃肿,要么关键约束在非触发场景下失效。
6.4 团队协作:技能文件的 review 流程
技能文件是团队资产,必须走 review。但 review 什么?不是 review 文笔,是 review可执行性。我要求每个技能 PR 必须包含:
- 一个"陷阱任务"的测试记录,证明技能确实改变了代理行为;
- 触发条件的正例和反例各一个;
- 如果修改了已有技能,说明为什么旧版本不够好。
这套流程跑下来,技能库的质量会稳定很多。没有测试的技能,和没有测试的代码一样不可信。
7. 我实际用下来的一些体会
技能库这东西,最大的价值不是"让代理更听话",而是逼团队把隐性知识显性化。写 skill 的过程中,你会发现很多"我们一直这么做但没人说清楚为什么"的规矩。把这些写下来,本身就是一次团队对齐。
另一个体会是:别指望一次写对。我第一版 TDD 技能写了 200 多字,代理遵循度一般。后来加了反例、加了"为什么"、把步骤拆得更细,遵循度才上来。技能文件是要迭代的,把它当代码一样对待——有版本、有测试、有 review。
最后说个反直觉的:技能不是越多越好,也不是越详细越好。一个 50 字的精准指令,胜过 500 字的泛泛而谈。代理的注意力是稀缺资源,你写的每一个字都在争夺它的注意力。写 skill 的最高境界,是用最少的字,让代理做出最正确的行为。