AIGC标识 如何构建一份让项目可持续维护的 AGENTS.md

你的仓库里现在住着一位新同事。

他读过全网的代码,聊什么都头头是道;他从不请假,一天能改三十个文件;他对你写的每一行注释都怀有真诚的敬意——前提是他能看懂。看不懂的时候,他会非常客气地把它们删了。

这就是今天的开发现实:AGENTS.md 不是写给未来人类同事的 README,而是写给这位实习生的入职须知。而实习生的悲剧永远是同一个——他不是不听话,他是太听话了,认真地把好事做坏。

我最近读了一份写得相当好的 AGENTS.md(285 行,来自一个叫 NextCoWork 的 Electron 项目),它几乎回答了「怎么给 agent 立规矩」这个问题的全部。这篇文章拆开讲讲,凭什么它好。

一、别讲理念,讲症状

文档开头有一句话,基本就是整份文件的方法论:规则按「违反了会出什么事」来写。

对比一下这两种写法:

❌ 记得在 useEffect 的 cleanup 里清理事件监听器。

✅ on() 的返回值必须进 useEffect 的 cleanup。漏一个,HMR 每次热更叠一层监听器,表现为一次状态更新触发 N 次重渲染,只在 dev 出现,极难定位。

左边是理念,右边是症状。理念读完就忘,症状会留在脑子里——因为它描述的是一段具体的、糟心的、可以想象的排查经历。

写每条规则的时候,问自己一个问题:违反它的那天,屏幕上会出现什么?把那个画面写进规则里。「且零报错」这种话尤其值钱,因为它预告了 agent 最害怕的场景:一切看起来都是对的。

二、给规则配上坐标和数字

这份文档里到处都是 services/sessions.ts:18、App.tsx:139 这样的地址。这不是炫技,是给 agent 一条可以自己走过去的路——他不用猜「范例大概长什么样」,直接打开看。

数字同理:「components/ui 已有 19 个原子控件」「渲染层 any 出现 0 次」「26 个 tsx 超过 400 行,最大 2291 行」。数字干两件事:一是可验证,agent 写完能自己核对有没有越线;二是可守线,「0 次」是一条清清楚楚、越过去就知道自己越过去了的线。

「尽量避免 any」则什么线都不是。

三、主动交代烂账,否则他会替你「顺手修好」

文档里有一节叫「已知违规」,把几个越界 import、四处裸写的频道字符串一一点名,后面跟着一句关键的话:

碰到那块代码时顺手修,不要专门开一轮重构。

还有一节技术债清单,细到「有个空的重复目录,见到可删」。

为什么这个动作重要?因为 agent 看到代码不符合文档时,默认行为就是顺手统一一下风格。你不写明「这账我知道,先记着」,他就替你开一轮你从没批准过的重构,然后在回复的末尾用一句话带过。烂账清单的本质不是自曝其短,是取消他自作主张的授权。

四、立法,要冲着他的真实弱点去

这份文档最有味道的部分,几乎全是冲着 LLM 的习惯性失误写的:

「不做防御式 UI——画出来的每个控件都是一次会失败的承诺。」「别为了消掉 React 警告挂 index key。」「改既有文件一律用精确替换,整文件写回去等于把你读取之后别人做的所有改动删掉。」「一条你看不出用途的注释,默认它比你知道得多。」

这些规则有个共同点:人不常犯,agent 常犯。人会犯的错,ESLint 管得住,code review 管得住;agent 会犯的错——过度重构、覆写并发改动、删除看不懂的注释、用防御式 UI 弥补后端的无能——只有这份文档管得住。

写 AGENTS.md 之前,先回忆上一个 agent 把你哪个仓库改崩过。那份回忆就是目录。

五、写理由,别只写结论

判空写显式比较:x !== null / x !== undefined / flag === true。理由是这个代码库里 ''、0、false 全是有意义的值。

结论只约束文档写到的场景,理由会自己走到文档没写到的场景里去。下次他写一个你当初没想到的判断,那句「0 是有意义的」会自己生效——这是理由相对结论唯一的、但致命的优势。

同一个逻辑,文档里还有一条更狠的:看起来多余的代码,必须注释为什么不多余。空判、去抖、多出来的一次 await,全是最容易被后来者当垃圾清掉的东西,清掉之后的 bug 往往要几周才复现。不写理由,你的代码就活在「随时被善意简化」的风险里。

六、规则会打架,所以要有终审法官

文档的最后一行是这样的:

最后一条,优先级高于上面全部:只做被要求的改动,且破坏面最小。

规则一多必然冲突——「架构优先于先跑起来」对上「最小 diff」,听谁的?不给裁决顺序,agent 会自己挑一个,而且每次挑得不一样。一句话就够,但必须有。这也顺带定义了这份文档的价值观:怀疑一切「顺手」。

七、把踩过的坑,写成坑的位置

「新增 locale 要同时改:SUPPORTED_LOCALES、两份 catalog、设置项校验、语言选择器,然后跑 typecheck + test。」这种联动清单,代码里完全看不出来,只能靠文档传承。

比清单更值钱的是验证命令本身的陷阱:

绝不手敲 tsc -p tsconfig.web.json(不带 --noEmit):产物会落在源码旁边并盖住源码,dev/build/vitest 从此加载旧版本且零报错。

这种句子不是「最佳实践」,是事故报告。一次事故换一行文档,这才是 AGENTS.md 里含金量最高的资产。你踩过的每一个坑,都值得写成坑的坐标。

八、文档会过期,让代码注释接手

最后这点最容易被忽略:AGENTS.md 会烂。这份文档自己就承认「多处注释指向已不存在的 docs/」。

所以它做了个聪明的设计——要求新代码带需求注释:这块代码为了满足什么需求而存在,不满足会出什么症状。再配上一条「别人的注释是事故现场,不读懂不准删」。于是 AGENTS.md 是宪法,代码注释是判例法:agent 走到哪个文件,哪个文件就有当地规则,随代码生、随代码灭,不指望有人回来维护那份中央文件。

带行号、带计数的段落也一样:改代码的那天,记得回文档同步。文档里每个 :18 都是一根引线。

写完之后,自查五问

  1. 每条规则背后有症状吗?
  2. agent 能顺着 file:line 自己核对吗?
  3. 仓库的烂账交代了吗?
  4. 规则冲突的时候,谁赢?
  5. 半年后这段还成立吗?谁负责回来改它?

五个问题里但凡有一个答不上来,那一条就还不能进文件。AGENTS.md 不是宣言,是一份持续更新的事故汇编——谁把它写得像宣言,谁的仓库就会用一种非常文明的方式慢慢烂掉,全程零报错。


案例来源:/Users/token/Desktop/code/NextCoWork · AGENTS.md(285 行)· src/renderer/src/**

要落成 .md 文件的话说一声。

posted on 2026-09-24 20:03  239573049  阅读(16)  评论(0)    收藏  举报