夜雨聆风学习资料网

ARTICLE · 1221363

让开发文档成为可追溯的Agent记忆

让开发文档成为可追溯的Agent记忆

今年 AI 圈最火的一个词一定是 Harness 。

基于 Harness 工程理念打造的 Agent,例如:codex、claude code、deepseek harness 已经让大众真实的感受到了 AI 的"魔力"。

我相信在众多对 Harness 的科普中你一定已经对它是什么有了深入的了解,但我很少看到有人讲:它对普通使用者意味着什么。

大多数人会认为,不开发 Agent、不训练模型,Harness 跟自己没什么关系。在我看来这是个挺大的误解,因为只要你使用AI,你每天和 AI Agent 打交道的那个工作区(Workspace),本身就是 Harness 的一部分。

大家应该都见过这个公式:

Agent = Model + Harness

我想再拆一层,来分解 Harness :

Harness = 记忆 + 边界 + 组合

  • • 记忆:提供“项目目标 + 已知事实 + 当前状态 + 关键历史”,让系统在多步过程中保持一致性与可追溯性
  • • 边界:规定“允许的动作/权限范围/安全与成本限制”,以及“触发人工确认或必须停止的条件”
  • • 组合:把工具与步骤按流程编排成可执行的工作流,支持模块化替换、失败重试与降级

如今,模型的能力已经非常强大,大厂开发的各种 Agent 也提供了开箱即用的编排与工具能力。在这种情况下,记忆的管理往往更容易被忽略,但它不仅是影响最终产出质量的重要因素,并且是普通使用者最能主动掌控的一环。在同一套模型与 Agent 下,谁提供了更关键、更完整、且被正确带入后续步骤的“记忆”,谁就更容易得到稳定高质量的结果。

回看 Agent 工程的演进,从提示词、上下文压缩,到 Skill,再到 Harness,它们解决的问题并不完全相同,但始终绕不开的核心就是解决“记忆”的问题:如何让 Agent 在有限的上下文里,知道该知道的事,记住该遵循的规则,并在持续工作中不偏离目标。

这篇文章想要讲的就是普通使用者该如何优化Agent的“记忆”。

记忆不在模型里

首先要理解,模型的每一次调用都是全新的。它本身没有记忆,它能"记得",是因为 Harness 体系要求在每次开工前,把"记忆"作为一段上下文重新喂给了它。

这段上下文有两部分:

  • • 短的那部分:这次对话刚才说了什么,跑到第几步
  • • 长的那部分:这个项目是什么、规矩是什么、之前踩过什么坑

短的那部分,由开箱即用的 Agent 自己管理。而长的那部分,来自你的工作区。

你放在工作区里的一切:README、架构说明、设计记录、踩坑笔记、AGENTS.md,或者你不写代码那么要处理的文件和表格——只要 Agent 读得到,它们都会变成模型这一次开工时的"长期记忆"。

只有"可追溯"的东西才算记忆

看到这里你可能会想到一个问题:如果模型读到的是"坏"文档呢?

散落的、互相矛盾的、三年前的、没人维护的文档,喂给模型,它只会更自信地犯错。

更别说一个项目持续使用 AI 辅助开发后,很容易积累数百份文档:需求、实现计划、任务清单、修复分析、评审、二次评审、验证记录,以及临时讨论。它们通常有真实价值,却不一定构成可靠的"记忆"。

某个功能在旧版本目录里有初版方案,在另一个版本目录里有重构计划,最新结论藏在评审修复记录中。旧文档可能同时写着"草案"和"功能已移除"。人需要回忆当时发生了什么,Agent 则可能把最容易搜索到的文件当成当前要求。

我发现问题出在文档之间的"关系"没有建立起来。 谁是谁的前置,谁在哪个版本里推翻了谁,哪一份是不可违背的设计决策,这些关系都没有作为文件的属性被记录下来。

所以我们需要一套规则来建立和维护这些文档的关系,并且通过 Skill 让 Agent 在合适的场景自动去维护、更新这些记忆。

在我的开发实践中,我总结出了这样一套体系。我把需要维护的关系确定为 4 种:有效性、归属、替代关系和证据。

你要回答的问题
要维护的关系
判断方式
该读哪一份?
归属
它属于哪个领域、哪次变更
它还有效吗?
有效性
现在还算不算数
凭什么相信它?
证据
谁能证明它现在还是这样
谁取代了谁?
替代关系
为什么换,换成了什么

这样的项目记忆保存在仓库里。它不依赖某个会话保留全部历史,也不等同于让模型永久记住项目。每次工作开始时,Agent 都能重新加载足够小、足够明确的上下文,并沿链接追溯依据。

换句话说,可追溯不是“文档写得好”,而是让这四种关系成为文件本身能查到的属性:打开一份文档,就该能看出它属于哪里、还算不算数、凭什么成立、被谁取代过。

接下来我们聊聊具体的做法,怎么把这些属性真的写进文件,并且让 Agent 在每次开发里替你维护。

一、六类文档作为“关系”的落点

建立“关系”的第一步,是给文档分类,给它们一个“身份”作为关系和管理的落点,其次不同类型的文档,"有效"的定义也根本不同。

在我看来开发文档至少要回答六类不同的问题。

问题
载体
维护方式
产品现在应该怎样工作?
当前产品规范
随已完成变更更新
系统现在怎样实现这些行为?
当前架构契约
随实现核验和更新
这一次准备改变什么?
变更记录
开发中更新,完成后保留
为什么选择这个方案?
决策记录(ADR)
保留理由,链接替代决策
什么实际进入了哪个版本?
发布记录
根据发布证据登记
过去发生过什么?
历史材料与验证证据
保留原意,可追溯

最重要的边界是"当前规范"和"某次变更的 spec"。

  • • 前者是持续维护的产品契约;
  • • 后者记录当时的改变目标。

不能要求后续读者从所有历史 spec 中自行合成当前规则。

同时,代码事实和产品要求也不是同一件事。实现与已批准要求不一致,可能是 Bug。应记录偏差,而不是为了让文档与代码相符,悄悄把需求改成现有行为。

二、当前知识按「领域」组织,开发过程按「变更」组织

一种适合长期维护的中大型规模仓库的结构如下:

小项目可以只保留一个规范文件、一个架构文件和变更记录;没有独立平台发布就不必拆发布目录。

这里有两个注意点:

  • • 不建议把版本号作为开发材料的主要归属。一个变更可能延期,也可能分平台发布;如果它的身份绑定版本目录,延期就会导致搬家、断链或多份副本。版本更适合作为引用变更的发布视图。
  • • 也不必把 features、bugs、reviews、fixes 拆成彼此隔绝的顶层仓库。一次功能交付里的评审与修复应留在同一个变更中。独立审计可以单独建档,再关联它产生的修复事项。技术债同样是一种待处理变更,不需要另建第二套待办状态。

三、让事项身份稳定,让状态诚实

每个变更有一个稳定 ID,例如 CHG-2026-001。ID 不随发布延期或完成状态改变,已经分配的 ID 不再复用。多人(多worktree)并行开发合并代码时唯一ID也可以帮助Agent快速定位文档冲突。

当然也可以直接使用已有 Issue ID;顺序编号并不是唯一选择。

这个体系中最重要的是动手新建一条变更记录之前,让Agent先搜一遍。

例如,新建的是 docs/changes/2026/CHG-2026-NNN-<slug>/README.md。那么搜索就是在 docs/changes/、docs/specs/、docs/decisions/ 里 grep 相关的关键词并查看和维护变更的“摘要” docs/changes/index.md。

变更具体 changes 的 README 只维护必要的元数据:

状态词表必须有明确语义。

以我开发中的实际情况为例:可以从 proposed、planned、in-progress、blocked、done、cancelled 开始;历史迁移确有需要时再加入 unknown 和 superseded。

几个容易混淆的边界:

  • • done 表示约定范围内完成验收,不代表已经发布。
  • • unknown 表示历史证据不足,不是"看起来应该做完了"。
  • • 已经交付的方案后来被替代,原变更仍然完成;旧规范可以标记废弃并指向新规范。
  • • 未完成方案被另一方案取代,才适合使用 superseded,并给出替代关系。

四、架构文档

架构文档需要分层:

  1. 1. 摘要:系统边界、跨领域不变量、阅读入口。它帮助 Agent 判断接下来该读什么。
  2. 2. 详细合约:当前有效的所有权、数据流、并发边界、失败处理和平台差异。修改对应领域时读取。
  3. 3. 历史证据:旧方案、实施经过、当时的验证结果。需要解释过去时读取。

总体架构文件保留模块模块划分、依赖方向和跨域关系,领域文件维护具体合约。产品规范描述外部可观察行为,避免复制整套内部实现细节。

“摘要”的要点是:帮助选择阅读范围。把长文删到很短,并不自动得到好的摘要。把二十多个标题合成八个标题,也不会降低实际阅读成本。

五、引用应该跟随语义,而不是章节序号

具体文档中的章节号是展示顺序,不适合成为长期地址。一次更新导致内容重排后,全仓的"见 §9.3"都可能失效。

更稳定的引用形态是文件路径与语义锚点,例如 architecture/editor.md#editor-document-ownership。可以用英文 slug 标题作为稳定标识,在其后补中文解释;具体渲染行为要在实际工具中验证。

整理旧引用时,先建立对照:旧文档与位置、原规则、目标文件与锚点、处置说明。需要验证的是原规则是否有去向,而不是新文件里一共有多少条目。

当前文档、Agent 指令和代码注释中的有效引用应更新。历史记录通常保留当时语境,通过映射解释旧位置。确需修复历史链接时,只修引用,并记录原因,不顺手改写当时的结论。

六、项目记忆需要证据

这里的关键认知是:"类型名还能搜到"和"规则仍然成立"之间有明显距离。

核验方式
能支持的结论
不能单独证明的结论
路径存在
文件可定位
内容仍有效
符号存在
类型或方法仍存在
它仍承担文档描述的职责
阅读相关代码
某个边界或分支的实现
所有运行时场景都正确
自动化测试
覆盖场景下行为符合断言
未覆盖场景也正确
人工验证
特定环境与步骤下的真实表现
其他平台和环境同样成立

在实践中我认为有以下几个要点:

  1. 1. 记录证据时,优先给出 commit、文件与符号,以及必要的测试或复现步骤。
  2. 2. 行号可以辅助定位,但单独的行号会随着修改漂移。
  3. 3. 尚未核验的部分明确标注,不能因为整份文件移进了 architecture 目录,就宣称其中所有断言已经核验。

另外,不必每次都重审整个知识库。应优先核验高风险领域和本次实际修改的规则,逐步扩大覆盖。对大规模历史文档,这是比一次性补齐所有证据更可持续的做法。

七、用脚本检查规则的执行情况

规范写得再清楚,没人照着做也等于没写。而"照着做"这件事,Agent和人都可能疏忽。所以我们通过脚本来检查规范的执行情况。

这类脚本只查四类东西,全是机械判定:

查什么
典型错误
变更、ADR、规范、发布记录的元数据
status: done
 但正文没有验证记录;supersedes 指向不存在的 ID
仓库内链接和反引号里的路径
[规范](../Documentation-Guide.md)
 指到不存在的文件
索引与实际文件是否一致
新建了变更但忘了登记;或者索引里有、目录没了
迁移清单与实物是否对应
登记"已迁走"的文件还留在原地

这些规则的共同点是:对错不需要判断,只需要比对。 字段在不在、值在不在词表里、 文件在不在、链接打不打得开。

它输出的是退出码而不是报告:0 没问题,1 有问题,2 参数错。所以挂进任何脚本或者 CI 都行。

还有一个很实用的机制叫基线:把已知的旧问题记在文件里,之后只对新增的问题报错。这样你不用为了启用它,先花两周修完所有历史欠账。

脚本检查的局限

脚本检查最容易给人的错觉,是它绿了你会以为文档没问题。

在真实的开发中,我遇到过4种在最开始的设计的脚本检查中漏掉的问题:

  • • 模糊匹配掩盖断链。 按文件名辅助搜索对代码风格里的定位提示有用,但真正的 Markdown 链接必须按它的解析语义走。文件所在目录没有 README,而仓库根目录恰好有个同名文件,不代表这个链接能打开。
  • • 示例标题不是真实锚点。 直接用正则扫所有井号标题,围栏代码块里的示例也会 "通过"锚点检查。
  • • 按数量冻结债务会漏报替换。 同一个文件修掉一条旧断链、同时新增一条,错误总数不变,但问题变多了。
  • • 索引和交接也会过期。 某个优化已经完成,README 的交接区却还写着未开始。这种矛盾对 Agent 伤害最大,因为交接区往往是他最先读的东西。

所以在使用脚本检查时要有意识到:它是回归护栏,不是质量证明。并且也需要查漏补缺持续迭代。

八、AGENTS.md、Skill、治理规范各司其职

这里需要警惕的是三种载体最容易被写成三份重复的规则。

治理规范 docs/Documentation-Guide.md是你项目自己的规范。例如,目录结构、文件命名、状态词表、完成条件的定义、检查命令、阅读预算。当然它也不是必须的,如果小型项目可以在AGENTS.md、docs的readme中直接定义这些规则。

Skill 则回答"如何执行新建、继续、评审、迁移和收尾"。

写 Skill 时有两条容易被忽略的约束:

  • • 不要假设目标项目已经有某个文件名、目录、标识符或检查器。 先读它现有的 AGENTS.md 和文档入口,再沿用既有约定。
  • • Skill 应引用现有规范,按当前任务加载相关记录,调用仓库里真实存在的检查命令。 它不应复制全部架构。
  • • 它不应把所有内容都强制扩展成五份文档。小事项一份 README 足够;复杂事项再拆 spec、plan、tasks、verification。流程的价值来自减少遗漏,而不是增加填写量。

九、验收成果

最后,当你搭建好这套体系后,最重要的是设计场景验证这些规范是否能被Agent自动的执行和遵守。同时在持续的开发中和你的Agent交流持续完善属于你项目的真实细节。

用一次普通开发检验整个体系

假设我现在要修复富文本编辑器撤销错误。你应该看到:

  1. 1. Agent 先从文档入口找到当前编辑器规范与架构合约,再搜索相关历史变更。已有进行中的同一事项就继续维护,否则建立新的变更记录。
  2. 2. 修复过程中,计划和任务说明准备做什么,验证记录说明实际测试了什么。
  3. 3. 结束时,更新当前合约中的受影响规则;若没有改变系统边界,就不往架构摘要追加实现经过。留下一个短交接快照,链接实际证据和未解决事项。

一次任务之后,新建一次Agent对话,再修改同一需求的细节,这一轮对话中Agent应能迅速回答:现在的规则是什么,为什么改过,代码在哪里,验证到了哪里。

这四个问题,本质上就是 Agent 每次开工时要去回答的四个问题。 它们比目录数量、文章行数或文档总量更适合作为治理成效的判断依据。

改动边界检验

这张表总结的是各种“改动”操作的边界:什么该做,什么可以不做。

改动
动作
内部实现调整,不影响已文档化的行为或合约
可以不改文档,但要说明具体理由
局部行为、配置或领域规则变化
更新拥有这条规则的当前规范或领域合约
模块职责、依赖方向、数据合约、并发边界、生命周期或跨域策略变化
核验并更新受影响的架构合约;总览和摘要只在模块结构、全局不变量或导航变化时才动
新增、移动或删除文档、模块入口
更新受影响的索引与引用
长期取舍、备选方案或决策理由变化
按项目约定记录或扩展决策记录

写在最后

回到开头那个问题:Harness 跟普通使用者有什么关系?

关系在于,你可能改变不了模型的智力,也可能没得选 Agent 用哪套工具。但在 Harness 的记忆方面,工作区里放什么、怎么放,是你说了算。你把它当垃圾场,它就是垃圾场;你把它整理成一份可追溯的知识库,Harness 每一次开工,都能准确的拿到需要的信息。

所以让开发文档可追溯,从来不是"给团队看"的规范问题,也不是为了让目录看起来整齐。

你是在给 Agent 装记忆。

One More Thing:三个开箱即用的Skill

我把文章介绍的这套经验整理成了三个开箱即用的 Skill,开源在了我的github,点击「阅读原文」即可跳转:

Skill
用来做什么
什么时候用
jade-maintain-dev-docs
日常治理:新建或继续变更、同步架构、记录决策、交接、迁移历史
每次任务开始与结束时
jade-design-maintained-project-docs
从零设计一套文档结构
项目要长期维护,有多领域、并行变更或反复交接
jade-design-lightweight-project-docs
建立最小文档结构
小库、工具、原型、低协作成本的项目

相关学习资料