本集对应 DeepSeek Harness 模块 T4 的一节课:「Agent Notes 与 AGENTS.md:用 AI 开发 AI 的规训」。
来源:xueai.miyang.cn(小山学堂 · 洛小山《学 AI 产品,从入门到精通》)
大规模用 AI 写代码的仓库最怕失忆。AI 每次会话都是新的,它不记得上个月否决过什么;人也记不住三个月前为什么否掉某个方案。两者叠加的结果是:同一个坏主意被反复提出,同一段代码被反复重构回去,文档写了没人更新、慢慢烂掉。
答案是两份东西:
| 能力 | 落点 | 一句话 |
|---|---|---|
| 路径即身份 | {lifecycle}/{class}/yyyy-mm-dd-topic.md | 路径承载状态、类别、时间、主题 |
| 四状态流转 | proposed / implemented / rejected / archived | 移动文件 + 改 Status 行,同一变更完成 |
| 六类别封闭集 | feature / bug-fix / simplification / architecture / process / testing | 多一种即被门禁拒绝 |
| 非平凡变更硬规矩 | 根 AGENTS.md 第 122 行 | 同 PR 必须新增或更新至少一篇笔记 |
| 防失忆核心节 | Alternatives considered | 必填,记录每个备选与落选原因 |
| 格式门禁 | scripts/verify-agent-note-format.ts(94 行) | doc-sync 一环,CI 每次跑 |
| 提案腔禁令 | BANNED_IMPLEMENTED 正则 | 已实施笔记不许留 Proposal/Plan 等标题 |
| 禁索引 | scripts/agent-note-tree.ts | 出现 INDEX.md 直接报错 |
| 文档门禁 | pnpm run doc-sync | 词数预算 / 一事实一家 / 双语配对 |
本地快照真实笔记数:proposed 25 / implemented 506 / rejected 11 / archived 142。
每篇笔记的路径就是它的完整身份:{lifecycle}/{class}/yyyy-mm-dd-topic.md。不用打开文件就知道它是什么。
| 生命周期 | 篇数 | 语义 |
|---|---|---|
| proposed | 25 | 提案,实施前评审 |
| implemented | 506 | 决策已交付,与代码同步的活文档 |
| rejected | 11 | 否决后冻结,防止重犯的疫苗 |
| archived | 142 | 永久冻结的历史,不许再碰的化石层 |
类别是封闭集合:feature、bug-fix、simplification、architecture、process、testing 六种,多一种即被门禁拒绝。原因在遍历——放错地方的笔记会对遍历隐身。
流转机制:换状态 = 移动文件 + 改 Status 行,两件事必须在同一个变更里完成,门禁交叉检查。proposed 转 implemented 时,Proposal 章节要改写成现在时的 Decision。时态变化逼着「我们打算这么做」变成「我们现在这么做」。
根 AGENTS.md 第 122 行:非平凡变更必须在同一个 PR 里新增或更新至少一篇笔记。
什么算非平凡:改了行为、架构、跨包约定、流程工具、磁盘格式、协议格式,或任何维护者日后可能重新审视的决策。只有纯机械的局部编辑才豁免。清单给的是判断标准,不是感觉。
关键在于笔记跟代码走同一个评审、同一次合并——这解决了文档体系最常见的死法:代码先上、文档欠着。欠着的文档永远不会补,因为它没有截止日期,也没有人会因为它在评审里打回你。
Alternatives considered 必填,列出每个真实的备选方案和落选原因。.agents/notes/README.zh.md 第 115 行原话:
记录决策时不记录它击败了什么,就是在邀请反复争论
只写「我们决定用 A」的文档防不住最该防的事——同一个坏主意被反复提出。必须写清备选 B、C 是什么、为什么落选。下次有人(或 AI)提出同样方案时,翻开就能看到它当年输给了谁。
rejected 是疫苗:被否决的提案冻结保存,结论写在 Status 行第一眼可见处。保留有门槛——只有决策依据还能防住一种「诱人且影响重大的错误」才留,否则三个文件(英文、中文、一致性记录)一起删。门槛很重要,否则目录会变成垃圾场。
archived 是化石:指导价值降低的 implemented 笔记移入后永久冻结,禁止编辑、翻译、移动、删除,manifest 只追加。
历史是证据,改过的证据不能作证。
改一份历史文档等于修改证据链。以后有人追溯为什么当年这么决定,看到的是被修饰过的版本。
scripts/verify-agent-note-format.ts 共 94 行,是 doc-sync 门禁的一环,CI 每次都跑:
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'],
rejected 的正则带 .+:Status 行必须带一行拒绝理由,光写 rejected 过不了——强制写下「为什么」,而非只记录结果。
BANNED_IMPLEMENTED 正则(第 36 行):已实施的笔记里不许出现 Proposal、Plan、Migration plan、Acceptance criteria 这类提案腔标题——implemented 描述现在时的事实,计划早该兑现成决策。
一篇笔记是不是真的走完了它该走的路,看标题就知道。留着提案腔标题 = 流转没走完,或走完了没清理。把「状态流转」编码进格式校验,比事后人工巡检可靠得多。
684 篇活跃与归档笔记没有目录。结构检查脚本遍历根目录时专门盯着 INDEX.md,一旦出现直接报错:
集中式的 Agent Note 索引被禁止,请浏览生命周期与类别的目录树,或全仓库搜索。
理由:集中式索引是最容易腐烂的文档——每加一篇都要记得更新,忘一次就开始撒谎。而一份会撒谎的索引比没有索引更糟:没有索引你至少知道要去搜,有索引你会信它,然后漏掉它没记的东西。删掉索引,腐烂的可能性就为零。
同一个循环还把生命周期集合钉成封闭集,任何不认识的顶层文件夹都报「未知生命周期」。这个禁令本身也是一篇笔记:implemented/process/2026-07-19-remove-generated-agent-note-index.md。
根目录 AGENTS.md 共 149 行,AI 每次会话都会加载。四条最有代表性的约定:
| 条目 | 行号 | 内容 |
|---|---|---|
| 信任类型边界 | 115 | 同进程类型化边界上信任 TypeScript,别为静态接口已保证的值写运行时校验;校验只放真边界 |
| 不许硬编码 | 112 | 随部署变化的选择必须是配置文件可改字段,DEFAULT_* 常量不算可配置 |
| 配置错大声失败 | 113 | 能在加载时发现就在加载时抛,绝不静默跳过缺失引用 |
| 空 catch 署名 | 118 | 吞掉什么、为什么别的异常到不了这里都要写,try 块只许一条语句 |
「信任类型边界」这条的价值在于它同时说了两件事:哪里不用写,以及哪里必须写。只说一边的规范等于没说。
共同点:每一条都能被检查。 要么门禁能查,要么评审者扫一眼能判断。像「代码要优雅」这种写了等于没写的口号,一条都没有。
verify-doc-budgets 变红(docs/AGENTS.md 第 57 行)。.i18n.yaml 三个文件,记录存两侧 git blob hash,改一侧未重新确认配对即变红(docs/i18n/README.md 第 10 至 11 行)。统一由 pnpm run doc-sync 驱动,完整清单在 scripts/run-gates.ts。文档纪律不是靠自觉,是靠 CI 变红。
写 AGENTS.md 时给自己的测试:把条款拿给同事看,问他们哪条没法执行。没法执行的删掉重写。数量别贪多——DSH 也是从少量规则长起来的。
| Claude Code | Grok Build | OpenAI Codex | DeepSeek Harness | |
|---|---|---|---|---|
| 决策在哪 | 博客/发布说明/代码注释 | 模块注释 + commit 历史 | AGENTS.md 硬性禁令 + 评审 skill | Agent Notes 四状态 |
| 微型决策记录 | 有(如 autoCompact.ts 断路器注释) | 有(mod.rs 开头职责说明) | — | 有 |
| 有状态流转 | 无 | 无 | — | 有 |
| 格式门禁 | 无 | 无 | — | 有 |
| 被否决方案可查 | 基本无处可查 | 该维度缺失 | 无 rejected 目录 | 11 篇,三家独一份 |
Codex 的约束线比 DSH 更硬(硬性禁令 + 会重读同一节的评审 skill),缺的是另一半——没有 rejected 目录,某次改动为何撤回,后来者只能去代码里倒推。
notes/ 加四个文件夹,文件名带日期和主题。{lifecycle}/{class}/yyyy-mm-dd-topic.md?Alternatives considered 是不是真的列了具体备选与落选原因,而非「我们考虑过」?dsh20-播客.mp3dsh20-播客.srtscript.txt本内容改编自小山学堂《学 AI 产品,从入门到精通》,为二次演绎配音版。
来源:xueai.miyang.cn(小山学堂 · 洛小山)