上下文工程是使用 AI 编程智能体时最重要的能力。本指南介绍如何组织项目上下文、CLAUDE.md 文件和任务描述,让 Claude Code 尽可能一次就写出高质量代码。

作者:Claude Directory
发布于:2026-04-02
原文:https://www.claudedirectory.org/blog/context-engineering-claude-code

有些开发者使用 Claude Code 时,只能得到平庸的结果;另一些人却能让它一次就写出足以投入生产的代码。两者之间的差距,几乎总能归结为同一件事:上下文工程

所谓上下文工程,就是有意识地组织并提供信息,让 AI 能够发挥出最佳水平。它并不等同于提示词工程,而是比后者深入得多。提示词工程关注的是如何措辞一条具体请求;上下文工程关注的则是完整的信息环境,包括项目文件、CLAUDE.md、目录结构、提交历史,以及你在一段时间内组织和描述任务的方式。

本指南将介绍一套经过实践检验、能够持续改善 Claude Code 输出质量的方法。


为什么上下文工程比提示词工程更重要

当你让 Claude Code“添加支付流程”时,最终结果的质量取决于 Claude 能否在内部回答下面这些问题:

  • 这个项目使用什么框架?
  • 现有代码遵循哪些模式?
  • API 路由放在哪里?组件又放在哪里?
  • 项目如何处理错误、身份验证和状态?
  • 测试遵循什么约定?
  • 以前尝试过哪些方案,又否决过哪些方案?

如果 Claude 只能猜测这些问题的答案,你得到的往往是千篇一律的通用代码。如果答案已经包含在项目上下文中,它写出的代码就会像是出自团队成员之手,自然地融入现有项目。

提示词工程是在问一个更好的问题;上下文工程则是让 Claude 掌握回答好这个问题所需的知识。


第一层:以 CLAUDE.md 为基础

在所有能提供给 Claude 的上下文中,CLAUDE.md 是投入产出比最高的一项。每次会话开始时,它都会被自动加载,并影响 Claude 接下来所做的一切。

大多数 CLAUDE.md 的问题出在哪里

许多开发者会把 CLAUDE.md 写成 README,也就是对项目做一份静态介绍。这当然有用,却没有抓住重点。Claude 读过几个文件后,就能大致判断项目是做什么的。它真正需要的,是那些无法仅靠阅读代码推断出来的信息:

  • 不仅告诉它做了什么决策,还要说明为什么这样决策
  • 代码本身无法体现的约束条件
  • 应该遵循的模式,以及需要避开的模式
  • 构建、测试、代码检查和部署所需的命令

一份高信息密度的 CLAUDE.md 模板

# CLAUDE.md

## 命令
`npm run dev` — 在 3000 端口启动开发服务器
`npm run test` — 运行 Jest 测试(修改代码后执行)
`npm run test:e2e` — 运行 Playwright 端到端测试
`npm run lint` — 执行 ESLint 和 Prettier 检查
`npm run typecheck` — 执行 TypeScript 严格模式检查

## 架构决策
- **状态管理**:使用 Zustand,不要使用 Redux。我们已在第一季度完成迁移,禁止重新引入 Redux。
- **API 层**:内部 API 使用 tRPC,对外集成使用 REST。
- **数据库**:PostgreSQL + Drizzle ORM,迁移文件位于 /drizzle/。
- **身份验证**:使用 NextAuth v5 的凭据提供程序,会话策略为 JWT。

## 代码约定
- 除非组件需要交互,否则所有新组件都必须采用 React Server Components。
- 条件 className 使用 lib/utils 中的 `cn()`,禁止使用三元表达式拼接字符串。
- 错误边界应包裹每个路由段,而不是单个组件。
- API 错误响应采用以下结构:`{ error: string, code: string, details?: unknown }`

## 禁止事项
- 禁止使用 `any` 类型。使用 `unknown`,然后进行类型收窄。
- 禁止添加桶式导出(通过 index.ts 重新导出)。直接从源文件导入。
- 除页面和布局外,禁止使用默认导出。
- 未经用户确认,不得安装新依赖。

## 测试
- 单元测试与被测文件放在一起:`foo.ts` → `foo.test.ts`
- 模块模拟使用 `vi.mock()`,禁止使用 jest.mock(项目使用 Vitest)
- 测试行为,不测试实现细节。测试 API 路由时,优先编写集成测试而不是单元测试。

注意这份模板的规律:每个部分都在回答一个 Claude 原本只能靠猜测的问题。其中,“禁止事项”尤其有价值——它可以阻止 Claude 套用那些虽然常见、却并不适合当前项目的做法。

CLAUDE.md 的层级:用户、项目与目录

Claude Code 会按照层级加载多份 CLAUDE.md 文件:

  1. ~/.claude/CLAUDE.md:个人偏好,适用于所有项目
  2. ./CLAUDE.md:项目根目录规则,适用于当前仓库
  3. ./src/CLAUDE.md:目录专属规则,在该目录中工作时生效

你可以有策略地利用这套层级:

  • 个人 CLAUDE.md:个人代码风格、偏好的库、回答格式偏好
  • 项目 CLAUDE.md:架构、约定、命令和约束条件
  • 目录 CLAUDE.md:特定模块的模式和局部约定
# src/components/CLAUDE.md

## 组件模式
- 每个组件使用独立目录:ComponentName/index.tsx + ComponentName.test.tsx
- Props 接口定义在同一个文件中,不从外部导入
- 所有会渲染 DOM 元素的组件都使用 forwardRef
- Storybook stories 不是硬性要求,但复杂组件建议添加

这样一来,Claude 在组件目录和 API 目录中工作时,就能分别遵守不同的约定。上下文会随着当前代码位置自动调整。


第二层:让项目结构本身提供上下文

文件组织方式也是一种隐含的上下文。Claude 会通过目录树判断各类代码应该放在哪里。结构清晰的项目能显著提升 Claude 的表现,因为目录结构本身已经替它回答了一部分问题。

有助于 Claude 理解项目的结构模式

src/
  app/              # Next.js 路由——Claude 知道页面应该放在这里
    api/            # API 路由——Claude 会遵循 REST 约定
    (auth)/         # 路由组——Claude 能理解这种分组方式
  components/
    ui/             # 基础组件(Button、Input、Card)
    features/       # 面向具体功能的组合组件
    layouts/        # 布局组件
  lib/
    db/             # 数据库客户端、Schema 和迁移
    auth/           # 身份验证配置
    utils/          # 共享工具函数
  hooks/            # 自定义 React Hooks
  types/            # 共享 TypeScript 类型

项目遵循常见结构时,Claude 需要的额外说明会更少。它可以自行推断:新的 API 路由应该放进 src/app/api/,新的 Hook 应该放进 src/hooks/,新的工具函数则应该放进 src/lib/utils/

会让 Claude 困惑的反模式

  • 一个扁平目录中堆放数百个文件:Claude 无法推断它们的分组关系
  • 命名方式前后不一致:文件名同时混用 camelCase 和 kebab-case,会向 Claude 传达规则混乱的信号
  • 遗留死代码和未使用文件:Claude 可能引用甚至扩展已经废弃的模式
  • 多套模式彼此竞争:同一件事存在两种不同写法,会让 Claude 在它们之间反复摇摆

如果项目确实存在结构问题,可以在 CLAUDE.md 中用一句话覆盖旧模式:“新组件统一放在 src/components/features/ 中,忽略 src/old-components/ 中的遗留模式。”


第三层:正确描述任务

你如何描述任务,将直接决定 Claude 如何着手解决它。优秀的任务描述通常具有三个共同点:说明目标、列出约束条件,并明确什么才算完成

“目标—约束—完成标准”框架

较弱的提示词:

添加邮件通知

更强的提示词:

为订单状态变更添加邮件通知。

目标:当订单状态变为“已发货”或“已送达”时,向客户发送邮件。

约束条件:
- 使用 package.json 中已经安装的 Resend,以及 src/lib/email/ 中的现有邮件模板
- 通过现有的 Bull 任务队列发送邮件,不要在请求流程中直接发送
- 只有在用户偏好设置中启用了邮件通知时才发送

以下条件全部满足才算完成:
- 订单状态变为已发货或已送达时,会创建一项邮件任务
- 邮件使用现有的品牌模板
- 测试覆盖正常流程和“用户已关闭通知”两种情况
- 其他状态变更不会触发邮件

写出后一种提示词只多花 30 秒,却能省下 15 分钟的来回沟通和反复修改。

引用现有实现,不要重复描述

与其在提示词里从头解释某种模式,不如直接让 Claude 参考现有代码:

为订单添加一个 PATCH 端点,沿用
src/app/api/users/[id]/route.ts 的实现模式——使用相同的校验方式、
错误处理方式和响应格式。

Claude 会读取你指定的文件,并准确复用其中的模式。这通常比用自然语言重新描述模式更可靠。

用渐进式披露处理复杂任务

面对大型功能时,不要把全部要求一次性塞进一个提示词。应该分层补充上下文:

提示词 1:“我们要开发一项实时协作功能。
  先不要写代码,请阅读 src/lib/ws/ 中现有的 WebSocket 实现,
  以及 src/lib/db/schema.ts 中的文档模型。
  然后告诉我你准备采用什么方案。”

提示词 2:“方案不错。先实现服务端事件处理——
  包括文档内容变更和光标位置,暂时不用考虑 UI。”

提示词 3:“现在添加实时协作 UI 所需的 React Hooks 和组件,
  使用上一步实现的光标数据。”

每一轮提示词都会逐步扩充 Claude 的上下文。等到开始实现 UI 时,它已经深入理解了自己刚刚构建的数据层。


第四层:记忆与连续性

Claude Code 的记忆系统可以跨会话保留上下文。使用得当,你就不必在每次开始新对话时,反复解释相同的信息。

哪些内容适合写入记忆

记忆适合保存那些跨会话长期有效,同时又无法从代码中推断出来的信息:

  • 你的角色和技术水平,例如“我是资深后端工程师,但刚开始接触这个前端代码库”
  • Claude 曾经理解错误、经你纠正的偏好,例如“始终使用命名导出,不要使用默认导出”
  • 代码中没有体现的项目背景,例如“我们正在从 REST 迁移到 tRPC,因此新的端点应该使用 tRPC”
  • 外部系统信息,例如“缺陷跟踪位于 Linear 的 PLATFORM 项目中”

哪些内容不应该写入记忆

  • 代码模式,因为代码本身已经记录了它们
  • 文件路径,因为路径会发生变化
  • 当前任务状态,应该使用任务功能来管理
  • 已经写在 CLAUDE.md 里的任何内容

可以这样理解:记忆应该保存一位新团队成员必须知道、但目前没有记录在任何地方的信息。


第五层:把 Git 历史变成上下文

提交历史也是 Claude 能够读取的上下文。清晰、规范的提交记录,可以帮助 Claude 更准确地理解项目是如何演进的。

Claude 如何使用 Git 信息

探索代码库时,Claude 可以读取:

  • 最近的提交消息,用来了解改了什么、为什么要改
  • Diff 历史,用来观察代码如何演进
  • 分支名称,用来判断你当前正在做什么

这意味着,提交消息实际上就是 Claude 用来理解项目的“训练资料”:

# 不好的写法——Claude 什么也学不到
git commit -m "fix stuff"
git commit -m "updates"

# 好的写法——Claude 能理解修改意图和背景
git commit -m "fix: prevent duplicate emails when order status webhook fires twice"
git commit -m "feat: add rate limiting to public API endpoints (100 req/min per key)"

用分支名称补充上下文

在名称清晰的功能分支上工作,可以让 Claude 立刻理解当前重点:

git checkout -b feat/real-time-collaboration
# Claude 现在知道你正在开发实时协作功能
# 因而能够提出与此相关的建议

第六层:通过 MCP 服务器获取运行时上下文

MCP(Model Context Protocol,模型上下文协议)服务器让 Claude 能够访问外部系统中的实时数据。这是基础设施层面的上下文工程。

MCP 服务器 提供的上下文
数据库 MCP 真实的 Schema、示例数据和数据关系
Figma MCP 实际设计规范,而不是口头描述
GitHub MCP Issue、PR 和代码审查评论
Sentry MCP 真实错误轨迹、堆栈信息和出现频率
浏览器 MCP 页面实际呈现的效果和控制台错误

每接入一类服务器,就能消除一类猜测。没有数据库 MCP 时,你只能用语言描述 Schema;接入之后,Claude 可以直接读取真实的 Schema,并写出能够在实际数据上运行的查询。

{
  "mcpServers": {
    "postgres": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-postgres"],
      "env": {
        "DATABASE_URL": "postgresql://localhost:5432/mydb"
      }
    }
  }
}

汇总:上下文工程检查清单

每次开始一项重要的编码工作前,都可以快速过一遍下面这份清单。

项目基础

  • 已创建 CLAUDE.md,其中包含命令、架构决策和代码约定
  • 目录结构遵循一致且常见的模式
  • 已删除遗留死代码和废弃模式,或者对它们进行了说明
  • Git 历史中的提交消息足够清晰具体

会话准备

  • 已为外部数据源配置相关的 MCP 服务器
  • 记忆中包含长期有效的个人偏好和项目背景
  • 当前分支名称能够说明正在进行的工作

任务描述

  • 目标表达清楚,说明“做什么”而不是“怎么做”
  • 约束条件明确,包括库、模式和边界
  • 完成标准具体,覆盖测试、行为和范围
  • 已指出可供模仿现有模式的参考文件

迭代过程

  • 复杂任务已拆成渐进式提示词
  • 纠正 Claude 时说明了“为什么”,让它理解背后的原则
  • Claude 给出好结果后进行了确认,让它知道哪些做法有效

复利效应

上下文工程并不是一次性的配置工作,它带来的收益会随着时间不断累积:

  1. 第一周:写一份基础的 CLAUDE.md,并开始更有条理地描述任务。Claude 的输出会出现明显改善。
  2. 第一个月:根据真实会话持续完善 CLAUDE.md,用记忆记录你的偏好。Claude 开始像真正了解这个项目一样工作。
  3. 第三个月:用目录级 CLAUDE.md 指导它在代码库不同区域中的行为,同时用 MCP 服务器提供实时数据。大多数时候,Claude 第一次交付的代码就能通过你的审查。

那些最能发挥 Claude Code 价值的开发者,未必写出了更高明的提示词;他们真正做对的,是构建了更好的上下文环境。这项投入主要发生在前期,但回报会越来越快地增长。

不妨先从 CLAUDE.md 开始。在本指南介绍的所有方法中,仅这一份文件,就可能对你的 Claude Code 使用体验产生最大的改变。