技能与配置
你可能已经对 AI 说过第五遍「先跑测试,再说改好了」。这一章做的事,是把这句话写进一个文件。
- 上一章解决了「够得着」,为什么还需要另一样东西解决「会做事」;
- 一份技能文件长什么样,头部元信息与正文各写什么;
- 什么时候该写技能,为什么门槛是「同一件事说到第三遍」;
- 可判定的规则与检查点,以及配置文件的层级关系与常见写错法;
- 钩子与子智能体各自解决什么,代价在哪里。
一、够得着,与会做事,是两件事
上一章讲的协议让智能体能连上外部系统。连上之后还有一层麻烦:工具在手上,它仍不知道你们这一套做法。语言模型极其擅长照着长篇流程做事,把这种能力暴露出来,是任何一个运行框架都要提供的东西。
翻成具体场景:你给了它待办系统的工具,它知道怎么新建一条任务;它不知道你们班的做法是「先复现问题、再写一个会失败的检查、再改、最后把改动关联到原来那条任务上」。这套做法不在工具里,在人的脑子里。它每次都要你重新讲一遍,讲漏一步,它就在那一步上自由发挥。
这层关系可以说得很干脆:有了工具不等于会做事。技能(Agent Skill)就是用来补这一层的,它把「某类任务该按什么顺序做、做到什么程度算完」写成一份文件,智能体在需要时自己读进来。
二、技能文件长什么样
一个技能就是一个文件夹,核心是一份叫 SKILL.md 的文本文件。这个后缀说明它是 Markdown 格式,也就是用井号表示标题、用短横线表示列表的那种纯文本,记事本就能打开编辑。文件夹里还可以放脚本、参考资料和模板,那些属于可选部分。
flowchart TB F["技能文件夹"] --> S["SKILL.md 必需"] F --> P["scripts 可选 放可执行脚本"] F --> R["references 可选 放详细资料"] F --> A["assets 可选 放模板与图片"] S --> Y["头部元信息 写名字与用途"] S --> B["正文 写步骤与检查点"]
文件开头是一段被三条短横线夹起来的元信息,这种写法叫 YAML 头部(frontmatter)。里面至少要有两个字段。name 是技能的名字,最长 64 个字符,只能用小写字母、数字和连字符,不能以连字符开头或结尾,也不能出现两个连续的连字符,并且必须和文件夹名一模一样。description 是用途说明,最长 1024 个字符。
正文部分没有固定格式,一个实用的模板把它分成两段:一段写「什么时候该用这个技能」,一段写具体指令,例如分步骤的做法、这个领域的约定、遇到不清楚的需求时该向你确认。你完全可以按自己的习惯改,只要做到一件事:读的人照着做,能做出你要的结果。
---
name: weekly-report
description: 按固定格式生成每周学习周报。当我说「写周报」「这周总结」或者到了周日晚上时使用。
---
# 每周学习周报
## 什么时候用
- 我说「写周报」或「这周总结」时
- 周日晚上整理本周记录时
## 步骤
1. 读 notes/ 下面本周一到周日的记录文件,缺哪天的先告诉我,不要猜。
2. 按「本周完成、遇到的问题、下周计划」三段写,每段不超过 5 条。
3. 数字全部来自记录文件,记录里没有的数字一律不写。
4. 写完把提到但没做的任务单独列一行,标上「待确认」。
## 完成标准
- 三段齐全,每段都有内容
- 每个数字都能在记录文件里找到出处
- 待确认清单已列给我看
注意 description 那一行。它是整套机制里最关键的一行,因为智能体平时只加载这一行,靠它判断「现在这个任务要不要读那份文件」。写得含糊,例如「帮助处理报告」,它永远不会触发;写得具体,把你说过的原话也塞进去,命中率立刻不一样。
三、什么时候该写一个技能
没有一个精确的计数门槛,但有一个很好用的观察方法:去翻自己的对话记录,找三种时刻。你打断它的时刻,例如「等等,先别改,先跑一下测试」;你纠正它的时刻,例如「不对,我们项目里日期统一用协调世界时」;你重复说同一句话的时刻,例如第三次说「记得先写一个会失败的检查」。
这三种时刻就是技能素材。一句话被你说到第三遍,说明它属于流程,应该从对话里搬进文件。这个「三次」算不上硬指标,是从上面那个观察方法里推出来的可用门槛:说一遍可能是特例,说两遍可能是巧合,说三遍就是你每次都想要的东西。
反过来说,有些事不值得写成技能。一年做一次的活,写文件的时间比它带来的收益长;判断标准含糊、你自己也说不清的活,写成文件只会把含糊固定下来。判断的方法很简单:这件事你上个月做过吗,下个月还会做吗,两次做法的步骤一样吗。三个问题都是「是」,就值得写。
四、写判断标准和检查点
技能文件里最该反复打磨的地方,是「怎么判断做对了」。一个从没执行过的技能只是草稿,一份没有被跑通过的文件,和一份写得漂亮的愿望清单没有区别。
具体到写法,可以对比两组句子。下面这一组是要求,看起来很多,实际上没法判断做没做到:
- 代码要优雅
- 注意命名规范
- 保证质量
换一组,每条都能看一眼就判断是或否:
- 函数参数超过 3 个时改成对象参数
- 不写解释「做了什么」的注释,只写解释「为什么」的注释
- 错误信息里要说清是哪个操作、哪个对象失败,并附上原始错误
- 写完在文件末尾附一条检查命令的实际输出,不允许只写「已通过」
后面这种叫可判定的规则:看一眼代码就能判断是否违反。要求写不成可判定的形状,智能体就只能猜,猜错的概率不比不写低多少。
检查点比标准再往前一步,它说明在哪一步停下来核对。例如「改完后端先跑一遍测试,全部通过再动前端」「截图要包含整个页面,只截局部不算」「测完把自己启动的进程关掉,不要按进程名去杀」。最后这一条尤其值得记:按进程名去杀别的程序,会把别人开着的服务一起杀掉,也会把你自己刚启动的证据一起清掉。
五、配置文件:放在哪、写什么、为什么比口头交代可靠
技能解决「某类任务怎么做」,配置文件解决「这个项目是什么」。刚装好的智能体对你的项目一无所知,它用的是训练时见得最多的做法:装它最熟悉的那个包管理器,把文件放在它觉得顺手的目录,检查敲的是它猜出来的命令。你们项目原本的约定,一条都没被遵守。
配置文件就是一个普通的纯文本文件,最常见的名字是 AGENTS.md 和 CLAUDE.md,放在项目根目录,每次会话自动加载,和 README.md 并排。README 写给人看,介绍项目、贴截图、讲怎么上手;配置文件写给智能体看,讲命令、约定、边界和坑。
里面写四块就够。
| 块 | 写什么 | 解决什么麻烦 |
|---|---|---|
| 常用命令 | 安装、启动、测试、检查,四条能直接复制的完整命令 | 它不会用这个项目 |
| 目录地图 | 三到五个重要文件夹,每个后面一个说明 | 它不知道该去哪找文件 |
| 约定 | 命名、目录习惯、必须配的东西 | 产出不符合规范 |
| 安全与完成标准 | 不许读的文件、不许改的配置、怎样算做完 | 它不知道边界在哪 |
# AGENTS.md
课程作业管理项目,只用 pnpm。
## 常用命令(在仓库根目录运行)
- 安装依赖:pnpm install
- 启动开发:pnpm dev
- 单元测试:pnpm test
- 类型检查:pnpm typecheck
## 代码库地图
- src/pages 页面
- src/components 组件
- tests 测试
## 约定
- 组件放在 src/components 下,一个文件一个组件
- 新增页面必须配一个测试
## 安全
- 不要读取或打印 .env 文件
- 不要修改 .github/workflows
## 完成标准
- pnpm typecheck 与 pnpm test 全部通过
为什么要写「只用 pnpm」这种看起来很小的事?智能体的默认选择来自训练数据里最常见的做法:看见一个前端项目,它可能顺手就敲 npm install。你不写清楚,它就去猜;猜错了就要多一轮「报错、纠正、重来」,严重的时候还会装出两套依赖,把项目弄乱。
写进文件,比每轮口头交代可靠在哪
口头交代只活在当前这一轮对话里。文件不一样:它默认进入每一次新会话,是所有定制手段里唯一一个「不用你开口就在场」的东西。它还多了三个好处:能被同学看见,能留版本记录,能被评审和修改。
但文件有一个硬限制:它占的是模型的指令预算。有人根据一组实验给出的经验法则是,前沿的思考型模型大约能稳定遵循 150 到 200 条指令,而外壳的系统提示本身已经占掉约 50 条。另一项研究用 500 条指令测了 20 个模型,即使最好的模型准确率也只有 68%。你写进去的每一条,都在和别人抢位置。
| 来源 | 对根配置文件的长度建议 |
|---|---|
| 课程材料 | 300 行以内 |
| 一家模型厂商 | 根文件 200 行以内,指定一个负责人 |
| 一家工程团队 | 业界共识 300 行以内,他们自己的根文件不到 60 行 |
| 一款命令行工具 | 合并后的项目文档默认上限 32 KiB |
还有一个容易踩的坑:智能体读到配置文件时,同时会收到一句类似「这些内容可能与你的任务无关,除非高度相关否则不要理会」的提醒。文件里跟当前任务无关的段落越多,模型越可能把整份文件当成「大概无关」而淡着处理,连其中真正重要的规则一起漏掉。写进去的每一行都在向模型证明自己值得被读。
六、层级关系与常见写错法
配置文件不止一层,越往下越具体。以一款常见的编辑器智能体为例,它从四个地方读取。
| 层级 | 位置 | 谁能看到 |
|---|---|---|
| 托管策略 | 系统统一安装目录下的配置文件 | 组织内所有人,个人设置无法排除 |
| 用户级 | 你的用户目录下的配置文件夹 | 只有你,对你所有项目生效 |
| 项目级 | 仓库根目录的配置文件 | 全队共享,随项目一起保存 |
| 本地级 | 仓库里的本地配置文件 | 只有你,只在这个项目,不提交 |
加载顺序是从文件系统根一路往下到你的当前工作目录,逐级拼接,越靠近工作目录的文件越靠后读,也就越具体。工作目录以下的子目录文件不在会话开始时就加载,等到智能体真的去读写那个目录时才读进来,这叫按需加载。当前这一条请求的优先级最高,常驻指令排在它后面。
什么时候才需要子目录配置?只在目录之间真有差异的时候:不同的包管理器、自动生成的代码、单独的部署审批。子目录文件只写与根文件不同的部分,重复根规则等于白白占用窗口。
常见的写错法有四类。
- 写成一堆互相冲突的要求。文件前后两段,一段说只用 pnpm,另一段从别处抄来的示例里写着 npm install;一处说提交前必须跑测试,另一处说测试太慢可以先跳过。面对冲突它只会随机挑一个执行,你看到的结果时好时坏。
- 写得太长。把只在某个流程里用到的步骤搬到技能文件里,用的时候才读;把能被工具检查的规则交给检查工具;把领域知识放进文档目录,根文件里只留一句指路的话。
- 写进会变的东西。密钥、过时的目录清单、这周的任务笔记。前两样会让人误判权限边界,第三样下周就失效。
- 用一键生成命令把整份文件自动产出来就直接采用。配置文件是整个体系里影响面最大的一处,代码里一行坏东西影响一处实现,配置文件里一行坏东西影响每一次会话的调研、计划和实现。
七、钩子与子智能体:让检查自动发生,让脏活在别处做完
钩子是在固定事件上自动运行的一段命令或脚本,由外壳执行,跟模型记不记得、愿不愿意没有关系:事件发生,脚本就跑。事件的种类按节奏分三档:每个会话一次的有会话开始与会话结束;每轮对话一次的有你按下回车、它准备结束回答;每次工具调用一次的有调用之前与调用成功之后。
脚本用退出码表态:返回 0 表示放行,返回 2 表示「停下,不要做这件事」,其他非零值只报个错,流程照常往下走。最常见的两个用法:写完文件之后自动跑一次格式化,免得之后在自动检查里报错;准备结束回答之前跑一遍检查,不通过就返回 2 让它接着改。后一个用法要防死循环——脚本开头先看输入里表示「这一轮已经是因为钩子才继续的」那个字段,如果是,就直接放行。
子智能体是主智能体干活过程中派生出来的一个分身。它有自己的上下文窗口,可以自己去搜文件、读代码、跑命令,干完之后只把最后一段结论交回主会话。它解决的问题是上下文被中间过程塞满:让主智能体去「找出所有调用旧登录函数的地方」,它可能翻三十多个文件、跑十来条命令,这些过程对最后那句「一共 12 处,分布在 3 个模块」没有任何贡献,却会一直占着窗口。
写法是往约定好的目录里放一个 Markdown 文件:开头用三条短横线夹着的元数据(名字、用途说明、可用工具、用哪个模型),后面的正文就是它的系统提示。其中用途说明最关键,主智能体靠它决定什么时候自动派活,所以要写清「做什么」加「什么时候用」。子智能体可以并行运行,最多嵌套 5 层。代价也要说清:它从零开始收集上下文,会拖慢速度、多花 token;主智能体只看得到它的结论,结论错了你很难察觉。重要的结论,最好要求它附上证据——文件路径、行号、命令输出。
① 打开你的 AI 对话记录,往上翻,找一句你对它说过两遍以上的话。找不到就现在问它一个问题,看自己会不会补一句「记得先……」;
② 在桌面新建一个文件夹,名字只用小写字母和连字符,例如
check-homework;③ 用记事本在这个文件夹里新建
SKILL.md,注意保存时把「文件类型」选成「所有文件」,否则会存成 SKILL.md.txt;④ 文件里写三部分:三条短横线夹着的名字与用途、三步做法、一条完成标准。用途那一行把你说过的原话抄进去;
⑤ 保存,然后照着它写的内容自己动手做一遍。卡住的地方就是需要改的地方。