DeepSeek Harness · 工程方法論

Agent Notes 與 AGENTS.md:用 AI 開發 AI 的規訓

四狀態設計筆記和給 AI 看的編碼規範,團隊立刻能抄。

課程目標讀完你能說清三件事:一篇設計筆記怎麼用資料夾路徑表達自己的四個狀態、什麼樣的改動必須在同一個 PR 裡附一篇筆記、以及 AGENTS.md 怎麼把口頭規矩寫成能被門禁和評審執行的硬契約。看完就能照著給自己團隊搭一套。
互動演示 · 一篇筆記的一生

先玩再講。下面是 DSH 倉庫 .agents/notes/ 目錄的四個資料夾,數字是本地快照裡的真實筆記數。點「播放」看一篇真實筆記怎麼從 proposed 走到 implemented 再進 archived;切到「被拒路線」看另一篇怎麼被否決後凍結。中間那排是格式門禁的體檢項,每一步誰在把關看得一清二楚。

proposed/25 篇
提案,實施前評審
等著被檢驗的想法
implemented/506 篇
決策已交付
與程式碼同步的活文件
rejected/11 篇
否決後凍結
防止重犯的疫苗
archived/142 篇
永久凍結的歷史
不許再碰的化石層
格式門禁 verify-agent-note-format(CI 自動跑)
# Agent Note: 標題
Status 行與資料夾一致
## Problem 開頭
Alternatives considered 必填
點「播放」,看一篇真實筆記的完整流轉。
真實筆記抽屜(點標題看一句話摘要,都是倉庫裡能找到的原件)
邏輯拆解 · 為什麼 AI 寫程式碼的倉庫最怕失憶

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 行語法和必填章節。

scripts/verify-agent-note-format.ts第 22 至 33 行
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'],
}
原始碼快照說明:依據本地倉庫 deepseek-harness-master,核對檔案 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 · 給 AI 看的硬契約長什麼樣

再看另一半:根目錄的 AGENTS.md,149 行,AI 每次會話都會載入。它的 Conventions 一節值得逐條抄。挑四條最有代表性的,出處都是根 AGENTS.md:

  1. 信任型別邊界(第 115 行)。在型別化的同行程邊界上信任 TypeScript,別為靜態介面已經保證的值再寫執行時校驗和防禦測試。校驗只放在真正的邊界上:配置解析、模型返回的 JSON、磁碟檔案、行程與協議邊界。
  2. 外掛裡不許硬編碼可調參數(第 112 行)。隨部署變化的選擇必須是配置檔案裡可改的欄位,一個 DEFAULT_* 常量不算可配置。協議常量和安全不變數除外,那些就該焊死。
  3. 配置錯了就大聲失敗(第 113 行)。能在載入時發現的錯配就在載入時拋,不行也要在最早能解析的時刻拋,絕不靜默跳過一個缺失的引用。
  4. 空 catch 必須署名(第 118 行)。原文:「An empty catch names what it swallows and why nothing else can reach it; keep the try to 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 筆記在三家對比裡是獨一份。

OpenAI Codex:把約束寫成倉庫根 AGENTS.md 的硬性禁令,再配一份會把同一節重讀一遍的評審 skill,這條線比 DSH 更硬。缺的是另一半——沒有 rejected/ 目錄,某次每輪注入 git status 為何撤回,後來者只能去程式碼裡倒推。站內 把上下文治理寫進 code review 一課有逐條拆解。

你的團隊怎麼抄?三步。1、在倉庫裡建 notes/ 加四個資料夾,檔名帶日期和主題。2、定死格式:標題、Status 行、Problem 開頭、Alternatives considered 必填,照上面那 15 行寫個校驗腳本掛進 CI,半天工作量。3、在你的 AGENTS.md 裡立三到五條能被機器或評審檢查的硬契約,從怎樣的改動必須附筆記這條開始。數量別貪多,DSH 也是從少量規則長起來的。

課堂練習
01

給你的倉庫寫一份最小 AGENTS.md

只寫五條。要求:每條不超過三行;每條要麼能寫成腳本查,要麼評審者十秒內能判斷違沒違反;其中必須有一條規定什麼樣的改動必須附設計筆記。寫完做個測試:把五條拿給同事看,問他們哪條沒法執行。沒法執行的刪掉重寫。

02

推演一次不合規的流轉

某人把一篇 proposed 筆記直接 git mv 進 implemented/,沒改 Status 行,也沒把 Proposal 改寫成 Decision。對照上面第 22 至 33 行的規則表,寫出 verify-agent-note-format 會報出的每一條錯誤。再想一層:為什麼門禁要求這兩件事和移動檔案發生在同一個變更裡?

Takeaway:Agent Notes 用資料夾路徑承載狀態,用格式門禁保證每篇筆記都寫了「為什麼」和「擊敗了誰」,非平凡改動必須同 PR 附筆記。AGENTS.md 只收能被執行的硬契約。防失憶的關鍵動作只有一個:把被否決的方案連同理由一起凍結存檔。