到了 2026 年,模型能力已经显著提升,如何组织上下文比如何斟酌提示词措辞更重要。本文系统介绍 CLAUDE.md 架构、技能拆分、作为上下文提供方的 MCP,以及能够持续改善输出质量的实践模式。

作者:Sebastian Sleczka
发布于:2026-04-20
原文:https://www.codewithseb.com/blog/context-engineering-claude-code-guide

我曾经花了整整六个月钻研提示词的措辞。我会做 A/B 测试,对比“请重构这个函数”和“使用策略模式重构这个函数,并确保……”哪一种能带来更好的结果。有时长版本胜出,有时短版本反而更好,结果似乎毫无规律。

后来,我彻底换了一个方向。我不再反复修改提示词,而是开始重新组织在我输入任何内容之前,Claude 已经掌握的信息。提示词还是同样的提示词,措辞也完全没变,结果却明显更好。在重构任务中,大约有 60% 的情况从“基本正确,但还要手工修补”,变成了“一次生成即可提交”。

那一刻我才意识到:之前优化错了对象。 从提示词工程转向上下文工程,关键并不是找到更好的词语,而是建立更好的架构。

范式转变:从措辞转向结构

提示词工程是 2023 到 2024 年的主流方法。我们学会了让模型“一步一步思考”,学会使用系统提示词和少样本示例。这些方法依然有效,但边际收益已经越来越小。

到了 2026 年,模型已经足够强。只要周围的上下文正确,一条清晰、直白的自然语言指令就能产生很好的结果。真正的瓶颈已经转移:问题不再是“模型能不能理解我的需求”,而是“模型是否掌握了完成需求所需的正确信息”。

上下文工程,就是系统地组织 Claude 在任务开始前、执行中和结束后接收到的一切信息,让模型在恰当的时机获得恰好够用的信息——不多,也不少。

可以这样理解:

  • 提示词工程:“这个问题应该怎么说?”
  • 上下文工程:“在我提出这个问题时,Claude 应该已经知道什么?”

两者的差异很重要,因为上下文的效果可以累积。一套结构良好的上下文系统会改善每一次交互;一条精心设计的提示词通常只能改善当前这一次。

上下文的五个层级

Claude Code 不只读取你输入的提示词。它会从五个不同来源组装上下文:每一层的加载时机不同,承担的职责也不同。

层级 内容 加载时机 持久性
CLAUDE.md 行为指令和项目规则 会话开始时 永久(文件)
自动记忆 Claude 总结出的观察 会话开始时 永久(MEMORY.md)
技能 专用指令集 仅在调用时 永久(文件)
MCP 服务器 外部工具提供的实时数据 调用工具时 实时
对话历史 当前会话中的消息 持续累积 仅限当前会话

这些层级并非彼此独立,而是叠加成一套完整的上下文栈。理解它们如何相互作用,正是上下文工程的核心。

关键在于:越早加载的层级,消耗的 Token 越多,因为它们会被包含在后续每一次请求中。越晚加载的层级成本越低,但持久性也越弱。好的上下文工程,会把稳定且普遍适用的信息放在前面的层级,把特定于任务的信息放在后面的层级。

第一层:CLAUDE.md——上下文基础

CLAUDE.md 是能力最强、成本也最高的上下文层。它会在会话开始时读取,并在整个会话期间被纳入每一次 API 调用。因此,它非常适合存放始终适用的规则,却非常不适合存放只在特定情境下才有用的信息。

我写过一份完整的 CLAUDE.md 架构指南,详细介绍了四层文件结构、优化方法,以及如何通过缓存策略大幅降低成本。这里重点从上下文工程的角度说明。

适合写进 CLAUDE.md 的内容:

  • 项目结构和关键文件的位置
  • 代码约定,包括命名、模式和风格
  • 行为指令,例如“未经询问,不得修改迁移文件”
  • 构建与测试命令

不适合写进 CLAUDE.md 的内容:

  • 完整的 API 参考资料,应该改用技能或 MCP
  • 教程式说明,因为 Claude 本身已经掌握这些知识
  • 很少用到的操作流程,应该移入技能
  • 频繁变化的信息,应该通过 MCP 提供

有一个简单的判断标准:如果 CLAUDE.md 中的某一行无法改变 Claude 在大多数会话中的行为,那么它只是在每次会话中白白浪费 Token。

# 优秀的 CLAUDE.md——紧凑,且直接约束行为
## 技术栈
- Next.js 15 App Router、TypeScript 严格模式、Tailwind 4
- 数据库:通过 Drizzle ORM 使用 Postgres(Schema 位于 src/db/schema.ts)
- 身份验证:Better Auth(src/lib/auth.ts)

## 规则
- 在宣告任务完成前,先运行 `npm run typecheck`
- 禁止直接修改 src/db/migrations/ 中的文件
- 数据写入使用 Server Actions,不使用 API 路由
- 所有组件都使用命名导出

其中每一行都是指令,没有背景介绍,没有解释,也没有“最好这样做”的软性建议。这就是把上下文工程思维应用到基础层的结果。

第二层:自动记忆——Claude 自己学到的上下文

Claude 在项目中发现某些信息时,例如构建过程中的特殊情况、你偏好的模式,或者它曾经踩过的坑,可以把这些观察保存到 MEMORY.md。该文件会在会话开始时与 CLAUDE.md 一起加载。

从上下文工程的角度看,自动记忆是一种涌现出来的上下文:内容不是你写的,而是 Claude 写的;你的职责是定期整理。每隔几周,我都会检查一次 MEMORY.md,并完成三件事:

  1. 删除已经不再成立的观察,例如依赖库已经升级或代码模式已经改变
  2. 如果某项内容是普遍适用的重要规则,就将其提升CLAUDE.md
  3. 精简冗长条目,把它们压缩成一句话

过期的记忆比没有记忆更糟。如果项目已经从 React 18 升级到 React 19,Claude 却仍然“记得”项目使用 React 18,这份错误上下文会主动拉低输出质量。

第三层:技能——按需注入上下文

这一层让上下文工程真正变得有意思。技能是一类只有在调用时才会加载的指令文件:既可以通过 /command 语法调用,也可以由 Claude 判断相关性后自行加载。

我在技能与可复用工作流指南中详细介绍过它们的工作机制。技能对于上下文工程的重要之处在于:它们实现了渐进式披露。

与其把所有操作流程都塞进 CLAUDE.md,在每个会话、每次请求中反复消耗 Token,不如把专门的工作流拆成技能,只有需要时再加载:

.claude/
├── CLAUDE.md              # 30 行,始终加载
└── skills/
    ├── review-pr.md       # 80 行,执行 /review-pr 时加载
    ├── write-migration.md # 60 行,执行 /write-migration 时加载
    ├── deploy.md          # 45 行,执行 /deploy 时加载
    └── debug-perf.md      # 70 行,执行 /debug-perf 时加载

如果没有技能,这 255 行内容就会全部堆在 CLAUDE.md 中——每个会话都要加载,每次请求都要付出成本,还会冲淡 Claude 当前真正需要关注的指令。

使用技能之后,Claude 开始每个会话时只需读取 30 行核心上下文。当我输入 /review-pr,它才额外加载 80 行有关 PR 审查的指令,包括具体标准、检查清单和输出格式;输入 /write-migration 时,它拿到的则是数据库迁移工作流。

这就是渐进式披露原则: Claude 只看到当前任务需要的内容,其余信息继续留在磁盘上,直到真正被调用。

这不只是 Token 成本的问题,尽管成本确实很重要,具体计算可以参阅我的 Token 优化指南。它还关系到注意力。语言模型的注意力有限,不同指令会互相争夺注意力。相比涵盖所有可能场景的一大段文字,更少但更相关的指令往往能得到更严格的遵循。

第四层:MCP 服务器——实时上下文提供方

MCP(Model Context Protocol,模型上下文协议)服务器是最被低估的上下文层。大多数开发者把 MCP 看成“给 Claude 添加工具的方法”。这当然没错,但从上下文工程的角度理解更有价值:MCP 服务器是实时上下文的提供方。

静态上下文(CLAUDE.md 和技能)告诉 Claude 如何工作,MCP 服务器则告诉 Claude 此刻正在发生什么

  • 数据库 MCP 服务器提供当前 Schema 和示例数据
  • 监控 MCP 服务器提供实时错误率和指标
  • 项目管理 MCP 服务器提供当前迭代的工单和优先级
  • 文档 MCP 服务器提供最新的 API 参考资料

与静态上下文相比,MCP 最大的优势是数据始终保持最新。如果把 API Schema 写进 CLAUDE.md,API 每次变化都会让这份信息过期;如果连接一台读取 OpenAPI 规范的 MCP 服务器,Claude 就总能获得最新版本,而且只有在确实需要时才会加载。

我也写过一篇介绍 MCP 及其与其他智能体协议差异的文章。从上下文工程的角度看,最实用的结论是:任何频繁变化的信息,都应该通过 MCP 提供,而不是硬编码在静态文件中。

// .claude/settings.json
{
  "mcpServers": {
    "postgres": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-postgres"],
      "env": { "DATABASE_URL": "postgresql://..." }
    },
    "linear": {
      "command": "npx",
      "args": ["-y", "mcp-linear"],
      "env": { "LINEAR_API_KEY": "..." }
    }
  }
}

现在,当我说“实现当前迭代中的下一张工单”时,Claude 会从 Linear 读取真实工单,从 Postgres 获取当前数据库 Schema,然后编写同时符合两者的代码。不再需要复制粘贴工单描述,也不会引用已经过期的 Schema。

第五层:对话管理

这是最短暂的一层,也是最容易被开发者忽视的一层。对话历史本身就是上下文。每条消息、每段代码和每份错误输出,都会不断累积到 Claude 的上下文窗口中。

下面三种操作非常重要:

/clear:清空对话历史。切换任务时使用。如果你已经用 20 条消息排查一个 CSS 问题,现在准备实现一个新的 API 端点,那么前面的 CSS 上下文都是噪声,应该清掉。

/compact:把对话历史总结成更短的形式。适合在长会话中使用:任务没有改变,但累积的历史已经变得庞大。Claude 会保留重要上下文,舍弃冗长的来回沟通。

任务分组:相关任务放在同一个会话,不相关的任务拆到不同会话。“重构身份验证模块,然后更新测试和文档”属于一个会话;“修复 CSS 问题,然后实现支付 Webhook”应该拆成两个会话,并在中间执行 /clear

这里的上下文工程原则是:过期的对话上下文与过期的记忆一样,都会降低输出质量。 一段围绕功能 A 展开的 50 条消息,并不会帮助 Claude 完成功能 B,反而会把真正相关的上下文挤到注意力更弱的位置。

渐进式披露原则

如果要把上下文工程压缩成一个核心观点,那就是:Claude 只应该看到当前任务需要的内容,而且应该在这些内容刚刚变得相关时再加载。

各层级通过下面的方式落实这一原则:

  1. 始终加载:CLAUDE.md + 自动记忆(普遍适用的规则,约 500 个 Token)
  2. **按需加载:**技能(特定任务的指令,通过 /command 加载)
  3. **使用时加载:**MCP 服务器(实时数据,在 Claude 调用工具时加载)
  4. **自然累积:**对话历史(当前任务的上下文)
  5. **永不加载:**被 .claudeignore 排除的文件(无关代码、第三方目录和构建产物)

这与许多开发者的本能恰好相反:他们习惯一开始就把所有信息告诉 Claude,因为更多上下文似乎更保险。但超过某个临界点之后,上下文越多,注意力反而越分散。

反模式:哪些做法会破坏上下文质量

在研究过几十套 CLAUDE.md 配置,包括我自己的和其他人的之后,我总结出下面几种会持续拉低输出质量的模式。

1. 上下文过载

一份 200 行的 CLAUDE.md,里面塞满完整的 API 参考、风格指南、团队流程和部署步骤。Claude 会把它们全部读进去,却无法合理分配优先级,最终对各项规则的遵循都不稳定。**解决办法:**除了最核心的行为规则,其余内容全部拆成技能。

2. 指令冲突

CLAUDE.md 规定“所有数据写入都使用 Server Actions”,某个技能却要求“为这个端点创建 API 路由”。Claude 会陷入冲突,有时采用前一种方案,有时采用后一种。**解决办法:**建立明确的优先级。技能在自身负责的特定领域中覆盖 CLAUDE.md,并把这一点明确写下来。

3. 上下文过期

项目三个月前已经从 Pages Router 迁移到 App Router,MEMORY.md 却仍然写着“项目使用 Pages Router”。结果 Claude 有一半时间还在生成 Pages Router 代码。**解决办法:**每月检查一次 MEMORY.md,该删就删,不要犹豫。

4. 会话大杂烩

在同一个会话中连续处理五项互不相关的功能,中间从不清理上下文。做到第五项时,Claude 仍会引用前四项任务的零碎内容。**解决办法:**不相关的任务之间执行 /clear,同一项长任务执行过程中则使用 /compact

5. 上下文重复

同一条信息同时出现在 CLAUDE.md、技能文件和你的提示词中,而且三个版本还略有差别。Claude 看到后不得不自行协调这些差异。**解决办法:**保持单一事实来源,只引用,不重复。

实践案例:为复杂重构设计上下文

下面用一个真实任务演示我会如何准备上下文:把一段庞大的 API 路由处理器重构成多个独立的服务模块。

第一步:确认 CLAUDE.md 提供了正确的基础规则

## 架构
- 服务位于 src/services/,每个业务领域一个文件
- API 路由只调用服务,不包含业务逻辑
- 所有服务都导出带类型的接口

这些是长期规则。它们适用于所有会话,而不只是当前这次重构。

第二步:为重构模式创建一个技能

# .claude/skills/refactor-to-service.md

## 重构:API 路由 → 服务模式

从 API 路由中提取业务逻辑时:

1. 找出所有业务逻辑,即除请求解析和响应格式化以外的全部内容
2. 创建带有类型接口的 src/services/[domain].ts
3. 把业务逻辑移入服务函数
4. 更新 API 路由,导入并调用服务
5. 移动现有测试,使其直接测试服务
6. 为路由本身添加一项轻量集成测试

## 约束条件
- 服务不得导入 Next.js 中的任何内容,例如 `NextRequest` 或 `cookies()`
- 每个服务函数都必须接收普通的带类型参数,并返回普通的带类型结果
- 错误处理:服务抛出带类型错误,路由捕获错误并格式化 HTTP 响应

这份内容只有在调用重构技能时才会加载,因此不会增加日常会话的负担。

第三步:确保 MCP 提供最新状态

Postgres MCP 服务器向 Claude 提供当前数据库 Schema,文件系统访问则让它获得真实代码。无需复制粘贴,也不会引用过期资料。

第四步:启动一段干净的会话

/clear
/refactor-to-service
重构 src/app/api/orders/route.ts——把订单业务逻辑提取到 src/services/orders.ts

Claude 此时拥有:核心项目规则(CLAUDE.md,约 500 个 Token)+ 重构指令(技能,约 200 个 Token)+ 实时数据(MCP,按需加载)+ 一份干净的对话历史。静态上下文总计约 700 个 Token。相比之下,一份试图覆盖所有场景的 CLAUDE.md 可能就要占用 2,000 个 Token。

最终,Claude 会严格遵循我需要的模式,使用当前 Schema 中的类型,并完成一份首次运行即可通过类型检查的干净重构。

如何衡量上下文质量

怎么判断上下文工程是否真的有效?可以关注三个指标。

1. 提示词缓存命中率

在 Claude Code 中运行 /cost,查看缓存命中百分比。较高的命中率说明静态上下文(CLAUDE.md 和技能)足够稳定,能够被高效复用。如果缓存命中率低于 40%,上下文很可能在请求之间变化得过于频繁;常见原因包括对话历史过于冗长,或者上下文加载方式不确定。我在 Token 优化策略一文中介绍过,如何把自己的缓存命中率从 12% 提升到 61%。

2. 首次生成可用率

记录 Claude 的输出有多大比例不经修改就能直接使用。在引入上下文工程之前,我处理复杂任务的首次生成可用率大约只有 30%;重新组织上下文层级后,这一比例接近 60%。模型并没有变得更聪明,它只是获得了更好的信息。

3. Token 效率

用有效输出 Token 数除以输入 Token 总数。如果发送 50,000 个上下文 Token,只换来 500 个有用的代码 Token,效率比就是 1:100。把庞大的单体 CLAUDE.md 改造成由技能组成的分层系统后,我的常见比例从大约 1:80 提升到大约 1:30。结果相同,成本只剩三分之一。

思维方式的转变

不要再把 Claude Code 当成一个必须精心设计提示词的聊天机器人,而要把它看成一套需要正确配置的系统。

你真正输入的提示词,其实是最不重要的部分。等到你开始输入指令时,上下文层级已经决定了 80% 的输出质量:CLAUDE.md 建立行为基线,技能加载相关流程,MCP 服务器提供当前数据,对话历史则划定任务边界。

提示词只负责说明要做什么,上下文决定最终能做得多好。

这就是上下文工程:重点不是更好的措辞,而是更好的架构。

参考资料

资料来源

延伸阅读


~Seb 👊