# 上下文工程：让模型看对东西

> 智能体的表现取决于它每一步读到了什么。本课讲清上下文里装了什么、为什么越长越差，以及压缩、即时检索、外部记忆、子智能体四种手段各自适合什么场景，最后落到项目说明文件该怎么写。

- 网址：https://coding.chengyu.dev/learn/engineering/context-engineering/
- 学习路径：AI 编程的工程方法
- 预计用时：30 分钟；核实日期：2026-10-25
- 官方来源：[Effective context engineering for AI agents（Anthropic 工程博客）](https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents)，[Context Rot: How Increasing Input Tokens Impacts LLM Performance（Chroma 技术报告）](https://www.trychroma.com/research/context-rot)，[Lost in the Middle: How Language Models Use Long Contexts（Liu 等，TACL）](https://arxiv.org/abs/2307.03172)，[Explore the context window（Claude Code 官方文档）](https://code.claude.com/docs/en/context-window)，[Best practices for Claude Code（Claude Code 官方文档）](https://code.claude.com/docs/en/best-practices)，[How Claude remembers your project（Claude Code 官方文档）](https://code.claude.com/docs/en/memory)，[Create custom subagents（Claude Code 官方文档）](https://code.claude.com/docs/en/sub-agents)，[Effective harnesses for long-running agents（Anthropic）](https://www.anthropic.com/research/effective-harnesses-for-long-running-agents)，[How we built our multi-agent research system（Anthropic）](https://www.anthropic.com/research/multi-agent-research-system)，[How Claude Code uses prompt caching（Claude Code 官方文档）](https://code.claude.com/docs/en/prompt-caching)，[AGENTS.md 指南（OpenAI Codex 文档）](https://learn.chatgpt.com/docs/agent-configuration/agents-md)

## 学习目标

- 能说清一次模型请求里到底装了哪些内容，并用工具查看自己会话的构成
- 能解释上下文变长为什么会让表现变差，并据此决定何时清空、何时压缩
- 能按任务特征在压缩、即时检索、外部记忆、子智能体之间做选择
- 能写出一份短而有效的 CLAUDE.md / AGENTS.md，并删掉没用的内容

下午两点开的会话，到了五点半，智能体开始「变笨」：说过不要碰迁移文件，它碰了；让它用项目里已有的日期工具，它又自己写了一个；纠正到第三遍，它道歉，然后犯同样的错。很多人这时会怀疑模型状态不好，或者想换个更大的模型。更常见的真相是：**它的上下文已经被你们三个小时的对话、读过的文件和失败的尝试塞满了**，而你写的那几条关键规则，淹没在里面。

这一课讲上下文工程（context engineering）：不是怎么把提示词写漂亮，而是怎么决定模型每一步看到什么。对每天和智能体协作的人来说，这是比提示词技巧更值钱的基本功。

## 模型每一步到底看到了什么

先纠正一个直觉：模型并不「记得」上一轮。每次请求，工具都会把**全部内容重新发送一遍**：系统提示词、工具定义、项目说明文件、此前每一条消息、每一次工具调用的结果，最后才是你这一句话。Claude Code 官方文档说得很直白：模型在两次请求之间不保留任何东西。

在你敲下第一个字之前，Claude Code 已经往里装了这些东西：

| 内容 | 什么时候进来 | 特点 |
|---|---|---|
| 系统提示词与工具定义 | 会话开始 | 你改不了，但 MCP 和插件会增加它 |
| CLAUDE.md（或 AGENTS.md）与自动记忆 | 会话开始 | 每次会话都带着，体量归你管 |
| MCP 工具名、Skill 描述 | 会话开始 | 只有名字和描述，正文按需加载 |
| 读过、改过的文件 | 智能体读取时 | 大文件动辄几千到几万 token |
| 命令输出、测试日志 | 每次工具调用后 | 最容易失控的一项 |
| 消息与回复 | 每一轮 | 包括所有失败的尝试 |

会话感觉不对劲时，先运行 `/context` 看各类别占用和已加载的说明文件，再决定怎么处理；`/memory` 可打开并编辑这些文件。

## 为什么越长越差

「上下文窗口有一百万 token，塞满又何妨？」问题恰恰在这里：**窗口能装下，不等于模型能用好**。几项公开研究给了不同角度的证据：

- **Lost in the Middle（Liu 等，2023，发表于 TACL）**：相关信息在输入开头或结尾时模型表现最好，在中间时明显下降，专为长上下文设计的模型也如此。
- **Context Rot（Chroma，2025 年 7 月）**：测试了 18 个模型，把任务难度固定、只拉长输入。结论是输入越长，表现越不稳定，哪怕任务本身很简单；干扰项越多，下降越明显。报告还提醒：在「大海捞针」基准上拿满分，不代表长上下文真的可靠。
- **Anthropic 的解释框架**：他们把上下文看成有限的「注意力预算」，每多一个 token 就消耗一点，因此要追求「能把事情做对的最小高信号 token 集合」。

这些研究多数测的是检索和问答，不是写代码，不能直接照搬结论。但它们与工程体感一致，也与 Claude Code 官方最佳实践开篇的判断一致：大多数建议都源于同一个约束，**上下文很快填满，而且越满表现越差**。

我自己的判断是，工程上要警惕的不是「遗忘」，而是**稀释与污染**：

- **稀释**：你的关键规则只有三行，周围是三万 token 的日志，规则的相对权重就被冲淡了。
- **污染**：失败的尝试、被推翻的假设留在历史里，后续步骤仍会受它们影响。官方的建议很具体：同一个问题纠正超过两次，就 `/clear`，带着学到的东西写个更好的开场提示重来。干净会话加更好的提示，通常胜过堆满纠错的长会话。

## 四种手段，各管什么

管理上下文的手段，归纳下来就四种。它们不是互相替代，而是针对不同情形。

| 手段 | 做法 | 适合 | 代价 |
|---|---|---|---|
| 压缩（compaction） | 把历史总结成摘要，用摘要接着干 | 同一个任务连续推进，历史里有大量需要保留的决定 | 细节会丢，摘要质量取决于它「觉得」什么重要 |
| 即时检索（just-in-time） | 只保留文件路径、查询语句这类轻量引用，需要时再用 grep、读文件拉进来 | 代码库很大、事先不知道要看哪里 | 多几步探索；没有方向感时会乱读 |
| 外部记忆（notes） | 把进度、决定写进文件（PLAN.md、progress.md），需要时读回来 | 跨会话的长任务，或要经得起 `/clear` | 要维护；写得差就是新的噪声 |
| 子智能体隔离 | 把调研、跑测试交给独立上下文，只收一份摘要 | 需要读大量文件、产出冗长输出的子任务 | 额外的 token 开销；摘要之外的细节拿不到 |

**压缩**是最常用也最容易被误解的。Claude Code 会在接近上限时自动压缩，你也可以手动 `/compact 重点保留 auth 相关的改动`，让摘要偏向你关心的部分。压缩之后，项目根目录的 CLAUDE.md、自动记忆和计划模式写出的计划文件会从磁盘重新注入，最近读改过的文件最多重读五个，超过 5000 token 的只留路径引用。而**子目录里按需加载的 CLAUDE.md、带 `paths:` 的规则，压缩后要等重新触发才会回来**，所以必须长期生效的规则，放根目录说明文件里。可以在 CLAUDE.md 里写一句压缩指引，比如「压缩时务必保留已修改文件清单和测试命令」，让摘要别丢这两样。

**即时检索**是 Claude Code 自己的默认思路：CLAUDE.md 预先加载，其余靠 glob 和 grep 现用现找。Anthropic 的文章把它概括为混合策略：一部分关键信息预加载，其余让智能体自己探索。对你的启示是：**文件命名和目录结构本身就是检索线索**，第 6 课会专门讲。

**外部记忆**解决的是「会话会结束」。Anthropic 在长时任务的 harness 文章里给过一个很实用的模板：第一次会话生成初始化脚本、进度文件和一份功能清单（JSON，每项有 `passes` 标记），之后每次会话只做一项、提交一次、更新进度。新会话靠读这几个文件进入状态，不依赖前面的对话。他们特意选 JSON 而不是 Markdown，因为模型更不容易随手改写它。

**子智能体**是保护主上下文的利器。读二十个文件弄清鉴权流程，让子智能体去做，主会话只收一段总结。需要记住几点：子智能体从干净的上下文开始，看不到你的对话；它会读取 CLAUDE.md（内置的 Explore 和 Plan 除外），但不会带上主会话的自动记忆，所以**关键约束要在委派的提示里重述一遍**。别神化它：Anthropic 自己的多智能体研究系统文章提到，多智能体消耗的 token 约是普通对话的 15 倍，并且不适合「所有智能体需要同一份上下文」或「彼此依赖很强」的任务，文中还特别提到大多数编码任务可并行的子任务比研究类任务少。子智能体适合隔离「读得多、产出少」的活，不是并行写代码的万能药。

### 怎么选：一张速查

- 会话已经长了，但还在做同一件事 → `/compact`，带上重点说明。
- 要换一件不相关的事 → `/clear`，别带着旧上下文。
- 刚走了死路 → `/rewind` 回到之前的节点，别原地反复纠错。
- 只想问个小问题 → `/btw`，回答不进入对话历史。
- 要读一大堆文件才能回答 → 子智能体。
- 任务要跨天、跨会话 → 写进文件（计划、进度、决定），下次读回来。

## 项目说明文件：写什么，不写什么

CLAUDE.md 是你能直接影响上下文的一份文件，因为**它每次会话都在，每一行都在和任务竞争注意力**。官方的判断标准很好用：对每一行问「删掉它，智能体会犯错吗？」不会，就删。

| 该写 | 不该写 |
|---|---|
| 智能体猜不到的命令（怎么跑单个测试、怎么起依赖服务） | 读代码就能看出来的东西 |
| 和默认习惯不同的代码风格 | 语言的通用惯例 |
| 分支、提交、PR 的约定 | 整个 API 文档（给链接就行） |
| 项目特有的架构决定与「为什么」 | 逐个文件的介绍 |
| 环境的坑（必需的环境变量、不能碰的目录） | 「写干净的代码」这类空话 |

篇幅上，官方建议每个 CLAUDE.md 控制在 200 行以内。用 `@路径` 导入别的文件能帮你组织，但**导入不会减少上下文占用**，被导入的内容随它一起加载。真正按需加载的办法有两个：子目录里的 CLAUDE.md（读到该目录文件时才加载），或带 `paths:` 的规则文件。偶尔才用的流程，如发布检查，更适合做成 Skill。

还有一个常被忽略的事实：**说明文件是建议，不是强制**。官方文档明确说它是上下文而非配置，需要「无论如何都要发生」的事，用 Hook。「每次编辑后跑格式化」写进 CLAUDE.md，智能体大概率会做；写进 Hook，一定会做。

一份够用的示例，不到三十行：

```markdown
# 项目：shop-api（Node 22 / TypeScript / Postgres）

## 命令
- 校验（改完一批代码后必须跑）：`npm run verify`（格式、类型、lint、单测）
- 只跑一个测试：`npm test -- src/orders/refund.test.ts`
- 本地依赖：`docker compose up -d db`，然后复制 `.env.example` 为 `.env`

## 约定
- 数据库访问只能经过 `src/db/queries/`，路由里不要写 SQL
- 已合并的迁移文件不可修改，只能新增迁移
- 金额一律用整数分，不要用浮点
- 提交信息格式：`模块: 动词开头的说明`

## 不要碰
- `src/generated/` 是代码生成产物，改动请运行 `npm run codegen`

## 压缩时
- 保留已修改文件清单和最近一次失败的测试输出
```

关于 AGENTS.md：它是多款工具共同认可的说明文件名。Codex 会读取 `~/.codex/AGENTS.md`，再从仓库根目录一路向下合并到当前目录，每个目录最多取一个文件，合并后默认上限 32 KiB（可由 `project_doc_max_bytes` 调整）。Claude Code 当前版本在目录里只有 AGENTS.md、没有任何 CLAUDE.md 时会直接读它（需要 v2.1.277 及以上）；两者都存在时只读 CLAUDE.md，除非 CLAUDE.md 里用 `@AGENTS.md` 导入它。所以多工具团队的稳妥做法是：**内容写在 AGENTS.md，CLAUDE.md 只放一行导入加上 Claude 专属的少量内容**。

最后一个原则：说明文件要像代码一样维护。智能体总无视某条规则，多半是文件太长、规则被淹没；总问文件里已回答过的问题，多半是写得含糊。出问题先回头检查文件，别再加一句「务必」。

**给说明文件做一次瘦身审计**

```text
请审计本仓库根目录的说明文件（CLAUDE.md，如果没有就看 AGENTS.md），目标是让它更短、更有效。

逐行判断，并把结果整理成表格（行号、原文摘要、结论、理由）：
1. 能通过读代码、读 package.json 或配置文件直接得出的内容，标记为「可删」；
2. 互相矛盾或已经过时的规则，标记为「冲突」，并指出矛盾的另一处；
3. 只在个别目录或个别任务才用到的内容，标记为「应下沉」，建议放到子目录 CLAUDE.md、带 paths 的规则或 Skill；
4. 属于必须每次都执行的动作（格式化、禁止修改某些文件），标记为「应改 Hook」；
5. 其余的标记为「保留」。

先只输出表格和你的建议，不要修改任何文件。我确认后你再动手。
```

> 小测：一个会话已经进行了三小时。你就同一个问题纠正了智能体三次，它仍然在重复犯错。最合适的做法是？

> 小测：下面哪个子任务最适合交给子智能体，而不是在主会话里直接做？

- [ ] 在自己常用的项目里运行 `/context`，看清启动时已经占用了多少，以及 CLAUDE.md 是否被加载
- [ ] 用上面的审计提示词给你的说明文件瘦身，目标是核心内容不超过 200 行
- [ ] 把偶尔才用的流程（发布、迁移）从说明文件移到 Skill 或子目录说明文件
- [ ] 为长任务建立一个 PLAN.md 或 progress.md，试一次「`/clear` 后读文件继续干」的接力
- [ ] 如果团队用多种工具，把共用规则统一放进 AGENTS.md，并用 `@AGENTS.md` 让 CLAUDE.md 导入它

延伸阅读：[CLAUDE.md 与记忆](/learn/claude-code/claude-md/)、[上下文管理](/learn/claude-code/context/)、[子智能体与并行](/learn/claude-code/subagents-parallel/)。下一课讲怎么在动手之前先把任务想清楚：[规格驱动与计划先行](/learn/engineering/spec-driven/)。
