AGENTS.md 上下文策略层设计:让 Agent 稳定执行的关键
1. 为什么一个 Markdown 文件能决定 Agent 的上限第一次看到AGENTS.md这个命名很多人会下意识把它归类成又一个 README 的变体——无非是给项目加个说明文档写写怎么安装、怎么跑测试。但如果你真的在 Agent 开发这条路上踩过几轮坑就会明白这个文件的位置远比 README 关键得多。README 是写给人看的而AGENTS.md是写给 Agent 看的这两者的读者模型完全不同导致它们的写法、结构、信息密度要求也完全不一样。我在早期做 Agent 项目时犯过一个典型错误把项目规范、代码风格、目录约定全部塞进 README然后指望 Agent 在每次任务开始时自动读取。结果就是 Agent 要么完全忽略这些内容要么读了一半就开始自由发挥生成的代码风格和项目既有约定南辕北辙。后来我才意识到问题不在于 Agent 不够聪明而在于我把上下文策略和人类文档混为一谈了。AGENTS.md的本质不是文档而是上下文策略层——它是 Agent 在每次执行任务前用来快速建立我在哪、我要遵守什么、我能用什么这套认知的入口文件。这个认知转变之后我重新设计了项目里的AGENTS.md把原本散落在 README、CONTRIBUTING、各种.editorconfig里的关键约束按照 Agent 的读取习惯重新组织。效果非常明显Agent 生成代码的一次通过率从大概六成提升到了八成以上返工次数大幅下降。这不是玄学而是因为 Agent 的上下文窗口是有限资源你给它什么、以什么顺序给、给多少直接决定了它的行为质量。所以这篇内容我想聊的不是如何写一个好看的 Markdown 文件而是如何把AGENTS.md当作 Agent 的上下文策略层来设计。适合正在做 Agent 开发、被 Agent 行为不稳定困扰、或者想让 AI 辅助编码更可控的从业者。无论你用的是哪家框架这套思路都是通用的因为它针对的是 Agent 的认知机制而不是某个具体工具的实现细节。2. AGENTS.md 与 README 的本质分野2.1 读者模型不同导致的信息组织差异README 的读者是人人有耐心、有背景知识、能跳读、能根据经验补全缺失信息。所以 README 可以写得比较松散可以有大量背景铺垫可以用详见某某文档这种引用方式。但 Agent 不一样Agent 没有耐心这个概念它的阅读本质上是一次性的上下文注入读完就开始干活不会回头翻第二遍。这就带来一个直接后果AGENTS.md里的信息必须是自包含的、可立即执行的。你不能写代码风格参考团队规范因为 Agent 不知道团队规范在哪你得直接写使用 2 空格缩进字符串统一用单引号函数名用 camelCase。每一条约束都应该是 Agent 读完就能直接套用的形式而不是需要二次查找的指针。我在实际项目里做过对比测试同样一份约束写成参考 ESLint 配置和直接列出五条核心规则Agent 的遵守率差了将近一倍。原因很简单前者需要 Agent 主动去读配置文件而 Agent 在任务执行过程中往往不会主动做这种额外动作除非你明确要求。2.2 上下文窗口是稀缺资源另一个关键差异是上下文预算。README 可以写几千字因为人读的时候是分段的不占用工作记忆。但AGENTS.md的内容会直接占用 Agent 的上下文窗口而这个窗口是有限的。你塞进去的每一段文字都在挤占 Agent 用来理解任务、推理逻辑、生成代码的空间。这意味着AGENTS.md的写作原则是信息密度优先而非完整性优先。不是所有规范都要写进去而是只写那些Agent 容易犯错且后果严重的约束。比如缩进风格这种小事Agent 大部分时候能猜对可以不写但所有数据库操作必须走 ORM 层禁止裸写 SQL这种涉及架构安全的约束就必须写而且要写得非常明确。我一般会把AGENTS.md控制在 200 到 400 行之间超过这个长度就要开始做减法。减法的标准是这条约束如果 Agent 违反了会不会导致返工如果不会就删掉。这个标准听起来粗暴但实测下来非常有效因为它逼着你只保留真正关键的信息。2.3 一个对照表看清两者定位维度READMEAGENTS.md主要读者人类开发者Agent信息组织按叙事逻辑按执行优先级长度容忍度高可长文低需精简引用外部文档可以尽量避免更新频率低随项目约定变化核心目标让人理解项目让 Agent 正确执行这张表不是绝对的但它能帮你快速判断某条信息该放哪。我自己的习惯是凡是解释为什么的内容放 README凡是告诉 Agent 怎么做的内容放AGENTS.md。两者可以有少量重叠但主体分工要清晰。3. 上下文策略层的四层结构设计3.1 第一层项目身份与边界声明AGENTS.md的开头部分我建议用最短的篇幅说清楚三件事这个项目是什么、技术栈是什么、Agent 在这个项目里的角色边界是什么。这三件事看起来简单但它们是 Agent 建立我在哪认知的基础。具体写法上我一般会用类似这样的结构先一句话说明项目类型比如这是一个基于 TypeScript 的 CLI 工具用于处理日志聚合然后列出核心技术栈包括语言版本、框架、包管理器最后声明边界比如Agent 只负责修改 src 目录下的代码不要动 scripts 和 config 目录。边界声明这一条特别重要因为 Agent 在没有明确边界时会倾向于顺手修改它认为相关的东西。我遇到过 Agent 为了实现一个功能顺手把构建配置也改了结果导致 CI 挂掉。后来在AGENTS.md里明确写了禁止修改 package.json 和 tsconfig.json如需变更请先说明理由这类问题就基本消失了。3.2 第二层代码约定与风格约束这一层是AGENTS.md的主体也是最容易写臃肿的部分。我的经验是把它分成硬约束和软偏好两类。硬约束是必须遵守的违反了就要返工软偏好是尽量遵守的违反了可以接受但不理想。硬约束一般包括命名规范、文件组织方式、错误处理模式、日志格式、测试要求。软偏好包括注释风格、函数长度建议、变量命名倾向。在AGENTS.md里硬约束要写得非常具体软偏好可以写得宽松一些。举个例子硬约束我会写成所有异步函数必须用 try-catch 包裹catch 块中必须记录 error 对象并重新抛出业务异常软偏好我会写成函数尽量控制在 50 行以内超过时考虑拆分。这种区分让 Agent 知道哪些是红线哪些是建议执行时的优先级判断会更准确。3.3 第三层任务执行流程与检查点这一层是很多人会忽略的但它对 Agent 的行为稳定性影响巨大。Agent 在执行复杂任务时如果没有明确的流程指引很容易跳步或者顺序错乱。所以在AGENTS.md里定义一套标准的任务执行流程能显著提升输出质量。我常用的流程模板是这样的先读需求、再列计划、然后逐步实现、每步实现后自检、最后跑测试。这个流程听起来很基础但写进AGENTS.md之后Agent 的行为会明显更有条理。特别是每步实现后自检这一条能逼着 Agent 在生成代码后回头检查一遍减少低级错误。检查点也很关键。我会在AGENTS.md里明确列出几个必须停下来确认的节点比如修改超过 3 个文件时先输出变更计划、涉及数据库 schema 变更时先说明影响范围。这些检查点相当于给 Agent 装了几个刹车防止它一路狂奔到错误的方向。3.4 第四层常见陷阱与反模式清单最后一层是经验沉淀区专门记录那些Agent 在这个项目里容易犯的错。这一层的内容是随着项目推进不断积累的每次发现 Agent 犯了一个新错误就把它加进去。比如我在一个 React 项目里发现 Agent 总是忘记给 useEffect 加依赖数组导致无限渲染。后来在AGENTS.md里加了一条所有 useEffect 必须显式声明依赖数组禁止省略这个问题就再也没出现过。类似的还有禁止在 render 函数中做数据请求、所有 API 调用必须走统一的 request 封装等等。这一层的价值在于它是项目特有的不是通用规范。通用规范 Agent 可能已经知道但项目特有的陷阱只有踩过才知道。所以这一层要写得具体、有场景最好带上为什么的简短说明让 Agent 理解背后的原因而不是机械遵守。4. 让 Agent 真正读懂的关键写法4.1 用祈使句替代描述句这是我在实践中总结出的最有效的一条规则。Agent 对祈使句的响应准确率明显高于描述句。比如项目使用 2 空格缩进这种描述句Agent 可能会理解成项目曾经用过 2 空格缩进或者项目倾向于 2 空格缩进执行时就不一定严格遵守。但写成使用 2 空格缩进指令性就强很多。我把AGENTS.md里所有约束都改成了祈使句形式效果立竿见影。具体来说就是把XX 是 YY改成使用 YY把项目遵循 XX 规范改成遵循 XX 规范。这个改动很小但对 Agent 的行为影响很大。4.2 关键约束用加粗和独立段落强化Agent 在读取长文本时对格式的敏感度比人低但对重复和突出的敏感度高。所以关键约束不要藏在段落中间要单独成段并且用加粗标记。我一般会把最重要的三到五条约束放在AGENTS.md靠前的位置每条单独一段加粗关键词。比如禁止在组件中直接调用 fetch必须使用 src/api 下的封装函数这种写法Agent 的遵守率明显高于把它混在一段普通文字里。这不是玄学而是因为格式突出会影响 Agent 的注意力分配。4.3 用示例替代抽象描述抽象描述对 Agent 来说是最难执行的。比如错误处理要规范这种话Agent 根本不知道什么叫规范。但如果你给一个示例代码块展示什么是规范的错误处理Agent 就能直接模仿。我在AGENTS.md里会放少量示例代码专门用来展示正确写法。这些示例不用多每个关键约束配一个就够。示例的选择标准是短、典型、可直接复制。太长的示例会占用上下文反而得不偿失。4.4 明确不要做什么比要做什么更重要这一点可能有点反直觉但在 Agent 场景下确实如此。Agent 的默认行为是尽量多做所以明确禁止某些行为比鼓励某些行为更有效。我在AGENTS.md里专门有一节叫禁止事项列出所有 Agent 不应该做的事。这一节的内容包括禁止修改的文件、禁止使用的 API、禁止的代码模式、禁止的依赖引入。每一条都写得很具体比如禁止引入 lodash项目已有 utils 目录提供所需工具函数。这种明确的禁止清单能大幅减少 Agent 的越界行为。5. 实测中踩过的坑与修正过程5.1 坑一约束太多导致 Agent 选择性失明早期我犯过一个错误把AGENTS.md写到了 800 多行恨不得把所有规范都塞进去。结果发现 Agent 反而开始忽略一些约束特别是排在后面的那些。我一开始以为是 Agent 能力问题后来才意识到是上下文过载导致的注意力稀释。修正方法是做减法把约束按重要性排序只保留前 20 条最关键的。剩下的要么合并要么删掉。删掉的标准是如果 Agent 违反了这条我能不能在 code review 时轻松发现并修正如果能就删掉因为 code review 本身就是一道防线不需要AGENTS.md重复承担。5.2 坑二约束之间互相矛盾这个问题比较隐蔽。有一次我在AGENTS.md里写了所有函数必须有 JSDoc 注释又在另一处写了保持代码简洁避免冗余注释。结果 Agent 在这两条之间反复横跳有时候加注释有时候不加行为很不稳定。后来我意识到约束之间必须有明确的优先级。现在我会在AGENTS.md开头声明一个优先级顺序比如正确性 可读性 简洁性当约束冲突时Agent 按这个顺序判断。这个声明很短但能解决大量边界情况。5.3 坑三更新AGENTS.md后 Agent 行为没变化这个坑最让人困惑。我明明更新了AGENTS.md但 Agent 的行为还是老样子。排查了半天才发现是因为 Agent 的上下文注入有缓存机制或者是因为任务开始时读取的是旧版本。修正方法是每次更新AGENTS.md后显式在任务指令里提醒 Agent请重新读取 AGENTS.md。另外我会在AGENTS.md顶部加一个版本号和更新日期方便追踪。这个习惯看起来多余但在多轮迭代中能省很多排查时间。5.4 坑四把为什么写太多挤占了怎么做我一开始写AGENTS.md时喜欢解释每条约束背后的原因觉得这样 Agent 能理解得更深。但实测下来解释性内容占用了大量上下文反而挤压了执行性内容的篇幅。Agent 并不需要理解为什么它需要的是清晰的怎么做。现在的做法是每条约束只写一句简短的为什么控制在 20 字以内剩下的篇幅全部留给具体的执行细节。比如使用 ORM 层避免 SQL 注入风险括号里就是全部的解释足够了。6. 不同项目类型下的策略调整6.1 前端项目组件约定与状态管理是重点前端项目的AGENTS.md我会重点写组件组织方式、状态管理约定、样式方案。因为前端 Agent 最容易犯的错就是组件结构混乱、状态管理不一致。具体来说会明确所有组件使用函数式写法、状态优先用 hooks跨组件共享用 context、样式统一用 CSS Modules这类约束。另外前端项目要特别注意依赖管理。Agent 很容易随手引入新依赖所以我会在AGENTS.md里明确引入新依赖前必须先说明理由并获得确认。这一条能避免很多依赖膨胀问题。6.2 后端项目错误处理与数据层约束是核心后端项目的重点在错误处理、数据访问、日志记录。我会明确所有对外接口必须有统一的错误响应格式、数据库操作必须走 repository 层、关键操作必须记录结构化日志。这些约束直接关系到系统的可维护性和可观测性。后端项目还要特别注意事务边界。Agent 在处理涉及多表操作的逻辑时容易忘记事务所以我一般会写涉及多表写入的操作必须使用事务包裹事务边界在 service 层。6.3 数据与脚本项目可复现性是第一原则数据类项目的AGENTS.md核心是保证可复现性。我会明确所有随机操作必须设置固定种子、数据处理步骤必须幂等、中间结果必须落盘并记录路径。这些约束能保证 Agent 生成的脚本每次运行结果一致方便调试和验证。脚本项目还要注意参数处理。Agent 容易把参数硬编码在脚本里所以我会写所有可变参数必须通过命令行参数或配置文件传入禁止硬编码。6.4 不同类型项目的约束侧重对照项目类型首要约束次要约束常见陷阱前端组件结构状态管理依赖膨胀后端错误处理事务边界裸写 SQL数据脚本可复现性参数化硬编码工具库API 稳定性向后兼容破坏性变更这张表是我在多个项目里总结出来的实际使用时可以根据项目特点调整。关键是抓住每个类型的首要约束把它写透其他约束作为补充。7. 把 AGENTS.md 纳入版本管理的工作流7.1 与代码同仓库、同分支管理AGENTS.md必须和代码放在同一个仓库跟随同一个分支。这一点很重要因为不同分支的代码约定可能不同AGENTS.md也要跟着变。我见过有人把AGENTS.md放在单独的仓库里结果分支切换时约束和代码对不上Agent 行为混乱。同仓库管理还有一个好处是AGENTS.md的变更可以走 code review 流程。每次修改约束都要有人 review确保约束是合理的、不矛盾的。这个流程能防止AGENTS.md随着时间推移变得臃肿和混乱。7.2 用变更日志记录约束演进我会在AGENTS.md末尾维护一个简短的变更日志记录每次修改的内容和原因。比如2024-06-01新增禁止引入 lodash 的约束原因是依赖膨胀。这个日志不用很详细但能帮助团队理解约束的来龙去脉。变更日志还有一个作用就是当 Agent 行为出现异常时可以快速排查是不是最近的约束变更导致的。我遇到过几次 Agent 行为突变最后都是通过变更日志定位到某条新约束写得不清楚。7.3 定期审查与精简AGENTS.md不是写完就不管的需要定期审查。我一般每个月会过一遍看看有没有过时的约束、有没有可以合并的约束、有没有需要新增的约束。审查的标准还是那个这条约束如果删掉Agent 会不会犯错如果不会就删。精简的过程也是理解项目的过程。每次精简都会让我更清楚项目的核心约定是什么哪些是真正重要的哪些只是历史遗留。这个过程对团队新人理解项目也很有帮助。8. 从上下文策略层看 Agent 协作的长期价值把AGENTS.md当作上下文策略层来设计短期看是为了让 Agent 少犯错长期看其实是在积累项目的可协作性资产。这个资产的价值会随着 Agent 能力的提升而放大因为 Agent 越强它对上下文的质量就越敏感——好的上下文能让强 Agent 发挥出更强的能力差的上下文会让强 Agent 也表现平庸。我在多个项目里验证过这个判断。同样的 Agent在AGENTS.md写得好的项目里能独立完成中等复杂度的任务在AGENTS.md写得差的项目里连简单任务都要反复纠正。这个差距不是 Agent 本身造成的而是上下文策略造成的。所以我的建议是不要把AGENTS.md当成一个顺便写写的文件而要把它当成项目基础设施的一部分来投入。每次发现 Agent 犯错不要只想着下次提醒它而要想怎么把这个教训固化到AGENTS.md里。这样积累下去你的项目会变得越来越Agent 友好协作效率也会越来越高。最后分享一个我自己的小习惯我会在AGENTS.md里留一个待观察区域记录那些还不确定是否要固化的约束。比如某条约束只出现过一次问题我会先放在待观察区观察几轮。如果问题重复出现就升级为正式约束如果不再出现就删掉。这个机制能防止AGENTS.md被一次性问题污染保持约束的精炼和有效。