最近,DeepSeek 开源的 deepseek-harness (dsh) 实在是太火了,除了完善的harness 工程,其一切皆插件的原则也是值得我们学习的架构。而我在翻看源码的时候,翻到根目录下的 .agents/notes 时,发现了一套很有意思的设计。
用 Claude Code、Cursor 或者其他 Agent 写项目时,大家大概率都遇到过这几个抓狂的场景:
-
AI 的"短期记忆" :昨天在新 Session 里跟它反复对齐的架构约束,今天换个窗口它就忘光了,又开始按自己的套路写。
-
反复扯皮 :遇到某个边界问题,AI 兴冲冲给出一个"显而易见"的解法,而这个解法其实团队三个月前踩过坑且明确否决过。
-
文档写完即腐烂:传统的 RFC 或设计文档,写的时候全是"计划支持 X"、"预计下周重构 Y",代码一合并就没人管了,半年后全成了误导 AI 的过时垃圾。
dsh 仓库里这套 .agents/notes 机制,本质上就是给 Agent 和人类团队搭的一套随代码一同演进的"外置架构记忆" 。它通过目录流转和自动化脚本门禁,把"当初为什么这么选、放弃了什么"变成了可执行的契约。
一、 它是怎么划分 Note 的?
在 dsh 中,所有 Note 文件的路径规则非常简单明确:
Plaintext
{lifecycle}/{class}/YYYY-MM-DD-topic-title.md
整个目录由 生命周期(顶级目录) 和 领域分类(二级目录) 交叉构成:
Plaintext
.agents/notes/
├── proposed/ # 方案提案期(写给未来:准备做,但还没落地)
├── implemented/ # 代码落地期(写给现在:已经合入,真实代码的现状)
├── rejected/ # 方案否决期(写给后来者:被枪毙的路线,附带否决理由)
├── archived/ # 过时封存期(写给历史:陈旧的稳定决策,只读冻结)
├── AGENTS.md # 给 Agent 读的行为指导
└── README.md # 目录规范契约
1. 四个生命周期(Lifecycle)
-
proposed/:方案评审期。全部使用将来时态,记录痛点、预期方案、验收标准(Acceptance criteria)与潜在风险。 -
implemented/:已上线生效。必须全部使用现在时态 ,描述当前代码的真实逻辑,严禁残留"计划"、"预计"等推测性字眼。 -
rejected/:被明确否决的方案。在 Header 里写清楚一句话拒绝原因,永久保留。 -
archived/:通过脚本迁入的陈旧决策,迁入后计算哈希并只读锁定,防止干扰日常检索。
2. 六个封闭分类(Class)
为了防止大家建出一堆 temp/、misc/ 之类的模糊目录,dsh 用脚本直接锁死了二级分类,只允许这 6 种:
-
architecture:核心抽象、包依赖关系、跨模块通信契约。 -
feature:面向用户或模型的新能力、新 API。 -
bug-fix:疑难缺陷修复与事后复盘(Postmortem)。 -
simplification:删代码、收敛抽象、降低复杂度的重构(只做减法,不加新功能)。 -
process:CI 门禁、发版工具链、依赖管理规范(非业务运行时代码)。 -
testing:测试基础设施、单测/集成测试用例规范。
二、 为什么这么设计?几个很妙的细节
翻看他们的规范和 CI 脚本,能发现几个特别克制但非常实用的设计考量:
1. 为什么不用 INDEX.md?
很多知识库喜欢在根目录维护一个总索引表。但在多分支、多 Agent 并行开发时,每次提 PR 大家都去改这个 INDEX.md,极其容易造成 Git Merge Conflict 。dsh 直接扔掉中心化索引,路径本身就是天然分类,找文档直接靠目录层级或全局全文搜索。
2. 状态变更靠 git mv,而不是改文件里的 Tag
当一个提案被实现时,操作是直接把文件从 proposed/ 移动到 implemented/。这样在提 PR 和看 Commit Log 时,一眼就能看出"这次变更让哪个提案正式落地了"。同时,Agent 检索当前代码规范时,只需扫描 implemented/,省去解析大量元数据的 Token。
3. 强制记录 Alternatives considered(手下败将)
这是整套体系最精彩的地方。dsh 强制要求每篇 Note 必须写"被否决的备选方案以及为什么否决"。
代码本身只能告诉你系统"现在是怎么跑的",但讲不清"为什么不用更简单的做法 B"。一旦把"做法 B 为什么会造成内存泄漏/安全穿透"白纸黑字记在 Note 里,后续接手的同事或者 AI 就不会再脑抽跑去提一个做法 B 的 PR。
4. 转正时必须抹除"计划语态"(Kill Spec-speak)
提案合入 implemented/ 时,必须把 ## Proposal 改写为 ## Decision,把将来时改成现在时,同时删掉验收标准,换成得失分析(## Consequences)。这样避免了文档里永远留着一堆"未来打算做某某事"的空话。
5. 靠 CI 脚本做门禁,不靠人的自觉
光靠口头约定,时间一长格式肯定会变形。dsh 写了几个非常直接的 CI 检查脚本:
-
verify-agent-note-format.ts:检查文件头、强制校验必须有
Problem和Alternatives、只要在implemented/里搜到Proposal或Acceptance criteria直接 CI 报错。 -
agent-note-tree.ts:只要发现 6 个预设分类之外的目录直接拦截。
三、 落库后的 Note 怎么跑起来?
平时的人机协作流转可以概括为很顺畅的一条线:
Plaintext
[开工前] -> Agent 检索 implemented/architecture 与 rejected/ (避开暗坑)
[定方案] -> 在 proposed/{class}/ 起草 Note (理清 Alternatives)
[写代码] -> 完成业务实现与测试
[提 PR] -> git mv 到 implemented/{class}/ + 改写为现在时事实
[走 CI] -> 门禁脚本自动校验格式与时态
[过时后] -> 调用 skill 自动化归档到 archived/
四、 中文模板与实战案例
1. 翻译后的标准 Note 模板
Markdown
# Agent Note: <简明标题>
Status: implemented
## 问题背景 (Problem)
说明此前系统存在的问题,解释促成此次技术决策的初始动因。
## 决策现状 (Decision)
使用现在时态客观陈述当前已交付代码的实际设计与运行机制。
## 技术细节 (Technical Details)
记录当前代码的实际接口契约、时序逻辑或实现要点。
## 备选方案与否决原因 (Alternatives considered)
- **<备选方案 A>** --- 已否决: 记录放弃该方案的关键技术考量(如内存泄漏、安全穿透等)。
- **<备选方案 B>** --- 已否决: 记录为什么不选社区流行库(如维护停滞、ABI 冲突等)。
## 代价与后果 (Consequences)
客观记录本次决策带来的收益与妥协代价(比如:换取了类型安全,但每次新增接口多了几行样板代码)。
2. 真实演练示例:跨进程 IPC 通道注册机制
以客户端开发为例,落地后的 Note 类似这样:
文件路径:.agents/notes/implemented/architecture/2026-08-17-strict-ipc-channel-registry.md
Markdown
# Agent Note: 严格类型化的 IPC 通道注册机制
Status: implemented
## 问题背景 (Problem)
此前渲染进程与主进程通信采用动态字符串拼接方式,缺乏编译期类型校验,窗口销毁后未能及时注销监听器,导致多窗口切换时频繁触发内存泄漏与安全告警。
## 决策现状 (Decision)
所有 IPC 通信统一收口在 `src/main/ipc/registry.ts` 中显式注册。Preload 层仅通过 `contextBridge` 暴露由强类型白名单定义的 API 契约,严禁向前端暴露泛型透传的 `ipcRenderer.send(dynamicChannel)` 接口。
渲染进程窗口在触发 `before-unload` 时,由封装的 `useIpcListener` Hook 自动执行反注册流程。
## 备选方案与否决原因 (Alternatives considered)
- **封装全局泛型透传接口 `invoke(channel, ...args)`** --- 已否决: 虽然能减少样板代码,但完全绕过了安全沙箱的审查机制,且无法在主进程进行精确的生命周期回收。
- **引入第三方 RPC 封装库(如 electron-trpc)** --- 已否决: 该依赖引入了多余的 WebSocket 抽象层,增加了打包体积,且与现有的 Worker 线程模型不兼容。
## 代价与后果 (Consequences)
彻底根除了跨进程通信导致的内存泄漏与未授权 API 调用。代价是每新增一个 IPC 接口需要额外编写 5 行类型声明与胶水代码。
五、 如何在自己的项目中轻量引入?
如果想在自己的项目里试试这套体系,不必一开始就抄全套脚本,推荐分步跑通:
-
建目录 :在项目根目录建好
.agents/notes/{proposed,implemented,rejected,archived}。 -
立规矩(Prompt 注入) :在项目的
CLAUDE.md、.cursorrules或 Agent 系统提示词里加上一句: "修改核心架构或公共契约前,先查阅.agents/notes/;做出非直觉的技术决策时,必须在同一个 PR 中补充/更新对应 Note" 。 -
加个轻量 Pre-commit:写个几十行的小脚本,在 Git Hook 里校验一下 Note 文件头格式和时态,防止写跑偏。
-
Code Review 时多看一眼:Review PR 时除了看业务逻辑,顺带看一眼 AI 是否把"否决的备选方案"写到位了。
相关源码出处参考
对底层实现感兴趣的朋友,可以直接看 dsh 仓库里的这几个文件:
-
Note 规范文档 :.agents/notes/README.md
-
面向 Agent 的操作指引 :.agents/notes/implemented/AGENTS.md
-
分类树校验脚本 :scripts/agent-note-tree.ts
-
归档自动化工作流定义 :.agents/skills/dsh-archive-agent-notes/SKILL.md