跳到主要内容

你的 Agent 从未读过的 ADR

· 阅读需 12 分钟
Tian Pan
Software Engineer

你的团队在 2024 年拒绝了微服务拆分。当时开了一个两小时的会议,Slack 上的讨论异常激烈,还有一份对比运营成本的电子表格。结论是坚定的:在平台团队交付多区域部署之前,保持单体架构不变。当时在场的每一个人都还记得这件事。

你的编码 Agent 当时并不在场。上周二,它再次提议拆分——语气自信,带着一份干净的迁移计划和一份论据充分的设计文档。再上个周二,另一个 Agent 会话建议提取计费服务。下周,第三个 Agent 会建议用你评估过并两次拒绝的消息中间件来替换你定制的任务队列。就它们所能看到的信息而言,它们都没有错。它们只是看不到决策的价值所在,因为解决这些问题的推理过程存在于已过期的 Slack 线程、未记录的会议以及两名工程师的脑海中,而其中一人已经离职了。

这是 Agent 辅助工程的一种悄然发生的失败模式:已解决的问题正以机器速度被重新审议。而解决办法是大多数团队在十年前归类为“官僚主义、有则更好”的一项实践——架构决策记录(ADR)。

无文档记录的决策正以机器速度被重新辩论

人类团队总是会丢失决策背景。人们会遗忘、离职,或者从未被告知。但人类的重新辩论有天然的速率限制:一名新工程师可能只提议一次被拒绝过的事情,然后被告知“我们在 2024 年试过那个,这是它行不通的原因”,吸收了这些“传说”,并成为其传播者。这种部落知识系统虽有损耗但具备自愈能力,因为撞到墙的人会留下来,并记住墙在哪里。

Agent 破坏了这两个特性。它们不会留下来——每个会话都从零开始,完全不记得曾被告知“我们试过那个”。而且它们不会只提议一次被拒绝的事情;只要局部证据表明可行,它们每次都会提议,也就是每次阅读代码时都会提议。一个刻意选择“错误”模式的代码库——为了读取性能而保留的反规范化表、因为下游团队无法处理重试而保留的同步调用、为了合规原因而锁定的旧框架版本——在新鲜的 Agent 看来,就像是一个充满改进机会的代码库。

更糟糕的是,Agent 会付诸行动。人类初级工程师会在设计评审中提出迁移,那时有人可以拦住。而一个进行自主重构、依赖升级或“清理”扫荡的 Agent 可能会直接开始做你决定不去做的事情,现在拒绝的理由必须被重新推导、重新辩论并重新执行——在代码评审中,以你的机器集群生成 Diff 的任何频率进行。运行多个并发 Agent 会话的团队报告称,同一个提议在一周内的三个不同 PR 中独立出现。每一个都会消耗评审者的精力。总成本是对你曾做过的每一个无文档记录的决策征收的税。

一个令人不安的洞察:决策从未真正“尘埃落定”。它只是对当时在场的人来说尘埃落定。一个组织的决策持久性仅取决于其最持久的呈现形式,而对大多数团队来说,这种呈现形式是集体记忆。Agent 只是揭露了实际记录下来的内容是多么稀少。

为什么 ADR 突然变得比以前更重要

架构决策记录自 Michael Nygard 在 2011 年提出以来一直存在:每个重大决策对应一份简短文档,记录背景、决策本身及其后果。编号、不可变,并存储在代码旁边的仓库中。这种格式从未在价值上失败过——它在经济性上失败了。编写记录需要一名有动力的工程师花费三十分钟;而回报却是分散的、延迟的,且主要累积在未来的陌生人身上。用 DORA 的术语来说,ADR 始终是精英团队的实践,因为只有精英团队才能持续为未来的利益支付当下的成本。

Agent 在两方面翻转了这种经济模式。

收益不再是分散或延迟的。仓库里的 ADR 是 Agent 今天在运行下一个任务时就可以加载的上下文。事实证明,ADR 几乎意外地完美适配 LLM 的消费:它们是自然语言,因此不需要检索流水线或模式(Schema);它们简短且自洽,因此可以放入上下文窗口而不会挤占任务空间;它们与代码共存,因此探索文件系统的 Agent 发现它们的方式与发现 README 的方式相同。结构化的知识库或本体论在机器推理方面可能优于松散的文档——但 ADR 以极小的仪式感捕获了大部分价值,因为 ADR 的单元就是推理链:情况是这样的,选项有这些,这就是为什么我们选择了这一个。

与此同时,编写它们的成本已经崩塌。在做出决策的那一刻——在设计讨论中、在 PR 争论中、在 Agent 会话进行到一半时——你可以让 Agent 在上下文仍处于窗口中时起草 ADR。人类的工作缩减为验证记录的推理是否为真实的推理,而非看似合理的重构。三十分钟的有动力工程师时间变成了三分钟的评审。

当一项实践的成本下降十倍,且其收益从“明年”变为“下个会话”时,它就不再是成熟度的信号,而变成了基础设施。这就是转变:ADR 不再是纪律严明的团队与未来队友沟通的方式。它们是任何团队与其那部分没有长期记忆的劳动力沟通的方式。

将“我们已经尝试过”接入循环

拥有 ADRs 和让智能体阅读它们是两个不同的问题。一个智能体从未查看过的 docs/adr/ 文件夹,与一条已经过期的 Slack 聊天记录一样毫无用处。关键在于如何接入,这分为三个级别。

第一级:提高记录的可发现性。 将 ADRs 保存在代码仓库中,放在常规位置,并使用纯 Markdown 格式。在你的智能体指令文件——CLAUDE.mdAGENTS.md 或任何你的工具所读取的文件——中引用该文件夹,用一行文字说明其中的内容以及何时咨询它:“在提议架构更改之前,请检查 docs/adr/ 以了解先前的决策。”那些在行动前进行探索的智能体会找到它们;而那些不主动探索的智能体,至少也会被指引到那里。这不需要任何成本,却能避免最严重的犯错。

第二级:注入关键决策。 可发现性依赖于智能体主动选择去查看,而对于少数那些最容易被反复争论的决策——关于数据库、服务边界、框架选择的决策——“寄希望于此”并不是一种策略。在指令文件本身中总结这些决策,每条总结占一行,并附上指向完整记录的指针:“我们有意使用单体架构 (ADR-0012);请勿提议服务拆分。”完整的 ADR 承载了当智能体需要解释或扩展决策时的推理过程;而摘要则承载了当它只需要不违反决策时的约束。

第三级:将决策放在产生诱惑的地方。 正在重构你的支付模块的智能体并不知道 ADR-0031 的存在,也不知道它与此相关。在故意写得“奇怪”的代码处添加一行注释——// 有意保持同步,参见 ADR-0031——将全局文档转化为局部上下文,恰好在智能体查看它所管辖的代码时出现。这与对人类读者有效的技巧相同,但智能体给出的回报更稳定:它们会阅读正在编辑的文件中的每一行,这比我们大多数人做得都要细致。

这三个级别的共同模式是:推理必须在 决策点可检索,而不仅仅是存储在某个地方。针对智能体知识库的实验不断发现同样的差距——对松散文档的检索能够可靠地呈现 什么 被决定了,却可靠地遗漏了 为什么,因为“为什么”散布在多个来源中,不会同时出现在任何单个文本块中。ADRs 通过将“为什么”和“是什么”合在同一个文档中,从结构上解决了这个问题。但前提是该文档必须在循环路径中。

新的失效模式:比决策活得更久的 ADR

这是没有人警告过你的事情,因为当只有人类阅读这些文档时,这几乎无关紧要:一份关于你后来已经撤销的决策的权威记录,比没有任何记录还要糟糕。

人类读者在处理文档时带有天然的怀疑态度。一位工程师在看到一份禁止使用 Kubernetes 的 2023 年 ADR 时,如果眼前的代码库里明显充满了 Helm charts,他会立即化解这种矛盾——文档过时了,代码才是事实,继续工作。智能体并不会可靠地这样做。指令文件或决策记录带有一种 权威性,这是环境代码所不具备的;这正是它发挥作用的完整机制。让当前的 ADR 变得强大的那份权威,也会让过时的 ADR 变得具有毒性:智能体会拒绝使用你现在偏好的方法,将新代码“纠正”回已被取代的决策,并引用该记录作为依据——而且带着一种遵循指令的自信语气,因为它确实是在遵循指令。

对于人类来说,过时的文档是摩擦力。对于在每次请求时都会重新加载文档的智能体来说,过时的文档是持续输送的毒药。

经典的 ADR 规范中已经包含了答案;它只是从一种良好的卫生习惯变成了关键支撑。永远不要编辑一份已接受的记录——写一份新的记录来取代它,并在旧记录的 头部本身 标记其已被取代,而不仅仅是在智能体可能永远不会阅读的索引中标记。状态行现在起到了实际作用:一个打开 ADR-0012 的智能体必须能在该文件中看到 ADR-0031 已经取代了它。除此之外,还有两种做法值得坚持:

  • 将决策反转视为代码更改。 撤销某个决策的 PR 应该在同一个 diff 中更新 ADR 链和指令文件摘要,就像你会同时更新类型签名及其调用点一样。一个只存在于合并提交中的反转,无异于重蹈 2024 年 Slack 聊天记录的覆辙。
  • 审计摘要,而不只是记录。 你在第二级注入到 CLAUDE.md 中的单行约束是你拥有的最高权威、也是漂移风险最高的地方。每季度进行一次“我们是否仍然相信这些?”的审查成本很低。智能体甚至可以自己完成第一轮审查:交叉引用每条陈述的约束与当前的代码库,并标记出代码已不再遵守的那些。

这里隐藏着一个有用的认知重构:你的决策记录现在是你“劳动力”的配置文件。没有人会在发布配置文件后从不复查——或者更确切地说,那些这样做的团队,最终会得到他们应得的事故。

记录下“为什么”,因为读者已经变了

十五年来,推行 ADR 的理由是出于同理心:为那个你从未谋面的未来同事写下它。而现在的理由是出于吞吐量:写下它,否则你的智能体会永远为此争论不休,一次又一次地提出那些自信的建议,而你将为每一次重新审议支付评审带宽——这是智能辅助工程中唯一无法成倍增加的资源。

从你下一次反转或拒绝的决策开始;那是你能写下的价值最高的记录,因为拒绝的理由正是代码永远无法展示的东西。在争论还新鲜时让智能体起草它,花三分钟确保记录的“为什么”是真实的“为什么”,然后在产生诱惑的地方放一个指向它的指针。随着时间的推移,回填那些“金曲”——你的团队已经两次否决的每个提案都在告诉你接下来该写哪份 ADR。你的智能体正在以它们那种迟钝的方式,生成一份关于你未记录决策的优先级列表。

做对这一点的团队会注意到一些微妙的变化:这种规范从未真正关乎文档。它关乎强制决策具有可阐明的理由——一种由于部落知识的存在而让每个人都能跳过的强制函数。智能体只是结束了这段宽限期。

References:Let's stay in touch and Follow me for more thoughts and updates