1. 从"每次重新解释一遍"说起:无状态模型到底卡在哪
如果你最近半年深度用过任何一款AI编程助手,大概率经历过这种循环:新开一个会话窗口,把项目结构、技术栈、代码规范、命名习惯、测试框架、目录约定重新讲一遍,AI才勉强给出能用的代码。换个窗口,一切归零。这不是模型不够聪明,而是它的工作方式决定的——大语言模型本质上是无状态的,每一次推理都是一次独立的函数调用,上一次对话里你费尽口舌建立的上下文,在会话结束的那一刻就蒸发了。
这个特性在聊天场景里无所谓,但在编程场景里是致命的。写代码从来不是孤立行为,它高度依赖项目上下文:这个模块为什么用这个设计模式、那个工具函数为什么不能随便改、接口返回值的字段命名为什么和数据库列名不一致。这些"隐性知识"散落在老代码、注释、提交记录和团队成员的脑子里,AI看不到,就只能靠猜。猜对了是运气,猜错了你还得花时间纠正它。
于是很多人开始想办法"喂"上下文:把关键文件贴进对话、写一段长长的系统提示词、用注释在代码里埋提示。这些做法短期有效,但都有同一个毛病——不可复用、不可维护、不可协作。你写的提示词只有你自己看得懂,换个人接手就得重新理解;项目一升级,提示词里的信息就过期了,但没人记得去改。
Memory工程要解决的就是这个问题。它的核心思路很朴素:既然模型无状态,那就把"状态"外置成一份结构化、可版本管理、可被工具读取的文件。这份文件不依赖某一次对话,而是跟着代码仓库走,谁打开项目谁就能拿到同一份上下文。而AGENTS.md这类声明式配置,正是这种思路目前最接地气的落地形态——用一份人类可读、机器可解析的Markdown文件,把"这个项目是什么、怎么跑、有哪些规矩"一次性说清楚。
这篇文章不打算停留在概念层面。我会把无状态模型带来的具体痛点拆开,讲清楚Memory工程为什么必须走"声明式"这条路,然后给出一份可以直接抄的AGENTS.md结构,再聊聊实际落地时那些文档里不会写的坑。无论你是刚接触AI编程的新手,还是已经在团队里推AI协作的老手,应该都能从中找到能直接用的东西。
2. 无状态模型的三个真实痛点,以及为什么"多轮对话"救不了
2.1 上下文窗口不是记忆,只是临时工作台
很多人有个误解:既然模型支持几十万token的上下文窗口,那把整个项目塞进去不就行了?理论上可以,实践上很蠢。原因有三层。
第一层是成本。上下文窗口里的每一个token都要参与计算,你每次提问都带着几万token的项目代码,费用和延迟都会成倍上涨。第二层是注意力稀释。上下文越长,模型对其中关键信息的抓取能力越弱,这在业界被称为"lost in the middle"现象——开头和结尾的信息记得牢,中间的大段内容容易被忽略。第三层是时效性。代码是活的,今天贴进去的版本明天就变了,而上下文窗口里的内容是静态快照,不会自动更新。
所以上下文窗口解决的是"单次任务的信息量"问题,不是"跨会话的记忆"问题。把这两者混为一谈,是很多AI编程实践走偏的起点。
2.2 提示词工程的天花板:个人技巧无法变成团队资产
早期大家靠"提示词工程"来弥补无状态,写一堆"你是一个资深工程师""请遵循以下规范"之类的话术。这套东西在个人使用时有奇效,但一旦进入团队协作就崩了。
问题在于,提示词是隐性的、口头的、易失的。A同学调好了一段提示词,效果很好,但他没法把它"安装"到B同学的编辑器里;就算复制粘贴过去,B同学的项目结构不一样,提示词里的具体路径和约定全对不上。更麻烦的是,提示词没有版本概念,改了就是改了,没人知道上一版长什么样,也没法回滚。
这本质上是一个知识管理问题,而不是提示词技巧问题。团队需要的不是某个人手里的"咒语",而是一份所有人都能读、能改、能追溯的共享文档。
2.3 模型换代带来的"记忆断层"
还有一个容易被忽略的痛点:模型是会换的。今天你用A模型,明天可能换成B模型,或者同一个模型升级了大版本。不同模型对提示词的敏感度、对格式的偏好、对指令的遵循程度都不一样。你为A模型精心调教的提示词,换到B模型上可能完全失效。
如果上下文信息是硬编码在提示词里的,那每次换模型都是一次重写。但如果上下文是声明式地存在一份标准文件里,换模型时只需要调整读取和注入的方式,内容本身不用动。这就是声明式配置相对于命令式提示词的核心优势——内容和载体解耦。
把这三个痛点摆在一起看,结论就很清楚了:无状态模型需要的不是更长的上下文,也不是更花哨的提示词,而是一份外置的、结构化的、可版本管理的项目记忆。这份记忆的载体,就是接下来要讲的AGENTS.md。
3. 声明式配置为什么比命令式提示词更适合做Memory载体
3.1 声明式与命令式的本质区别
先把这个概念说清楚,不然后面全是空中楼阁。命令式是"告诉系统怎么做",声明式是"告诉系统要什么"。放到AI编程场景里:
- 命令式提示词:你写一段话,指导AI"先读package.json,再检查src目录,然后按照ESLint规则生成代码……"——你在描述流程。
- 声明式配置:你写一份文件,说明"本项目使用TypeScript严格模式,测试用Vitest,组件放src/components"——你在描述事实。
区别在哪?命令式的流程是死的,换个任务就得重写;声明式的事实是活的,任何任务都能基于同一份事实去推理。AI拿到"本项目用Vitest"这个事实后,你让它写测试它就知道用Vitest,你让它改测试它也知道用Vitest,不需要你每次都交代一遍。
3.2 声明式配置天然适配版本管理和协作
AGENTS.md本质上就是一个Markdown文件,放在代码仓库根目录。这意味着它自动获得了Git带来的一切好处:有历史、能diff、能review、能回滚、能分支。项目规范变了,改这个文件,提交记录里清清楚楚;新人入职,读一遍这个文件就知道项目全貌;多个AI工具接入,读的是同一份文件,行为一致。
这一点是命令式提示词永远做不到的。提示词存在某个工具的配置里,是私有的、黑盒的、不可协作的。而声明式文件是公开的、透明的、属于项目本身的。
3.3 一份文件,多个消费者
声明式配置的另一个好处是消费者多样。同一份AGENTS.md,可以被不同的角色读取:
| 消费者 | 读取方式 | 用途 |
|---|---|---|
| AI编程助手 | 工具自动注入或手动引用 | 生成符合项目规范的代码 |
| 新加入的开发者 | 人工阅读 | 快速理解项目结构和约定 |
| CI/CD流程 | 脚本解析特定字段 | 校验代码是否符合声明规范 |
| 代码审查工具 | 解析规则段落 | 自动检查PR是否违规 |
一份文件服务多个场景,这是命令式提示词想都不敢想的。你写一次,收益是复利的。
3.4 从"教AI做事"到"给AI事实"的思维转变
我观察到一个很有意思的现象:新手用AI编程,总想着"怎么把AI教明白";老手用AI编程,想的是"怎么把项目讲清楚"。前者是命令式思维,后者是声明式思维。
教AI做事,你永远教不完,因为任务无穷无尽。给AI事实,你只需要把项目的基本盘交代清楚,剩下的让模型自己去推理组合。这就像带新人:你不会手把手教他每一个操作,而是给他一份项目文档,让他自己查、自己判断。AGENTS.md就是这份文档。
4. 一份能直接用的AGENTS.md该长什么样
4.1 结构设计:分区块,每块只讲一件事
AGENTS.md不是越长越好,而是要结构清晰、信息密度高。我实践下来,一份好用的文件应该包含以下几个区块,每个区块职责单一:
# 项目名称 ## 项目概述 一句话说明这个项目是做什么的,面向什么用户。 ## 技术栈 - 语言:TypeScript 5.x(严格模式) - 框架:React 18 + Vite - 状态管理:Zustand - 测试:Vitest + Testing Library - 包管理:pnpm ## 目录结构 - src/components:通用UI组件,每个组件一个目录 - src/features:按业务功能划分的模块 - src/lib:与业务无关的工具函数 - src/types:全局类型定义 ## 编码规范 - 组件一律使用函数式写法,禁止class组件 - 所有导出必须有显式类型标注 - 禁止使用any,必要时用unknown加类型守卫 - 命名:组件用PascalCase,函数和变量用camelCase,常量用UPPER_SNAKE_CASE ## 常用命令 - 开发:pnpm dev - 构建:pnpm build - 测试:pnpm test - 类型检查:pnpm typecheck ## 注意事项 - 修改src/lib下的工具函数前,先搜索所有引用点 - 新增依赖前先确认是否已有同类库 - 提交前必须通过类型检查和测试这个结构的好处是:AI读一遍就知道项目全貌,人读一遍也能快速上手。每个区块都是事实陈述,不是流程指导,所以不会随任务变化而失效。
4.2 技术栈区块:写版本,别写"最新版"
技术栈这块有个坑:很多人写"React最新版""Node LTS",这种模糊描述对AI毫无帮助,甚至有害。AI可能按它训练数据里的版本来生成代码,和你项目实际用的版本对不上。
正确做法是写明确版本号。React 18和React 19的API有差异,TypeScript 4和5的类型系统行为不同,这些差异会直接影响AI生成的代码能不能跑。写清楚版本,AI才能生成匹配的代码。
另外,包管理器一定要写。pnpm、npm、yarn的lock文件格式不同,命令也有细微差别。AI如果不知道你用pnpm,可能给你生成npm install的命令,虽然能跑但和项目约定不一致。
4.3 目录结构区块:讲清楚"什么代码放哪里"
目录结构这块,光列目录名没用,要说明每个目录的职责边界。比如"src/components放通用组件"和"src/components放组件"是两回事,前者告诉AI什么该放进去,后者什么都没说。
我习惯在每个目录后面加一句职责说明,必要时加一句"什么不该放这里"。比如:
src/lib:与业务无关的纯函数工具,不依赖任何业务类型src/features:按业务域划分,每个feature内部可以有自己的components、hooks、utils
这种"边界说明"能极大减少AI把代码放错地方的概率。
4.4 编码规范区块:只写"反直觉"的规则
编码规范最容易写成一堆废话。"代码要清晰""命名要有意义"这种话,AI本来就知道,写了等于没写。真正有价值的是反直觉的、项目特有的规则。
比如"禁止使用any"就是反直觉的,因为AI默认可能用any图省事;"组件一律函数式"也是,因为AI可能生成class组件。这些才是需要明确声明的。而"变量命名要有意义"这种通用规范,写进去只是占地方。
判断标准很简单:如果一条规则是AI默认就会遵守的,就别写;如果AI默认会违反的,必须写。
4.5 注意事项区块:把踩过的坑写进去
这个区块是AGENTS.md最有价值的部分,因为它承载的是项目特有的隐性知识。比如"修改某个工具函数前先搜索引用点",这条规则背后可能是某次线上事故;"新增依赖前先确认是否已有同类库",背后可能是依赖膨胀的教训。
这些知识在传统项目里靠口口相传,新人要踩一遍坑才知道。写进AGENTS.md后,AI和人都能直接获得,省下大量试错成本。
5. 落地实操:让AI真正读到并遵守这份配置
5.1 不同工具的读取方式不一样
写好了AGENTS.md,下一步是让AI工具真正读到它。这里有个现实问题:不同工具的读取机制不同。
有些工具会自动扫描项目根目录的约定文件并注入上下文,你什么都不用做;有些工具需要你在配置里显式指定文件路径;还有些工具只认特定文件名,你得把内容放到它认的那个文件里。
我的建议是:以AGENTS.md为主文件,其他工具需要的格式用软链接或构建脚本生成。这样内容只有一份,维护成本最低。如果工具支持自定义路径,直接指向AGENTS.md即可。
5.2 手动引用作为兜底方案
如果工具不支持自动读取,那就手动引用。在对话开头说一句"请先阅读项目根目录的AGENTS.md",大多数工具都能理解并去读。虽然多了一步,但比每次重新交代上下文强太多。
更稳妥的做法是把关键信息做成一个可复制的片段,存在编辑器snippet里,新开会话时一键插入。这样既保证了信息一致,又不用依赖工具的自动读取能力。
5.3 验证AI是否真的读懂了
写完配置不代表AI就遵守了。你需要主动验证。方法很简单:问AI几个只有读了配置才能答对的问题。比如"这个项目用什么测试框架""组件应该放在哪个目录"。如果AI答对了,说明配置生效;如果答错或含糊,说明没读到或者读到了没理解。
这个验证步骤很多人跳过,结果配置写了等于白写。花两分钟验证一下,能省下后面无数次纠正。
5.4 把配置纳入代码审查流程
AGENTS.md既然是项目文件,就应该和代码一样接受审查。每次修改都应该有明确的理由,改动应该被review。我见过太多团队把这份文件当成"一次性文档",写完就扔在那,项目变了文件不变,最后AI读到的是过期信息,反而帮倒忙。
把AGENTS.md的更新纳入PR流程:改代码规范的同时改配置,加新依赖的同时更新技术栈区块。让它成为活文档,而不是死档案。
6. 踩坑实录:那些文档里不会写的经验
6.1 配置写太满,反而干扰AI判断
刚开始用AGENTS.md时,我犯过一个典型错误:把能想到的所有规则都写进去,文件写了上千行。结果AI反而变笨了——因为信息太多,重点被淹没,模型抓不住关键约束。
后来我做了减法,只保留真正影响代码生成质量的规则,文件压缩到一百多行,效果反而更好。这印证了前面说的"注意力稀释"原理:不是信息越多越好,而是信噪比越高越好。
判断哪些该留:问自己"如果AI不知道这条,会不会生成错误的代码"。会,就留;不会,就删。
6.2 规则之间打架,AI会随机选一个
另一个坑是规则冲突。比如你在编码规范里写"优先使用函数式编程",又在某个feature的说明里写"这个模块用class组织",AI遇到具体任务时就懵了,可能随机选一个执行。
解决办法是明确优先级。在文件开头加一句"当通用规范与模块特定说明冲突时,以模块特定说明为准"。这样AI就有了判断依据,不会瞎猜。
6.3 更新不及时,AI拿着旧地图找新路
最隐蔽的坑是配置过期。项目重构了目录结构,但AGENTS.md没改,AI还按旧结构生成代码,你看着一堆路径错误的import,还以为是AI变笨了。
我的做法是把配置更新和代码改动绑定:每次重构目录、换依赖、改规范,都在同一个PR里更新AGENTS.md。这样两者永远同步,不会出现"地图和地形对不上"的情况。
6.4 别指望一份配置适配所有任务
最后说个心态问题:AGENTS.md不是银弹。它能解决"项目上下文缺失"的问题,但解决不了"任务描述不清"的问题。如果用户的需求本身模糊,AI读再多配置也生成不出对的代码。
配置解决的是背景知识,需求描述解决的是当前目标,两者缺一不可。别把配置当成万能药,它只是把AI从"完全瞎猜"提升到"有依据地推理"。
7. 从Memory工程看AI编程的下一步
把AGENTS.md这类声明式配置放到更大的视角里看,它其实是AI编程范式转换的一个缩影。早期的AI编程是"对话式"的,人和模型你一句我一句,上下文靠对话维持;现在正在转向"配置式"的,项目上下文外置成文件,模型按需读取;再往前看,很可能是"代理式"的,AI代理自主读取项目记忆、规划任务、执行并验证。
这个演进路径背后有一条清晰的主线:把隐性的、易失的、私有的知识,变成显性的、持久的、共享的资产。AGENTS.md只是这条路上的一个站点,但它已经足够实用,足够让今天的开发者受益。
我自己的体会是,自从把项目上下文沉淀成声明式配置后,用AI写代码的体验发生了质变。以前是"每次都要重新教",现在是"打开就能用";以前是"只有我会用",现在是"团队都能用"。这种从个人技巧到团队资产的转变,才是Memory工程真正的价值所在。
如果你还没开始做这件事,建议从最小的AGENTS.md开始——哪怕只写技术栈和目录结构两个区块,也比什么都不写强。用起来之后,你会自然发现还需要补充什么,然后逐步迭代。这份文件不需要一次写完美,它需要的是持续维护、和项目一起生长。