过去大半年,我几乎每天都会和 AI 编程助手一起改代码,做得最多的一件事居然是“自我介绍”:项目用什么框架、构建命令是什么、测试脚本怎么跑、代码风格有哪些规矩——每次新开一个会话,这些都得重新讲一遍。讲得多了你会发现,AI 不是不聪明,是“记性不好”——每一次对话都是无状态的,模型本身不保留任何跨会话的项目记忆。这也是为什么最近“Memory 工程”和“AGENTS.md 声明式配置”这两个概念会同时被大家反复讨论。它们本质上都在回答同一个问题:怎么让一个无状态模型在具体项目里干活时,表现得像一位熟悉项目的资深同事。这篇文章会从无状态模型的痛点讲起,拆解 Memory 工程的分层思路,再手把手带你写出一份能用的 AGENTS.md,并分享我在真实项目里踩过的坑。项目不管大小,这套方法都能用上。
1. 无状态模型的困境:每次对话都是“重新认识项目”
1.1 无状态到底意味着什么
先把这个词掰开看看。无状态(stateless)并不是说 AI 不会聊天,而是说每次会话结束后,模型不会把这段对话沉淀成任何持久记忆。它所有的“知识”都来自当前这个上下文窗口:你贴进去多少,它就看见多少;窗口一关,一切清零。这个设计在通用对话场景下问题不大,但在软件工程里是硬伤——一个稍微像样的项目,背后是几十个技术决策、上百条约定、上千个文件累积出来的复杂度。你让模型在没有历史记忆的前提下每次都产出符合项目气质的代码,本质上靠的不是能力,是运气。
打个比方:这就像一个业务能力很强、但患有失忆症的工程师。他每天走进办公室,连公司正在做哪个产品都不记得。你花半小时给他讲清楚业务背景,他当天能把需求改得不错;第二天你再找他,他又一脸茫然。项目模块一多,你甚至每天要花大半个上午在“恢复记忆”上。我在项目刚开始引入 AI 辅助编码时,最直观的感受就是:能力是够的,沟通成本是真高。
1.2 无状态带来的三个代价
第一个代价是重复劳动。项目里最常用的命令、目录职责、命名规则,每次会话都要重新描述。你把这个信息组织成一段几百字的“项目自我介绍”,粘贴、等待、验证,一上午重复三五次,时间就没了。而且现在很多 AI 工具是计费的,反复粘贴同样的上下文,等于每一轮都在为重复信息买单。
第二个代价是风格漂移。同一个项目里,AI 周一写的模块和周五写的模块,可能完全是两种风格,因为它根本不知道周一你俩刚决定了“错误码统一走枚举”。无状态导致每一次产出都是一锤子买卖,等代码合到一起,你就开始还债式重构。我在一个模拟项目里就吃过这个亏:不同会话生成的接口层,一个用了封装好的统一响应体,一个直接裸返回对象,集成时改了好几天。
第三个代价是上下文挤出。为了让 AI 干活靠谱,你得往 prompt 里塞项目背景,可背景塞得越多,真正留给代码分析和推理的 token 就越少。长上下文和有效上下文是两回事——你贴了三千字的项目介绍,模型是“看过”了,但当它需要认真分析一个文件时,注意力预算已经被背景故事吃掉大半。越大的项目,这个问题越严重。
1.3 为什么“记住”比“变聪明”更值得投入
模型本身的推理能力当然还在提升,但仔细想一下:把一个 70 分的模型放进一个信息充分的环境,实际产出可能超过一个 82 分模型配合一片空白上下文。这就是 Memory 工程出现的根本原因:模型负责推理,我们负责让它“记得”。与其眼巴巴等下一代模型变聪明,不如先把已经存在的能力充分释放出来。所以后来我给自己定了一个原则:凡是超过一周还要重复讲解的项目信息,就不该留在 prompt 里,它应该被固化成一个文件,被模型自动读取。这个文件,最终长成了 AGENTS.md。
2. Memory 工程:给无状态模型搭建记忆系统
2.1 从 prompt 工程到 memory 工程
过去几年大家热衷研究 prompt 工程,本质上是在单次对话这个狭小空间里做优化:怎么写指令、怎么组织示例、怎么让输出更稳。但无论 prompt 写得多漂亮,它都是“一次性消费品”,换一个会话就得重来。Memory 工程换了一个思路:把信息放在模型每次开工都会自动读取的位置,让它成为一种可持续复用的基础设施。打个比方,prompt 工程是教你每次开会都精确表达需求,Memory 工程是干脆给新员工一本入职手册。
这个转变带来的直接收益是,AI 的“下限”被抬高了。哪怕你某次 prompt 写得很随意,只要项目里的记忆文件足够好,模型产出的代码依然能踩在项目的既有约定上。这比我过去抠 prompt 字眼要稳健得多。
2.2 记忆的四层架构
| 记忆层次 | 典型载体 | 主要作用 | 主要局限 |
|---|---|---|---|
| 会话记忆 | 当前对话的上下文窗口 | 保持单个任务的连贯性 | 会话结束即失效,不可复用 |
| 项目记忆 | AGENTS.md、README、架构文档、CI 配置 | 让 AI 一上来就理解项目全貌与约定 | 需要人工维护,容易写长写旧 |
| 用户记忆 | 个人偏好配置、常用命令别名 | 贴合你个人的工作习惯 | 不同工具之间难以互通 |
| 外部记忆 | 向量检索库、团队知识库、检索接口 | 突破上下文窗口的容量上限 | 引入检索延迟和额外复杂度 |
这四层里,项目记忆是性价比最高的切入口。它不需要搭服务、不依赖特定平台,就是一个文本文件,却能在每次会话里稳定发挥作用。AGENTS.md 就是在这个层次上被越来越多工具认可的规范格式:放在仓库根目录,模型启动时自动读取,用纯 Markdown 描述项目关键事实。换句话说,它正在成为“项目记忆”层的通用接口。
2.3 声明式配置:范式转换的本质
AGENTS.md 被称作“声明式配置”,这个定语值得停下来理解一下。命令式的写法是教 AI 一步步做事:“你先打开 package.json,找到 scripts 里的 test 项,然后运行 npm test,如果失败就把错误贴出来……”声明式的写法是告诉它这个世界是什么样:“本项目测试命令是 npm test;任何改动合并前必须通过测试。”至于怎么执行、先做什么后做什么,交给模型自己判断。
声明式的好处,我在实践里体会得很深。第一,稳定。它描述的是相对不变的事实和约束,不会因为模型版本、工具偏好而漂移。第二,可审查。一个 Markdown 文件可以走 git diff、走 code review,出了问题能追溯,“项目记忆”本身也是代码资产。第三,人机两用。新同事入职看这份文件也能快速上手,团队的知识沉淀不只是喂给 AI,也是喂给人类。所以范式转换的本质,是把“给 AI 下命令”变成“让 AI 理解项目”——命令是一次性的、脆弱的,而理解是可以积累、可以复利的。
3. AGENTS.md 核心结构拆解:像写操作手册,而不是写 prompt
3.1 先想清楚定位
写 AGENTS.md 之前,最重要的事是搞清楚它不是什么。它不是魔法咒语,也不是越详细越好的巨型规则列表。它应该是一份“仓库操作手册”:模型在每次会话开始时把它自动读进上下文,理解“这个项目是怎么运转的”。它的读者有两个:AI 和未来的人类同事。所以好的 AGENTS.md 读起来应该像一位资深工程师在交接项目时说的话,而不是一份法律条文。
落笔之前,请默念这几条原则:
- 宁短勿长。AGENTS.md 是要进上下文的,太长会挤占代码分析的注意力。我的经验是 100 到 200 行是甜点区,超过 300 行就要考虑瘦身。
- 最重要的信息放在最前面。模型读取上下文时前面的信息权重更高,把命令、关键约束这种核心事实前置。
- 只写事实,不写形容词。“测试跑得很快”这种话毫无意义,“test 套件约 800 个用例”才有用。
- 命令要可直接复制运行。自己先跑一遍验证,别写“理论上可以”的命令。
- 深度内容用链接外置。架构细节、设计文档放在 docs/ 里,在 AGENTS.md 中给出入口就行。
3.2 六大核心板块
项目概述。一段话讲清楚项目是干什么的、技术栈是什么、有什么硬性约束,比如必须兼容旧版本、必须离线可用、必须满足某种合规要求。AI 有了这个底,后续所有判断都不会跑偏。我见过很多项目漏了“约束”,结果 AI 理所当然地用上了线上 API,完全没意识到部署环境是内网。
常用命令。build、test、lint、format、run——这几类命令是 AI 每天都要用的。把每个命令的完整形式和前置条件写清楚,注意包管理器到底是 npm、pnpm 还是 yarn,需要哪些环境变量,有没有依赖的服务要先启动。这里写错一个字符,AI 能在这条命令上反复折腾半小时。
项目结构。不用把整个目录树贴进来,标注关键目录的职责就够:入口在哪、业务代码在哪、测试放哪、公共组件和工具函数放哪。模型知道了地图,改代码时才能迅速判断该动哪个文件,而不是全仓库瞎翻。
编码与设计规范。把项目里真正稳定、真正会被违反的规则写进来。命名风格、错误处理约定、状态管理方式、接口返回结构,每一条都要具体。最有效的写法是配一两个 bad/good 反例——AI 看到例子,理解成本直接降一半。
工作流。说明一个人在仓库里从想法到合入的完整路径:分支怎么拉、提交信息格式是什么、测试要过哪几层检查、合并前需要跑什么。AI 按这套流程干活,产出的改动才不用你返工。
已知的坑与约定。把团队用血泪换来的教训写下来。比如“某个模块的构建缓存经常失效,改完配置要先清缓存”,或者“历史遗留代码里有两套日期处理,新代码一律用新工具类”。这些信息平时散落在聊天记录里,如今终于有个地方能安放。
3.3 一份可以直接抄的模板
下面是模板,结构上用上面六个板块,内容按你的项目替换。注意不要复制即用,一定要跑一遍命令、确认信息属实。
# AGENTS.md ## 项目概览 本项目是一个面向中小团队的轻量任务管理工具,核心价值是离线优先、多端同步。 技术栈:前端 TypeScript + React + Vite,后端 Go + PostgreSQL,桌面端用轻量桌面封装方案。 硬性约束:所有核心功能必须离线可用;禁止引入需要联网才能运行的第三方服务。 ## 常用命令 # 依赖安装(使用 pnpm) pnpm install # 启动前端开发服务(端口默认 5173) pnpm dev:frontend # 启动后端 API(需要先启动本机 PostgreSQL,连接串见 .env.local) pnpm dev:backend # 运行全部测试 pnpm test # 运行 lint(提交前必须通过) pnpm lint # 生成类型定义(修改后端接口后执行) pnpm gen:api ## 项目结构 src/frontend/ 前端应用代码,入口为 src/frontend/main.tsx src/backend/ 后端 API 服务,入口为 src/backend/main.go src/shared/ 前后端共享的类型定义与常量 tests/integration/ 端到端集成测试,使用 Playwright docs/ 架构设计与决策记录 ## 编码与设计规范 - 前端组件使用函数式组件 + Hooks,不使用 class 组件。 - 所有日期时间统一使用 src/shared/time.ts 里的工具函数,禁止自行 new Date() 做运算。 - 接口错误码统一使用 src/shared/errors.ts 中的枚举,禁止直接返回数字字面量。 - 新增 API 必须同步在 src/shared/api.ts 中补充类型,并运行 pnpm gen:api。 - 禁止在组件内直接写 fetch,统一走 src/shared/request.ts 封装。 - 反例:直接 setState 后立刻读取该 state(React 状态更新是异步的)。 ## 工作流 1. 功能分支统一从 main 拉出,命名使用 feature/xxx 或 fix/xxx。 2. 提交信息格式:type(scope): description,例如 fix(auth): 修复 token 过期后无提示。 3. 提交前必须运行 pnpm lint 和 pnpm test。 4. 合并前需要在本地启动前后端做冒烟验证。 ## 已知的坑 - vite 的 HMR 在部分 Windows 环境下失效,改配置后需手动重启 dev server。 - 历史代码 migrate 目录里有两套数据库命名风格,新增迁移统一使用 snake_case。 - 后端测试需要真实 PostgreSQL,不要用内存数据库跑集成测试,结果会不一致。这个模板的密度刚好:六个板块都覆盖,又不至于让模型读到后面已经完全忘了前面。团队小、项目简单,甚至可以更短;项目复杂度上去之后,优先给“规范”和“坑”两个板块扩容。
4. 实操过程:把 AGENTS.md 从“写了”变成“有用”
4.1 第零步:盘点项目事实
别一上来就凭记忆写。我推荐走一遍“项目盘点”,把仓库当成一个陌生代码库来读。先看 README,再看包管理清单和锁文件,看 CI 工作流里到底跑了哪些命令,看有没有 Makefile、Taskfile、docker-compose,最后翻一翻 docs/ 和架构文档。这个步骤的目的,是把散落在各处的项目事实收集齐。
收集时一定要亲自动手跑一遍关键命令,不要只读文档。很多项目的 README 半年没人更新,CI 才是真相来源。以 CI 为准,以你实际跑通为准。如果项目有多个服务,把启动顺序和依赖关系摸清楚:是先起数据库、再做迁移、再启动后端、最后起前端?这些在你看来理所当然的顺序,对 AI 来说完全是黑盒。
4.2 写命令与结构:给 AI 一张准确的地图
写“常用命令”时,别偷懒。把命令写成完整可复制执行的形式,不要省略路径,不要留“省略号”。如果某个命令需要环境变量,直接写清楚变量名;如果有服务依赖,明确注明“先启动 XX 再执行 XX”。AI 不会自动知道后端要先起来、数据库要先迁移,这些信息你不写,它就得猜,猜就得错。
写“项目结构”时,目录树展开到第二层或者第三层就够,每一层标一句职责。重点标出“新增功能时最可能动的位置”和“测试位置”——AI 改完代码如果能自己找到测试文件并补测试,你的 review 负担会小很多。我自己的习惯是在模板里加一句“入口是 XX”,因为模型拿到一个不熟悉的前端项目时,最常见的迷路方式就是找不到 main 文件。
4.3 把“骂过 AI 的话”变成规范
这一步是 AGENTS.md 里最有价值的部分,也是最多人忽略的部分。方法很简单:去翻最近两周的 code review 记录,找出 AI 生成的代码里被你反复吐槽的那些点。那些“怎么又写成这样”的瞬间,就是规范素材。比如我自己的项目里,AI 最爱犯这几个错:组件里直接写请求、错误码返回裸数字、改了接口忘了同步类型。每一条现在都在 AGENTS.md 里有对应的禁止或应当条目,并且都配了反例。
配例子非常关键。纯文字的规范“禁止在组件内直接请求数据”,模型理解得模棱两可;给一个“不这么写”和“应该这么写”的微型示例,它立刻就能照做。规范贵精不贵多,5 到 10 条真正踩过的坑,比 50 条宽泛建议强得多。宁可每条都短,也不要写一大段抽象原则。
4.4 用一套验收问题验证
写完别急着下班。开一个全新会话,模拟自己是第一次接触这个项目,问 AI 几个问题:
- 这个项目怎么启动?
- 新增一个列表查询接口,我需要改动哪些文件?
- 我改了类型定义后还要做什么?
- 提交代码之前要跑什么检查?
如果 AI 的回答能直接引用 AGENTS.md 里的命令和路径,说明配置生效了;如果答得含糊,就检查文件位置、文件名,以及信息是不是被太长篇幅埋住了。另外,也可以直接让 AI 复述 AGENTS.md 的关键内容,看它是否真的读进去了。这个方法笨,但很可靠。
4.5 迭代节奏:让它成为活文档
项目会变,AGENTS.md 也必须跟着变。我现在的做法是:每次重构涉及目录或命令时,顺手更新;每次发现新的坑,顺手记一条;提交合并时把 AGENTS.md 的改动和其他代码一起走评审。为了快速判断文件是否过时,我还会在文件里保留一个“最后校验日期”字段,超过一个月没动过,就主动检查一遍。
还有一点经验:AGENTS.md 初始版本不需要追求完美,先只写“常用命令”和“项目结构”两节,就能立刻见效。剩下的规范、工作流、坑,都是在日常使用中逐渐沉淀的。它是一份活文档,不是一次性的交付物。很多团队一开始雄心壮志,写了三百行规则,然后发现维护不动,最后整份文件形同虚设——反倒是从小处起步、随用随补,能真正活下来。
5. 常见问题与排查技巧实录
5.1 问题速查表
| 症状 | 可能的根因 | 解决办法 |
|---|---|---|
| AI 完全无视 AGENTS.md | 文件太长,核心信息被淹没 | 重排结构,把最重要的 10 条放最前面,砍到 200 行以内 |
| 模型说“没找到这个文件” | 文件名或位置不符合读取规则 | 放到仓库根目录,确认文件名大小写正确 |
| 命令跑不通 | 写的是过时命令,或没写前置条件 | 以 CI 为准逐条验证,补全环境变量和服务依赖 |
| 风格规范总被违反 | 规范写得太抽象,缺少反例 | 每条规范配一个 bad/good 微型例子 |
| 多个目录出现多份配置互相打架 | 子目录与根目录规则冲突 | 删除子目录冗余配置,统一以根目录为准 |
5.2 我在实践里踩过的几个坑
第一个坑:第一版 AGENTS.md 写了四百多行,事无巨细,从代码风格到团队流程全塞进去。结果模型经常“看了开头就忘了结尾”,核心命令反而没被执行。后来我把文件砍到一百五十行,把“常用命令”和“硬性约束”提到最前面,效果立竿见影。上下文是有预算的,你要替模型决定什么值得占这个预算。
第二个坑:我最早写了不少命令式条文,比如“你必须先跑测试再提交”“禁止使用任何第三方请求库”。看起来挺严格,实际执行率很低。后来把命令式改成声明式事实——“提交前测试必须通过”“项目内所有请求统一走 src/shared/request.ts”——AI 的遵守度明显提高。我推测原因是:命令式指令像是在跟模型的默认行为打架,声明式描述则像是给它补上了项目知识缺口,协作比对抗顺利得多。
第三个坑:一开始我把 AGENTS.md 放在 docs/ 子目录里,想着“文档都归 docs”。结果工具根本不会自动去那里读,等于白写。这类文件必须放在工具约定的位置,最常见的约定就是仓库根目录。如果你用了子目录,也先确认工具是否支持级联读取,别想当然。
第四个坑:文件写完之后没人维护。三个月后依赖升级、目录重构,AGENTS.md 里的命令已经跑不通了,AI 照着旧命令改代码,给出的建议全是过时的。现在我把 AGENTS.md 列进了“改动定义完成”清单,任何合并请求如果改变命令或结构,必须同步更新它。把它当成代码一样对待,它才会像代码一样可靠。
5.3 如何验证 AI 真的“记住了”
判断 AI 有没有吃透这份记忆文件,我一般看三个信号。第一,它给出的方案里是否自然使用了你在 AGENTS.md 里写过的术语和命令,比如提交信息格式、错误码枚举名。第二,它的提问质量——如果它一上来就问“这个项目的测试在哪”,说明文件没生效;如果它直接说“测试在 tests/integration 下,我先去补用例”,说明记忆到位了。第三,冷启动测试:隔一周再开新会话,重复上一节里的验收问题,看回答是否依然稳定。
还有一个实用的小技巧:真正重要的 AGENTS.md 内容,可以在文件开头用一两行标识性的句子,比如“错误码统一走 errors.enum.ts”。这种句子简洁、独特、容易在模型输出里被辨认。如果你在 AI 的回答里看到它被自然引用,就知道它确实读进去了,而不是在泛泛而谈。
6. 写在最后:范式转换不会一夜完成
这半年折腾下来,我最大的体会是:AGENTS.md 的价值不在于那个文件本身,而在于它逼着我把项目里散落的知识做了一次结构化整理。给 AI 写 AGENTS.md 的过程,其实就是把 README、CI、评审意见和脑子里那点经验重新编排成一张地图。地图画好之后,受益的不只是 AI,还有一个月后回来接手模块的自己,以及新加入的同事。
我也不觉得 AGENTS.md 会是终点。工具的规则文件叫什么、放在哪里,标准还在演化,很可能明年就有新的格式。但那条底层原则会留下来:把项目的关键事实从人的脑子里、聊天记录里释放出来,变成机器可读、人也可读的持久记忆。这是无状态模型时代最值得做的一笔投资。
如果你还没开始,我建议今天就拿一个小项目试水,先写“常用命令”和“项目结构”两节就够了,剩下的规范以后再补。别追求完美,先让它转起来。转起来之后你会发现,原来那些每天重复的“项目自我介绍”,终于可以彻底删掉了。