AGENTS.md 只答三个问题:东西在哪儿、怎么跑、什么不能碰。 每一段都有「能不能拿来判」的硬要求,写在里头的话必须可判定。 Demo 里三段各查一遍:地图一个目录写错、入口三条真跑、禁区拦两条放一条。
从这一篇开始进第二部分:Harness 四件套。第一件是 AGENTS.md——给 Agent 看的项目说明书。
它不是给人看的文档。
人读文档会跳读、会猜、会问;Agent 不会,它按写的执行,没写的就当不存在。
这个差别决定了写法:少写「我们注重质量」这种话,多写「改完跑 python tasks.py test」。
Demo 造了一份真仓库和三段真说明书,
然后逐段检查:地图里的目录在不在、入口的命令跑不跑得动、禁区拦不拦得住。
结果是「三个目录在、一个不在」「三条入口都跑得动」「三条改动拦两条」。
一、三段结构
三段,顺序也有讲究:先说在哪儿,再说怎么动,最后说别碰什么。
| 段 | 回答的问题 | 典型条数 |
|---|---|---|
| 项目地图 | 东西在哪儿 | 三到六条 |
| 关键入口 | 怎么跑、怎么验 | 两条到四条 |
| 禁区 | 什么绝对不能碰 | 两条到五条 |
为什么这个顺序?因为它对应 Agent 的动作顺序:
先找文件、再跑命令、中途判断「这一步能不能做」。
你按它的动作顺序写,它读一遍就能用。
别写成一篇介绍。
「本项目是一个基于情绪识别的聊天应用,采用分层架构」——
这句话你知道有用,它读着也没错,但它不知道拿这句干什么。
换成「src/api/ 是后端,src/web/ 是前端」,它就能去找文件了。
二、关键入口怎么写
入口要写成「一条能粘进终端的命令」,不是「大概这样跑」。 区别在于:前者它照抄就能跑,后者它得翻译。 翻译就会翻错。
| 不要这么写 | 要这么写 |
|---|---|
| 用测试框架跑一下测试 | python tasks.py test |
| 启动开发服务器 | python tasks.py run |
| 检查代码风格 | python tasks.py lint |
Demo 里三条入口都真跑了一遍,全过。 这个「真跑一遍」值得当成习惯—— 入口写错是最亏的错:它照着跑、报错、然后自己换一条命令,接下来所有的失败你都会归因错。 还有一条建议:入口不超过四条。 跑、验、加一条「只跑一个文件」,就够了。 列十几条命令的说明书,它记不住,你也不维护。
三、禁区要写死
禁区的要求只有一个:能拿来判。
「注意数据安全」判不了——它不知道什么算安全。
「data/ 下一个字都不许改」能判:路径匹配就拦。
Demo 里拿三个改动去撞:
data/stopwords.txt 拦住、package-lock.json 拦住、src/emo.py 放行。
三条拦两条,是因为禁区写成了「路径匹配」的形状。
写禁区的三个常见维度:
不能改的目录(线上导出、历史包袱)、不能碰的文件(锁文件、密钥)、不能做的动作(不许加依赖、
不许改表结构)。
最后一条最容易漏:动作类禁区。
「不许新增第三方依赖」这一条,能挡掉很多「顺手装个库」的行为。
而这些行为往往是最难回滚的——装进去的包,你怎么知道它有没有被用上?
四、动手 Demo:三段结构:地图、入口、禁区,各查一遍
这个 Demo 造一份真仓库、写一份三段式 AGENTS.md,然后逐段验证。 地图查「目录真不真在」、入口查「命令跑不跑得动」、禁区拿三个改动去撞。 全部真跑,没有任何一项是摆样子。
secs = _sections(AGENTS_1) # 按二级标题切段
for it in secs[u"项目地图"]: # 地图:目录在不在
d = re.search(r"`([^`]+/)`", it).group(1).rstrip(u"/")
ok = os.path.isdir(os.path.join(base, d))
for it in secs[u"关键入口"]: # 入口:真跑一遍
cmd = re.search(r"`([^`]+)`", it).group(1)
code, out = py(base, parts[1], args=parts[2:])
实跑输出:
--------------------------------------------------------------------
三段结构:地图、入口、禁区,各查一遍
--------------------------------------------------------------------
① 先写一份 AGENTS.md,三段
--------------------------------------------------------------------
## 项目地图(4 条)
## 关键入口(3 条)
## 禁区(3 条)
三段加起来十二行。它不解释「为什么」,只写「是什么、在哪儿、不许碰什么」。
② 地图:写的目录是不是真在
--------------------------------------------------------------------
[在] src
[在] tests
[在] docs
[不在] services
三个在、一个不在。写错的那个目录会让它去翻一个不存在的地方,翻不到就自己猜。
! 地图最常见的问题不是写少,是写旧。
目录改了名、合了并,文件没人更新——而 Agent 会当真,按旧名字找地方。
③ 入口:写的命令跑不跑得动
--------------------------------------------------------------------
[能跑] python tasks.py run -> 退出码 0 | 听你这么说我也高兴
[能跑] python tasks.py test -> 退出码 0 | 通过 3 / 失败 0
[能跑] python tests/test_main.py -> 退出码 0
三条入口都真跑了一遍。
入口写错是最亏的:它照着跑、报错、然后自己换一条命令——你后面看到的失败,全是这一条引起的。
④ 禁区:拿三个改动去撞
--------------------------------------------------------------------
[拦住] data/stopwords.txt 落在 data/ 禁区
[放行] src/emo.py 不在禁区里
[拦住] package-lock.json lock 文件不许手改
三条里拦住两条。禁区的写法有个要求:要能拿来判,不能只表态度。
! 「注意数据安全」这种写法没法判——它不知道什么算安全。
「data/ 下一个字都不许改」能判:路径匹配就拦。禁区的每条都要写成这种形状,否则等于没写。
⑤ 三段各答一个问题
--------------------------------------------------------------------
| 段 | 回答的问题 | 写不好会怎样 |
|---|---|---|
| 项目地图 | 东西在哪儿 | 它去翻不存在的地方 |
| 关键入口 | 怎么跑、怎么验 | 它自己发明命令,失败归因全乱 |
| 禁区 | 什么绝对不能碰 | 它顺手改了线上导出的数据 |
地图四条,三个在、一个不在。
不在的那个是 services/——目录改了名,说明书没跟着改。
这是地图最常见的问题:不是写少,是写旧。
三条入口全跑通。
注意第三条「只跑一个文件」也验了一遍——它是最容易被写错的一条,
因为大家只记得住「跑全部」那条。
禁区三条拦两条。
package-lock.json 那条能拦住,是因为禁区写的是「任何锁文件不许手改」,
而不是「注意依赖版本」。
后一种写法拦不住任何东西。
最后那张表是这一篇的全部内容:三段各答一个问题,每段写不好都对应一类事故。
这三段加起来十二行,但它决定了后面所有任务对不对。
30 秒实验
- 给你的项目写三段,每段先写三条。 写完拿地图那三条去目录里对着看——大概率有一条已经过时了。
- 把你常用的命令粘成一行一行的。 粘完自己跑一遍,跑不通的就是写错的。
- 写三条禁区,都用「路径匹配」或「动作」的形状。 写不出来「能判」的,就别写。
常见误区
- 「AGENTS.md 是给人看的文档。」 人读文档会猜,Agent 不会。 没写的它当不存在——所以「注意质量」这种话对它等于零。
- 「写得越全面越好。」 全面等于没人维护。 十二行能维护,四十行三个月后就过期了。
- 「禁区写「注意安全」就够了,它应该懂。」 它不懂。 「注意安全」没有边界,它只能按自己的理解来——而它的理解跟你的不一样。
自测三问(点开对答案)
1. 三段为什么按「地图、入口、禁区」这个顺序? —— **因为它对应 Agent 的动作顺序:先找文件、再跑命令、途中判断能不能做。** **按它的动作顺序写,它读一遍就能用。** 2. 什么样的禁区写法算「能判」? —— **路径匹配或动作匹配。** **「`data/` 下不许改」「不许新增依赖」能判;「注意数据安全」判不了。** 3. 为什么入口要写成能直接粘的命令? —— **因为它不会「翻译」。** **写「用测试框架跑一下」,它得猜是哪个框架、哪条命令**——猜错就换一条, **然后你的失败归因全乱。**下一篇 6.2 讲分层:根目录和子目录谁覆盖谁,以及为什么「子只能加、不能减」。
- ☐ (本篇完,下一篇 6.2 根目录与子目录分层)