第 3 章

上下文工程

Claude Code 的每一项功能都可以追溯到一个约束:context window。它是你智能体工作流的 CPU、RAM 和硬盘。用错误的东西填满它,Claude 就会忘记你的指令;让它空着,Claude 就会浪费周期去重新发现你已经知道的东西。获得非凡结果的开发者并不是更擅长写提示词——他们是更好的上下文工程师。

常驻文件

CLAUDE.md 不是文档。它不是 README。它是注入到 Claude 在你的项目中处理的每一个请求中的指令。这个区别很重要,因为它意味着 CLAUDE.md 中的每一行都有成本:它在对话的每一轮中消耗 context window 空间,减少了可用于实际工作的空间。

Claude Code 从多个位置按照严格的层级加载 CLAUDE.md 文件。主目录中的 CLAUDE.md 适用于每个项目。项目根目录中的 CLAUDE.md 适用于该仓库。子目录中的 CLAUDE.md 文件在 Claude 工作于这些目录时适用。如果你的组织将托管 CLAUDE.md 文件部署到系统目录,那些文件适用于该机器上的每个用户。

加载是叠加的。Claude 能看到所有的文件,分层叠加在一起。这意味着你的主目录级别文件应该包含到处适用的偏好——你偏好的测试方法、你的提交消息风格、你选择的语言。你的项目级别文件应该包含仓库特定的知识。子目录文件应该包含仅与代码库该部分相关的上下文。

使用 /init 引导

如果你从零开始,/init 命令可以为你完成初稿。它会分析你的代码库——检测构建系统、测试框架、代码模式和目录结构——并生成一个已填入基本内容的入门 CLAUDE.md。输出并不完美,但它是一个坚实的基础,你可以随时间精炼。把 /init 看作脚手架:它把结构搞对,这样你就可以专注于只有你才拥有的项目特定知识。

@path 导入系统

一种机制让这个系统可扩展:@path 导入。你可以不内联所有内容,而是在 CLAUDE.md 中写入 @docs/architecture.md 来引入外部文件。这让根文件保持精简,同时让 Claude 能访问更深入的参考材料。

该语法支持多种形式:

@README.md                          # 相对于 CLAUDE.md 的位置
@docs/git-instructions.md           # 子目录引用
@~/.claude/my-project-instructions.md  # 主目录引用

导入的内容仍然消耗上下文,但这种间接引用使你的 CLAUDE.md 更易读、更易维护。你可以在单独的文件中组织参考材料——API 契约、编码标准、架构决策记录——并只导入与当前项目相关的内容。

500 行准则

500 行准则的存在是有原因的。超过这个阈值,指令之间就开始相互竞争。Claude 不会忽略臃肿的 CLAUDE.md——它会在太多指令之间稀释注意力,最重要的指令和几乎无关的指令获得相同的权重。精简的 CLAUDE.md 不是锦上添花,而是性能要求。

如果 Claude 尽管有规则禁止但仍然不断做你不想让它做的事,文件可能太长了,规则被淹没了。如果 Claude 问你 CLAUDE.md 中已经回答了的问题,措辞可能存在歧义。像对待代码一样对待 CLAUDE.md:出问题时审查它,定期修剪它,并通过观察 Claude 的行为是否真正改变来测试修改。你可以通过添加强调来调整指令——"IMPORTANT" 或 "YOU MUST"——以提高关键规则的遵守程度。

应该放入什么——以及不应该放入什么

问题不是"Claude 应该知道什么?"而是"Claude 无法自行推断出什么?"这个过滤器可以排除大多数开发者本能地放入 CLAUDE.md 的内容。

应该放入 CLAUDE.md 的内容:

  • 构建和测试命令。你的项目特定的测试运行器调用、构建命令和 lint 步骤——Claude 无法在不尝试并可能失败的情况下推断出你的项目使用哪些命令。
  • 与标准惯例不同的代码风格偏差。如果你的团队使用制表符而不是空格,或者你的 Python 项目遵循非 PEP8 的导入顺序,请说明。否则 Claude 将遵循标准。
  • 仓库规范。哪些分支受保护。你是否 squash-merge 还是 rebase。PR 是否需要特定的标签格式。
  • 架构决策及其理由。"我们使用六边形架构。领域逻辑绝不能从基础设施层导入。"Claude 可以看到你的代码结构,但看不到其背后的设计意图。
  • 环境异常。"测试数据库运行在端口 5433,不是 5432。""CI 使用运行时版本 18,不是 20。"这些是会浪费时间的隐形陷阱。
  • 数据异常和已知问题。"users 表的 created_at 列在 2024 年 3 月之前有空值,这是因为一次迁移 bug。"Claude 最终会发现这一点,但发现过程会消耗你的上下文和时间。

不应该放入 CLAUDE.md 的内容:

  • Claude 可以从代码推断的内容。它会读取你的依赖清单、配置文件、目录结构。当项目使用的框架和语言从仓库中显而易见时,告诉 Claude 这些是在浪费上下文。
  • 标准语言惯例。Claude 已经了解主要语言的常见风格指南和惯用模式。只记录偏差即可。
  • 知名库的冗长解释。不要将框架文档粘贴到 CLAUDE.md 中。Claude 对流行工具有大量训练数据。
  • 常见工作流的分步说明。Claude 知道如何创建 pull request、运行迁移或设置测试文件。告诉它你项目特定的变体,而不是通用流程。

你的 CLAUDE.md 的质量与 Claude Code 的效果直接相关。在多日项目中积累了五次或更多会话的精炼 CLAUDE.md 内容的团队,报告的效果比每次从零开始的团队好得多。

上下文成本对比

并非你配置的所有内容成本都相同。理解成本模型是一个会话保持敏锐 200 轮和在 40 轮后就被压缩之间的区别。以下是完整的图景:

CLAUDE.md —— 会话开始时加载所有 CLAUDE.md 文件的完整内容。每次请求都有成本。

Skills —— 会话开始时加载描述,使用时加载完整内容。低成本(每次请求只有描述)。

MCP servers —— 会话开始时加载所有工具定义和模式。每次请求都有成本。

Subagents —— 生成时带有指定 skills 的新鲜上下文。与主会话隔离。

Hooks —— 触发时外部运行。零上下文成本,除非 hook 返回额外上下文。

默认情况下,skill 描述在会话开始时加载,以便 Claude 决定何时使用它们。在 skill 的 frontmatter 中设置 disable-model-invocation: true 可以将 skill 对 Claude 完全隐藏,直到你用 /skill-name 手动调用它。这为你只自己触发的 skills 将上下文成本降至零。

决策矩阵:Skill vs. Subagent vs. CLAUDE.md vs. MCP

每种机制服务于不同的目的。决策取决于信息何时需要以及保持可用性的成本有多高:

使用 CLAUDE.md 的场景: 信息在每一轮都需要。构建命令、风格规则、架构约束、环境异常。这是常驻通道。保持在 500 行以内。

使用 skill 的场景: 信息在特定任务类型中需要。数据库迁移流程、部署清单、API 集成指南、复杂的重构工作流。Skills 按需加载——它们的描述在每次请求中花费少量成本,但完整内容只在 Claude 或你调用时才加载。

使用 subagent 的场景: 任务涉及读取许多文件、处理冗长输出或执行会使你的主上下文膨胀的探索。Subagents 在自己的 context window 中运行。只有摘要返回到你的会话。这使得 subagents 不仅是一种并行工具,更是一种上下文管理策略。

使用 MCP 的场景: 你需要对外部服务的结构化访问。MCP 工具定义在会话开始时加载并持久存在,因此你配置的每个 MCP server 都有持续成本。但其结构化接口、权限集成和 hook 可见性使其成为敏感或频繁使用的外部集成的正确选择。

使用 hook 的场景: 你需要在不消耗任何上下文的情况下扩展 Claude 的行为。Hooks 作为外部进程运行。一个验证文件路径的 PreToolUse hook、一个记录操作的 PostToolUse hook、一个设置环境的 SessionStart hook——这些都不消耗任何上下文。这使得 hooks 成为最节省上下文的扩展机制。

分类在任一方向上搞错都会造成损害。将参考材料放在 CLAUDE.md 中,你就在每次请求中浪费上下文。将始终需要的规则放在 skill 中,Claude 在该 skill 未加载时就会违反它们。

审计你的上下文预算

这个成本模型有一个实际含义:如果你发现自己经常上下文不足,首先审计你的常驻成本。运行 /mcp 查看每个 server 的 token 成本。审查你的 CLAUDE.md 中是否有可以移到 skills 中的内容。检查你是否加载了很少使用的 MCP servers。/context 命令将你当前的上下文使用情况可视化为彩色网格,准确显示空间去了哪里。

Skills vs. CLAUDE.md:加载的区别

Skills 和 CLAUDE.md 都为 Claude 提供指令。区别在于它们何时加载,而这个区别决定了你的上下文预算。

CLAUDE.md 的内容在每次请求时加载。它始终存在,始终消耗上下文。这使其成为 Claude 持续需要的信息的正确位置:构建命令、风格规则、架构约束。

Skills 是按需的。它们的描述在会话开始时加载——每个 skill 功能的简短摘要——但完整的 skill 内容只在 Claude 调用该 skill 时加载。一个包含 200 行数据库迁移指令的 skill 在 Claude 实际需要执行迁移之前几乎不花费任何成本。到那时,skill 内容才加载到该特定交互的上下文中。

使用 disable-model-invocation 的手动专用 Skills

某些 skills 不应该由 Claude 决定何时使用。部署 skill、发布清单、复杂的重构工作流——你希望自己用 /skill-name 调用这些,而不是让 Claude 决定时机。

在 skill 的 frontmatter 中设置 disable-model-invocation: true 可以将 skill 对 Claude 完全隐藏。它不会在会话开始时加载描述。它不会出现在 Claude 的可用工具中。上下文成本为零,直到你手动调用它。这是对你不常使用的或只应在明确选择时运行的 skills 最激进的上下文优化。

---
description: Deploy to production
disable-model-invocation: true
---
Follow the production deployment checklist...

决策规则很简单。如果 Claude 在每一轮都需要知道某件事——"始终使用单引号"、"绝不修改 /legacy 中的文件"——那就放在 CLAUDE.md 中。如果 Claude 需要特定任务类型的参考材料——详细的部署流程、复杂的重构清单、API 集成指南——那就是一个 skill。如果只有你应该决定何时触发该 skill,请添加 disable-model-invocation: true。

Auto-Compaction:上下文填满时会发生什么

在大约 95% 的上下文容量时,Claude 触发自动 compaction。这不是崩溃。这是一个受控过程,但理解它的工作原理很重要,因为它决定了什么能存活下来。

在 compaction 过程中,Claude 清除较早的工具输出并总结对话历史。它二十轮前读取的文件内容、早期探索的命令输出、中间推理——所有这些都被压缩成一个摘要。这释放了上下文空间但丢失了细节。

Compact Instructions

CLAUDE.md 的一个部分会得到特殊处理:标题"Compact Instructions"下的任何内容。该部分中的内容在 compaction 过程中被明确保留。这是你指定 Claude 即使在对话历史被压缩后也必须记住的内容的机会。

将 Compact Instructions 用于:

  • 无论会话运行多长时间都绝不能违反的关键约束。
  • 当前任务描述,如果它足够复杂以至于摘要可能丢失重要细节。
  • 在会话早期做出的、影响所有后续工作的关键决策。

使用 /compact 进行定向 Compaction

你可以用 /compact 手动触发 compaction,并通过添加焦点指令来指导什么能存活下来:/compact Focus on the API changes。这告诉 compaction 过程在总结时优先考虑对话的哪些方面。没有焦点指令时,compaction 会自行判断什么重要——这通常是合理的,但有时会丢弃不该丢的内容。

在 80% 时手动 compaction 通常比等到 95% 时的自动 compaction 更好。你有更多控制权,compaction 有更多空间来工作,而且你可以在对话历史仍然相对新鲜时进行指导。

你还可以使用 CLAUDE_AUTOCOMPACT_PCT_OVERRIDE 环境变量覆盖 compaction 阈值。将其设置为 80 会更早触发 compaction,在 window 临界满载之前释放空间。设置得更高会延迟 compaction,但在触发前的最后阶段可能导致性能下降。

使用 /rewind 进行选择性摘要

有时你不想压缩整个对话——只想压缩一部分。按两次 Escape(或使用 /rewind 命令)打开回退菜单。一个可滚动列表将你的每个提示显示为检查点。选择一条消息,你会得到四个选项:

  • Restore code and conversation —— 将一切恢复到那个时间点。
  • Restore conversation only —— 回退对话但保留当前代码。
  • Restore code only —— 保留对话但回退文件更改。
  • Summarize from here —— 将从所选点向前的所有内容压缩为摘要,保持该点之前的对话完整。

"Summarize from here"选项是精确的 compaction。如果你花了三十轮探索一个死胡同然后找到了正确的方法,你可以摘要化探索过程(释放上下文)同时保留有成效的工作的完整细节。这比整对话 compaction 精确得多。

PreCompact Hook

PreCompact hook 事件在 compaction 开始之前触发,无论是手动还是自动触发。它接收触发类型(manual 或 auto)以及传递给 /compact 的任何自定义指令。虽然它不能阻止 compaction,但它可以执行准备工作——保存会话状态、记录上下文使用情况,或注入应该影响 compaction 摘要的上下文。这是上下文管理的编程扩展点。

/context 命令

/context 命令将你当前的上下文使用情况可视化为彩色网格。它显示系统提示、CLAUDE.md 内容、MCP 工具定义、对话历史和活跃工作各消耗了多少空间。这是上下文工程的诊断工具——当性能下降时,/context 告诉你原因。如果 MCP servers 在你提出任何问题之前就消耗了 30% 的上下文,你就知道该修什么了。

实际要点:如果你的会话经常触及 compaction,你要么在一个会话中做得太多,要么你的常驻上下文成本太高。两者都是可解决的。

会话分叉作为上下文恢复

当一个会话的上下文被污染——太多死胡同、太多不再相关的探索、auto-compaction 丢失了关键指令——你有一个除了重新开始之外的恢复选项。

--fork-session 标志创建一个新会话,该会话以现有会话的完整历史开始,但从该点分叉。原始会话保持不变。分叉获得一个新的会话 ID 和一个独立的对话线程。

当你的会话有你想保留的有价值上下文,但你需要将工作引向不同方向时,使用这个选项。你不需要重新解释项目设置、你已经做出的决策和你正在采取的方法,而是分叉会话并在拥有所有这些上下文的情况下开始新的方向。

claude --continue --fork-session

会话分叉不能替代良好的上下文管理。它是一个逃生舱,用于当会话的上下文尽管你尽了最大努力但仍偏离了你的需求时。

非代码领域的 CLAUDE.md

CLAUDE.md 不仅适用于软件项目。机构记忆最有说服力的示范来自一个使用 Claude Code 进行投资组合优化的人——一个没有传统代码库的金融分析项目。

工作流程:将金融账户的数据导出为电子表格,创建一个补充文本文件包含无法结构化导出的信息,起草一个详细的目标提示,并初始化一个指向包含所有这些内容的目录的 Claude Code 会话。在多个会话中,Claude 解析数据、分类持仓、识别差距,并生成了一个分阶段优化计划,包含税务影响分析、基金推荐和前后配置对比网格。

关键部分:他们建立了一个 CLAUDE.md 文件,积累的不是代码知识而是领域分析记忆。解析过程中发现的数据异常。早期会话中做出的策略决策。经过迭代细化后达成一致的目标配置。当他们几天后回来时,Claude 精确地从他们停下的地方继续,因为 CLAUDE.md 承载了项目的分析状态。

这种模式适用于任何工作跨越会话的领域:研究项目、数据分析、技术写作、基础设施规划、合规审查。CLAUDE.md 的内容会变化——不再包含构建命令和编码约定,而是包含数据模式、分析框架和领域特定的约束。但机制是相同的:持久化的上下文让每个会话都比上一次更聪明地开始。

工作成果的复利效应

同一位从业者指出了一个值得拥有自己名字的模式:工作成果复利。完成财务规划后,他们将 Claude Code 指向同一个项目目录——会话历史、规划文档、分析产物——并要求它写一篇关于该过程的博客文章。第一个项目的交付成果成为了完全不同交付成果的输入。

这不是偶然。这是基于文件系统的上下文的结果。Claude 产生的每个产物——计划、分析、报告、代码——都作为文件存在于你的项目目录中。该文件可供未来的会话、未来的 skills、未来的 subagents 使用。你用 Claude Code 做的工作不会被困在对话记录中。它在文件系统中积累,每个新会话都可以建立在之前所有工作之上。

自适应角色的 CLAUDE.md

Anthropic 的产品设计团队发现了一件软件工程师可能想不到的事:当用户不是开发者时,CLAUDE.md 的工作方式不同。

团队中的设计师创建了自定义的 CLAUDE.md 文件,告诉 Claude 他们是编程经验有限的设计师,需要详细的解释和更小、更渐进的变更。这极大地提高了 Claude 响应的质量——不再是假设工程流利的简短技术输出,Claude 生成了分步解释,更详尽地注释了代码,并做出了更容易审查的较小变更。

这就是角色自适应上下文。CLAUDE.md 不仅描述项目;它还描述用户。数据科学家可能会指定他们偏好带有可视化的探索性脚本。技术写作人员可能会指定他们需要冗长的提交消息和每次变更附带的文档。设计师可能会指定他们需要面向 UI 的解释和可视化差异。

这意味着:即使在同一团队中,CLAUDE.md 也不是一刀切的。项目级别的 CLAUDE.md 文件承载共享标准。主目录中的用户级别 CLAUDE.md 文件承载个人偏好。这种组合意味着 Claude 同时适应项目的需求和个人的工作风格。

持续改进循环

Anthropic 内部一个数据基础设施团队的模式将会话结束时更新扩展为不仅是积累知识。他们让 Claude 总结已完成的工作并建议改进——不仅是添加到 CLAUDE.md 的内容,还包括对工作流本身的改进。

这个区别微妙但重要。大多数团队使用会话结束时的更新来添加知识:"测试数据库使用端口 5433"或"模块 X 存在循环依赖"。持续改进模式还捕获流程改进:"部署脚本应该在构建之前运行 lint"或"API 模块的 CLAUDE.md 指令导致 Claude 生成过于冗长的错误处理器——简化它们"。

这创建了一个反馈循环,CLAUDE.md 不仅在它知道什么方面演进,还在它如何指导方面演进。随着时间的推移,指令本身基于观察到的结果而精炼。该团队报告说,这个循环——每次会话后同时更新知识和流程——使后续迭代明显更有效,因为 Claude 不仅基于更好的知识工作,还基于更好的指令工作。

机构记忆

当你将 CLAUDE.md 视为一个跨会话积累知识的活文档时,它变得更有价值得多。

这是这个模式。你用一个最小的 CLAUDE.md 开始项目——构建命令、风格规则、一些架构说明。在你的第一次会话中,Claude 发现你的测试套件需要一个特定的环境变量。它了解到某个模块有一个需要绕过的循环依赖。它发现 API 客户端抛出非标准格式的错误。

所有这些知识都存在于会话上下文中。当会话结束时,它就消失了。

解决方法是一个会话结束时更新循环。在关闭会话之前,让 Claude 回顾它学到了什么并建议对 CLAUDE.md 的添加。Claude 会基于它遇到的问题提出具体、明确的添加建议。你审查它们,接受有用的部分,并提交更新后的 CLAUDE.md。

随着时间的推移,CLAUDE.md 积累了通常存在于资深工程师脑中的那种知识:异常、变通方案、"这就是我们这样做的原因"的解释——这些从来没有人写下来的东西。但现在它们被写下来了,每个未来的会话——无论你还是任何团队成员——都从中受益。

这创造了一种复利效应。每个会话都比上一个会话以更多的知识开始。在第一次会话中浪费时间的错误不再发生,因为它们已被记录在 CLAUDE.md 中。项目特定的知识库不断增长,Claude 的效果也随之增长。

将 CLAUDE.md 签入版本控制的团队将这种效应放大到整个团队。一个开发者的发现成为了每个人的上下文。

防止重复错误

机构记忆的一个特定应用值得单独成节,因为它解决了一个特定的、常见的挫折:Claude 反复犯同样的错误。

Anthropic 的内部团队记录了一种模式,其中 Claude Code 在一个项目内持续犯特定的工具调用错误或遵循不正确的模式。解决方法是精确的:在 CLAUDE.md 中添加一条针对性的指令来解决确切的错误。

例如,如果 Claude 不断从错误的模块路径导入,添加:"When importing database utilities, use @app/db/utils not @app/utils/db. The latter path exists but is deprecated."

如果 Claude 不断生成因时序问题而失败的测试:"All async tests in this project must use the waitForCondition helper, not setTimeout. The CI environment has non-deterministic timing."

这些针对性添加比一般指令更有效,因为它们解决了 Claude 已经展示出的特定失败模式。它们不是理想化的指导方针——它们是针对你的项目中观察到的 Claude 行为 bug 的补丁。

规格驱动的上下文

CLAUDE.md 在手动更新之间是持久但静态的。对于复杂的多会话项目,有一种补充方法:规格文档。

规格文档是对你要构建的内容的详细描述,在实现开始之前编写。它作为文件存在于你的仓库中——spec.md、design.md,或任何适合你项目的命名。你用 @path 导入从 CLAUDE.md 中引用它,或在每次会话开始时告诉 Claude 去读取它。

规格的力量在于它们能在上下文污染和会话重启中存活。当一个会话压缩并丢失了你早期讨论的细节时,规格仍然存在,在 Claude 可以重新读取的文件中保持不变。当你开始一个新会话时,规格提供了完整的上下文,无需你重新解释任何内容。

规格优先开发还产生了一个你可以在任何代码编写之前审查的具体产物。你可以让 Claude 编写规格,审查它,通过对话精炼它,然后才进入实现。规格成为跨多个会话、多个 subagents 和多天工作保持实现一致的真理来源。

这种模式对于跨越两到三个以上会话的项目最为重要。没有规格,每个新会话都以有损的意图重新解释开始。有了规格,每个会话都以精确的、版本控制的目标描述开始。

AGENTS.md:跨智能体标准

CLAUDE.md 是 Claude Code 特有的。但如果你的团队使用多种 AI 编码工具——或者你的仓库的贡献者使用不同的智能体——有一个值得了解的补充标准。

AGENTS.md 是一个用于智能体特定文档的开放标准。它存在于你的仓库根目录中,适用于二十多种不同的编码智能体。README 面向人类,而 AGENTS.md 包含编码智能体需要的额外上下文:开发环境设置、编码标准、测试流程和架构约束。

与 CLAUDE.md 的实际重叠是显著的,关键见解在于信息架构。一个试图包含一切的臃肿的 AGENTS.md(或 CLAUDE.md)——有时长达数百行——会消耗大量上下文。精简的方法使用渐进式披露:一个简洁的根文件,带有一个文档引用表,指向 Claude 可以按需阅读的详细文档。不要内联你的 API 文档,而是引用它。不要粘贴你的架构指南,而是导入它。

# AGENTS.md

## Dev Environment
- How to set up and navigate

## Standards
- Code style, naming, patterns

## Testing
- How to run and write tests

## Docs Reference
| Topic | File |
|-------|------|
| API contracts | docs/api.md |
| Architecture | docs/architecture.md |
| Deployment | docs/deploy.md |

精简版本加载快、上下文成本最小,并让智能体只在需要时才拉取详细文档。这与 CLAUDE.md 的 500 行准则相同的原则,应用于更广泛的智能体生态。

使用 Subagents 进行上下文隔离

Subagents 不仅关乎并行性。它们是一种上下文隔离策略。

当 Claude 调用 subagent 时,subagent 在自己的 context window 中运行。它读取文件、运行命令、处理数据——全部在隔离环境中。只有返回给父会话的摘要消耗主窗口上下文。一个读取了五十个文件并运行了二十个命令的 subagent 在自己的窗口中消耗大量上下文,但你的主对话永远不会看到其中任何内容。

实际影响是显著的。在一个有记录的案例中,一个开发者在一次复杂的迁移项目过程中编排了十四个 subagents。每个 subagent 完成任务、提交更改并返回简短摘要。十四个全部完成后,主会话的上下文使用量为 200,000 token 窗口中的 143,000 token——百分之七十一。系统提示和工具定义约占百分之十。编排开销——创建任务、跟踪进度、综合结果——消耗了其余部分。所有实际的实现工作都没有触及主上下文。

这意味着一个读取二十个文件并运行复杂分析的 skill 不会消耗二十个文件的主上下文。只有最终结果返回。在自己的 context fork(context: fork)中运行的 skills 工作方式相同。

对设计的含义:如果你有一个需要读取许多文件或处理大量数据的复杂工作流,将其打包为 skill 或委派给 subagent 比在主对话中直接运行相同步骤更节省上下文。主对话保持精简。繁重的工作在隔离中完成。

你还应该仔细考虑 skill 或 subagent 返回什么。一个将整个分析作为巨大文本块返回的 skill 违背了隔离的目的。设计输出为简洁摘要,给主对话所需内容,而不附带结果是如何产生的负担。

使用 SessionStart Hooks 实现动态上下文

CLAUDE.md 是静态的——它只在有人编辑时才改变。但有些上下文本质上是动态的:CI 管道的当前状态、未解决问题的列表、其他团队成员的最近提交、当前分支与 main 的关系。

SessionStart hooks 解决了这个问题。在 SessionStart 事件上配置的 hook 在 Claude Code 启动时运行外部命令并将输出注入为上下文。与 CLAUDE.md 内容(每次请求都消耗上下文)不同,hook 输出只注入一次并被视为会话初始化的一部分。

实际示例:

  • 一个检查当前分支上开放的 pull request 并总结审查评论的脚本。
  • 一个列出自当前分支分叉以来 main 上最近提交的命令,以便 Claude 知道发生了什么变化。
  • 一个读取环境特定配置并将其注入为上下文的脚本,使 Claude 的行为适应开发、预发布和生产环境。

SessionStart hooks 补充了静态的 CLAUDE.md。CLAUDE.md 提供不变的项目知识。Hooks 提供时间点的状态。两者结合,为 Claude 同时提供了永久规则和当前情况。

融会贯通

上下文工程不是单一技术。它是一个相互关联的决策系统:

  • CLAUDE.md 承载持久的、始终需要的知识。保持在 500 行以内。专注于 Claude 无法推断的内容。用 /init 引导,用 @path 导入扩展,通过会话结束时更新来精炼。
  • Skills 存放按需加载的参考材料。将它们用于特定任务类型的指令,这些指令如果持续加载会浪费上下文。为你只手动调用的 skills 设置 disable-model-invocation: true。
  • Hooks 以零持续成本注入动态上下文。使用 SessionStart hooks 注入环境特定状态。使用 PreCompact hooks 为 compaction 做准备。
  • 规格 在会话和 compaction 之间持久化复杂的项目意图。将它们用于跨越两个以上会话的任何内容。
  • Subagents 将昂贵操作与你的主上下文隔离。委派冗长的探索和分析以保持你的主要对话敏锐。十四个 subagents 可以在不耗尽主会话上下文的情况下运行。
  • Compact Instructions 在 CLAUDE.md 中保护关键规则通过 auto-compaction。使用带有焦点指令的 /compact 进行定向 compaction,使用带有"Summarize from here"的 /rewind 进行精确的上下文清理。
  • 会话结束时的更新 将 CLAUDE.md 变成一个每次会话都在改进的知识积累器。将此扩展为持续改进循环,让 Claude 建议工作流改进,而不仅是知识添加。
  • 主目录中的自适应角色的 CLAUDE.md 让非开发者获得适当详细的解释和根据其专业水平调整的渐进式工作流。

掌握这个系统的开发者并不是工作更努力。他们每个会话都比上一个会话以更好的上下文开始。他们的 Claude Code 实例比大多数人类团队成员更了解他们的项目,因为知识被写下来了、版本化了、并自动加载了。

这就是上下文工程。不是提示词技巧。是基础设施。

要点总结
  • CLAUDE.md 内容在每次请求时消耗上下文;保持在 500 行以内,用 /init 引导,专注于 Claude 无法从你的代码中推断的信息。
  • 上下文成本对比表是你的预算指南:CLAUDE.md 和 MCP 每次请求都有成本,skills 成本低(仅描述),subagents 是隔离的,hooks 是免费的。
  • 将始终需要的规则放在 CLAUDE.md 中,将特定任务的参考材料放在 skills 中;对手动专用 skills 使用 disable-model-invocation: true 实现零上下文成本。
  • 使用 @path/to/import 语法保持 CLAUDE.md 精简,同时让 Claude 能访问单独文件中更深入的参考材料。
  • CLAUDE.md 中的 Compact Instructions 能在 auto-compaction 中存活;带有焦点指令的 /compact 指导什么被保留;带有"Summarize from here"的 /rewind 实现精确的上下文清理。
  • /context 命令显示你的上下文预算去了哪里——在责怪 Claude 上下文不足之前先审计它。
  • CLAUDE.md 适用于非代码领域:金融分析、数据科学、技术写作——任何工作跨越会话且上下文必须持久化的领域。
  • Subagent 上下文隔离效果显著:十四个 subagents 可以在不耗尽主会话的情况下运行,因为它们的工作保持在单独的 context windows 中。
  • 会话结束时的 CLAUDE.md 更新创造复利式的机构记忆;将此扩展为持续改进循环,在知识之外同时捕获工作流改进。
  • 针对观察到的特定错误的 CLAUDE.md 添加比一般指令更有效。
  • 规格文档能在上下文 compaction 和会话重启中存活,使其成为多会话项目的必备。
  • 会话分叉(--fork-session)保留有价值的上下文,同时让你可以将工作引向新方向。