最近和几个团队聊 AI 辅助编程的落地效果,发现大家卡在了同一个地方——模型本身没有记忆。每一次新会话,AI 就像刚入职的实习生,什么都要重新教一遍。你解释过八百遍的项目架构、编码规范、测试命令、模块边界,它转头就忘。这个问题的根源在于大语言模型本质上是无状态的推理引擎,每次交互都是独立事件。过去两年我们靠 Prompt Engineering 硬撑,把背景塞进每条提示词,但项目一复杂就撑不住了。直到我把注意力转向Memory 工程,用AGENTS.md 声明式配置把项目记忆固化下来,局面才有了根本性的改观。
这篇文章会把整个思路完整拆开:无状态模型到底卡在哪、Memory 工程的核心逻辑是什么、AGENTS.md 该怎么设计、怎么实现、以及我在真实项目里踩过的坑。不管你是重度使用 AI 编程助手的开发者,还是在做 Agent 应用工程化的技术负责人,这篇内容都值得花十分钟看完。
1. 项目概述与核心思路拆解
1.1 无状态模型的局限与范式转换的起点
所有主流大语言模型在 API 层面都是无状态的。你调用一次接口,发送一段上下文,拿到一段回复,然后这段上下文就作废了。下一次调用,模型不会记得上次说过什么。这在闲聊场景里问题不大,但在软件工程项目里是致命的。
一个真实项目的背景信息量是巨大的:技术栈的选择理由、目录结构的组织逻辑、代码风格的约定、历史上踩过的坑、下游接口的契约……这些知识分散在代码、文档、评审记录、聊天记录里。每次开新会话,意味着 AI 要重新学习这一切。而它根本学不完——上下文窗口有上限,人更懒得每个会话都把背景重写一遍。
我见过最典型的场景:某跨平台系统的开发者在同一个项目里开了十几个 AI 会话,每个会话都在问"这个项目的架构是什么样的""日志模块在哪里""为什么这里要用异步"。这些问题的答案明明写在项目的 README 里,但 AI 不知道主动去读,开发者也不想复制粘贴。结果就是 AI 给出大量"看似合理但完全不符合项目实际情况"的建议。
所谓范式转换,核心就是从这里开始的:我们不再把每一次 AI 交互当成一次性的"提问-回答",而是把它放进一个持续的、有记忆的工作上下文里。这个转换的本质,是从"每次对话都是新的"变成"每次对话都接着上次的脉络走"。而承接这个脉络的载体,不是模型的参数,而是项目本身的结构化信息。
1.2 从 Prompt Engineering 到 Memory Engineering
Prompt Engineering 解决的是"在有限的上下文里说什么",Memory Engineering 解决的是"哪些信息该被长期记住,以及如何组织"。这是两个层面的事。
Prompt Engineering 的极限在哪里?你会发现,随着系统越来越复杂,你不可能把所有知识都塞进上下文窗口。暴力扩大提示词只会带来两个后果:第一,上下文窗口被无关信息占满,模型注意力被稀释,回答质量不升反降;第二,提示词维护成本失控,改一个模块名要同步改几十条提示词,项目还没上线维护成本先涨了十倍。
Memory Engineering 的思路完全不同:它把"记忆"外置在项目本身。项目的 AGENTS.md 文件就是 AI 的长期记忆存储,里面记录了项目的事实性信息、约定和禁忌。AI 每次进入项目时,通过读取这个文件来"恢复记忆"。这就像你重新进入一个项目时先翻一遍 wiki,而不是让前同事把整个项目的背景再讲一遍。
这个思路的精髓在于声明式配置。你不需要写"如果用户问架构,就告诉他 XXX"这样的指令式逻辑,只需要声明"这个项目是模块化单体架构,模块边界在 XXX"。AI 会自动判断什么时候需要这条信息、怎么使用它。声明式的意思是告诉 AI"是什么",而不是"怎么办"。这更符合大模型的推理方式,也更容易维护——你更新的是事实,而不是一套可能互相矛盾的行为规则。
2. Memory 核心架构:记忆分层与方案选型
2.1 记忆分层:工作记忆、长期记忆与系统记忆
在 Memory 工程里,我们可以借鉴认知科学中的记忆分层概念,把 AI 的记忆分成三层来理解:
| 层级 | 对应载体 | 特点 | 管理方式 |
|---|---|---|---|
| 工作记忆 | 当前对话上下文 | 易失、有限、响应快 | 通过提示词精打细算 |
| 长期记忆 | AGENTS.md、项目文档、代码库 | 持久、结构化、需检索 | 通过配置文件组织 |
| 系统记忆 | 模型预训练知识 | 想改改不了,只能引导 | 通过提示影响调用权重 |
一个完整的 Memory 工程方案需要同时管理这三层。AGENTS.md 主要管长期记忆这一层,但它设计的好坏会直接影响工作记忆的利用率——如果长期记忆组织得好,工作记忆就不用被大量重复性背景信息占据,可以把宝贵的 token 留给当前具体的任务。
举个实际遇到的例子。某电商平台的前端项目里,开发者把一个组件库的使用约定写进了 AGENTS.md。之后每次让 AI 写新组件时,AI 自动就知道要遵循这套组件库的 API 风格,而不是从自己的预训练知识里猜一套通用方案。这个操作的意义在于:它把"项目特定的、动态变化的知识"从"模型预训练的、通用的知识"里剥离出来,用项目内文件去覆盖模型对项目的预测偏差。
这套分层架构的价值还体现在一个容易被忽略的细节上:工作记忆被释放之后,AI 的推理深度会明显提升。上下文窗口里塞满背景知识时,模型的"思考空间"被压缩了。当长期记忆承担了这些负担,模型可以把更多注意力放在"怎么把代码写好"而不是"这个项目到底是什么"上。
2.2 RAG、微调与声明式配置的边界
很多人会问:既然要做 Memory 工程,那直接上 RAG 或者微调不就行了吗?
RAG(检索增强生成)适合处理大规模动态知识库,比如几十万条文档的语义检索。但对于一个软件项目来说,核心约定和架构决策往往只有几百行。为了几百行信息搭一套向量数据库、部署 embedding 服务、维护索引更新,运维成本和延迟成本都不划算,属于杀鸡用牛刀。
微调适合改变模型的通用行为模式,比如让模型学会某种特定的输出格式或行业术语。但微调成本高、周期长,而且对项目级的信息更新极其不友好。你总不能每次改一个模块命名规范就去微调一次模型——那既不经济也不现实。
AGENTS.md 声明式配置恰好落在中间地带:它是轻量的、项目内嵌的、文本格式的长期记忆。不需要额外的存储服务,不依赖特定的模型,任何人打开项目就能看到 AI 的"记忆"长什么样。这种透明性是 RAG 和微调都给不了的。你可以在代码评审里直接 review AI 的记忆内容,可以把它纳入版本控制,甚至可以 fork 一份做对比实验。
这也是我最终把方案核心定在 AGENTS.md 而非其他重型方案的原因:在软件工程场景里,"项目记忆"的规模本来就有限,高频更新且需要人工掌控。声明式文件是最匹配这个需求特征的形态。
3. AGENTS.md 声明式配置深度拆解
3.1 AGENTS.md 应该包含什么
基于实操经验,一个高质量的 AGENTS.md 通常包含六个模块:
- 项目概览:项目是什么、技术栈、目标架构、当前阶段。
- 目录结构与模块边界:每个目录/模块的职责,哪些模块之间可以互相依赖,哪些不可以。
- 构建与测试:完整的构建命令、测试命令、lint 命令,以及执行顺序。
- 编码规范:命名风格、错误处理方式、日志规范、格式化工具,以及明确禁止的反模式。
- 关键决策记录:架构选型的原因、技术债清单、历史变更的重要背景。
- Agent 行为约束:AI 在项目里应该主动做什么、不应该做什么、不确定时该找哪个文件。
写的时候有两条核心原则。
第一,记录"稳定的事实",而不是"临时的任务"。AGENTS.md 不是待办清单,它是项目的"宪法"。像"本周要完成登录模块重构"这种临时事项不应该出现在这里,它属于任务管理系统。而"登录模块采用 OAuth2 标准,禁止自研认证方案"这种长期有效的架构约定,才值得写进去。
第二,记录"容易猜错的信息"。那些模型凭预训练知识大概率能猜对的东西不用写,写上去纯属浪费上下文。真正值得写的是那些"模型第一次大概率会猜错,而且错了代价很大"的信息。
比如"本项目使用 pnpm 而非 npm"和"本项目不禁止任何 ESLint 规则,但有义务保持新增代码零警告"这两条,就是典型的值得写入 AGENTS.md 的内容——因为它们都违背模型的默认预期。模型大概率会建议你跑 npm install,但项目用的是 pnpm,这就是一次错误的预言。
3.2 一个可落地的 AGENTS.md 结构模板
我用的模板大致是这样(已脱敏简化):
# 项目身份 - 项目名称:某跨平台数据同步系统 - 技术栈:TypeScript + Node.js + PostgreSQL - 架构风格:模块化单体(Module Monolith),不是微服务 # 目录地图 - src/modules/sync/ -> 数据同步核心逻辑,禁止依赖 UI 层 - src/modules/ui/ -> 界面层,通过接口与 sync 层交互 - src/shared/ -> 跨模块共享的类型与工具 # 构建与验证 - install: pnpm install - build: pnpm build - test: pnpm test(单元测试) - lint: pnpm lint # 编码约定 - 所有新增 API 必须有类型定义,JSDoc 注释遵循 TSDoc 风格 - 错误处理统一使用 AppError,禁止裸 throw Error - 日志使用结构化输出,禁止 console.log # 禁止事项(反模式清单) - 禁止在业务代码里使用 any - 禁止直接修改 src/modules/sync 下的文件而不更新设计文档 - 禁止引入新的外部依赖,除非经过架构评审 # Agent 行为准则 - 当需要修改 sync 模块时,先阅读其内部 README 和设计文档 - 当不确定 API 契约时,优先搜索对应接口的测试文件 - 回答问题时引用具体文件路径,不泛泛而谈可以看到,这里每一行都是"是什么",而不是"怎么做"。AI 拿到这份配置后,面对"给我加一个重试机制"这样的需求,会自动判断:重试逻辑应该放到 sync 模块,遵循现有错误处理规范,不能引入新的依赖,动文件之前得先看设计文档。如果有多条路径可选,它会选择配置文件暗示的那条。
这个模板在实践中还有一个关键补充:每个约束后面都应该说明理由。比如"禁止裸 throw Error"这一条,可以补一句"因为项目统一用 AppError 包装错误码,方便日志系统结构化采集"。加了理由之后,AI 对约束的遵循率会明显提升。模型更愿意服从"有道理"的规则,而不是无缘无故的命令。
3.3 配置粒度与层级设计
单文件 AGENTS.md 解决不了所有问题。项目大了以后,还需要考虑分层的配置体系:
- 全局层(用户级):定义个人对所有项目通用的偏好,比如"永远不主动提议重构""回答保持简洁"。
- 项目层(根目录 AGENTS.md):定义整个项目的共性和事实。
- 模块层(子目录 AGENTS.md):定义特定模块的契约和本地约定,比如"本模块只读数据,不做写入"。
- 会话层(临时指令):在特定会话里追加的临时约束,比如"本次只改测试不看业务代码"。
这种分层的好处是信息就近存放:AI 在深入某个模块时,只需要加载该模块的 AGENTS.md,而不必把整个项目的宪法都塞进上下文。我在一个多模块的数据平台项目里验证过,配置从单文件拆成分层结构后,上下文碎片占用明显下降,回答质量也有可感知的提升。
模块层 AGENTS.md 特别适合记录那些"代码里看不出来,但直接决定代码质量"的契约。比如某个数据管道模块,"所有新表必须注册在 schema_registry 里并附带 owner 信息"这种约定,写在模块层的 AGENTS.md 里,AI 在动手改这个模块时就一定能看到。试试只把这约定写在根目录的 AGENTS.md 里,AI 往往看到的是总则,根本不会把总则和具体这个模块联系起来。
4. 实操过程与核心环节实现
4.1 第一步:项目审计与信息提取
动手写 AGENTS.md 之前,先做一轮项目审计。我的做法是回答五组问题:
- 这个项目最核心的三个目录是什么?每个目录的职责边界在哪?
- 如果 AI 只能知道五件关于这个项目的事实,应该知道哪五件?
- 新成员(人类)第一次进入项目时最常问的三个问题是什么?
- 过去半年里,代码评审中重复出现的高频修改意见是什么?
- 哪些操作一旦搞错,代价巨大?(比如误删数据、错误发布、违规改库)
这些问题看起来简单,但回答出来的内容价值极高。特别是第 4 和第 5 组问题,直接决定了 AGENTS.md 里"禁止事项"清单的质量。我见过一个团队把这个方法用到极致——他们从代码评审历史里扒出了十二条高频意见,全部固化进 AGENTS.md 后的三个月里,AI 生成的代码在这十二条上的违规率直接降到了接近于零。
审计的时候有一个容易忽略的点:不要只盯着代码,还要看团队的沟通记录。很多项目里最有价值的约定并不写在文档里,而是散落在讨论区、评审记录和会议纪要中。比如"这个模块我们故意没有做单元测试,因为依赖的外部服务不稳定"这种背景信息,不翻沟通记录你是挖不出来的,但恰恰是 AI 最需要知道的。
4.2 第二步:渐进式配置迭代
AGENTS.md 最忌讳一上来就想写一份"终极版本"。我的建议是:第一版只写 25% 到 40% 的内容,先覆盖最有价值的那几条信息,然后跑起来,观察 AI 表现,再迭代。
具体的迭代节奏是这样:
- 第一周:只写项目概览、构建测试命令、三条最重要的编码约定。
- 日常使用中,凡是发现 AI 犯了"你不希望它犯"的错,记下来。
- 每周回顾一次,把反复出现的错误抽象成一条 AGENTS.md 条目。
- 每月做一次"冗余清理",把 AI 已经稳定遵守、不再需要的条目移出配置文件。
这个节奏的关键是让 AGENTS.md 成为一个"活的"文件,跟着项目的实际问题走,而不是凭空想象 AI 会需要什么。很多人的 AGENTS.md 写得很全,但一半以上内容 AI 根本用不上,反而拖慢了响应速度、稀释了注意力。冗余信息带来的上下文占用是真实的成本——每个 token 都在消耗上下文窗口的价值,而窗口是有限资源。
实际操作中,我还在这个流程里加了一个"反例库":每次发现 AI 犯了一个值得记录的错,不只是在 AGENTS.md 里加一条"禁止",还会把具体的错误实例(脱敏后)作为注释写在对应条目下面。这样后续回顾时能立刻理解这条约束背后的真实场景,而不是面对一句干巴巴的规则费解当初为什么这么写。
4.3 第三步:与现有工具链协同
AGENTS.md 不是孤立存在的。它需要和项目的日常工具链配合才能发挥效果:
- CI 流水线里可以对 AGENTS.md 做格式校验,保证提交前该文件是合法可解析的。
- 代码审查流程里把 AGENTS.md 当作契约,新增代码如果违反 AGENTS.md 中明确的约定,可直接标注 block。
- 如果项目里已经在用某种 AI 编程助手,把 AGENTS.md 路径配进它的启动加载列表。
这里有一个我踩过的坑:AGENTS.md 和 CLAUDE.md、COPILOT_INSTRUCTIONS.md 这些特定工具的配置文件并存时,内容冲突会让 AI 陷入混乱。我现在的做法是:用一个根目录 AGENTS.md 作为事实源(source of truth),其他工具的配置文件里只写"去读 AGENTS.md"这一条指令,以及工具特定的少量差异说明。这样维护成本从多份文件的同步变成了一份文件的更新,大幅降低了配置漂移的概率。
另一个协同要点是版本管理。AGENTS.md 一定要纳入 Git 版本控制,并且和代码变更一起评审。项目里每次大的重构、目录调整、技术栈变更,都应该同步更新 AGENTS.md。如果让配置文件脱离版本控制单独漂移,它很快就会变成"没有任何人真正知道它说了什么"的死文档。
5. 常见问题与排查技巧实录
5.1 AI 经常忽略 AGENTS.md 内容怎么办
这是最常遇到的问题。排查思路按顺序走:
- 确认配置文件是否在 AI 的加载路径里:路径拼写、文件名大小写、项目根目录识别是否正确。这个问题最常见,也最容易被忽略。
- 确认 AGENTS.md 是否被更大体积的其他上下文挤出了窗口:上下文窗口有限,如果会话刚开始就塞入了大量代码,AI 可能"看到"但"注意不到"配置文件。
- 确认措辞是否适合模型理解:AI 对"禁止""必须"这类强约束词更敏感,用"尽量避免""建议"之类的软措辞效果会大打折扣。
- 确认信息是否和模型预测冲突过大:如果 AGENTS.md 说的东西和模型预训练知识严重冲突,模型会出现"选择性遗忘"——它更倾向于相信自己熟悉的通用方案,而不是项目里的特殊约定。
实用技巧:关键约束在 AGENTS.md 里用两种方式各写一遍——先写"是什么"(声明事实),再写"为什么"(给出理由)。模型对"有理由的约束"的遵循率远高于"无理由的命令"。比如"禁止引入新的外部依赖,因为当前依赖树已锁定并经过安全审计",比单纯一句"禁止引入新依赖"管用得多。
5.2 AGENTS.md 上下文成本失控的处理
AGENTS.md 不是越长越好。当一个文件超过 300 行,基本可以判断它已经失控了。我遇到的失控案例大多是同一个模式:团队成员把每一条聊天里 AI 犯过的错都加了进去,日积月累堆出了一份"行为守则大百科"。
控制长度的三个手段:
- 分层拆分:把模块级的内容移到子目录的 AGENTS.md,让根目录文件保持精简。
- 定期清理:每季度做一次"瘦身",凡是 AI 已经稳定遵守三个月以上的条目,降级为普通文档,不留根目录。
- 收紧"写入标准":新增条目必须同时满足"项目特定性"和"违反代价高"两个条件,否则不改。给 AGENTS.md 设一个"提交门槛",避免成为垃圾桶。
这里还有一个微妙的问题:AGENTS.md 内容太多时,AI 对每一条的"注意力权重"其实是衰减的。就像人读一份一千条的规章制度,真正记住的可能只有前面几十条。精简不是偷懒,而是为了确保最重要的约束真正被模型接收。
5.3 配置与代码不同步的治理
AGENTS.md 和代码库一样会发生腐化。目录重组了、技术栈换了、模块合并了,但 AGENTS.md 还停留在三个月前。这比没有 AGENTS.md 更危险,因为 AI 会把过期的错误信息当作事实来用,拿三个月前的架构假设往新代码上套。
我的应对方法:把 AGENTS.md 纳入重构流程的必改清单。任何涉及目录变动、依赖变更、架构调整的变更请求,都要求在同一份变更里同步更新 AGENTS.md。同时,每次跨版本迭代结束时,花十五分钟把 AGENTS.md 从头读一遍,逐条核对是否仍然成立。这个习惯看着繁琐,但省下来的返工时间远超十五分钟的成本。
另一个小技巧:在 AGENTS.md 顶部加一个"最后更新日期"和"当前版本",养成每次修改都更新的习惯。后续排查"AI 为什么还在用旧约定"时,看一眼日期就能快速定位是不是配置过期了。这个方法简单到不值一提,但真实项目中受益极大——我曾经在排查一个持续两周的"AI 生成的代码总是引用已删除模块"的问题时,靠这个日期字段一眼锁定了根因:AGENTS.md 里那个模块的删除日期和实际删除日期差了整整一个月。
还有一个值得注意的边界:AGENTS.md 不是万能的。它管得住"项目层面的知识和约定",但管不住"模型能力本身的边界"。有些配置问题其实是模型推理能力不足或指令遵循能力不佳导致的,这时候调整 AGENTS.md 没什么效果,把精力投在更换模型或调整工作流上更划算。不要神化配置文件,它只是一个工程手段。
我实际摸索下来,AGENTS.md 这类声明式配置最大的价值不是省了多少 token,而是把 AI 辅助编程从"碰运气"变成了"可预期"。你不再祈祷这次会话的 AI 恰好理解项目背景,而是通过一份看得见、摸得着、可审查的文件,把项目的"长期记忆"固定下来。代码会变,需求会变,但这套思路是可以复用的。如果你也在做 AI 辅助编程的工程化尝试,强烈建议从一份最精简的 AGENTS.md 开始,跑起来,再迭代。半年后回头看,你会庆幸自己迈出了这一步。