Agent Notes 与 AGENTS.md:用 AI 开发 AI 的规训
四状态设计笔记和给 AI 看的编码规范,团队立刻能抄。
先玩再讲。下面是 DSH 仓库 .agents/notes/ 目录的四个文件夹,数字是本地快照里的真实笔记数。点「播放」看一篇真实笔记怎么从 proposed 走到 implemented 再进 archived;切到「被拒路线」看另一篇怎么被否决后冻结。中间那排是格式门禁的体检项,每一步谁在把关看得一清二楚。
等着被检验的想法
与代码同步的活文档
防止重犯的疫苗
不许再碰的化石层
DSH 是一个大规模用 AI 写代码的仓库。AI 每次会话都是新的,人也记不住三个月前为什么否决过某个方案。于是同一个坏主意会被反复提出,同一段代码会被反复重构回去。文档写了没人更新,慢慢烂掉。
DSH 的答案是两份东西。一套会流转的设计笔记,叫 Agent Notes,记代码和文档装不下的两件事:为什么这么做,放弃了什么。一份给 AI 看的行为守则,叫 AGENTS.md,把仓库的硬规矩写成 AI 每次会话都会读到的标准指令。
先看笔记。每篇笔记的路径就是它的完整身份:{lifecycle}/{class}/yyyy-mm-dd-topic.md。生命周期是顶层文件夹,proposed、implemented、rejected 三个活跃状态加一个 archived 归档层;类别是嵌套文件夹,feature、bug-fix、simplification、architecture、process、testing 六种,封闭集合,多一种都会被门禁拒绝。本地快照里的数字:25 篇 proposed、506 篇 implemented、11 篇 rejected、142 篇 archived,每篇还配中文对侧文件和一份一致性记录。
然后是那条硬规矩,写在根 AGENTS.md 第 122 行:非平凡变更必须在同一个 PR 里新增或更新至少一篇笔记。什么算非平凡?改了行为、架构、跨包约定、流程工具、磁盘格式、协议格式,或者任何维护者日后可能重新审视的决策。只有纯机械的局部编辑才豁免。笔记跟代码走同一个评审、同一次合并,所以不存在代码先上、文档欠着这回事。
每篇笔记还必须有一节 Alternatives considered,列出每个真实的备选方案和落选原因。.agents/notes/README.zh.md 第 115 行的原话是「记录决策时不记录它击败了什么,就是在邀请反复争论」。这一节是防失忆的核心:下次有人(或 AI)提出同样的方案,翻开笔记就能看到它当年输给了谁、为什么。
状态就是文件夹笔记换状态就是移动文件加改 Status 行,两件事必须在同一个变更里完成,门禁交叉检查。proposed 转 implemented 时,Proposal 章节要改写成现在时的 Decision。
rejected 是疫苗被否决的提案冻结保存,结论写在 Status 行第一眼就能看到。保留有门槛:只有决策依据还能防住一种诱人且影响重大的错误才留,否则三个文件一起删。
archived 是化石指导价值降低的 implemented 笔记移入归档层后永久冻结:禁止编辑、翻译、移动、删除,manifest 只追加。历史是证据,改过的证据不能作证。
这套体系没有停在文档层面。scripts/verify-agent-note-format.ts 一共 94 行,是 doc-sync 门禁的一环,CI 每次都跑。下面这段是它的规则表:每个生命周期的 Status 行语法和必填章节。
const STATUS: Record<string, RegExp> = {
proposed: /^Status: proposed$/,
implemented: /^Status: implemented$/,
rejected: /^Status: rejected — .+$/,
}
/** Required `##` headings per lifecycle, beyond the universal `## Problem` opener. */
const REQUIRED: Record<string, string[]> = {
proposed: ['## Proposal', '## Acceptance criteria', '## Risks'],
implemented: ['## Decision', '## Consequences'],
rejected: ['## Proposal'],
}
scripts/verify-agent-note-format.ts,核对日期 2026-08-13。代码块保留源码原文。注意 rejected 的正则:Status 行必须带一行拒绝理由,光写个 rejected 过不了。规则表下面几行还有一个 BANNED_IMPLEMENTED 正则(第 36 行):已实施的笔记里不许出现 Proposal、Plan、Migration plan、Acceptance criteria 这类提案腔标题,因为 implemented 笔记描述的是现在时的事实,计划早就该兑现成决策了。
另一个反直觉的设计:这 684 篇活跃与归档笔记没有目录索引。目录树本身就是清单,检索靠文件夹加全文搜索。有人想建 INDEX.md?结构检查脚本遍历 .agents/notes/ 根目录时专门盯着这个文件名,一旦出现直接报错,错误文案把话说死:集中式的 Agent Note 索引被禁止,请浏览生命周期与类别的目录树,或全仓库搜索。同一个循环还顺手把生命周期集合钉成封闭集,任何不认识的顶层文件夹都会报「未知生命周期」,因为放错地方的笔记会对遍历隐身。
出处:scripts/agent-note-tree.ts 第 44 至 56 行的结构检查循环,核对日期 2026-08-13。
为什么禁索引?集中式索引是最容易腐烂的文档:每加一篇笔记都要记得更新它,忘一次就开始撒谎。删掉索引,腐烂的可能性就为零。设计理由本身也是一篇笔记,在 implemented/process/2026-07-19-remove-generated-agent-note-index.md。
再看另一半:根目录的 AGENTS.md,149 行,AI 每次会话都会加载。它的 Conventions 一节值得逐条抄。挑四条最有代表性的,出处都是根 AGENTS.md:
- 信任类型边界(第 115 行)。在类型化的同进程边界上信任 TypeScript,别为静态接口已经保证的值再写运行时校验和防御测试。校验只放在真正的边界上:配置解析、模型返回的 JSON、磁盘文件、进程与协议边界。
- 插件里不许硬编码可调参数(第 112 行)。随部署变化的选择必须是配置文件里可改的字段,一个
DEFAULT_*常量不算可配置。协议常量和安全不变量除外,那些就该焊死。 - 配置错了就大声失败(第 113 行)。能在加载时发现的错配就在加载时抛,不行也要在最早能解析的时刻抛,绝不静默跳过一个缺失的引用。
- 空 catch 必须署名(第 118 行)。原文:「An empty
catchnames what it swallows and why nothing else can reach it; keep thetryto one statement.」吞掉了什么异常、为什么别的异常到不了这里,都要写出来,且 try 块只许一条语句。
这些条目有个共同点:每一条都能被检查。要么门禁能查,要么评审者扫一眼就能判断违没违反。代码要优雅之类写了等于没写的口号,一条都没有。
文档本身也有门禁。词数预算:根 AGENTS.md 不超过 1600 词,超了 verify-doc-budgets 变红,要么把内容挪去它该在的层级,要么压缩(docs/AGENTS.md 第 57 行)。一个事实一个家:同一条规则只许有一个权威出处,别处只放链接(第 15 到 17 行)。双语配对:每份文档是英文、中文加一份 .i18n.yaml 三个文件,记录里存两侧的 git blob hash,改了任何一侧没重新确认配对,门禁变红(docs/i18n/README.md 第 10 到 11 行)。这些门禁统一由 pnpm run doc-sync 驱动,完整清单在 scripts/run-gates.ts。
Claude Code:闭源,决策记录散在博客、发布说明和代码注释里。还原源码里能看到一类很有价值的注释,比如 autoCompact.ts 第 67 到 70 行那条带 BigQuery 生产数据的断路器注释(见 压缩双路径那一课)。这类注释是嵌在代码里的微型决策记录,质量不低。只是它们没有状态、没有格式门禁、没法按生命周期检索,被否决的方案更是基本无处可查。
Grok Build:基于已核对的本地快照,仓库里没有等价的设计笔记目录,决策依据主要在模块注释和 commit 历史里。模块注释写得不错(每个 mod.rs 开头一句职责说明),但被否决的方案这个维度是缺失的。DSH 的 11 篇 rejected 笔记在三家对比里是独一份。
你的团队怎么抄?三步。1、在仓库里建 notes/ 加四个文件夹,文件名带日期和主题。2、定死格式:标题、Status 行、Problem 开头、Alternatives considered 必填,照上面那 15 行写个校验脚本挂进 CI,半天工作量。3、在你的 AGENTS.md 里立三到五条能被机器或评审检查的硬契约,从怎样的改动必须附笔记这条开始。数量别贪多,DSH 也是从少量规则长起来的。
给你的仓库写一份最小 AGENTS.md
只写五条。要求:每条不超过三行;每条要么能写成脚本查,要么评审者十秒内能判断违没违反;其中必须有一条规定什么样的改动必须附设计笔记。写完做个测试:把五条拿给同事看,问他们哪条没法执行。没法执行的删掉重写。
推演一次不合规的流转
某人把一篇 proposed 笔记直接 git mv 进 implemented/,没改 Status 行,也没把 Proposal 改写成 Decision。对照上面第 22 至 33 行的规则表,写出 verify-agent-note-format 会报出的每一条错误。再想一层:为什么门禁要求这两件事和移动文件发生在同一个变更里?