做了几年 AI 辅助编程,我最深的感受是:模型能力的天花板一直在抬高,但"记性"始终是最短的那块板。你用 AI 写代码,头几轮对话它像个熟悉你项目的老同事,能准确说出模块边界、知道哪些代码不能乱动;可一旦对话变长、改动范围变大,它就开始忘事——漏掉你强调过的约束、推翻自己之前的结论、甚至对项目结构产生幻觉。不是模型变笨了,而是大多数 AI 编程工具本质上还是无状态的:每一次生成都是一次全新的开始,它只依赖当前输入里拼接出来的上下文,并不存在一个真正的"记忆仓库"。为了把项目的关键背景、架构决策、编码规范这些信息长期固定下来,社区里逐渐形成了 AGENTS.md 这类声明式配置文件,配合一套 Memory 工程的思路来管理 AI 的持久记忆。这篇文章我会从原理讲到落地,带你把这条从无状态到有状态的转变之路完整走一遍。
1. 无状态模型的痛点与范式转换
1.1 大模型为什么"记不住"
先聊一个基本问题:无状态到底意味着什么。
目前主流的大语言模型本质上是一个"输入到输出"的函数。你给它一段文本,它根据模型权重预测下一个 token,然后这次交互就结束了。下一次你提问,它不会天然记得上次说过什么,除非你把之前的历史对话重新拼接到输入里。所以,所谓的"上下文"并不是模型内部真有记忆,而是工具层手动帮你把对话记录、相关文件内容、系统提示词拼成一段长文本,再塞给模型。
这套机制在纯聊天场景下问题不大,你聊二十分钟,把聊天记录原样带上就行。但放进真实的软件工程项目里,矛盾立刻暴露。一个中型项目有几十个模块、上千个文件,还有一堆分散在代码注释、评审记录、事故复盘里的隐性约定。这些信息的总量远超上下文窗口的承载能力,你不可能每次提问都塞进整个代码库,也不可能把一周的对话历史完整保留。
于是出现了一个典型现象:AI 在第一轮能给出非常专业的方案,但聊到第五轮、第十轮,它开始遗忘你最初定义的约束,甚至自己推翻自己。我见过不少团队抱怨"AI 生成的代码前面挺好,后面就乱了",根源往往不在模型能力,而在状态管理。上下文里的信息是最容易被稀释的——越是靠前的约束,越容易被后续冗长内容冲淡,这就是所谓"迷失在中间"的问题。说白了,不是模型不想记,是它的"工作台"上只放得下这么多东西,而你还没有教会它哪些该优先保留。
1.2 从"每次重新解释"到"声明式约定"
针对"记不住"的问题,最朴素的应对方式是:在每次对话开头把背景重新粘贴一遍。"我们的项目是某订单中台,技术栈是某某,注意不要引入重型中间件,支付回调要做幂等……"这段话第一次说有用,第二次说有效,但每次都让每个开发者手动复制粘贴,版本还不统一,迟早变成消耗。
后来有人把这些信息写进项目根目录的文档,让 AI 工具自动读取。最早承担这个角色的是 README,但 README 本质是给人类看的项目说明书,充斥着"项目简介""快速开始""截图展示",对 AI 来说信息密度太低,它需要的是另一类内容:技术栈约束、目录结构说明、架构决策、编码规范、已知的坑。于是社区逐步演化出 AGENTS.md——一个定位非常明确的文件:不是给人看的说明,而是给 AI 代理看的"上岗手册"。
这个转变在我看来是一次真正的范式转换:从"每次对话由人类重新交代背景",变为"把背景沉淀成声明式配置,AI 每次启动时自动加载"。打个比方,以前你招一个实习生,每天上班都要把公司制度从头讲一遍;现在你把制度写成员工手册,新人入职自己读。前者依赖人的临场表达,后者依赖制度的确定性——效率和一致性完全不在一个量级。
1.3 范式转换背后的三个驱动力
这个转换不是凭空发生的,背后至少有三个现实驱动力。
第一个是上下文窗口的物理天花板。即便模型窗口越做越大,真正可用的"有效注意力"并没有线性增长,塞得越多,模型越容易忽略关键信息。与其盲目堆上下文,不如做减法:把最核心的约束提炼出来,用最精简的方式每次注入,把窗口留给真正的代码和任务细节。
第二个是多智能体协作的兴起。现在一个 AI 编程流程里往往有多个角色:代码生成、代码评审、测试补全、提交信息撰写。如果每个角色都靠临时对话获取背景,彼此之间根本没有共享记忆。AGENTS.md 这类文件正好充当了多个智能体之间的"公共黑板",让不同会话、不同工具读到同一份项目事实。
第三个是工具链的成熟。早期的 AI 编程工具只会读取当前文件或整个目录,不会专门解析项目级配置。现在的 IDE 插件、命令行代理和 CI 里的智能审查工具,都已经原生支持自动加载项目根目录下的约定文件。工具读得到,配置才有意义,这个范式才真正跑得通。
2. Memory 工程:给 AI 建立分层记忆
2.1 工作记忆:会话上下文的战场
聊 Memory 工程,第一件事是分清记忆的层级。最贴近模型的是工作记忆,也就是当前会话里拼接到输入中的全部内容:系统提示词、AGENTS.md 的内容、历史对话、检索到的代码片段。它的特点是容量有限、读写频繁、成本直接和 token 挂钩。
在实际使用中,工作记忆是"战场"。我见过不少开发者把所有相关资料一次性塞进对话,结果 AI 反而答非所问。原因很简单:上下文越长,模型对中间部分的注意力越稀薄。所以工作记忆的管理核心是裁剪和排序。我会习惯性地在长任务进行中做"对话压缩"——让 AI 把已完成的关键决策总结成几条要点,再开启新对话时只保留这份摘要,而不是把几万字历史全部拖进去。这听起来麻烦,但能显著提升后续生成质量,也能省下一大笔 API 调用成本。
2.2 短期记忆:项目级约定与 AGENTS.md
第二层是短期记忆,指的是跨会话但不过期太快的项目级事实。这就是 AGENTS.md 所在的位置。它写在项目里,每次 AI 启动时被自动加载,本质上是一个"静态记忆块":内容由人维护,写入一次、读取多次,不随对话状态变化。
这一层解决的是"每次都要自我介绍"的问题。AI 每次进入项目都能直接读到:项目是干什么的、技术栈是什么、目录怎么组织、哪些代码不能动、测试怎么跑。这就像给面试官一份简历,省去了前十五分钟的寒暄。值得强调的是,短期记忆的关键是"稳定且克制"。它不该试图包含所有知识,而只收录那些长期成立、频繁影响决策的约定。如果一份文件里写了两百条规则,AI 平均分配到每一条的注意力就很有限,效果反而不如只写二十条关键规则。
2.3 长期记忆:从代码检索到向量化知识库
第三层是长期记忆,解决的是"项目里某个细节到底在哪"的问题。当 AI 需要了解某个具体模块的实现细节,不可能全写进 AGENTS.md。这时要用检索手段:通过文件路径定位、关键词搜索、语义向量检索等方式,按需把相关代码片段拉进工作记忆。
这一层的实践通常叫 RAG(检索增强生成),但在编程场景里,路径检索往往比向量检索更实用。因为代码有明确的模块边界和文件结构,直接告诉 AI"去读某个目录下某个接口的实现"比让它做模糊语义匹配要可靠得多。我在项目里的做法是:在 AGENTS.md 里写清楚"支付模块在哪个目录、关键入口是哪个文件、异常处理约定在哪个文档",至于具体代码内容,由 AI 按路径去读。配置管骨架,检索管血肉,两者配合才完整。
2.4 记忆的成本:Token 预算与权重排序
三层记忆不是独立的,它们共享同一个约束:token 预算。所有记忆最终都要以 token 形式进入模型输入,而 token 就是钱、就是延迟、就是注意力稀释的风险。
我把整个记忆体系理解成一张成本表,心里大概有一个分配原则。对话性的工作记忆最贵,所以要频繁压缩;AGENTS.md 这类项目配置属于每次调用的固定开销,所以要控制篇幅,尽量做到一页以内;长期记忆按需检索,平时不占预算,但每次检索和注入也有成本,所以要尽量精准。三层之间的顺序也重要:越靠前的内容在上下文里位置越显眼,越容易被模型注意到。所以我会把最重要的约束放在 AGENTS.md 最前面,把补充说明往后放,把可选的例子放到最后。
| 记忆层级 | 存储载体 | 更新频率 | 每次成本 | 典型内容 |
|---|---|---|---|---|
| 工作记忆 | 会话上下文 | 持续变化 | 高 | 对话历史、当前任务、临时检索结果 |
| 短期记忆 | AGENTS.md 等配置文件 | 低频人工维护 | 固定开销 | 技术栈约束、目录结构、编码规范 |
| 长期记忆 | 代码库、文档库、向量索引 | 随代码演进 | 按需检索 | 模块实现细节、历史决策、接口契约 |
3. AGENTS.md 声明式配置的核心设计
3.1 AGENTS.md 到底是什么
先把这个文件讲透。AGENTS.md 是一种放在项目根目录的 Markdown 文件,专门为 AI 编程代理提供项目级指令。它和 README 最大的区别在于受众和用途:README 面向人类,讲清楚"这是个什么项目、怎么跑起来"就够了;AGENTS.md 面向 AI,讲的是"你在这个项目里应该遵守什么规则、按什么方式干活"。
这个文件的出现其实是社区自发形成的约定,各家 AI 编程工具有各自的规则文件命名,但 AGENTS.md 正在成为越来越多工具共同识别的标准之一。它的价值在于:让项目的"隐性知识"显性化。很多资深开发者嘴上说的"这个模块别乱动""这里必须走状态机""外键约束别直接改",此前只存在于口头传承和评审记忆里,现在可以被写成一个 AI 能稳定读取的声明式文件。
为什么要叫"声明式配置"?因为它描述的是"应该是什么",而不是"怎么做"。你不写"如果要修改订单状态,请先搜索状态机服务的全部调用点,然后评估影响,最后再修改并补充测试"这种命令式步骤,而是写"订单状态流转只能通过状态机服务完成"。至于 AI 怎么推理、怎么规划、怎么执行,那是它的事。声明式的好处是:规则更稳定、不会被过度细化、也更容易评审——人类看一遍就能判断对错。
3.2 一份配置文件的典型结构
根据我自己的实践,一份好用的 AGENTS.md 通常包含六个区块,按重要程度从高到低排列:
- 项目概述:三到五行说清楚项目定位、核心业务链路、服务边界。
- 技术栈约束:列出强制性技术选型,明确"禁用项"比"推荐项"更重要。
- 目录地图:标注关键模块的路径,告诉 AI 去哪里找什么。
- 编码规范:最容易踩坑的项目,包括命名、结构、异常处理、测试约定。
- 已知陷阱:过去踩过的坑、导致过事故的模式,直接写"不要这样做"。
- 常用命令:本地开发、测试、格式化、构建的命令,让 AI 不用猜测。
关键点在于"密度"。每一行都应该是机器可读级别的明确表述,避免"尽量""通常""可以考虑"这类模糊词。AI 对模糊指令的执行方差很大,你今天写"尽量减少不必要依赖",它可能理解成"可以加依赖但不要太多",也可能理解成"任何新增依赖都需要长文论证"。写配置的时候宁可语气强硬,也不要留出解读空间。
3.3 一个实战示例的逐段解读
下面是一个简化但不失真实的 AGENTS.md 示例,我用它在一个模拟的订单中台项目里跑过完整流程:
# AGENTS.md ## 项目概述 - 订单中台服务,负责订单创建、支付回调、超时关闭、售后单生成 - 模块化单体架构,按业务域划分包结构 - 核心链路:创建订单 -> 锁定库存 -> 支付回调 -> 履约派单 ## 技术栈约束(强制) - 语言:Java 21 LTS - 框架:某轻量 Web 框架,禁止直接使用底层 Servlet API - 数据库:PostgreSQL 16,禁止 ORM 自动建表,表结构变更必须走迁移脚本 - 禁止引入重量级中间件,新增外部依赖必须经过架构评审 ## 目录地图 - order-core:领域模型与业务规则,不依赖任何框架 - order-app:应用服务层,编排用例 - order-adapter:对外接口、消息监听、定时任务 - order-infra:数据库、缓存、外部服务调用 ## 编码规范 - 领域逻辑必须落在 order-core,禁止写在 adapter 里 - 所有对外接口返回统一响应结构 { code, message, data } - 业务异常必须继承 BizException,禁止直接抛出 SQLException - 新增对外接口必须附带契约测试 ## 已知陷阱 - 支付回调接口要求幂等,改动时务必保留并验证幂等逻辑 - 订单状态只能通过状态机服务流转,禁止在业务代码里直接 update 状态字段 - 超时关闭任务依赖分库分表键,做查询时不要省略分区条件 ## 常用命令 - 本地启动:./gradlew order-app:run - 单元测试:./gradlew test - 契约测试:./gradlew contractTest - 代码格式化:./gradlew spotlessApply逐段看,项目概述告诉 AI"我面对的是什么系统",技术栈约束直接划定边界,目录地图消除了"这个类该放哪"的猜测,编码规范把最容易引发评审冲突的几条铁律写死,已知陷阱则是从事故中提炼的保命条款,常用命令让 AI 做自动化操作时不用瞎猜。整个文件控制在六百字左右,一次加载成本极低,但覆盖了日常 AI 协作中百分之八十的背景需求。
3.4 声明式配置的设计取舍
声明式配置不是没有代价。它的第一个风险是"过度约束"。如果规则写得过细,AI 的发挥空间会被严重压缩,遇到规则没覆盖的场景反而可能手足无措。我遇到过一份把函数命名风格都规定到极致的配置,AI 生成的代码确实符合规范,但架构层面非常僵化。约束要管住的是"会出事的边界",而不是"所有可能的选择"。
第二个风险是"配置腐化"。项目在演进,技术栈在替换,模块在重构,但 AGENTS.md 可能半年没人更新。结果就是 AI 按照过时的规则写代码,越写越偏。解决这个问题的唯一办法是把配置文件当代码管:改动走评审、更新留记录、定期清理失效条目。我后面会专门讲维护流程。
还有一个取舍是关于文件拆分的。单文件最简单,但也最容易膨胀。当配置超过一两千字,我就会把它拆成主文件加附属文档,比如 AGENTS.md 只放最核心的二十条规则,然后在"详细规范"小节里指向 docs/agents/ 下的分文件。AI 工具读取主文件后按需再加载子文件,既保证了核心信息的可见性,又控制了默认上下文的体积。
4. 实操:从零搭建一套可用的项目记忆体系
4.1 梳理项目资产:哪些信息必须进配置
动手写 AGENTS.md 之前,先做资产盘点。我会按四个来源收集项目的"隐性知识"。
第一个来源是团队的经验文档和新人上手文档。很多团队有给新员工看的 onboarding 文档,里面往往藏着项目最核心的约定——只是写成给人看的形式,需要转译成机器友好的表述。第二个来源是代码评审记录。翻最近几十条评审意见,哪些类型的评论反复出现?"这里不应该直接改数据库""这个接口缺少幂等处理""测试命名不规范"——这些高频意见就是配置里最该写进去的规则。第三个来源是事故复盘和故障报告。凡是导致过线上问题的操作模式,都应该进"已知陷阱"。第四个来源是老开发者的口头约定。找两三位资深同事聊半小时,问一个问题:"如果让一个新 AI 代理独立改这个项目,你最怕它动哪里?"这些答案几乎就是配置的骨架。
盘点之后做分类,我习惯分成三档:绝对必要、重要、可选。绝对必要指的是"AI 不遵守就会出现严重问题"的规则,比如支付回调的幂等约束;重要指的是"不遵守会引发大量返工"的规则,比如目录分层;可选的是"遵守了体验更好"的偏好,比如注释风格。AGENTS.md 里只放前两档,可选内容放附属文档或者干脆不写。这个筛选动作非常关键,它决定了配置的密度和有效性。
4.2 撰写 AGENTS.md 的完整步骤
我建议按下面五步来写,每步都有明确的验证方式。
第一步,搭骨架。把 3.2 节的六个区块先列出来,每个区块写两三条你最有把握的规则,不追求一次写完。第二步,访谈补全。拿着骨架找团队成员做一轮快问快答,把 4.1 节收集到的信息填进去。特别注意记录"禁止类"表述,这类规则对 AI 的约束力最强。第三步,删减压缩。把初稿从头到尾读一遍,删掉所有"正确但没用"的话。判断标准很简单:这条规则如果删掉,AI 靠常识能不能做对?如果能,就删。第四步,格式化。统一用祈使句,明确主语,固定术语,比如"订单状态"就是"订单状态",不要说"状态字段"或"订单的状态值"。术语不统一,AI 的检索和匹配都会出问题。第五步,实测验证。用几个代表性任务测试配置效果:让 AI 新增一个对外接口、修改一个订单状态流转场景、修复一个已知 bug。看它是否走到了正确的模块、是否遵守了编码规范、是否避开了已知陷阱。
每个步骤之间不要拖太久,我建议一天内完成初稿,随后用一两周的实际使用来迭代。一开始追求完美反而会让配置文件难产,先让 AI 用起来,问题会自己暴露。
4.3 接入工具链与验证效果
配置写好了,接下来要确保 AI 工具真的在读。不同工具的接入方式不同,但都遵循一个通用逻辑:工具会在启动时查找工作目录下的 AGENTS.md(以及类似命名的配置文件),自动加载进上下文。
接入之后一定要主动验证。我常用的验证方法是:在 AI 工具里输入一个"元问题"——"根据项目配置,请列举本项目的前三条技术约束"。如果它能准确回答出"禁止 ORM 自动建表"这类内容,说明文件被正确读取;如果答不上来或者回答得非常笼统,就要检查配置路径是否正确、文件命名是否正确、是否被忽略规则排除。这个验证在每次改动配置后都要做一遍,因为 AI 工具的版本升级、工作目录变化都可能影响加载行为。
还有一个很容易被忽略的验证维度:行为验证。配置能被读到,不代表 AI 会遵守。我测试"遵守度"的方法是构造一个轻微违规场景——比如要求 AI 直接在某个业务模块里调用数据库接口,故意不给任何额外提示,看它是否根据 AGENTS.md 的规则主动拒绝或提出替代方案。这个测试能真实反映配置的约束力度。如果 AI 轻易就违规了,说明规则写得还不够显眼,或者位置太靠后,需要调整。
4.4 配置的迭代维护与版本管理
配置的维护,某种程度上比写初始版本更重要。我把 AGENTS.md 纳入了和代码同等的版本管理流程:每次改动走独立的提交,提交信息写明变更原因。为什么要单独提交?因为配置文件的变化往往意味着约定变化,如果混在其他代码改动里,评审时很容易被忽略,后续追溯也找不到记录。
具体的迭代节奏,我建议"事件驱动 + 定期清理"双轨并行。事件驱动是指:架构决策发生调整、技术栈替换、出现新的事故教训、团队评审反复提出同一类意见时,立刻更新配置。定期清理是指:每个月或者每个迭代周期结束,抽十五分钟过一遍配置,删掉已经失效的条目,合并重复的表述。你会发现配置会像一个活文档一样慢慢趋于稳定,前三个月改动频繁,之后进入一个低速维护的平稳期。
另外,如果项目规模增长,单文件装不下了,就按我在 3.4 节说的那样拆成主文件加附属文档,同时在主文件里用明确的指路语句告诉 AI 什么时候该去读哪个子文件。比如在"此外"段落写"关于数据库迁移的具体规范,见 docs/agents/database.md;涉及对外接口契约时,见 docs/agents/api.md"。记住,子文件要按需加载,不能默认全部塞进上下文。
5. 常见问题与排查技巧实录
5.1 AI "看不到"配置,问题出在哪
这是最常遇到的问题。我汇总过几类原因,顺序排查可以省不少时间。
首先是路径和命名问题。配置文件必须放在 AI 工具实际执行的工作目录下,通常是项目根目录,文件名要严格匹配工具要求的约定——大小写、扩展名都不能差。很多工具对文件名是大小写敏感的,AGENTS.md 写成 agents.md 可能就读不到。其次是工具支持问题。不是所有 AI 编程工具都原生支持读取这个文件,尤其是一些轻量插件或网页版工具。确认你的工具版本支持,或者查一下配置开关是否默认关闭。第三是编码问题。配置文件中如果包含非 UTF-8 编码的中文符号,可能解析出错,建议统一用 UTF-8 保存,尽量避免花哨的排版字符。最后是加载顺序问题。如果工具支持多个配置文件(比如全局配置、项目配置、会话指令),要确认项目配置没有被全局配置的设定意外覆盖。
我自己的排查习惯是三步走:先看文件路径和命名,再看工具日志是否显示加载了配置文件,最后用 4.3 节的"元问题"验证法确认。十次里有八次是前面两个问题。
5.2 指令冲突:配置之间的优先级怎么定
AI 可能同时收到来自多个来源的指令:全局配置文件、项目 AGENTS.md、当前对话里用户说的话。如果它们之间出现冲突,AI 的行为就变得不可预测。虽然各工具的实现细节不同,但我在实践中总结出一条经验规则:越具体、越贴近当前任务的指令,优先级越高。
按照这个规则排序:当前对话里你明确说的要求,通常应该优先于项目配置;项目配置优先于全局用户配置;全局用户配置优先于工具的默认行为。理由也简单:对话是"实时指令",说明此刻的需求最明确;项目配置是"长期约定",约束的是这个项目的所有场景;全局配置是"个人偏好",影响面最广但针对性最弱。
然而这条规则并不绝对。如果 AGENTS.md 里写着"支付回调保持幂等",而对话里让 AI"直接改状态不管回调",这时候正确行为是拒绝冲突指令,而不是盲目服从。为避免这类拉锯,我会在配置里给少数安全攸关的规则加上显式标注,比如在标题或规则前注明"此规则不可在对话中被覆盖"。这个声明本身不一定被所有工具支持,但在多数情况下能提高 AI 对规则的重视程度。
5.3 上下文被配置撑爆怎么办
AGENTS.md 写得太多是常见病。我见过一份长达三千字的配置,结果 AI 反而"不听指挥"——不是因为它叛逆,而是因为注意力被摊薄了,真正重要的规则淹没在大量次要信息里。处理办法只有一个:狠心删减。
我的做法是做一个"剪枝实验"。先把配置里每条规则标个权重分,1 分是"删了也无所谓",5 分是"删了会出事故"。然后把所有 1 分和 2 分的内容全部移除,把 3 分的压缩成一句话。剪完后,配置通常能缩减到原来的百分之四十。再把最重要的几条铁律手动移到文件最前面,用短句加粗。这个操作做完,AI 的规则遵守度往往会有立竿见影的提升。
如果缩减后仍然很长,就拆文件。主文件只留十五到二十条最高权重规则,其余按主题拆到子文件里,用"见 docs/agents/xxx.md"指引按需加载。记住一个原则:配置的目标是让 AI 在默认情况下就站在正确的方向上,不是给它一本百科全书。
5.4 团队协作:谁来维护 AGENTS.md
AGENTS.md 如果只靠一个人维护,迟早腐烂;如果人人都有权限改,很快会变成无人负责的杂货铺。我的建议是设立"配置维护人"角色——不一定是专职岗,但必须有明确的人对配置的准确性和更新节奏负责。维护人负责定期清理、把关改动、验证配置的有效性。
在实际协作中,我把 AGENTS.md 的变更纳入了代码评审流程。任何对配置的修改都必须经过至少两人评审,评审的标准是:这条规则是否仍然成立、表述是否清晰无歧义、和已有规则是否冲突、是否值得占用上下文预算。团队的新成员入职时,也把 AGENTS.md 作为必读文档——它能同时服务人类和 AI 两套读者,这是它最值的部分。
我也见过一些团队开"配置评审会",每月一次,半小时,专门过一遍 AGENTS.md 和附属文档。会上的讨论往往很有意思,比如"这条规则已经过时了""这两个约定其实是一回事""我们早就没人遵守这个命令了"。这些讨论本身就是知识管理的过程,价值不亚于最终的删改结果。
最后说点我自己的体会。这半年我最大的教训是:刚开始做 AGENTS.md 时总想写全,结果写了一版三千字的配置文件,AI 反而不听话了。后来删到六百字,把最重要的十条约束放在最前面,效果立竿见影。记忆工程不是给 AI 越多越好,而是给得越精准越好。如果你也想在自己的项目里引入这套做法,我的建议是从一个最小版本开始——先写十条约束,用两周时间观察 AI 的行为变化,再逐步补充。配置会随着你对 AI 协作方式的理解一起进化,这本身就是一条值得长期投入的工程路径。