给 Coding Agent 的上下文文件,应该写项目事实
上下文文件要解决的问题
2026 年初,一篇 arXiv 论文(arXiv:2602.11988)给出了一个反直觉的结论:给 Agent 提供 AGENTS.md 等仓库级上下文文件,平均将推理成本提高了 20% 以上,但并未普遍提升任务成功率。Martin Fowler 在 Context Engineering for Coding Agents 中把问题说得更精确——上下文工程的目标是找到最小的高信号 token 集合,最大化期望输出的概率。
问题出在信噪比太低。
我见过太多团队第一次写上下文文件,写的是「写高质量代码」「保持简洁」「注意安全」这类口号。Agent 的训练数据里有数十万个项目,它早就知道这些原则。这些信息的信息熵接近零——不携带任何新知识,却挤占了有限的上下文窗口,把 Agent 对项目规则的注意力稀释了。
上下文文件要做的事用一句话说:用项目特有的事实,替换 Agent 的通用猜测。
所谓「项目特有事实」,是 Agent 凭通用训练数据猜不到的东西——你的目录结构、你的封装层路径、你的测试放哪里、你的环境变量怎么读。判别标准很简单:Agent 凭通用知识能不能推断出来? 能推断出来,写了就是噪声;推断不出来但项目需要,漏写就是隐患。
我在实际项目中反复见到这种信息缺失导致的事故:Agent 直接读 process.env 跳过项目的强类型校验封装,测试文件放在源码目录下导致 CI 采集不到覆盖率,API 请求绕过统一的鉴权和重试层。Agent 不够聪明吗?不是——它在几万个项目上学到的最常见做法,恰好与你的项目规范冲突。
根因只有一个:没有人告诉它这个项目的规矩。
为什么上下文会变成噪声
那篇 arXiv 论文的数据已经表明,粗制滥造的上下文文件不仅无用,反而增加成本。拆开来看有三个层面。
信噪比:通用原则的信息熵为零
关键是「精」,不是「多」。
上下文文件写了 50 条「写高质量代码」之类的规则时,Agent 需要额外计算来过滤噪声——这些 token 信息熵接近零,不携带新知识,却消耗了上下文窗口容量,把 Agent 对项目特有规则的注意力稀释了。
认知负荷理论的映射
| 认知负荷类型 | 人类开发者 | Coding Agent | 对应上下文问题 |
|---|---|---|---|
| 内在负荷 | 理解业务逻辑 | 理解任务需求和代码结构 | 无法避免 |
| 外在负荷 | 查找散落各处的约定 | 从噪声规则中提取有用信息 | 低质量上下文文件的主要危害 |
| 相关负荷 | 构建心智模型 | 理解项目架构和边界 | 高质量上下文文件应该做的 |
口号式规则制造的就是外在负荷。Sourcegraph 的研究(Context Engineering: A Practical Guide)发现,Agent 在一个 K8s 百万行 monorepo 中 grep 出 4000 个结果后,上下文窗口被无关文件占满,根本没看到根因文件。
「中间迷失」效应
Liu et al.(2023)的 "Lost in the Middle" 研究表明,LLM 对开头和结尾的信息关注度最高,中间的信息最容易被忽略。这意味着:
- 最重要的项目约束放在文件开头
- 次要信息放在文件结尾
- 避免在中间塞入大量低优先级内容
Buildcamp 的 CLAUDE.md 终极指南 建议根上下文文件控制在 300 行以内,部分团队压缩到 60 行以内。LLM 能可靠遵循的指令总数约 150–200 条,系统提示词就消耗约 50 条。指令越多,每条被遵循的概率越低。
低价值 vs 高价值上下文
判别标准只有一条——Agent 凭通用训练数据能不能推断出来? 能,写了等于噪声;不能,漏写就是隐患。
| 低价值上下文(Agent 已知) | 高价值上下文(项目特有) | 为什么 |
|---|---|---|
| 写高质量代码 | TypeScript 严格模式,Biome 单引号无分号 | 「高质量」是空话,linter 配置才是约束 |
| 保持简洁 | 单文件不超过 500 行,超出需询问用户 | Agent 不知道你的「简洁」阈值 |
| 注意测试 | 测试放 apps/tests/,不放进 apps/src/ | 测试目录位置因项目而异 |
| 保持项目结构 | apps/ 是主应用,packages/ui/ 是共享组件 | 目录结构是最容易踩坑的信息 |
| 使用环境变量 | 不直接读 process.env,走 apps/src/config/env/ | Agent 不知道你有强类型封装 |
| 写好函数 | 页面文件只做路由装配,UI 放 _components/ | 文件职责划分是硬约束 |
| 注意安全 | API 请求走 apps/src/api/module,内含鉴权和重试 | Agent 不知道你封装了统一请求层 |
| 使用 REST 规范 | locale 列表放 apps/src/i18n/locales.ts | Agent 凭通用 Next.js 经验一定会做错 |
左列是口号,右列是事实。Agent 已经知道要写好代码,但你项目里「好」的具体定义——路径、阈值、归属——才是它需要被告知的事实。
从坏到好:上下文文件演进
案例一:某电商后台项目
第一版(口号集合,有效信息为零):
# AI 编程规则
- 写高质量的代码
- 保持良好的项目结构
- 注意代码安全
- 测试要写全面
- 代码要简洁易读
- 遵循最佳实践
- 做好错误处理
- 注意性能优化这 8 条规则,Agent 的训练数据里全有。它不会改变 Agent 的任何行为——除了增加噪声。
第二版(加入项目事实):
# 项目事实
- Monorepo 使用 pnpm + Turborepo,Node.js 22+
- `apps/web/` 是 Next.js 16 App Router 主应用
- `packages/ui/` 是共享 UI 组件库,基于 Radix UI
# 代码边界
- TypeScript 严格模式;Biome 配置在根目录 biome.json
- 单文件不超过 500 行,超出需询问用户
- 页面文件只做路由装配,复杂 UI 放 `_components/`
# 容易出错的归属
| 归属 | 错误做法 | 正确做法 |
|------|---------|---------|
| 环境变量 | 直接读 process.env | `apps/web/src/config/env.ts`(Zod 校验)|
| API 请求 | 组件内直接 fetch | `apps/web/src/api/` 模块函数 |
| 测试文件 | 放 src/ 目录 | `apps/tests/`,镜像源码目录 |
# 验证命令
- `pnpm verify` 运行全量检查
- 声称完成前必须运行并确认通过从口号到事实,行数从 8 行变成 20 行,信息量从接近零变成覆盖 4 个维度。每一行都是 Agent 猜不到的项目特有约束。
案例二:分层上下文
项目变大后,一个上下文文件可能不够。Martin Fowler 建议采用渐进式上下文——根文件放全局约定,子目录放局部规则:
.claude/rules/
api.md # 仅在编辑 src/api/ 时加载
database.md # 仅在编辑 src/db/ 时加载
testing.md # 仅在编辑测试文件时加载
api.md 示例:
---
paths:
- "src/api/**/*.ts"
---
# API 层规则
- 所有接口使用 `createApiHandler` 包装
- 输入校验统一用 zod schema,定义在 `_schemas.ts`
- 错误响应使用 `AppError` 类(code、message、statusCode)
- 鉴权信息从 `ctx.auth` 读取,不手动解析 JWTpaths 字段让规则只在编辑匹配文件时加载。编辑 UI 组件时,Agent 不需要看到 API 层规则,上下文窗口留给相关信息。
案例三:一条规则的打磨
Agent 直接读 process.env 是高频踩坑场景。我见过团队最初只写「不要直接读 process.env」——Agent 仍然会用 process.env,因为它知道这个 Node.js API。后来改成同时给出错误做法和正确做法,Agent 才稳定走封装路径:
| 归属 | 错误做法 | 正确做法 |
|------|---------|---------|
| 环境变量 | 直接读 process.env.PAYMENT_SECRET_KEY | 从 `@/config/env` 导入 env.PAYMENT_SECRET_KEY |教训:告诉 Agent 不要做什么不够,必须同时告诉它应该做什么,具体到 import 路径。
上下文文件维护流程
上下文文件会影响每一次 Agent 任务,过期内容会让 Agent 稳定地犯同一种错。我推荐的维护流程:
关键节点是Agent 踩坑后的反馈循环。每次 Agent 做出让你皱眉的决策,都应该回溯上下文文件,检查是否有缺失的规则。这和事后复盘(Post-mortem)是同一个思路——目的是把教训固化为系统约束。
常见踩坑点
| 踩坑点 | 后果 | 正确做法 |
|---|---|---|
| 规则互相矛盾 | Agent 随机选择,行为不稳定 | 全局只保留一个权威来源 |
用 /init 自动生成 | 充斥通用原则,噪声大于信号 | 逐行手写,每条经过思考 |
| 过期命令残留 | Agent 运行错误命令 | 技术栈变更后全文搜索检查 |
| 规则太抽象 | Agent 无法执行,等于没写 | 具体到路径、阈值、反例 |
| 文件超过 500 行 | 中间信息被忽略 | 控制 300 行以内,用 scoped 规则分担 |
| 只说「不要」 | Agent 不知道替代方案 | 同时给出正确做法 |
| 复制网上模板 | 引入无关规则 | 每条必须验证是否适用 |
上下文文件 vs 提示词 vs Hooks
| 维度 | 上下文文件 | 提示词 | Hooks / Linter |
|---|---|---|---|
| 时效 | 长期稳定,跨任务复用 | 一次性,针对当前任务 | 自动化,每次触发 |
| 内容 | 项目事实、边界、命令 | 目标、约束、验收标准 | 可执行的检查规则 |
| 可靠性 | Agent 可能忽略 | Agent 可能忽略 | 强制执行,无法绕过 |
| 适用场景 | 项目级约定、架构边界 | 任务描述、临时约束 | 格式、命名等可自动检查的规则 |
一个关键认知:上下文文件里的规则本质上是「请求」,不是「约束」。Agent 可能忽略它。2026 年的最佳实践是每条规则都配有自动化执行层——上下文文件告诉 Agent 应该怎么做,Hooks 和 Linter 确保即使 Agent 没遵守也会被纠正。
// .claude/settings.json
{
"hooks": {
"stop": [{
"command": "pnpm lint:fix && pnpm format",
"description": "Agent 完成后自动修复格式和 lint"
}]
}
}规则放在上下文文件,执行交给自动化——比纯靠 Agent 自律可靠得多。
可直接复用的模板
# 项目事实
- <包管理器> + <构建工具>;<运行时版本>
- <主应用目录> 是 <框架> <版本> <路由模式>主应用
- 常用命令:<dev>、<verify>、<lint>、<typecheck>
# 代码边界
- <语言><严格模式>;<Linter> 配置在 <路径>
- 单文件不超过 <N> 行,超出需询问用户
- 页面文件只做路由装配;复杂 UI 放到 <路径>
# 容易出错的归属
| 归属 | 错误做法 | 正确做法 |
|------|---------|---------|
| <易错项1> | <错误选择> | <正确路径> |
# 验证与交付
- 声称完成前,必须运行 <验证命令> 并确认通过尖括号是需要替换为项目事实的位置。不要保留任何模板默认值——通用占位符只会增加噪声。
检查清单
写之前
- 扫描
package.json,确认技术栈、包管理器、命令 - 画出目录结构树,标注每个目录的职责
- 列出最近 3 次 Code Review 中 Agent 或新人犯过的错误
- 确认项目是否有统一封装层(API 请求、环境变量、鉴权等)
写的时候
- 每条规则都问:Agent 凭通用知识能不能推断出来?
- 能用路径说明的不用抽象描述
- 能给出反例的给出反例(错误做法 → 正确做法)
- 根文件控制在 300 行以内
- 最重要的约束放在文件开头
写之后
- 用一条新任务测试 Agent 是否遵循规则
- 配置至少一条 Hook 自动执行最关键的规则
- 设置日历提醒,每个迭代结束后检查文件是否过期
写在最后
我花了不少时间和 Agent 打交道之后才意识到:上下文文件是让 AI 编程从「聪明猜测」变成「受约束地推进」的基础设施。
2026 年的研究已经表明:更多的上下文不等于更好的结果。arXiv 论文证明粗制滥造的上下文文件增加 20% 成本,Martin Fowler 提出信噪比原则,Sourcegraph 展示精确检索比暴力搜索快 50 倍。
按本文原则重写上下文文件后,Agent 在环境变量访问、测试目录归属等高频场景的错误率显著下降。花两个小时把上下文从口号换成项目事实,回报是后续每次 Agent 任务都不再重复踩同样的坑。
下次写上下文文件时,先问自己:
这条规则是 Agent 已经知道的通用原则,还是我们项目特有的边界?
前者,删掉。后者,写具体到路径和反例。
参考资料:
- Evaluating AGENTS.md — arXiv 论文,上下文文件对 Agent 成功率的实际影响
- Context Engineering for Coding Agents — Martin Fowler 的上下文工程方法论
- Context Engineering: A Practical Guide — Sourcegraph 的四支柱框架
- The Ultimate Guide to CLAUDE.md — Buildcamp 的 CLAUDE.md 最佳实践
- Lost in the Middle (Liu et al., 2023) — LLM 长上下文中间信息退化研究