2. 信息边界与上下文
这是什么:信息边界 = 决定 Agent 每次能看到的资料范围;上下文 = 它这一轮实际拿到的全部内容。
为什么用:上下文既是成本也是质量。塞太少它答不了案,塞太多它会变笨——两头都是错,中间那段才是甜区。
如何用:本章讲 AGENTS.md 怎么写才不臃肿、渐进式披露怎么落地,再讲 40% 利用率现象、压缩与交接两种解法。
2.1 AGENTS.md:当目录,别当手册
AGENTS.md(Claude Code 里叫 CLAUDE.md)是每个 Agent 启动时都会读到的项目说明。它的内容长度直接乘以会话次数——每个会话都付一遍。
最常见的翻车写法是把它写成「超级系统提示词」:所有规矩、所有背景、所有流程一股脑塞进去。结果是上下文被撑爆,Agent 反而更容易跑偏。
OpenAI 的做法很克制:AGENTS.md 只当目录用,大约 100 行,详细规则放到子文档里按需加载:
# AGENTS.md(约 100 行,只做索引)
## 项目是什么
一句话说明。
## 架构
- 分层规则:Types → Config → Repo → Service → Runtime → UI
- 详见 `docs/architecture.md`
## 常用命令
- 跑测试:`npm test`
- 跑起来:`npm run dev`
- 详见 `docs/commands.md`
## 质量标准
- 必须过 Lint 和类型检查
- 详见 `docs/quality.md`
## 执行计划
- 当前迭代目标见 `docs/plan/current.md`
另一派的极端是 Mitchell Hashimoto 的做法:AGENTS.md 的每一行都对应一个过去的 Agent 失败案例。Agent 犯一种新错,就加一条规则,同类问题以后就少一些。这是一份持续增长的防错清单——
# AGENTS.md
- 改 `packages/core/` 下的文件前必须先跑 `npm test -w core`,因为 2026-03 那次直接改了 schema 导致下游全挂
- 不要手改 `*.generated.ts`,跑 `npm run gen` 重新生成
- 提交信息用 Conventional Commits,因为 CI 的 changelog 靠它解析
两种写法可以合用:AGENTS.md 既是指南(指向子文档),也是防错清单(累积历史教训)。关键是短。
2.2 渐进式披露:文档也分层
文档分层和 技能 Skills 的渐进式披露是同一个思路:
AGENTS.md # 索引:约 100 行,标题 + 一句话摘要 + 路径
docs/
├── architecture.md # 详细架构说明
├── commands.md # 完整命令列表
├── quality.md # 质量标准与检查项
└── plan/
└── current.md # 当前迭代目标
Agent 的读取路径变成:先读 AGENTS.md 拿到目录 → 判断当前任务需要哪篇 → 只读那一篇。
这一步能省下多少?假设 docs/ 目录下一共有 200 行规则,而当前任务只需要「命令」相关的 15 行——分层之后省下 185 行 × 每个会话一次。
写 AGENTS.md 索引时的三条规矩:
- 标题 + 一句话摘要 + 相对路径,三件套齐全,Agent 不用猜
- 别把正文塞进索引,索引超过 100~150 行就该拆
- 规则要能机械化执行的,就别只写在这里(见第 5 章)
2.3 上下文利用率:40% 现象
有一个值得警惕的观察:某次公开演示中,168K token 的上下文窗口使用到大约 40% 后,Agent 的输出质量开始下降。
| 区间 | 占比 | 表现 |
|---|---|---|
| Smart Zone | 0 ~ 约 40% | 推理聚焦、工具调用准确、代码质量高 |
| Dumb Zone | 超过约 40% | 幻觉增多、兜圈子、格式混乱、代码变差 |
⚠️ 这个比例来自特定模型和任务,不能直接当成所有 Agent 的统一阈值。 但它提示了一件重要的事:上下文窗口不是越大越好,塞满一半就该怀疑「该丢点什么」。
为什么会这样?上下文里混着三类东西:
| 类型 | 例子 | 该不该留 |
|---|---|---|
| 当下要用的 | 当前任务描述、刚读的文件 | 留 |
| 过期但没删的 | 三轮之前的工具日志、已被推翻的旧决策 | 丢 |
| 重复的 | 反复出现的同一段资料 | 合并 |
模型对这三类的处理是一样的——都当成「当前上下文」来读。于是真正重要的信息被淹没,这就是「上下文喂越多,Agent 反而越蠢」的原因。
2.4 压缩 vs 交接:两种解法
发现上下文该瘦身时,有两条路:
路线一:压缩(compact)——把旧内容总结成摘要,留在同一个会话里。
适合:任务还在继续,历史里有价值的细节(比如「第一次尝试失败了,原因是 X」)。
危险:摘要会丢信息。压缩了三次之后,Agent 可能已经忘了最初的目标。
路线二:交接(context reset)——把当前状态提炼成结构化交接文档,然后清空上下文窗口,开一个新会话,只喂交接文档。
Anthropic 遇到过「上下文焦虑」:Sonnet 4.5 在上下文快填满时变得犹豫,甚至在任务还没完成时提前收工。他们后来的解法不是压缩,而是 context resets——清空窗口,用交接文档保住关键状态。
| 压缩 | 交接 | |
|---|---|---|
| 上下文 | 留在同一会话 | 清空后重开 |
| 保留什么 | 摘要 | 结构化交接文档 |
| 适合 | 细节仍有价值 | 任务太长、需要干净起点 |
| 风险 | 摘要丢信息 | 交接不完整 → 从错误状态继续 |
2.5 交接文档长什么样
交接文档是交接路线的成败关键。遗漏了已改文件、失败原因或下一步验证命令,新 Agent 就会从错误状态继续。
一份够用的交接文档:
# 任务交接:给用户表加软删除字段
## 目标
给 `users` 表加 `deleted_at` 字段,所有查询自动过滤已删除记录。
## 已完成
- ✅ `migrations/20261021_add_soft_delete.sql` 已写,字段 `deleted_at TIMESTAMP NULL`
- ✅ `server/models/user.py` 加了字段和全局查询过滤器
- ✅ `server/api/users.py` 的删除接口改为软删除
## 未完成
- ⬜ `server/api/users.py` 的列表接口要加分页时过滤(`apply_deleted_filter` 已写好但没接上)
- ⬜ 没写测试
## 改过的文件
- `migrations/20261021_add_soft_delete.sql`(新增)
- `server/models/user.py`
- `server/api/users.py`
## 失败原因(别重蹈)
- 第一次用 `ALTER TABLE ... DROP COLUMN` 回滚,Postgres 里事务已提交导致报错。
**正确做法**:新开一个 down migration,不要在同一个事务里 DROP。
## 下一步验证命令
```bash
pytest server/tests/test_users.py -k soft_delete
python -m server.migrations up
```
## 环境状态
- 数据库:本地 Docker,已跑过 `python -m server.migrations up`
- 分支:`feature/soft-delete`,已提交到 `a1b2c3d`
四个必备块:已完成 / 未完成 / 改过的文件 / 下一步验证命令,外加一节「失败原因」防止重蹈覆辙。
这个格式和 Agent Skills 的技能正文是同一个思路:给下一个执行者一份能直接接手的说明书。
2.6 L1 小结:信息边界的四条规矩
- 索引当索引:
AGENTS.md控制在百行上下,详细内容下沉到docs/ - 按需披露:让 Agent 先看目录再取需要的那篇,别把整个
docs/塞进去 - 监控利用率:上下文用掉一半就该开始怀疑,该丢的丢
- 该交接就交接:任务太长时,清空窗口 + 结构化交接文档,比硬撑着压缩更稳
🤝 记忆口诀:
AGENTS.md是门牌不是百科;上下文超半要减负;长活儿靠交接,不靠硬撑。
动手改
- 翻出你项目里的
AGENTS.md(没有就新建),统计行数;超过 150 行的,把最啰嗦的两节拆进docs/并在AGENTS.md里留索引 - 检查一遍:
AGENTS.md里有没有「为什么要这么写」这类解释性长文?把它们删掉或移到子文档 - 模拟一次交接:假设你现在要下班,把当前任务写成第 2.5 节那个格式的交接文档,看「未完成」和「下一步验证命令」两块能不能填全
小结
AGENTS.md是目录 + 防错清单,不是超级提示词;控制在百行上下- 渐进式披露:索引 → 判断需要 → 只读那一篇,省的是每会话一次的成本
- 上下文窗口不是越大越好:Smart Zone / Dumb Zone,超半就该丢东西
- 该丢的三类:过期工具日志、已推翻的旧决策、重复资料
- 压缩适合细节仍有价值的任务;长任务用 context reset + 结构化交接
- 交接文档四件套:已完成、未完成、改过的文件、下一步验证命令,外加失败原因
下一章:工具系统与执行编排,它到底怎么和外界打交道 🐸