你的 AI 代理有一份规则文件。它们没有你系统的模型。

打开你仓库根目录下的 CLAUDE.mdAGENTS.md.cursor/rules,按照它抵达一个代理时的样子去读:一整块文本,里面没有任何东西标出哪几行还成立。

你在里面找到的大部分是约定。用 tab。别用 any。尽早 return。用 %w 包装错误。这些行是耐久的,因为它们描述的是如何写一行代码,而代理会把它们应用到眼前的代码上。

然后是另一类行。描述你系统的那一类:有哪些服务存在,哪个 package 拥有什么,各层之间被允许怎样对话。正是这些行让这份文件有存在的价值,也正是这些行会腐烂。

我知道,因为我们的那份就腐烂了。

我们那份文件里过期的东西

Archyl 的仓库根目录下有一份 CLAUDE.md。按这个体裁的标准衡量,它算写得不错的:422 行,一棵架构树,配置变量,dependency injection 的接线,还有 discovery 流程的说明。每个碰到这个代码库的代理,在做任何别的事情之前都会先读它。

以下是我写这篇文章那天早上——2026 年 8 月 5 日——它写着的内容。第 392 行:

No test suite: The codebase currently has no Go test files or frontend tests.

没有测试套件:这个代码库目前没有 Go 测试文件,也没有前端测试。

backend/ 下面有 146 个 _test.go 文件,frontend/src/ 下面有 31 个测试文件。

第 140 行说:

AI Provider Abstraction: Supports both OpenAI and Ollama via ai.Provider interface.

AI 供应商抽象:通过 ai.Provider 接口同时支持 OpenAI 和 Ollama。

backend/internal/adapter/ai/resolver.go 在平台托管的 OpenAI 与 Ollama 路径之外,还路由到 OpenAI、Anthropic、Gemini、Bedrock 以及任何 OpenAI 兼容的端点。一个 switch 语句里有五种供应商类型。文件点了两种的名。

而那棵架构树,在第 74 行到第 87 行之间,列出了 internal/domain/ 下的十一个 package:c4projectuserteamadrprojectdocflowinsightsubscriptiondependencyhistory。今天 internal/domain/ 里有四十一个目录。在它没有提到的三十个当中,包括:conformancedriftapicontractmarketplacerealitymanagedagentmcpsession。也就是说,这份文件写下之后产品成长出来的东西,大部分都不在里面。

这些行在被敲下的那一天,每一行都是真的。之后没有一行被修正过,因为要修正就得有人注意到,而没有任何东西在看着。

这是一家卖架构文档的公司。如果自律就是解药,它在这里早该奏效了。

那份文件的两半之间毫无共同之处

约定那一半是可以强制执行的。"Go 里不许用 fmt.Println"是一次 grep。"Go 文件名必须是 snake_case"是一个脚本。代理违反了其中一条,linter 会在 CI 里说出来。如果约定本身变了,linter 就开始失败,然后有人去更新文件。这里有一个反馈回路,而且短得足以起作用。

系统那一半没有对应物。不存在一个用来检查"支付服务禁止直接访问数据库"的 go vet。没有东西去解析那句话,没有东西把它和仓库比对,没有东西在它不再吻合的时候失败。它是 markdown 文件里的散文,而散文没有失败模式。

所以一份规则文件其实是共用一个文件名的两份文档。一份被持续验证,另一份从不被验证,而文件里没有任何东西把两者区分开。"用 %w 包装错误"和"这个代码库没有测试"并排在同一份清单里,用的是同一种语气。前者是关于代理眼前那段代码的规则。后者是关于它根本没在看的 146 个文件的断言。

更难的那一半是缺席

过期是所有人都能想象出来的失败。更安静的那种更要紧:一份规则文件里只装着有人想到要写下来的东西,而它里面没有任何东西能区分"这个不存在"和"没人提过"。

我们那份从没提过 internal/adapter/marketplace/。这个 package 里放着一个 provider 接口和八个适配器:GitHub、GitLab、Argo CD、Datadog、Prometheus、Sentry、SonarQube、PagerDuty。CLAUDE.md 里的适配器清单停在 gitaistripeemailosvregistry。文件里关于 marketplace 的说法没有一句是错的。这份文件里根本没有 marketplace。

我没有做过那个实验——让一个代理去加第九个集成——我也不打算告诉你它会产出什么,因为那等于我在编造结果。我能告诉你的是,地图上没有画着 marketplace,而这是我读过的每一份规则文件的常态,包括我自己写的那些。

模型没有这个毛病。你可以问一个模型什么存在,并得到一个有意义的回答,因为那个回答是对一个集合的查询,而不是在散文里做检索。"什么在和支付服务对话"是一个图能回答、而一个段落回答不了的问题。

两个显而易见的答案,以及它们都不成立的原因

写一份更好的规则文件。 更长、更仔细、在 pull request 模板里加个勾选框。团队会这么做,而且能管用几个星期。它撑不住,原因和自律无关:每一行描述系统的话,都是别处某样东西的缓存副本,而缓存需要失效机制。在这里,失效机制就是有个人注意到。整个机制就这么多,而它和过去二十年里本该让架构图保持准确的机制是同一个。我们都知道结果如何;drift 检测指南是这套论证的长版本。

让代理去读仓库。 它能读,而且如果问题只关乎一个文件,它就应该去读。但读代码不会告诉你哪些边界是刻意划下的。一个服务前面的接口,无论它在那里是因为两年前一次故障之后做出的决定,还是因为有人就是喜欢接口,看上去一模一样。意图无法从它所产出的产物里反推回来。这正是规则文件一开始存在的理由,也正是删掉它同样不是答案的理由。

这家公司之外,也有人注意到了同一件事

Thoughtworks 把 "Architecture drift reduction with LLMs" 放进了 Technology Radar Vol. 34 的 Assess 环,该期于 2026 年 4 月发布。他们的开头:

Increased use of AI coding agents can accelerate drift from the intended codebase and architecture designs. Left unchecked, this drift compounds as agents and humans replicate existing patterns, including degraded ones, creating a feedback loop where poor code begets poorer code.

AI 编码代理使用量的增加,会加速代码库和架构设计偏离原本的意图。如果放任不管,随着代理与人类不断复制既有模式(包括那些已经劣化的模式),这种 drift 会不断累积,形成一个糟糕的代码催生更糟糕的代码的反馈回路。

Assess 一词,按雷达自己的定义,意思是 "worth exploring with the goal of understanding how it will affect your enterprise" ——值得探索,目的是理解它将如何影响你的企业。它不是对任何东西的推荐,更不是对我们的推荐。它只是一条记录:他们的一些团队正在尝试这个方向,而且还很早。

有用的部分是他们描述的那个形状:确定性的分析工具(他们点名了 Spectral、ArchUnit 和 Spring Modulith)与 LLM 评估相结合,因为结构可以由程序检查,而意图不能。他们汇报的那条经验也值得偷来用:第一次扫描翻出来的违规数量,会超过任何人愿意去分诊的量。

留意一下这个配方里没有的东西。没有谁给出的答案是——面对代理加速的 drift,就去写一份更长的 markdown 文件。

那个产物必须做到什么

两个性质。哪一个都不算稀奇。

它必须能枚举。 你应该能问"有什么存在",然后拿到那个集合,而不是某个人对它的回忆。这意味着一个你去查询而不是去阅读的产物,而这个差别在那些没人写下来的问题上表现得最为残酷。

它必须可证伪。 必须有某样东西把它和代码比对,并报告哪些部分已经不再成立,而且是按某种节奏,这个节奏不能是"等到有人注意到"。ArchUnit 为 Java 的分层规则做这件事。dependency-cruiser 为 JavaScript 的 import 做这件事。两者都是刻意收窄的,而两者都说明了同一个要点:值得拥有的产物,是程序能够对它提出异议的那种。

规则文件两条都不及格。它不枚举,也没有任何东西能对它提出异议。

我们站在哪里,以及我不能告诉你的事

Archyl 维护你系统的一个 C4 模型:系统、容器、组件、关系,由 AI 发现从仓库生成,再由人来审批,而不是由人来画。这个模型就是可枚举的那一半,代理通过 MCP 抵达它——相当于 181 个工具的规模——于是代理去问什么存在,而不是指望有人把它写了下来。约定的那一半是一份 conformance 目录:覆盖 23 项具名技术的 169 条规则,外加一套与语言无关的规则,是确定性的检查而不是散文。而且模型会被重新拿去和代码比对并打分,这就是可证伪这条性质。

有一个 MCP 服务器不是有意思的部分,任何把它当作差异化卖点卖给你的人,卖的是一个插座。Structurizr 也带了一个IcePanel 的正在公开测试。值得争论的问题是插座后面那个东西有没有被维护,因为一个在提供三月份就已经过期的模型的端点,只是让你更快地弄错而已。

我认为这就是那个真正重要的差别。我无法证明它。没有人测量过:从一个被维护的模型出发工作的代理,写出的代码形态是否比从一份仔细写就的规则文件出发工作的代理更好;在有人做出测量之前,那句话是关于一种机制的主张,而不是一个结果。请就这样持有它,并且如果有人把它说得比我刚才更斩钉截铁,请回推回去。

这里还有一处需要诚实交代的褶皱。Archyl 会生成一份规则文件。MCP 工具 get_agent_context 会把架构以 markdown 简报的形式返回,你可以把它 commit 进自己的仓库——那就是换了个名字的规则文件。文件从来都不是问题。问题在于它背后没有任何东西撑着,所以没有任何东西能重新生成它。作为一个被维护模型的缓存的规则文件,没有问题。作为唯一副本的规则文件,是某一个人在某一个下午所相信之事的快照。

五分钟版本,不花你一分钱

把上面的全部忽略掉,改做这件事。

打开你的规则文件。一行一行地看下去,把每一行标记为约定——意思是它告诉代理该怎么写代码——或者标记为断言——意思是它告诉代理关于你系统的某件事。然后,对每一条断言,写下什么会让你知道它已经不再成立。

我猜你会带着一片空白的第二列走到文件末尾。那就是缺口。你打算拿它怎么办是另一个决定,而且你不需要为了看见它去买任何东西。

我们那份花了几分钟,翻出了三行错的。修掉它们是一个 commit,而且不会改变任何结构性的东西:下一行会以同样的方式过期,而看着那一行的,同样什么都没有。