1. 为什么我决定告别“无状态”的AI编程方式
1.1 一个让人抓狂的失忆现场
半年前我对AI编程助手的态度是又爱又恨。爱是因为它的代码生成能力确实强,恨是因为它太容易“失忆”了。同一个项目我连续写了三个月,但每次新开一个对话窗口,它都要重新认识这个项目一遍——“项目用的什么框架?”“依赖怎么装?”“测试命令是什么?”——这些问题我反复回答了不下二十遍。
最崩溃的一次,是让它重构一个工具函数。它按照自己的想象把所有内部模块重写了一遍,命名风格不对、目录结构不对、错误处理方式也不对。我盯着屏幕愣了很久:它昨天刚帮我写过相邻的代码,怎么今天就什么都不记得了?
这让我意识到一个更深层的问题:我一直在用一种“无状态”的方式和AI协作。每次对话都是全新开始,每次都要重新交代背景,每次它给出的建议都可能和上个月的决定冲突。问题不在模型的智力,而在“记忆”。我把这个阶段称为“无状态AI编程”,它最大的特征就是:上下文是一次性的、临时的、不结构化的。
1.2 无状态的底层逻辑:模型本来就没有“硬盘”
想解决问题,得先弄明白为什么AI会失忆。大模型的本质是一个纯函数:输入一批token序列,输出下一段token序列。它内部没有像人一样的长期记忆存储,不存在“我记得你上次说过”这种机制。所谓的“记忆”,完全依赖每次请求时输入上下文里塞进去的信息。
这就好比一个只有工作台、没有书架的助手。你每次让它干活,都得把参考书重新堆到它桌上。它的工作台(上下文窗口)虽然不小,但毕竟是有限的,塞太多东西它就会注意力涣散,甚至出现“lost in the middle”——对上下文中间部分的信息越来越不敏感。
明白了这个原理,就知道“记忆”不是一个模型能力问题,而是一个工程问题:我们得想办法把项目相关的关键信息,稳定地装进它每次读取的上下文里,而且要以它最容易消化的格式组织。如果这些信息能跟着项目走、能进版本库、能被统一维护,那AI的“失忆”问题就从根上缓解了。
1.3 我试过的几种“伪记忆”方案,为什么都治标不治本
在彻底转向AGENTS.md之前,我试过好几种看起来可行的方案,各有各的死角。
第一种是临时长prompt。开工前先给它贴一大段项目背景、技术栈、约定,让它“记住”。问题是这段文本每次都要重写,不同成员写的质量参差不齐,而且一旦对话长了,前面的内容照样会漂移、被遗忘。这种方案的可持续性基本为零。
第二种是死磕单次长对话。要求AI在一个上下文里完成所有任务,不换窗口。但上下文窗口总有上限,项目稍微复杂一点就顶不住了。而且对话拖长了之后,AI对早期指令的遵循度会肉眼可见地下降。
第三种是把设计文档、技术方案全塞给它。听起来靠谱,实际上塞进去的信息大部分是噪音。它不得不在一堆文档里找关键命令和约定,准确率并不高。更麻烦的是,文档一旦和代码脱节,AI会自信地按照旧约定给你建议,反而帮倒忙。
这三条路走完,我基本确认了一件事:我们需要一种结构化的、跟项目走的、可维护的记忆载体。这个载体不能是临时口头叮嘱,也不该是冗长的文档,而应该是一个精炼的、声明式的配置文件。这就是AGENTS.md出现的意义。
2. Memory 工程的核心命题:AI到底该记住什么、忘掉什么
2.1 先把记忆分层:别把所有信息都叫“记忆”
研究Memory工程时,我做的第一件事是给“记忆”分层。因为不同信息的性质完全不同,维护方式、失效机制也完全不同,混在一起管理必出问题。
我自己习惯用四层模型:
| 记忆层级 | 存储位置 | 什么时候失效 | 谁来维护 |
|---|---|---|---|
| L0 模型知识 | 模型参数内部 | 几乎不失效 | 模型厂商 |
| L1 会话上下文 | 当前对话窗口 | 对话结束即失效 | 使用者临时提供 |
| L2 项目记忆 | AGENTS.md、代码库、配置文件 | 代码变更时需同步更新 | 项目成员共同维护 |
| L3 个人与团队约定 | 个人规则文件、团队规范文档 | 约定更新时失效 | 个人/团队负责人 |
过去我们花大量精力在L1上——每次对话前辛苦地铺陈背景。但L1是最脆弱的,随时会丢。真正值得投入的,是把那些反复使用的信息从L1“迁移”到L2,沉淀成项目自带的记忆资产。AGENTS.md正是L2的承载者。
分完层之后,我对“记忆工程”有了一个清晰的定义:把项目里稳定、高频、影响正确性的信息,以声明式的格式,固化到代码库中,让AI代理在每次工作前自动加载。
2.2 什么值得写进项目记忆,什么不值得写
这是我在实践中踩过最多坑的地方。一开始我恨不得把项目的所有细节都写进AGENTS.md,后来发现这完全是灾难。你需要一条判断准则来判断哪些信息值得写。
我的准则是:如果删掉这条信息,AI在大多数情况下还能不能干好活?如果它靠常识、靠代码本身就能推断出来,那就不写。如果它推断不出来,或者推断会出错,那就写进去。
按这个准则,值得写的是这三类:
- 直接影响正确性的:技术栈与版本、核心架构约束、模块边界、数据流向
- 频繁使用的:构建命令、测试命令、lint规则、代码规范
- 容易出错的:项目的领域术语、历史决策、代码生成时要避开的坑
不值得写的是另外三类:
- 代码里一眼能看到的:这个函数在哪、那个类叫什么,AI自己会读
- 一次性任务信息:本次功能的需求描述、临时任务指令
- 会频繁变化的信息:今天改明天变的细节,比如临时环境变量、某次调试方案
2.3 记忆不是越多越好:“提权”与“降噪”同样重要
很多人误以为给AI的信息越多,它就越懂项目。实际上上下文窗口里的信息就像桌面上的文件,每张纸都在争夺它的注意力。信息过载后,AI会出现两种典型问题:一是抓不住重点,二是把次要信息当成主要约束。
所以Memory工程里,除了“写什么”,还得管“怎么读”——我习惯用“提权”和“降噪”两个动作来优化。
“提权”是把关键约束放到配置文件的显眼位置、用固定格式输出、用明确的祈使句。比如“测试命令必须用pytest,不要用unittest”,比“项目测试用的是pytest比较多”有效得多。高优先级规则我还会在AGENTS.md顶部专门设一个“Hard Rules”区块。
“降噪”是把那些可有可无的说明、模棱两可的描述、和代码库里已有内容重复的段落统统删掉。一份好的AGENTS.md,读起来应该像给新同事的交接手册,只有“必须知道”的事,而不是项目流水账。记住:AI对这个配置文件的注意力,和它对prompt的注意力一样有价值。精简,就是给记忆提权。
3. AGENTS.md 落地实操:目录、模块与一份可用模板
3.1 从“每次叮嘱”到“项目自带说明书”
理解记忆分层之后,我真正落地的方式是用AGENTS.md。它本质上是一个声明式配置文件,放在项目根目录,AI代理启动时会自动读取它,把它作为理解项目、执行任务的指导上下文。
它和传统prompt有个本质区别。传统prompt是“命令式”的:每次对话都要临时给AI下达指令——“请记住项目背景”“请按照这个规范来”。而AGENTS.md是“声明式”的:项目里自带一份说明书,AI来了自己读,不依赖每次有人想起来叮嘱一遍。
| 维度 | 命令式(临时prompt) | 声明式(AGENTS.md) |
|---|---|---|
| 触发方式 | 每次手工粘贴/输入 | AI代理启动时自动加载 |
| 持久性 | 对话结束即失效 | 随项目代码库长期存在 |
| 维护责任 | 使用者临时编写 | 项目成员共同维护、随代码演进 |
| 一致性 | 不同人写的质量参差 | 统一格式,稳定可审查 |
| 版本管理 | 无 | 进入git,随PR一起变更 |
用生活中的类比来说:命令式就像每次让新同事干活前口头交代一遍规矩,说多了难免漏;声明式则是入职时递上一本手册,把公司规章制度、工作流程写得一清二楚,它会自己翻,还会一直带着。
3.2 AGENTS.md 放哪、写什么:目录结构与内容模块
AGENTS.md 最基础的位置是项目根目录。项目大了之后,还可以在子目录放局部的AGENTS.md,比如前端目录、后端目录、测试目录等。AI代理读取时会按层级合并:根目录定义全局规则,子目录定义局部规则,子目录优先级更高。
我在根目录的AGENTS.md里主要放六个模块:
- Project Overview(项目概览):一句话说明项目是干什么的,技术栈是什么,核心目录结构长什么样。这段让AI在几秒内建立对项目的基本认知。
- Commands(命令清单):构建、测试、运行、代码检查、部署等命令,每条都要准确。AI最常犯的错就是用错命令,这一块能直接减少错误。
- Code Style(代码规范):命名习惯、注释风格、错误处理方式、格式化约定。特别注意项目内已有的惯例,比如用
snake_case还是camelCase,返回值是Result类型还是直接抛异常。 - Architecture Rules(架构约束):模块边界、依赖方向、禁止事项。比如“领域层不允许依赖基础设施层”“所有外部请求必须先经过网关层”。这些约束AI不会从代码里自学到,必须显式告诉它。
- Workflow(工作流约定):开始一个任务前要做什么、改完代码后要做什么、提交前要跑哪些检查。让AI按照团队统一的工作流干活。
- Domain Terms(领域术语表):项目里特有的业务名词、缩写、概念解释。AI不了解业务语境时会乱猜,有了术语表就稳了。
3.3 一份可以直接套用的最小可用模板
以下是我目前自己在新项目里使用的模板,精简到能直接复制改用的程度:
# AGENTS.md ## Project Overview 一句话描述项目目标。技术栈:主要语言、框架、包管理器。 核心目录: - src/ 业务源码 - tests/ 测试代码 - scripts/ 工具脚本 ## Hard Rules - 禁止使用全局可变状态,除非有显式的生命周期管理 - 所有新增业务代码必须带对应测试 ## Commands - 安装依赖: npm install - 本地开发: npm run dev - 运行测试: npm test - 代码检查: npm run lint - 生产构建: npm run build ## Code Style - 命名:变量/函数用 camelCase,组件/类用 PascalCase,常量用 UPPER_SNAKE_CASE - 注释:公共API写JSDoc,内部实现写简短行注释即可 - 错误处理:优先返回失败结果类型,不在业务代码里抛出裸异常 - 格式化:使用项目自带 prettier 配置,不要手工调整 ## Architecture Rules - UI层 → 应用层 → 领域层 → 基础设施层,禁止反向依赖 - 外部API调用必须封装在基础设施层,业务层不得直接接触HTTP客户端 - 数据库操作只允许出现在仓储实现中 ## Workflow - 开始任务前:先读相关模块已有代码,确认现有风格再动手 - 修改完成后:运行 npm test 和 npm run lint,确保全部通过 - 提交代码前:检查diff,确认无调试残留、无临时注释 ## Domain Terms - SKU:商品最小库存单位 - 渠道包:按分发渠道区分的构建产物,不同渠道含不同配置 - 回放流:用户在直播结束后查看的录播数据流这个模板大概80多行,对多数中大型项目已经够用。关键是每个区块都保持“具体、可执行、可验证”,不要让AI猜。
3.4 AI代理是怎样“读取”这份配置的
这里稍微解释一下执行机制,不然很多人会误以为AGENTS.md有什么魔法。它本质上不是模型原生的能力,而是当前主流AI编程工具和代理框架普遍支持的一种约定:工作开始时,工具会自动读取项目根目录下的AGENTS.md(以及子目录的局部版本),把内容拼进系统提示词或上下文开头,然后随每个请求一起发送给模型。
因为这份配置始终出现在上下文的前部位置,它对后续输出的影响力很大。你可以把它理解为“给模型的起手式”——模型还没看你的代码,先看到了你给它定下的规矩。这种位置优势,比在对话中间临时插入一段说明有效得多。
需要注意的是,AGENTS.md提供的约束并不是“强制执行”的。模型没有编译期检查,它只是在下一次生成时大概率遵守这个上下文。所以配置内容必须足够清晰、没有歧义、没有内部冲突,否则AI依然会“选择困难”。这也解释了为什么后面我要专门讲踩坑记录——声明式配置写不好,比不写还糟糕。
4. 实测收益与踩坑记录:配置不是写得越多越好
4.1 我实测到的三个明显变化
给项目接上AGENTS.md之后,我观察了两周,有三个变化特别明显。
第一个是新对话进入状态的速度。以前新开窗口要先寒暄、铺垫、贴背景,起码花三五分钟。现在不用了——打开项目就能直接聊需求,AI对这个项目已经有基本认知,第一版答案的质量直接高了一个档次。
第二个是输出风格和项目约定的一致性。过去AI生成的代码一眼就能看出来是“模型生成的”——命名随意、结构扁平、错误处理敷衍。现在它严格遵守项目规范,命名风格、模块划分、错误处理方式都和已有代码融为一体,review的时候少改很多东西。
第三个是建议的可靠性提升。因为配置里写了架构约束和领域术语,AI给出的方案会更贴合项目实际情况。以前它会为了看起来“聪明”而过度设计,现在它知道这个项目的边界在哪,哪些方案在这个项目里根本不适用。
4.2 踩过的坑:长篇小说式写法、规则冲突、过时配置
收益很香,但坑也不少。我把踩过的坑分成四类,每一类都是真实经历。
坑一:写得像长篇小说,token占太多反而失效。我最早把AGENTS.md当成设计文档来写,动辄四五百行,结果AI确实“读”了,但注意力被稀释得很厉害——它记住了开头一两段,后面全被忽略了。后来我把配置压到100行以内,只保留不可推断的信息和硬约束,反而效果好得多。验证方法很简单:看AI的输出是否稳定遵守中后段的规则,如果不遵守,说明你的配置太长了。
坑二:规则和代码重复,导致维护双倍成本。我在配置里写了一大段“controllers层包含哪些文件、services层包含哪些文件”,结果代码一重构,配置文件就过期了。AI照着旧规则给建议,越帮越忙。后来我把所有能从代码本身读出来的内容全部移出配置,AGENTS.md只保留“代码里看不出来的约定”。
坑三:规则内部冲突且没有声明优先级。有一次根目录写了“所有模块禁止直接访问数据库”,但子目录的AGENTS.md又写了“报表模块可以直接写SQL”。AI遇到这种冲突会随机选择一条遵守,行为不可预测。解决方法是:在文件里显式声明“子目录规则优先于根目录规则”,并且全局规则里对冲突情况给出明确的裁决策略。
坑四:过时配置没清理,AI守着旧约定。项目某个模块换了技术方案,但AGENTS.md里的旧约束没删。AI每次改这个模块都严格遵守旧约定,代码风格新旧割裂。这个坑最隐蔽,因为AI不会主动指出你的配置过时了。我后来的做法是:每次技术方案变更时,把“更新AGENTS.md”也列进任务清单,当成一等公民来对待。
4.3 一套经过验证的维护节奏
基于这些坑,我逐渐形成了一套维护节奏,现在分享出来供参考。
首先是更新时间点。技术栈变更、架构调整、命令改变、代码规范调整——这四个时刻必须同步更新AGENTS.md。我把这条要求写进了团队的工作流约定,谁改动这些内容,谁就负责更新配置。
其次是纳入code review。AGENTS.md对我来说是代码资产,不是文档附属品。每次PR我都要看一下“本次改动是否需要同步AGENTS.md?”如果需要但没改,直接打回。我甚至会让AI自己在PR描述里检查“改动是否违反现有AGENTS.md规则”,相当于用AI来监督AI。
最后是定期清理。我习惯按项目节奏,每完成一个里程碑就过一遍AGENTS.md,删掉不再适用的规则、合并重复表述、更新术语表。这个习惯的价值在于:配置文件的体积会自然膨胀,定期削峰才能让它保持“精炼可读”的状态。
5. 从个人项目到团队协作:共享记忆的维护与演进
5.1 团队场景:AGENTS.md 变成团队的“公共记忆”
AGENTS.md最初是我个人项目里的实验,后来我把它带进了团队项目,效果比预想的更好。原因是,它天然适合多人协作者维护的“公共记忆”——团队里每个人都可能有自己的一套和AI协作的口头习惯,但把这些习惯沉淀到一份共享配置文件之后,AI无论和谁协作,行为都保持一致。
这里有个很好用的分工:README是给人看的,AGENTS.md是给AI代理看的。README负责介绍项目、快速上手、架构选型,它面向的是人类读者,需要故事性和引导性。AGENTS.md面向的是AI代理,它需要的是精确、命令式、无歧义的规则。两份文件可以互相引用,但不要互相替代。把内容混在一起,AI会分不清哪些是需要执行的规则,哪些只是背景说明。
团队场景下还有一个额外的好处:code review中反复强调的约定,可以同步沉淀进AGENTS.md。比如“这个模块的接口设计要注意什么”“这种异常场景我们统一怎么处理”,写进去之后AI会在后续生成时自动遵守,减少人工review反复唠叨的成本。
5.2 记忆的版本化与自动化:AGENTS.md 的演进方向
我现在看到的一个趋势是:AGENTS.md这类配置文件正在从“手工维护的文档”变成“和代码同步演进的工程资产”。让配置文件随代码一起进git只是第一步,更新的方向有三个。
第一个是记忆的自动提取。目前AGENTS.md的内容基本靠人来写,但理论上可以从git历史、commit消息、issue记录里自动提炼项目事实,生成初稿,再由人来review。已经有团队在实验这种模式,让AI自己总结“项目这周的约定变化”,然后人工确认合并进AGENTS.md。
第二个是配置的运行时校验。既然配置里写了“禁反向依赖”,能不能做成一个hook,在CI阶段跑一遍静态分析,违反配置规则就直接报错?这种把“声明式约定”变成“可执行检查”的思路,我认为是声明式记忆的最终形态——AI不仅能读规则,代码库本身也有机制来执行规则。
第三个是跨项目的个人记忆库。除了项目级的AGENTS.md,还可以维护一份个人级的规则文件,比如“我偏向函数式风格”“注释不写版权头”“错误处理必须显式”。AI代理会先读个人规则,再读项目规则,两层结合。这对自由职业者或经常切换项目的开发者尤其有价值。
5.3 最后分享一点个人体会
研究Memory工程这段时间,最让我醒忹的一点是:AI编程的瓶颈已经不完全在模型能力,而在于我们对“上下文”的管理方式。AGENTS.md不是什么神奇魔法,它只是把“如何和AI协作”这件事,从临时的口头叮嘱,变成了项目里一等的代码资产。
我现在每开一个新项目,第一件事不是写README,而是先写AGENTS.md——先把AI需要知道的东西定下来,再让AI帮我把其他东西写出来。刚开始维护它的时候会觉得麻烦,但运行一两个星期之后,你会明显感觉到和AI协作的体验完全不同:它不再是那个每次见面都要重新认识项目的陌生人,而是一个读过项目手册、懂得行规的老搭档。
如果你也在和AI协作开发的过程中被“失忆”困扰,我真心建议你从一份一百行左右的AGENTS.md开始。不用追求完美,先写最常被问到的那些问题和最常被违反的那些规则,然后跟着项目一起演进。你会回来感谢自己的。