跳到正文

上下文工程

最后更新:2026 年 3 月

"上下文工程是一门艺术:在正确的时间,将正确的信息填入上下文窗口。"——Andrej Karpathy

本指南涵盖从上下文预算的 Token 数学,到构建模块化、团队规模的配置系统的全部内容。它是终极指南中更广泛配置章节的配套文档——那些章节介绍单项技术,本文档则展示如何将它们组合成一个连贯的系统。


目录

  1. 什么是上下文工程
  2. 上下文预算
  3. 配置层级
  4. 模块化架构
  5. 团队配置组装
  6. 上下文生命周期
  7. 质量度量
  8. 上下文缩减技术
  9. 成熟度评估
  10. Token 审计工作流
  11. 研究模式
  12. 注意力机制与可靠性
  13. Token 压缩工具

1. 什么是上下文工程

定义

Andrej Karpathy 创造了这个说法:"上下文工程是一门艺术:在正确的时间,将正确的信息填入上下文窗口。"

这句话包含三个不那么显而易见的要求:

  • 填充:上下文窗口应当经过有意识的填充,而非随机堆砌。大部分留空是浪费模型能力;杂乱塞满则浪费 Token 并降低输出质量。
  • 正确的信息:并非所有信息都同等重要。架构决策比代码风格偏好更有价值。负面约束("永远不要将原始 SQL 错误返回给客户端")比宏观目标("写干净的代码")更具可操作性。
  • 正确的时间:针对后端代码的路径范围规则,在编辑前端组件时毫无价值。凡事都加载的懒惰做法会降低遵循质量。

提示工程 vs. 上下文工程

这两个术语经常被混淆,但区别很重要:

维度提示工程上下文工程
范围单次请求整个会话或系统
持续时间单次交互跨交互持久存在
投入方式逐请求设计前期系统设计
规模个人团队或组织级别
产出物一个提示字符串一个配置系统

提示工程是为单一任务设计正确的问题。上下文工程是确保 Claude 在任何任务开始前就具备正确背景知识的系统。即使提示写得再好,底层上下文工程薄弱,结果依然平庸——因为模型缺乏对项目结构、规范和约束的整体理解。

一个实用的类比:提示工程是给承包商写一封好邮件;上下文工程是确保承包商在读第一封邮件之前就理解项目的入职流程、代码风格指南、架构文档和团队规范。

上下文工程 vs. 上下文优化

两个术语在文献中都有出现,有时可以互换,但它们并不相同。

维度上下文工程上下文优化
核心问题哪些信息应该在上下文中?能最大化结果的最小高信号 Token 集是什么?
目标完整性与正确性效率与信号密度
方法识别模型需要知道什么删除它不需要知道的内容
失败模式遗漏关键信息过度填充——太多无关内容
产出物一个上下文系统一个经过精简的高保真提示或配置

一个有用的心智模型:上下文工程回答"包含什么",上下文优化回答"删除什么"。

实践中两者都要做。工程阶段构建完整图景:架构决策、规范、约束。优化阶段进行修剪:去除冗余、压缩冗长规则、归档过时条目、将子系统特定内容限定路径范围。第 8 节中的缩减技术就是优化阶段。

综合 vs. 推理

一个值得明确提出的相关区别:

  • 上下文综合是有状态的、迭代的。它跨会话积累知识,在规范变更时更新,并反映项目历史。CLAUDE.md 就是上下文综合。
  • 推理是短暂的、可丢弃的。每个推理步骤使用上下文产生输出,然后丢弃中间状态。Claude 的链式思维就是推理。

将推理产物(中间想法、调试痕迹、错误输出)当作上下文综合材料是一个常见错误。这会用短暂状态污染上下文,加速上下文退化。要分清什么应当持久保存(综合),什么应当丢弃(推理噪声)。

为什么重要

大语言模型本质上是上下文窗口计算机。输出质量受限于输入质量。这不是软性说法——它有硬性技术依据:

  1. 模型在会话之间没有持久记忆(不借助专门工具的话)。除非有意提供上下文,每次会话都从零开始。
  2. 模型无法推断未明确说明的规范。如果你想要 TypeScript 接口而非 type 别名,必须明确说明。如果你想在抛出错误前先记录日志,也必须明确说明。
  3. 模型对指令的位置和表述方式很敏感。一条埋在 500 行 CLAUDE.md 第 400 行的指令,比前 50 行的指令被遵循的可能性要低得多。

投入上下文工程的团队普遍反映修改轮次减少、规范遵循更好、输出更可预测。投入是前期的(构建系统),但回报在每次交互中复利积累。

一个有用的诊断视角转换:大多数 AI 输出失败是上下文失败,而非模型失败。 当 Claude 生成泛泛回应、忽视规范或产出不匹配你的技术栈的代码时,模型几乎从来都没有问题——是它接收到的上下文不完整、相互矛盾,或者在错误时机缺少正确信息。这一视角转换将故障排查从"AI 不擅长这个"转变为"上下文中缺少什么?"

三个层次

Claude Code 中的上下文工程在三个不同层次上运作:

层次机制范围加载时机
全局配置~/.claude/CLAUDE.md所有项目始终
项目配置./CLAUDE.md + 路径范围模块当前项目每次会话
会话内联指令、/add、标志位仅当前会话运行时

每个层次都有不同的权衡。全局配置始终开启,但无法引用项目特定细节。会话指令灵活但短暂。项目配置是主力:结构化、可版本控制、可审查。

良好的上下文工程意味着将每条信息放在正确的层次——不是把所有东西塞进一个文件,也不是将关键知识留在会话层次,每次对话结束后就消失殆尽。

静态上下文 vs. 动态上下文

上述三层系统是静态上下文——在会话开始前组装的配置文件,在整个会话过程中保持稳定。Claude Code 主要是一个静态上下文系统,这也是为什么 CLAUDE.md 的结构和路径范围如此重要。

随着向智能体工作流演进,第二类出现了:动态上下文,在智能体运行时于推理阶段动态组装。

类型组装方式Claude Code 中的示例
静态会话前从文件加载CLAUDE.md、路径范围模块、技能文件
动态运行时通过工具获取工具调用输出、文件读取、网络请求、MCP 数据

实践中,每次 Claude Code 会话都同时使用两者。静态上下文(你的配置)设定行为边界;动态上下文(Claude 读取的文件、工具结果)为每个任务提供具体信息。上下文工程涵盖两者,但失败模式不同:静态上下文问题表现为持续性的规范违反;动态上下文问题表现为 Claude 在任务中途基于过时或不完整信息行动。

对于构建自动化流水线和智能体的团队,Anthropic 2025 年 9 月的工程博文 "Effective context engineering for AI agents" 深入介绍了动态上下文侧。

为什么上下文退化是结构性的,而非偶然的

Transformer 模型对所有 Token 进行两两注意力计算。这意味着上下文窗口中注意力关系的数量以 n² 增长,而非线性增长。上下文长度翻倍,关系数量翻四倍。在 20 万 Token 时,这意味着数十亿次两两计算,模型的注意力变得越来越分散。

这不是未来模型会消除的 bug,而是架构本身的必然结果。上下文退化——随着上下文增长,指令遵循质量逐渐下降——在结构上是内置的。其含义:你无法靠更大的上下文窗口解决上下文退化问题。解决方案是保持上下文精简,并在需要时才加载信息。

即时检索 vs. 预加载

有两种策略为 Claude 提供所需信息:

策略机制适用场景
预加载(RAG)在推理前检索并注入所有可能相关的上下文已知的、稳定的上下文需求
即时检索按需检索上下文,仅在需要时才检索动态的、任务特定的上下文

预加载是熟悉的 RAG(检索增强生成)模式:构建检索索引,提前将相关片段注入提示。当你事先知道模型需要什么信息时,这种方式有效。

即时检索实现起来更复杂,但在规模上更有效:模型使用工具调用、MCP 服务器或文件读取,在任务需要时动态检索信息。上下文中只有当前步骤所需的信息。

Claude Code 的行为体现了这一模式:CLAUDE.md 提前加载(预加载,始终相关),而文件内容和工具结果则在推理时通过 read_fileglobgrep 和 MCP 调用即时检索。glob 和 grep 工具是即时检索层——只有在任务涉及某些文件时,才将这些文件的内容放入上下文。

Memory 工具(测试版)

从 Claude Sonnet 4.5 开始,Anthropic 发布了公开测试版的 Memory 工具。它允许 Claude 跨会话存储和检索持久性事实,无需手动管理 CLAUDE.md。该工具维护一个结构化知识库,Claude 在需要相关上下文时进行查询。

这与 CLAUDE.md 不同:CLAUDE.md 是静态配置(始终加载),而 Memory 工具是动态检索(按需查询)。对于构建智能体的团队,Memory 工具减少了在配置文件中手动编码知识的需求。

长任务中的链式思维

链式思维(CoT)提示能改善孤立任务的模型推理能力。然而,Anthropic 的工程数据表明它可能在长智能体循环任务中降低性能。原因:CoT 会生成额外 Token,延长上下文,从而加速后续步骤的上下文退化。在跨越 20+ 次工具调用的任务中,这一效应是可测量的。

实践规则:将 CoT 用于复杂的孤立推理步骤,而非作为智能体工作流的通用策略。在长时运行中,优先使用压缩后的中间输出,而非延伸的推理链。


2. 上下文预算

Token 数学

一个中等规模项目的具体基准:

来源典型 Token 范围
全局 CLAUDE.md1,000 – 3,000 Token
项目 CLAUDE.md(根目录)2,000 – 8,000 Token
路径范围模块(所有已激活)1,000 – 5,000 Token
导入的技能/命令500 – 3,000 Token
始终开启的上下文总计~5,000 – 20,000 Token

Claude Sonnet 4.6 有 20 万 Token 的上下文窗口。这意味着即使是较大的始终开启配置预算(2 万 Token)也只占窗口的约 10%——剩余 18 万 Token 可用于实际工作:代码文件、对话历史、工具输出。

实践规则:始终开启的上下文应保持在上下文窗口的 5% 以下。 超过这个比例,你在占用实际任务内容的空间,而任务内容每 Token 的价值远高于常驻指令。

150 条指令上限

来自运行大型 CLAUDE.md 文件的团队的经验性观察:超过约 150 条独立规则后,模型开始选择性忽略其中一些。这不是硬性截断——它取决于规则复杂度、重叠程度和位置——但这是一个可靠的信号:更多规则并不等于更好的遵循。

机制是注意力分散:当提示包含数百个潜在相关的约束时,模型的注意力会被分散。高显著性规则(位置靠前、措辞强烈、近期添加的)会挤占低显著性规则。

HumanLayer 的生产数据显示,使用结构化上下文的团队——规则更少、更具体、层级组织——比使用无差别长规则列表的团队有 15-25% 更好的遵循率。

含义:规则质量胜过规则数量。 二十条具体可操作的规则胜过两百条泛泛的愿景性规则。

按文件大小估算遵循率下降趋势

Plain
CLAUDE.md 行数        遵循率(估算)
─────────────────     ─────────────────────
1 – 100               ~95%
100 – 200             ~88%
200 – 400             ~75%
400 – 600             ~60%
600+                  ~45% 且持续下降

这些是估算基准,不是保证值。路径范围划定和模块化架构可以在更大的规则总量下保持更高的遵循率,因为它确保在任何给定时刻上下文中只有相关规则。

上下文过载的迹象

当始终开启的上下文过大或过于嘈杂时,你会看到可预测的失败模式:

  • 规则被静默忽略:Claude 持续遵循 80% 的规范,但忽略本应适用的特定规则。
  • 行为矛盾:Claude 在某些文件中应用某条规则,在其他文件中却不应用;或根据措辞应用相互矛盾的规则。
  • 首次响应缓慢:模型在生成输出前需要更多时间处理大型上下文(在简单任务中表现为更长的延迟)。
  • 泛泛输出:Claude 退回到通用最佳实践,而非应用项目特定模式——这是项目上下文未被保留的信号。

当你看到这些模式时,诊断方向是:进行上下文审计(参见第 7 节),而不是添加更多指令。

MECW:最大有效上下文窗口

宣传的上下文窗口与实际有效的上下文窗口并不相同。企业级上下文工程部署持续发现,在达到官方限制之前,有意义的准确性下降就已开始。来自生产经验的常见数据:约为宣传限制的 92%。

对于 Claude Opus 4(宣传 20 万 Token),这将实际上限设定在约 18.5 万 Token,超过这个值,复杂推理任务的准确性会出现可测量的下降。机制是第 1 节(为什么上下文退化是结构性的)描述的 n² 注意力缩放:随着上下文增长,注意力操作呈平方级缩放,窗口中间位置的有效权重递减。

在重度上下文负载下,上下文退化在中间窗口位置会将准确性降低 30% 以上。实践含义:一个 12.8 万 Token、内容优质且维护良好的上下文窗口,胜过一个 100 万 Token、充斥过时堆积内容的窗口。100 万 Token 的窗口并不消除问题,只是推迟它,同时增加每次请求的成本。

"我应该直接用 100 万 Token 的上下文窗口吗?"这个问题实质上是关于信噪比,而非能力。一个堆积了工具输出噪声、过期对话轮次和冗余指令的大窗口,并不比一个精心维护的小窗口更强大,只是更贵、更慢。

实用 MECW 目标

窗口宣传值实际上限(92%)退化开始影响准确性的位置
Claude Sonnet 4.6200K~184K~150K+
Claude Opus 4200K~185K~150K+

这些是工程估算值,不是保证值。将其作为规划数字:如果你的会话经常接近 15 万 Token,应在准确性成为问题之前就实施压缩、渐进式卸载或路径范围划定,而不是等到问题出现后才处理。

路径范围与预算效率

路径范围划定是减少始终开启上下文最有效的单项技术。它不是为整个代码库加载所有规则,而是只加载与当前上下文中的文件相关的规则。

没有路径范围划定的典型项目:

Plain
始终开启:包含后端 + 前端 + 数据库 + API 规则的根 CLAUDE.md = 8,000 Token

采用路径范围划定的相同项目:

Plain
始终开启:包含共享规则的根 CLAUDE.md = 2,000 Token
在 src/api/ 中工作时激活:api 模块 = +1,500 Token
在 src/components/ 中工作时激活:frontend 模块 = +1,200 Token
在 prisma/ 中工作时激活:database 模块 = +800 Token

结果:始终开启上下文减少 40-50%,覆盖范围没有任何损失。每个子系统在工作于该子系统时获得完整的规则集。


3. 配置层级

三层架构

Plain
┌──────────────────────────────────────────────┐
│  全局(~/.claude/CLAUDE.md)                  │
│  身份标识、语气、通用工具、跨项目规范          │
├──────────────────────────────────────────────┤
│  项目(./CLAUDE.md + 路径模块)               │
│  架构决策、技术栈规范、团队规则、部署流程      │
├──────────────────────────────────────────────┤
│  会话(内联指令、标志位)                     │
│  临时覆盖、实验约束、一次性任务参数            │
└──────────────────────────────────────────────┘

后面的层次覆盖前面的层次。会话指令可以覆盖项目规则;项目规则可以覆盖全局默认值。这为你提供了逃生通道,无需永久修改共享配置。

全局配置

位置~/.claude/CLAUDE.md

适合放这里的内容

  • 身份标识和沟通风格偏好
  • 通用工具偏好(RTK、偏好的 CLI 工具)
  • 跨项目编码规范(提交消息格式、PR 风格)
  • 适用于所有地方的安全约束
  • 语气和输出格式默认值

不适合放这里的内容

  • 项目特定的架构决策
  • 技术栈特定的规则(React hooks、Prisma 模式)
  • 部署或环境细节
  • 随项目变化的任何内容

大小目标:全局配置保持在 200 行以内。这是你在每个项目的每次会话中都始终开启的开销。把它搞大会对所有项目造成同等损害。

Markdown
# 示例:简洁有效的全局 CLAUDE.md

## 沟通
- 用用户写作时使用的语言回应
- 直接给出答案,不要冗长铺垫
- 书面输出中不使用破折号

## Git
- 提交消息:祈使语气,主题行不超过 72 字符
- 未经要求不提交

## 代码风格
- 偏好显式错误处理,而非静默失败
- 仅在引用已追踪 issue 时添加 TODO 注释

项目配置

位置./CLAUDE.md(项目根目录)

适合放这里的内容

  • 使用的技术栈和版本
  • 架构决策及其原因
  • 本代码库特有的团队规范
  • 文件组织模式
  • 测试要求和覆盖率目标
  • 本项目特有的安全约束
  • 子系统模块的路径范围导入

结构模式

Markdown
# 项目:[名称]

## 技术栈
- 语言:TypeScript 5.3
- 框架:Next.js 14(App Router)
- 数据库:通过 Prisma 使用 PostgreSQL 16
- 测试:Vitest + React Testing Library

## 架构
- 默认使用 Server Components;仅在需要交互性时使用 `"use client"`
- API 路由放在 /app/api;路由处理器中不含业务逻辑
- 业务逻辑放在 /lib/services;每个 service 是一个普通函数模块

## 规范
- 文件命名:文件用 kebab-case,React 组件用 PascalCase
- 错误处理:将 service 调用包裹在 Result<T, E> 模式中(见 lib/result.ts)
- API 响应中不暴露原始数据库 ID;使用 UUID

## 路径范围模块
@src/api/CLAUDE-api.md
@src/components/CLAUDE-components.md
@prisma/CLAUDE-db.md

金发女孩问题:抽象层级

生产环境 CLAUDE.md 文件中持续出现两种失败模式:

过于模糊:"写干净的代码"、"遵循最佳实践"、"保持函数短小"。这些指令对模型没有任何影响。模型对"干净的代码"有自己的概念,早在你的指令存在之前就有了,它会默认使用那个概念,而那个概念可能与你的项目需求不符。愿景性规则会被忽略。

过于细粒度:"使用 2 空格缩进"、"导入块后加空行"、"私有方法名以下划线开头"。这些是 linter 规则,不是认知决策。它们应该放在 .eslintrc.editorconfigprettier.config.js 中,由工具确定性地执行,而非由大语言模型概率性地遵循。放在 CLAUDE.md 中浪费上下文预算,且执行效果不可靠。

有效的抽象层级:捕捉如果没有该指令,模型会做出不同决策的情况。测试标准是:"Claude 在没有项目上下文的情况下,这里是否会合理地做一些不同的事情?"如果是,则这条规则属于 CLAUDE.md。如果答案是愿景性的,删掉它。如果 linter 已经执行了它,也删掉它。

层级示例判定
过于模糊"写干净的代码"删除——模型会忽略,无行为变化
过于模糊"遵循安全最佳实践"删除——用具体约束替代
有效"API 响应中永不暴露原始数据库 ID;使用 UUID"保留——具体,否则模型会采用其他默认值
有效"service 函数使用 Result<T, E> 模式,而非 try/catch"保留——具体,覆盖了常见默认值
过于细粒度"使用 2 空格缩进"删除——交给 Prettier
过于细粒度"每个函数都加 JSDoc 注释"删除——交给 lint 规则

架构选择、质量标准和"不要做什么及其原因"的明确规则是有效层级。愿景性的和机械性的都是噪声。

会话配置

机制:当前会话的内联指令、/add-dir 或系统提示标志位。

适合放这里的内容

  • 一次性任务约束("本次重构中,不要更改公共 API 接口")
  • 实验参数("在这个文件中使用我正在测试的新错误格式")
  • 调试约束("本次会话中记录每次工具调用")
  • 项目规范的临时覆盖

会话指令不会持久化。会话结束时就消失了。任何发现自己在多次会话中重复使用的指令,都应该放到项目配置中,而不是会话层次。

决策树:这条规则应该放在哪里?

Plain
这条规则与我工作的每个项目都相关吗?
├── 是 → 全局 CLAUDE.md
└── 否 ↓

这条规则与特定文件或子系统相关吗?
├── 是 → 路径范围模块(如 src/api/CLAUDE-api.md)
└── 否 ↓

这条规则与整个项目相关吗?
├── 是 → 项目 CLAUDE.md(根目录)
└── 否 ↓

这条规则只适用于当前任务或会话吗?
├── 是 → 内联会话指令
└── 否 → 重新思考:这真的是规则,还是一次性偏好?

导入链和覆盖语义

导入链流向:全局 → 项目根目录 → 路径范围模块 → 会话

存在冲突时:

  • 更具体的覆盖不那么具体的(路径范围优先于根目录,根目录优先于全局)
  • 同一层次中,后声明的覆盖先声明的
  • 会话指令覆盖所有持久配置

实际示例:你的全局配置说"使用 2 空格缩进"。你的项目配置说"Python 使用 4 空格缩进"。你的会话说"匹配现有文件风格"。本次会话中,会话指令胜出,Python 文件默认 4 空格,其他所有内容 2 空格。

明确记录你的覆盖。一个与父级规则相矛盾但未记录的覆盖,会在审计时造成困惑。


4. 模块化架构

单体配置的问题

没有结构的 600 行 CLAUDE.md 是生产环境中最常见的失败模式。症状:

  1. 来自不同领域的规则混在一起——React 组件规范紧挨着数据库迁移规则
  2. Claude 读完全部 600 行,但注意力预算意味着第 5 页的规则比第 1 页的权重低
  3. 新团队成员无法快速找到相关规则
  4. 更新需要扫描整个文件以找到相关规则,然后才能编辑
  5. 随文件增长,遵循率持续下降

解决方案是架构性的:将单体文件分解为专注的模块,然后使用路径范围划定,仅在相关时才加载每个模块。

路径范围模式

机制:Claude Code 在 CLAUDE.md 中支持 @path/to/file.md 导入。当路径范围导入激活时,该模块的规则只有在指定路径下的文件处于范围内时才会添加到上下文中。

文件结构

Plain
project/
├── CLAUDE.md                       # 根配置,共享规则 + @导入
├── src/
│   ├── api/
│   │   └── CLAUDE-api.md           # API 特定规则
│   ├── components/
│   │   └── CLAUDE-components.md    # React/UI 特定规则
│   └── lib/
│       └── CLAUDE-lib.md           # 工具/共享库规则
├── prisma/
│   └── CLAUDE-db.md                # 数据库和迁移规则
└── tests/
    └── CLAUDE-tests.md             # 测试规范

带导入的根 CLAUDE.md

Markdown
# 项目配置

## 共享规则
[...此处放共享规则...]

## 子系统模块
@src/api/CLAUDE-api.md
@src/components/CLAUDE-components.md
@src/lib/CLAUDE-lib.md
@prisma/CLAUDE-db.md
@tests/CLAUDE-tests.md

路径范围模块示例src/api/CLAUDE-api.md):

Markdown
# API 规则

- 路由处理器只放在 /app/api;不在其中内联业务逻辑
- 所有端点必须在处理前用 Zod 验证输入
- 错误响应使用标准格式:{ error: string, code: string }
- 永远不要记录可能包含 PII 的请求体;只记录 ID
- 所有公共端点必须带有限流响应头
- 认证:在中间件中验证 JWT,而非在单个处理器中

该模块的 6 条规则仅在 src/api/ 中工作时才在上下文中。在 src/components/ 中工作时,它们不消耗上下文预算。

技能 vs. 规则

这一区分使用不足,但很重要:

维度规则技能
本质约束、标准、规范能力、流程、工作流
激活时机始终执行按需调用
示例"TypeScript 中永不使用 any""如何添加新的 API 端点"
位置CLAUDE.md.claude/skills/
Token 成本始终开启仅在调用时加载

规则定义 Claude 默认应该做什么和不应该做什么。它们设定可接受输出的边界。

技能定义如何执行需要了解你项目特定模式的复杂多步骤任务。它们在 Claude 需要执行特定类型任务时加载,而非始终加载。

实际示例:规则说"API 端点必须有 Zod 验证"。技能说"这是在本项目中创建新 API 端点的逐步模式,包括 Zod schema 模式、错误处理包装器、认证中间件钩子和测试文件结构"。

把端点创建流程放在规则里,意味着每次会话都要加载 40 行流程指令,即便你根本不在创建端点。把它放在技能里,意味着只在创建端点时才加载这 40 行。

规则永不在 API 响应中暴露原始数据库 ID。技能如何为实体生成并使用基于 UUID 的公开标识符。

社区技能库

预构建的技能集合降低了模块化上下文工程的初始投入:

  • anthropics/claude-code-skills(官方):Anthropic 维护的技能模板,涵盖常见开发工作流
  • ibelick/ui-skills:用于前端项目的 UI 组件和设计系统技能

这些可以克隆、检查并根据你的项目规范进行调整,而非从零开始构建。将它们作为起点——fork 并修改以匹配你的技术栈和命名规范,而非直接照搬使用。

渐进式按需加载

原则:不要提前加载所有内容。只加载当前任务所需的内容。

核心配置(始终开启)

  • 架构决策及其原因
  • 编码标准和命名规范
  • 安全约束
  • 工具偏好

上下文模块(按任务加载)

  • 部署流程(部署时加载)
  • API 模式(在 API 层工作时加载)
  • 测试模板(编写测试时加载)
  • 数据库迁移流程(修改 schema 时加载)

使用技能的实现模式

Plain
.claude/
├── skills/
│   ├── deploy-production.md      # 加载时机:"部署这个"
│   ├── add-api-endpoint.md       # 加载时机:"为 X 添加端点"
│   ├── write-migration.md        # 加载时机:"添加数据库列"
│   └── create-component.md      # 加载时机:"为 X 创建组件"

每个技能文件包含带有项目特定模式的逐步流程。Claude 在检测到任务类型时加载它,而非主动预加载。

MCP 工具数量与上下文预算

MCP 服务器将工具定义注入系统提示。每个服务器添加其工具 schema,在任何用户内容出现之前就消耗上下文预算。Anthropic 的工程指南建议:

  • 每个项目激活的 MCP 服务器少于 10 个
  • 所有激活服务器的工具总数少于 80 个

超过这些阈值,工具定义开销会明显减少实际任务内容可用的 Token 数。在 80+ 个工具时,你仅在工具 schema 上就消耗了 1.5-2 万 Token——这些预算原本可以用于代码上下文、对话历史和文件内容。

渐进式按需加载原则对 MCP 服务器同样适用。按需加载 MCP 服务器,而非为每个项目激活所有可用服务器:

JSON
{
  "mcpServers": {
    "database": { },
    "github": { }
  }
}

抵制"以防万一"就把所有可用 MCP 服务器添加到项目设置的冲动。每个已加载但不活跃的服务器都是纯粹的开销。如果某个服务器在项目中不足 20% 的会话中被使用,它不应该出现在默认项目配置中。

反模式:单体 CLAUDE.md

它的样子

Markdown
# CLAUDE.md(600 行)

## 规则
1. 使用 TypeScript
2. 禁止 any 类型
3. 提交前运行测试
4. API 端点需要认证
5. 用 Prisma 查询数据库
6. React 组件用 PascalCase
7. 用 ./scripts/deploy.sh 部署
8. 发布前检查 OWASP Top 10
[...492 条更多规则...]

为何失败

  • 规则 1-20 获得约 95% 的注意力权重;规则 500+ 只获得约 30%
  • 前端开发者读到他们不需要的后端数据库规则,反之亦然
  • 没有逻辑分组意味着找到相关规则需要通读所有内容
  • 添加新规则需要检查整个文件是否有冲突
  • 随文件增长,遵循率持续下降

解决方案

  1. 按领域将规则提取到路径范围模块中
  2. 根 CLAUDE.md 只保留共享规则和导入声明
  3. 将流程知识移至技能文件
  4. 提取后,目标是根 CLAUDE.md 在 150 行以内

结构性元数据文件

规则和结构是两种不同类型的上下文。将它们混在一起会产生既太大无法始终加载、又太重要不能跳过的文件。

规则上下文回答的是:在这个项目中我应该怎么工作? 它存在于 CLAUDE.md 和路径范围模块中。它相对稳定,几乎始终相关。

结构性上下文回答的是:这个项目的形态是什么? 有多少 API 路由、哪些领域有组件、嵌套的 CLAUDE.md 文件在哪里、有多少 Prisma 模型。这些信息只在实施任务时需要——创建新文件、添加路由、探索陌生领域——在调试、编写文档或代码审查时无关紧要。

始终加载结构性上下文是 Token 的浪费。完全没有它意味着 Claude 在每次实施任务开始时都要手动浏览文件系统,消耗对话轮次并产生噪声。

解决方案:一个小型、自动生成的 YAML 文件(约 1K Token),捕捉代码库的结构形态,在 CLAUDE.md 中作为指针注册,而非自动导入。

包含什么——五个部分,不多不少:

部分内容示例
layers架构层级,包含根路径和文件数量routers: { root: "src/api", count: 33 }
component_domains功能领域,包含路径和组件数量{ name: "chat", count: 66 }
nested_contextssrc/ 下的所有 CLAUDE.md / AI_INSTRUCTIONS.md,包含行数和重点{ path: "src/server/CLAUDE.md", lines: 45 }
stats汇总数字:总文件数、测试数量、schema 模型数total_ts_files: 543
key_pathsClaude 经常出错的关键路径prisma_schema: "src/server/db/prisma/schema.prisma"

将文件保持在 1K Token 以下。超过这个数,你添加的细节应该属于实际源文件。

指针注册模式

不要在 CLAUDE.md 中用 @machine-readable/code-map.yaml 自动加载这个文件。而是将其注册在一个引用表中,告诉 Claude 文件包含什么以及何时使用它:

Markdown
## 上下文索引(按需加载)

| 文件 | 内容 | 何时加载 |
|------|------|---------|
| machine-readable/code-map.yaml | 架构层级(数量 + 根路径)、组件领域、嵌套上下文文件、项目统计 | 任何实施任务之前:新文件、新路由、新组件 |
| machine-readable/ai-config.yaml | 完整 AI 工具配置:规则、技能、命令、智能体、Hooks | 审计或修改 AI 配置时 |
| PROJECT_INDEX.md | 详细架构叙述、ADR、领域词汇表 | 仅在深度架构工作时 |

这个模式可扩展:Claude 在会话开始时读取表格,知道存在哪些参考文件及其用途,只在当前任务需要时才加载它们。调试会话永远不会触碰代码地图;实施任务通过一次工具调用就能加载它。

"自动生成"在实践中意味着什么

生成脚本只需要做三件事:对每个层根调用 readdirSync 统计文件数量,遍历 src 树统计 .ts/.tsx 文件总数,以及 glob 查找嵌套的 CLAUDE.md 文件以填充 nested_contexts。无需 AST 解析、数据库查询或网络调用。整个脚本运行时间不到一秒。将其添加到你的 pnpm ai:sync(或等效)任务中。

关键设计约束:绝不在此文件中添加手工整理的内容。 一旦这样做,你就有了一个可能产生漂移的文件。自动生成的文件不会对代码库的当前状态撒谎;含有手工内容的文件会并且确实会撒谎。

生产示例(Méthode Aristote EdTech 平台,约 1,300 个源文件):

YAML
version: "1.0.0"
architecture: "Client → tRPC → Router → Service → Repository → Prisma"

layers:
  routers:
    root: "src/server/api/routers"
    description: "第一层 — Zod 验证,委托给 service"
    count: 33
  services:
    root: "src/server/api/services"
    description: "第二层 — 业务逻辑,enforcePermission()"
    count: 61
  repositories:
    root: "src/server/api/repositories"
    description: "第三层 — 仅 CRUD Prisma"
    count: 38

stats:
  total_ts_files: 543
  total_tsx_files: 798
  prisma_models: 48
  unit_tests: 268

将此文件注册为指针后,Claude 通过一次查找就能回答"有多少个 tRPC 路由?",而无需手动遍历 src/server/api/routers/。对于实施任务"添加支付路由",它能立即知道正确的根路径、数量和架构约束——无需读取一个源文件。

可在 examples/context-engineering/code-map-template.yaml 获取即用模板。


5. 团队配置组装

N × M × P 问题

在团队规模下,上下文工程面临组合挑战:

  • N 位开发者:不同的角色、工具、沟通偏好
  • M 个项目:不同的技术栈、规范、部署目标
  • P 个配置:每位开发者 × 每个项目都需要一个配置

手动维护 N × M 个独立的 CLAUDE.md 文件是不可持续的。共享规范变更时,你需要更新 N × M 个文件。创建新项目时,需要从零开始构建。开发者角色变更时,需要重建配置。

解决方案是基于配置文件的组装:一套共享的模块基础,加上指定要包含哪些模块和覆盖哪些个人偏好的个人配置文件。

N × M × P 变成了 N 个配置文件 × 1 个共享模块基础——可管理。

配置文件 YAML 结构

每位团队成员有一个声明式指定其配置的 YAML 配置文件:

YAML
# profiles/alice.yaml

profile:
  name: "Alice"
  role: "frontend"
  tools:
    - typescript
    - react
    - tailwind
  conventions:
    - atomic-design
    - accessibility-first
  communication:
    language: "en"
    verbosity: "concise"

modules:
  include:
    - shared/core-rules.md
    - shared/git-conventions.md
    - shared/security-baseline.md
    - frontend/react-patterns.md
    - frontend/tailwind-conventions.md
    - frontend/testing-rtl.md
    - frontend/accessibility-checklist.md
  exclude:
    - backend/database-rules.md
    - backend/api-design.md
    - devops/deployment-procedures.md

overrides:
  - "Prefer named exports over default exports"
  - "Use Radix UI primitives before writing custom components"
YAML
# profiles/bob.yaml

profile:
  name: "Bob"
  role: "backend"
  tools:
    - typescript
    - nodejs
    - postgresql
    - prisma
  communication:
    language: "en"
    verbosity: "detailed"

modules:
  include:
    - shared/core-rules.md
    - shared/git-conventions.md
    - shared/security-baseline.md
    - backend/api-design.md
    - backend/database-rules.md
    - backend/error-handling.md
    - backend/performance-patterns.md
  exclude:
    - frontend/react-patterns.md
    - frontend/tailwind-conventions.md

overrides:
  - "Use structured logging (pino) with request context IDs"
  - "Always measure before optimizing; profile first"

模块库结构

共享模块库存在于仓库中并进行版本控制:

Plain
.claude/
├── modules/
│   ├── shared/
│   │   ├── core-rules.md           # 通用团队标准
│   │   ├── git-conventions.md      # 提交和 PR 规范
│   │   ├── security-baseline.md    # 不可妥协的安全规则
│   │   └── testing-standards.md   # 覆盖率和测试质量规则
│   ├── frontend/
│   │   ├── react-patterns.md
│   │   ├── tailwind-conventions.md
│   │   ├── testing-rtl.md
│   │   └── accessibility-checklist.md
│   ├── backend/
│   │   ├── api-design.md
│   │   ├── database-rules.md
│   │   ├── error-handling.md
│   │   └── performance-patterns.md
│   └── devops/
│       ├── deployment-procedures.md
│       ├── monitoring-conventions.md
│       └── infrastructure-rules.md
├── profiles/
│   ├── alice.yaml
│   ├── bob.yaml
│   └── carol.yaml
└── scripts/
    └── assemble-context.sh

组装脚本

组装脚本读取配置文件并将指定的模块拼接成 CLAUDE.md:

Bash
#!/usr/bin/env bash
# scripts/assemble-context.sh

set -euo pipefail

PROFILE="${1:-}"
CHECK_MODE="${2:-}"

if [[ -z "$PROFILE" ]]; then
  echo "Usage: ./assemble-context.sh <profile-name> [--check]"
  exit 1
fi

PROFILE_FILE=".claude/profiles/${PROFILE}.yaml"
OUTPUT_FILE="CLAUDE.md"
MODULES_DIR=".claude/modules"

if [[ ! -f "$PROFILE_FILE" ]]; then
  echo "Profile not found: $PROFILE_FILE"
  exit 1
fi

# 用 yq 或 python 解析配置文件
MODULES=$(python3 -c "
import yaml
with open('$PROFILE_FILE') as f:
    profile = yaml.safe_load(f)
for m in profile['modules']['include']:
    print(m)
")

# 组装输出
ASSEMBLED=$(mktemp)

echo "# Claude Code Configuration" > "$ASSEMBLED"
echo "# Generated from profile: $PROFILE" >> "$ASSEMBLED"
echo "# Generated at: $(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "$ASSEMBLED"
echo "" >> "$ASSEMBLED"

while IFS= read -r module; do
  MODULE_PATH="${MODULES_DIR}/${module}"
  if [[ -f "$MODULE_PATH" ]]; then
    echo "## From: ${module}" >> "$ASSEMBLED"
    cat "$MODULE_PATH" >> "$ASSEMBLED"
    echo "" >> "$ASSEMBLED"
  else
    echo "WARNING: module not found: $MODULE_PATH" >&2
  fi
done <<< "$MODULES"

# 追加个人覆盖
python3 -c "
import yaml
with open('$PROFILE_FILE') as f:
    profile = yaml.safe_load(f)
overrides = profile.get('overrides', [])
if overrides:
    print('## Personal Overrides')
    for o in overrides:
        print(f'- {o}')
" >> "$ASSEMBLED"

if [[ "$CHECK_MODE" == "--check" ]]; then
  if diff -q "$OUTPUT_FILE" "$ASSEMBLED" > /dev/null 2>&1; then
    echo "OK: CLAUDE.md matches profile $PROFILE"
    rm "$ASSEMBLED"
    exit 0
  else
    echo "DRIFT: CLAUDE.md does not match profile $PROFILE"
    diff "$OUTPUT_FILE" "$ASSEMBLED"
    rm "$ASSEMBLED"
    exit 1
  fi
fi

mv "$ASSEMBLED" "$OUTPUT_FILE"
echo "Assembled CLAUDE.md from profile: $PROFILE"

用法

Bash
# 从配置文件生成 CLAUDE.md
./scripts/assemble-context.sh alice

# 检查漂移(用于 CI)
./scripts/assemble-context.sh alice --check

CI 漂移检测

团队成员从配置文件重新生成 CLAUDE.md,但基础模块会随时间演进。没有漂移检测,开发者可能运行着过时的配置——一个早于某条安全规则的添加或某项规范更新的版本。

一个 GitHub Actions 任务可以检测到这种情况:

YAML
# .github/workflows/context-drift.yml

name: 上下文漂移检测

on:
  schedule:
    - cron: '0 9 * * 1'   # 每周一上午 9 点(UTC)
  push:
    paths:
      - '.claude/modules/**'

jobs:
  check-drift:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: 安装依赖
        run: pip install pyyaml

      - name: 检查所有配置文件是否有漂移
        run: |
          DRIFT=0
          for profile_file in .claude/profiles/*.yaml; do
            profile=$(basename "$profile_file" .yaml)
            echo "Checking profile: $profile"
            if ! ./scripts/assemble-context.sh "$profile" --check; then
              echo "DRIFT detected in profile: $profile"
              DRIFT=1
            fi
          done
          exit $DRIFT

      - name: 漂移时发送通知
        if: failure()
        uses: actions/github-script@v7
        with:
          script: |
            github.rest.issues.create({
              owner: context.repo.owner,
              repo: context.repo.repo,
              title: '检测到上下文漂移——CLAUDE.md 需要重新生成',
              body: '一个或多个团队配置文件与当前模块库不同步。运行 `./scripts/assemble-context.sh <profile>` 重新生成。',
              labels: ['context-engineering']
            })

使用配置文件入职

对于新团队成员,入职流程变成:

Bash
# 1. 复制适合你角色的入门配置文件
cp .claude/profiles/template-frontend.yaml .claude/profiles/yourname.yaml

# 2. 根据你的偏好编辑配置文件
vim .claude/profiles/yourname.yaml

# 3. 生成你的 CLAUDE.md
./scripts/assemble-context.sh yourname

# 4. 验证输出
cat CLAUDE.md

# 5. 提交你的配置文件(不是生成的 CLAUDE.md——它在 .gitignore 中)
git add .claude/profiles/yourname.yaml
git commit -m "chore: add context profile for yourname"

在项目根目录的 .gitignore 中添加 CLAUDE.md。YAML 配置文件才是真实来源,而非生成的文件。


6. 上下文生命周期

指令债务

规则会积累,却很少被删除。这就是指令债务:过时、冗余或相互矛盾的规则逐渐堆积——每条规则仍在消耗上下文预算。

指令债务的迹象:

  • 某条规则引用了你六个月前就停止使用的库
  • 两条规则对同一模式说法相反
  • 某条规则只适用于特定迁移期间的一个边缘情况
  • 同一约束在不同部分被重述了三次
  • 开发者注释掉或忽略特定规则,因为它们与当前实践冲突

指令债务的成本是复利式的:每条冲突或无关规则都会排挤一条有用规则,而当规则冲突时模型行为会变得不可预测。

季度审计节奏:每季度(或在重大项目里程碑后)安排一次上下文审计。审计提示:

Plain
审查 CLAUDE.md 中的每条规则:
1. 相关性:这条规则仍适用于当前的技术栈和模式吗?
2. 具体性:这条规则是否可操作,还是太模糊无法执行?
3. 冲突:这条规则与另一条规则相矛盾吗?
4. 覆盖:这条规则是否已被另一条更通用的规则覆盖?

对每条规则,分类为:保留 | 更新 | 归档 | 删除

以实际 Claude 会话的形式运行此审计,提供当前 CLAUDE.md 并要求进行结构化审计。

更新循环

在 Claude 输出错误后,最常见的错误是手动修复输出然后继续。这是浪费了学习机会。

坏循环

Plain
Claude 生成错误模式
→ 开发者手动修复
→ 下次会话:Claude 再次生成错误模式
→ 开发者再次手动修复
→ 无限循环

好循环

Plain
Claude 生成错误模式
→ 开发者确认根本原因(缺少规则?规则模糊?规则冲突?)
→ 开发者用更正的或新的规则更新 CLAUDE.md
→ 下次会话:Claude 生成正确模式
→ 规则永久保留在配置中

更新循环是你的配置系统从经验中学习的方式。每次错误输出都是信号,表明上下文工程中有什么缺失或损坏。将其视为针对 CLAUDE.md 的 bug 报告,而非一次性失败。

规则更新的实用格式

从失败案例添加规则时,包含内联的原因说明:

Markdown
- 为 service 函数使用 `Result<T, E>` 类型,而非 try/catch
  (原因:service 层的 try/catch 对调用者隐藏了错误类型;
   Result 强制在调用点进行显式错误处理)

原因说明有两个目的:帮助未来的审计者理解规则存在的原因,以及给 Claude 更好的上下文来正确应用规则。

Sprint 后的知识输入

在每个 sprint 或发布周期结束时,进行一个简短的知识输入会话:

  1. 新模式:"本 sprint 中,我们为 Y 类问题标准化了 X 方法。将其添加到 CLAUDE.md。"
  2. 发现的反模式:"我们尝试了 X,它导致了 Y。添加规则以避免它。"
  3. 架构决策:"我们决定使用 X 而非 Y,因为 Z。记录这一点,这样 Claude 就不会建议 Y。"
  4. 已弃用的模式:"我们正在从 X 迁移走。添加规则使用 Y 代替,并标记现有的 X 用法。"

这保持上下文系统的时效性,无需定期大规模改动。

ACE 流水线

对于在自动化或半自动化工作流中运行 Claude Code 的团队,ACE 流水线提供了结构化执行模型。这是一个跨会话运行的配置持久化循环。它与 arXiv:2510.04618(Stanford/SambaNova,2025 年 10 月)不同,后者使用同一缩写词表示推理时上下文演化技术。关于基于此流水线的操作改进,参见第 10 节(信号分类)和第 11 节(循环闭合)。

Plain
组装(Assemble)→ 检查(Check)→ 执行(Execute)

组装:从团队配置文件和项目模块构建上下文。产出特定于开发者和任务上下文的 CLAUDE.md。

检查:运行金丝雀验证——一组 3-5 个测试提示,在实际任务前验证关键行为。如果金丝雀检查失败,在继续之前修复上下文问题。

执行:用经过验证的上下文对实际任务运行 Claude。

Bash
#!/usr/bin/env bash
# ace.sh — 组装、检查、执行

PROFILE="${1:-}"
TASK="${2:-}"

if [[ -z "$PROFILE" || -z "$TASK" ]]; then
  echo "Usage: ./ace.sh <profile> <task-description>"
  exit 1
fi

echo "=== 组装 ==="
./scripts/assemble-context.sh "$PROFILE"

echo "=== 检查 ==="
./scripts/run-canaries.sh
CANARY_EXIT=$?

if [[ $CANARY_EXIT -ne 0 ]]; then
  echo "金丝雀检查失败。在执行前修复上下文问题。"
  exit 1
fi

echo "=== 执行 ==="
claude "$TASK"

会话回顾

每次 Claude Code 会话结束时,关闭之前请问:

Plain
回顾本次会话构建或修改的内容:
1. 我们使用了哪些不在 CLAUDE.md 中的模式?
2. 我必须纠正什么,可以变成规则?
3. 我们做了哪些应该记录的决策?

基于本次会话生成 3-5 条 CLAUDE.md 候选规则。

这需要 2-3 分钟,并生成具体的改进候选项。你审查并决定添加哪些。随着时间推移,这就是配置系统积累真正项目知识而非仅仅泛泛规则的方式。

上下文链式传递

上下文链式传递是一种模式,其中一个上下文窗口的输出成为下一个的结构化输入。每次会话在前一次的基础上构建,向前传递经过整理的摘要,而非丢弃状态。

这与会话分关注点流水线(在新鲜上下文模式部分)不同,后者将关注点隔离到独立会话中。上下文链式传递允许视角积累:每次会话的洞见丰富下一次会话的起始上下文。

模式结构

Plain
会话 1:
  输入:任务定义 + CLAUDE.md
  工作:研究、探索、初始实现
  输出:summary.md(决策、待解问题、验证的模式)

会话 2:
  输入:任务定义 + CLAUDE.md + 会话 1 的 summary.md
  工作:基于会话 1 发现的实现
  输出:更新的 summary.md + 代码产物

会话 3:
  输入:任务定义 + CLAUDE.md + 更新的 summary.md
  工作:审查、精化、集成
  输出:最终产物 + CLAUDE.md 更新用的 lessons.md

关键纪律:向前传递的摘要必须经过整理,而非原始记录。原始记录会重新引入上下文退化。经过整理的摘要是 200-500 Token 的精炼发现:已做决策、已验证方法、已标记死路。

适用场景

  • 多天任务,每次会话从头重建上下文代价高昂
  • 早期会话产生制约后期会话的发现的研究任务
  • 跨会话积累的理解很重要的迭代设计任务

不适用场景

  • 有明确原子边界的任务(一次会话、一个交付物、下次从新开始)
  • 早期会话假设被证明错误,需要彻底重来的情况

上下文链式传递是有意扩展上下文的。它与 Ralph Loop 相反,后者丢弃状态。当积累的理解是资产时使用链式传递;当积累的状态是负担时使用 Ralph Loop。


7. 质量度量

自我评估问题

定期(至少每季度)对你的 CLAUDE.md 进行这些问题的审查:

相关性

  • 这条规则仍适用于当前的技术栈、库和团队实践吗?
  • 这条规则是为一个已不存在的问题而写的吗?
  • 新团队成员能理解这条规则存在的原因吗?

具体性

  • 这条规则是否足够具体,让 Claude 知道何时适用?
  • 这条规则是否至少有一个具体示例或反例?
  • 两位开发者是否可能对这条规则有不同理解?

冲突

  • 这条规则与同一文件中的另一条规则相矛盾吗?
  • 这条规则与路径范围模块中的规则相矛盾吗?
  • 这条规则与全局规则相矛盾,却没有明确覆盖它吗?

覆盖

  • 这条规则是否是已存在的更通用规则的特例?
  • 这条规则是否已被其他地方陈述的架构决策所隐含?

未能通过一个以上这些检查的规则,就是更新或删除的候选项。

金丝雀检查

金丝雀检查是简单的测试提示,用于验证 Claude 遵循关键规范。在对 CLAUDE.md 进行重大更改前后运行它们,以捕获回归。

结构:3-5 个提示,足够简单可以快速回答,但足够具体可以揭示遵循失败。

React/TypeScript 项目的示例金丝雀集

Bash
# scripts/run-canaries.sh

PASS=0
FAIL=0

check() {
  local name="$1"
  local prompt="$2"
  local expected_pattern="$3"

  result=$(claude "$prompt" --output-format text 2>/dev/null)

  if echo "$result" | grep -qE "$expected_pattern"; then
    echo "PASS: $name"
    PASS=$((PASS + 1))
  else
    echo "FAIL: $name"
    echo "  Expected pattern: $expected_pattern"
    echo "  Got: $(echo "$result" | head -5)"
    FAIL=$((FAIL + 1))
  fi
}

check "TypeScript 接口" \
  "生成一个接受 name 和 age prop 的 React 组件" \
  "interface.*Props"

check "命名导出" \
  "创建一个格式化日期的工具函数" \
  "^export (function|const)"

check "禁止 any 类型" \
  "写一个处理用户数据的函数" \
  "^((?!: any).)*$"

check "错误结果类型" \
  "写一个从 API 获取用户数据的 service 函数" \
  "Result<"

echo ""
echo "金丝雀:$PASS 通过,$FAIL 失败"
[[ $FAIL -eq 0 ]]

何时运行金丝雀

  • 合并 CLAUDE.md 更改之前
  • 添加新的路径范围模块之后
  • 团队成员报告意外的 Claude 行为时
  • 作为 CI 漂移检测任务的一部分

遵循率追踪

非正式但有效:对于 CLAUDE.md 中的每条关键规则,追踪在 10 次连续的、该规则应适用的交互中,Claude 违反它的频率。

规则违反次数/10状态
Props 使用 TypeScript 接口1/10健康
Service 函数使用 Result 类型0/10健康
API 响应中不暴露原始数据库 ID3/10审查规则
带请求上下文的结构化日志5/10规则太模糊
发布前检查 OWASP Top 108/10无法操作

违反率 >20% 的规则以以下三种方式之一失效:

  1. 太模糊无法一致应用
  2. 与另一条规则冲突
  3. 在文件中位置太靠后,无法获得足够注意力**"太模糊"的修复方案**:添加一个合规示例和一个违规反例。

"冲突"的修复方案:找到冲突,决定哪条规则应该胜出,更新或删除失败的规则,并添加明确说明。

"位置太靠后"的修复方案:将规则移至文件的前三分之一,或其所在部分更显眼的位置。

上下文债务分数

你的上下文工程系统健康状态的单一指标:

Plain
上下文债务分数 = (total_rules / 150) × (conflicts_found / total_rules) × 100

其中:

  • total_rules = 所有已加载配置文件中独立规则的数量
  • 150 = 近似注意力上限
  • conflicts_found = 与另一条规则相矛盾的规则数量
分数范围状态行动
< 30健康标准季度审计
30 – 60退化修剪和去重;修复冲突
60 – 80需要大规模重构
> 80危急从最重要的 30 条规则重新开始

运行分数计算

Bash
# 统计规则数量(近似:以 - 开头的行)
TOTAL_RULES=$(grep -c "^- " CLAUDE.md 2>/dev/null || echo 0)

# 统计冲突需要人工审查或使用大语言模型审计
# 使用:claude "扫描 CLAUDE.md 并统计相互矛盾的规则数量。返回数量。"

echo "总规则数:$TOTAL_RULES"
echo "手动或用 Claude 运行冲突审计"

上下文漂移检测

现有的遵循指标(金丝雀检查、违反率)需要人工解读:当你注意到时才知道规则被违反。系统性漂移检测是一个补充层,在出现糟糕输出之前自动检测行为变化。

这些方法来自机器学习可观测性领域。它们对在自动化流水线中运行 Claude 的团队更相关,但概念同样适用于交互式使用。

余弦距离方法

最简单的生产就绪方法。对模型输出(对固定探测提示的响应)进行嵌入,并测量与已知良好基线嵌入的余弦距离。

  1. 定义 5-10 个固定探测提示,测试关键规范(等同于金丝雀提示)。
  2. 在稳定时间点("黄金基线"),捕获输出并计算其嵌入。
  3. 在每次后续运行中,计算相同提示的输出并测量与基线的余弦距离。
  4. 当平均距离超过阈值时发出警报(句子级嵌入通常为 0.15-0.20)。

能捕获什么:渐进的风格漂移、规范侵蚀、输出结构变化——所有这些都在违反率增加之前。

漂移特征比例

比余弦距离更细粒度。不是单一距离指标,而是追踪哪些特定嵌入维度超过了阈值变化。这告诉你输出的哪些方面发生了变化(长度、正式程度、代码风格),而不仅仅是某些东西发生了变化。

实际实现需要嵌入模型和监控存储。从余弦距离开始;只有在需要诊断什么在漂移时才添加特征级追踪。

最大均值差异(MMD)

一种基于核的方法,用于比较两个输出分布。MMD 回答的问题是:"这段时间的输出与基线时段在统计上有显著差异吗?"它能稳健处理高维嵌入,不需要指定要追踪哪些特征。

MMD 比余弦距离设置成本更高,但在输出方差自然较高时产生的误报更少。适用于有大量输出量的团队(每天数百次 Claude 运行)。

统计距离阈值

无论使用哪种方法,阈值都很重要:

距离指标警报阈值说明
余弦距离> 0.15适用于大多数句子嵌入
欧氏距离因维度而异先对嵌入进行归一化
曼哈顿距离因维度而异比欧氏距离对异常值更稳健

这些是起点。根据你的基线方差进行校准:如果你的输出自然变化很大(创意任务),使用更宽松的阈值。

何时使用漂移检测

  • 不进行逐输出人工审查的自动化流水线
  • CLAUDE.md 更改后,验证行为是否保持稳定
  • 升级 Claude 模型版本时(版本间行为会变化)
  • 任何上下文配置更改后的回归检测

对于有定期人工审查的交互式开发,金丝雀检查和违反率追踪(已在上文介绍)就已足够。

随时间追踪的有用指标

指标如何测量目标
始终开启的上下文大小wc -w CLAUDE.md ~/.claude/CLAUDE.md< 5,000 词
规则数量grep -c "^- " CLAUDE.md< 150
文件年龄`git log --follow -p CLAUDE.mdhead -20`
关键规则违反率手动抽查< 20% 违反
金丝雀通过率./scripts/run-canaries.sh100%(全部通过)

8. 上下文缩减技术

路径范围划定:最高杠杆的技术

路径范围划定在不损失覆盖的情况下将始终开启上下文减少 40-50%。对于超过约 200 行配置的项目,这是最有影响力的单一结构性改变。

实施步骤:

  1. 识别代码库中的自然领域边界(API、前端、数据库、测试、基础设施)
  2. 为每个领域在领域目录中创建 CLAUDE-{domain}.md 文件
  3. 将根 CLAUDE.md 中特定领域的规则移至相应模块
  4. @path/to/CLAUDE-domain.md 导入替换根 CLAUDE.md 中移动的内容
  5. 用金丝雀检查验证遵循情况

重构后目标:根 CLAUDE.md 在 150 行以内(仅共享规则和导入声明)。

负面约束

根据经验,负面约束("永不做 X")在防止错误模式方面比正面指令("做 X")效果好 15-25%。这违反直觉——你可能认为"做 X"更清晰。但在实践中,模型需要主动抵制做错事的倾向;明确说出错误的事情并说"永不"更显著。

模式表述遵循率
正面(较弱)"所有后端服务使用结构化日志"~75%
负面(较强)"后端服务中永不使用 console.log;使用结构化日志(pino)"~90%

技术:对于任何错误模式是常见默认值的规则(原始 try/catch、console.log、默认导出、any 类型),将规则表述为命名要避免的具体模式的负面约束。

规则压缩

冗长的解释性规则消耗 Token 并分散注意力。将解释压缩至本质:

之前(冗长,38 词):

Markdown
- 创建 React 组件时,务必为 props 使用 TypeScript 接口,
  并在组件声明之前定义它们,而非内联,
  以提高可读性并支持复用。

之后(压缩,9 词):

Markdown
- React props:TypeScript 接口,声明在组件之前,永不内联。

压缩后的版本遵循率更高——更短的规则以更高的注意力权重处理。在真正需要理解时,将解释保存为原因说明格式。

压缩启发式:如果一条规则需要超过一行,问自己额外内容是约束还是解释。将解释移至注释(以 #> 引用前缀)或原因注解。将执行的约束保持在一行以内。

去重

同一约束多次陈述(用不同措辞)不会强化它——而是分散总注意力预算。找到并删除语义重复项。

重复的常见来源

  • 一条规则在通用部分,一条更具体的版本在路径范围模块中
  • 为修复问题添加了规则,却没有删除它取代的更模糊的原始规则
  • 在合并期间从不同团队成员的配置中复制的规则

去重工作流

Plain
扫描 CLAUDE.md 中的语义重复项。如果两条规则约束相同行为,
即使措辞不同,也视为重复。列出所有重复对,
并根据具体性和清晰度建议保留哪个版本。

将此作为 Claude 提示针对你的 CLAUDE.md 运行。审查建议并合并。

归档模式

删除规则时,你会丢失它存在原因的知识。那些制度记忆可能很有价值——六个月后,有人可能会尝试重新引入该规则所防止的同一模式。

与其删除过时规则,不如将它们归档:

Plain
.claude/
├── CLAUDE.md              # 活跃规则
└── CLAUDE-archive.md      # 带退休说明的历史规则

归档条目格式

Markdown
## 已归档规则

### [2026-01 退役] 使用 MongoDB 存储会话
替换为:使用 PostgreSQL 的 sessions 表存储会话。
原因:统一使用单一数据库;MongoDB 仅用于会话且增加了运维复杂性。

归档文件不由 Claude 加载——它是给人类的参考文档。它防止相同的争论和错误重复发生。

规则的 80/20 原则

在大多数生产配置中,20% 的规则占 Claude 重要决策的 80%。其余 80% 的规则涵盖边缘情况、风格偏好和很少出现的情况。

识别你的前 20%:

  1. 列出 CLAUDE.md 中的每条规则
  2. 对每条规则估计:"这条规则在会话中有多频繁地有意义地改变 Claude 的输出?"
  3. 每天都适用的规则:保留、优先级高、放在靠前位置
  4. 每周都适用的规则:保留,放在中间
  5. 每月才适用的规则:考虑归档或移至按需加载的技能
  6. 很少适用的规则:归档

目标不是消除覆盖——而是确保最重要的规则不会被最不重要的规则所稀释。

位置很重要:将你前 20% 的规则放在 CLAUDE.md 的前三分之一处。注意力权重在长文档中并不均匀——早期内容具有更高显著性。

用代码思考

这一模式由 context-mode v1.0.64 命名,并由 Contieri 于 2026 年 4 月独立描述为"寻求分析师而非分析",它解决了探索任务中 Token 浪费的常见来源。

问题:要回答"哪些文件导入了模块 X?",一个朴素的智能体会逐一打开并读取文件。30 个候选文件意味着 30 次工具调用和可能超过 15,000 Token 的文件内容进入上下文,其中绝大多数与实际问题无关。

模式:不要读取文件,而是指示智能体编写并运行一个小脚本(bash、Python、jq)来查询、统计或过滤,然后只返回结果。结果是 1 次工具调用和约 50 Token,而非 30 次调用和 15,000 Token。

示例

查找哪些文件导入了某个模块:

Bash
grep -r "import X" src/ --include="*.ts" | wc -l

识别超过大小阈值的文件:

Bash
find src/ -name "*.ts" -size +50k | sort

按目录统计测试覆盖率:

Bash
find src/ -name "*.test.ts" | sed 's|/[^/]*$||' | sort | uniq -c | sort -rn

何时应用:任何"探索并报告"而非"编辑"的任务。发现任务、统计、模式匹配、依赖分析和按内容查找文件都是候选项。如果你发现自己在编写描述读取许多文件以收集统计数据的智能体指令,"用代码思考"模式通常适用。

与子智能体的关系:子智能体在隔离环境中以自己的上下文预算执行。"用代码思考"将所有内容保留在主智能体中,但使用脚本作为探索机制。两种方法都避免将无关文件内容加载到上下文中。对于真正需要读取文件内容(编辑、代码审查、理解逻辑)的任务,子智能体更合适。对于可化简为统计或列表的纯发现任务,单次脚本调用更快更便宜。

渐进式上下文卸载

来自 LangGraph 的 Deep Agents SDK 对长时运行智能体的研究,这个三层级联解决了随时间积累上下文的问题,同时不失去对信息的访问。

问题:处理许多文件、API 调用和工具结果的长时运行智能体积累的上下文会不断增长,直到触及窗口限制或通过上下文退化降低准确性。截断(丢失数据)和无限积累(降低准确性)都不正确。

三层级联

第一层——大型工具输出(阈值:2 万 Token):卸载到文件系统。将完整输出写入临时文件,只将文件路径和 10 行预览注入上下文。智能体可以在需要时请求完整内容。

第二层——积累的工具调用参数(阈值:上下文接近中间点):卸载旧工具调用。仅保留最近 N 次工具调用的完整内容;对较旧调用的参数进行摘要或删除。工具结果比工具调用参数更有价值,用于继续任务。

第三层——消息历史(阈值:上下文接近限制):对消息历史进行有损摘要。仅作为最后手段——这会引入第 11 节(渐进式摘要风险)中描述的风险。仅在第一层和第二层耗尽后才应用。

使用 PostToolUse Hook 的 Claude Code 等效实现

JSON
{
  "hooks": {
    "PostToolUse": [{
      "matcher": "Bash",
      "hooks": [{
        "type": "command",
        "command": "python3 ~/.claude/hooks/offload-large-output.py"
      }]
    }]
  }
}
Python
# ~/.claude/hooks/offload-large-output.py
import json, sys, tempfile, os

data = json.load(sys.stdin)
output = data.get("tool_result", {}).get("content", "")

THRESHOLD = 20_000  # 字符数,大约 5K Token

if len(output) > THRESHOLD:
    tmp = tempfile.NamedTemporaryFile(
        mode="w", suffix=".txt", delete=False,
        prefix="/tmp/claude-output-"
    )
    tmp.write(output)
    tmp.close()
    preview = "\n".join(output.splitlines()[:10])
    print(json.dumps({
        "tool_result": {
            "content": (
                f"[输出太大——已保存至 {tmp.name}]\n"
                f"预览(前 10 行):\n{preview}\n"
                f"使用:cat {tmp.name}"
            )
        }
    }))
else:
    print(json.dumps(data))

此 Hook 透明地拦截大型 bash 输出,将其写入临时文件,并注入包含预览的路径。智能体看到紧凑摘要,并知道在需要时在哪里找到完整内容。同样的模式适用于任何工具类型:MCP 工具结果、文件读取或 API 响应都可以卸载到文件系统并通过路径引用。

摘要:按影响分类的缩减技术

技术上下文缩减难度遵循率影响
路径范围划定40-50%中等+15-25%
负面约束0%(重新表述)每条规则 +15-25%
规则压缩20-30%+5-10%
去重10-20%+5-15%
归档模式10-30%+5-10%
80/20 优先级排序0%(重新排序)+10-20%
用代码思考探索任务 90%+不适用(替代调用)
渐进式卸载可变(取决于层级)中等防止退化

针对存在上下文债务项目的最高杠杆操作序列:

  1. 路径范围划定(最大结构性收益)
  2. 去重(消除噪声)
  3. 压缩(锐化剩余规则)
  4. 归档(安全清除过时规则)
  5. 重新排序(优先级排序最重要的规则)
  6. 渐进式卸载(用于长时运行或多步骤智能体工作流)

9. 成熟度评估

上下文工程能力分阶段发展。大多数团队到达第 2 级就停止了——不是因为更高级别很复杂,而是第 2 级的失败是不可见的。输出质量可以接受,所以继续推进的压力从未出现。这个评估使差距变得可见。

六个级别

级别名称存在什么失败模式
0无配置没有 CLAUDE.md 的大语言模型泛泛输出,零项目感知
1扁平配置单一 CLAUDE.md,无结构规则堆积,约 100 行后遵循率下降
2结构化配置有章节、清晰组织,全局/项目分离个人使用有效,团队规模使用时崩溃
3模块化配置路径范围模块,刻意分层规则有维护但没有验证
4可度量配置金丝雀测试、遵循率追踪、生命周期管理系统有效,但随时间悄悄漂移
5工程化系统配置文件、CI 漂移检测、ACE 流水线、季度审计节奏

自我评估

回答每个问题。在第一个"否"处停止——那就是你当前的级别。

第 0 → 1 级:你的项目中有 CLAUDE.md 文件吗?

第 1 → 2 级:你的配置是否区分了全局规范(在 ~/.claude/CLAUDE.md 中)和项目特定规则(在 ./CLAUDE.md 中)?章节是否清晰分隔?

第 2 → 3 级:子系统特定规则是否在路径范围模块中,而非根 CLAUDE.md 中?你的根 CLAUDE.md 是否保持在 150 行以内?

第 3 → 4 级:你是否有验证关键规范的金丝雀检查?你是否追踪最重要规则的违反率?你是否在重大里程碑后运行上下文审计?

第 4 → 5 级:团队成员是否从配置文件组装 CLAUDE.md,而非直接编辑?是否有 CI 漂移检测,当配置偏离源模块时发出警报?你是否进行会话回顾,将新模式反馈到配置中?

各级别应做什么

你的级别下一步行动
0创建一个包含 5-10 条规则的最小 CLAUDE.md。参见第 3 节了解放什么。
1分离全局和项目配置。将跨项目偏好移至 ~/.claude/CLAUDE.md
2识别 2-3 个访问量最高的子系统。为它们创建路径范围模块。
3为违反最多的规则编写 3-5 个金丝雀提示。自动化它们。
4为团队成员引入配置文件。添加 CI 漂移检测。开始会话回顾。
5维护季度审计。系统已构建——工作是持续校准。

大多数团队在一个下午就能从第 0 级升至第 2 级。从第 3 级升至第 4 级需要建立度量习惯,而非更多配置。更高级别的瓶颈不是知识——而是将配置视为活系统而非一次性设置的纪律。


10. 信号分类与因果归因

扁平的摩擦分数(错误 × 3 + 重试 × 2)告诉你发生了多少摩擦,但不告诉你是你配置的哪个部分造成的。在一个运行 ACE-v1 循环十周的项目上,这个差距产生了误导性的优先级队列:Bash 工具产生了 3,377 次重试,而 Read 是 597 次,Edit 是 254 次。原始数量指向 Bash 是问题所在,但实际模式是缺少批处理指令,而非错误的 Bash 规则。没有类型化信号,策划者会修复错误的层次。

命名说明:arXiv:2510.04618(Stanford/SambaNova,2025 年 10 月)使用"ACE"表示推理时上下文演化技术。这里描述的 ACE 是跨会话操作的配置持久化循环,而非在会话内部。不同概念,相同缩写。以下 v2 改进适用于本指南的定义。

信号类别

用五类分类法替换扁平分数。每个事件获得一个类别和一个归因候选项。

类别定义示例
语法工具错误、解析失败、格式错误的调用工具调用中的无效 JSON
语义输出被用户拒绝,通过澄清重试"不,我的意思是另一种格式"
流程规则冲突、缺失步骤、错误执行阶段先写后读违规
对齐语气违规、超出范围的更改、幻觉声明Claude 添加了未请求的重构
性能Token 超限、上下文溢出、任务中强制 /compact会话在 85% 上下文时退化

权重应反映影响,而非频率。生产关键流程中的单次对齐违规,比本地脚本上的 50 次语法重试成本更高。

因果归因

对于每个摩擦事件,捕获活跃上下文:加载了哪些规则文件、调用了哪些技能、哪个配置文件处于活跃状态。这让策划者可以构建规则-摩擦相关性表,而无需对完整会话历史运行大语言模型评判。

YAML
# 摩擦事件 schema
id: evt_20260519_bash_batching_001
timestamp: "2026-05-19T14:32:00Z"
session_id: "2094ff6d"
category: procedural
tool: Bash
retry_count: 4
description: "三次顺序 Bash 调用,一次批量调用本可完成"
active_rules:
  - .claude/rules/lean-ctx.md
  - .claude/rules/bash-safety.md
active_skills: []
profile: default
suspected_cause: "lean-ctx.md 缺少明确的 Bash 批处理指令"
resolved: false

将事件存储为仅追加的 YAML 文件或换行分隔的 JSON。它们是策划者的原材料;默认保存在本地并在 .gitignore 中,除非你的团队选择共享信号存储(参见第 11 节)。

按模式追踪

除个别事件外,还要随时间按模式追踪摩擦。用一个字典替换每周总计:

YAML
friction_patterns:
  week: "2026-W20"
  write_before_read: 10
  gitignore_violation: 10
  exit_code_1_unchecked: 38
  bash_no_batching: 47
  permission_denied_hook: 12

模式时间序列让你能够衡量合并的规则是否产生了任何效果。没有它,你只是在猜测。


11. 循环闭合:基于 PR 的策划

第 5 级的隐藏失败模式是开放循环:策划者生成建议但没有任何东西被合并。在 Aristote 项目的十周 ACE-v1 操作中,两份相隔十周的策划报告提出了相同的两个规则候选项。两者都没有被合并。循环是开放的,系统生产报告而非进展。

闭合循环需要让策划者的输出易于执行。机制:策划者生成 Git PR 而非普通报告。

PR 解剖

每个策划者 PR 包含四个内容:

  1. 配置差异:提议的确切规则或技能更改(一个 git diff 就绪的补丁,而非散文)
  2. 摩擦证据:驱动建议的 3-5 个摩擦事件,带有链接回信号文件的事件 ID
  3. 金丝雀结果:10-20 个探测提示的前后对比(见下文)
  4. 升级说明:如果此建议已在之前的报告中提出但未被执行,PR 包含一个计数器("此建议出现在之前的 2 份报告中,未采取行动")

人类审查并合并或关闭。策划者永远不会直接修改规则。这就是成熟上下文工程工作流中"增强"的含义:循环通过人类判断而非自动化关闭。

A/B 金丝雀探测

在提出更改之前,策划者对当前配置和提议配置运行一小组探测提示。探测是简单的、有代表性的输入,用于测试被更改的规则。

Bash
# canary-ab.sh
OLD_CONFIG="$1"
NEW_CONFIG="$2"
PROBES_FILE="${3:-canary-probes.yaml}"

for probe in $(yq '.probes[].id' "$PROBES_FILE"); do
  question=$(yq ".probes[] | select(.id == \"$probe\") | .prompt" "$PROBES_FILE")
  old_out=$(run_with_config "$OLD_CONFIG" "$question")
  new_out=$(run_with_config "$NEW_CONFIG" "$question")
  compare_outputs "$probe" "$old_out" "$new_out"
done

每个 PR 10 到 20 个探测就足够了。使用余弦相似度进行初步筛选;只对相似度低于 0.85 的探测运行大语言模型评判。这使大多数 PR 的金丝雀成本接近零,并将判断保留给真正需要它的边缘情况。

多时间尺度操作

在单一节奏上运行策划者会产生两种失败模式:过于频繁会使审阅者不堪重负,过于稀疏则摩擦会无形地积累。使用三个循环:

循环触发条件操作
实时PostStop Hook 触发将摩擦事件追加到本地信号存储
每周Cron(周六 02:00)策划者汇总本周,如果信号阈值满足则生成 PR
季度手动宪法审计:检查规则重叠、归档休眠技能、审查配置文件整合候选项

季度循环无法以任何有意义的方式自动化。它需要阅读系统的实际行为,而非仅仅其记录的信号。

信号本地化决策

摩擦信号存储在哪里决定了策划者可以访问什么。三个选项:

选项工作方式最适合
A. 本地 Cron信号保留在开发者机器上;策划者作为 macOS launchd 任务或本地 Cron 运行单人开发、隐私优先、无需维护基础设施
B. 推送信号存储PostStop Hook 将匿名化信号推送到私有仓库或 S3 存储桶;策划者在 CI 中运行5 人以上团队,需要多开发者协调(第 14 节)
C. 托管开发环境信号默认落入共享环境(Codespaces、Coder)已因其他原因使用托管开发环境的团队

选项 A 是单人开发者的正确默认值。选项 B 对于任何想要跨开发者模式分析或多开发者配置文件协调的团队是必要的;信号存储应该是私有仓库,而非 SaaS 平台,以防敏感路径和工具数据落入第三方服务器。只有当团队出于其他原因已经致力于托管开发环境时,选项 C 才值得考虑。

建议压制

在三份连续报告中出现但未采取任何行动的建议应该改变状态:它要么移至"待人工决策"状态,并在下一个 PR 中带有阻塞标志,要么以"不会修复"状态关闭,并附有有记录的原因。允许建议静默重复与开放循环是相同的失败模式,只是更微妙。


12. 弹出:有纪律的反工程化

上下文工程栈的每个部分都帮助你添加更多:更多规则、更多技能、更多配置文件部分。没有任何部分帮助你删除停止工作的内容。这是这门学科缺失的一半,其缺失是第 5 级系统悄悄退化的原因。

上下文债务通过添加而积累。六个月前为某个 sprint 写的规则可能与三条更新的规则冲突,在作者从未预料到的边缘情况下触发,并在每次会话中产生摩擦。没有弹出机制,它永远保留,因为删除它感觉有风险,审计它没人有时间。

弹出启发式

三个指标驱动弹出候选项:

激活阈值:在过去 N 个月内未触发的规则很可能是无效负载。信号:如果它们防止的模式没有出现在摩擦日志中,要么规则运作得很好,要么没有人写触发它的代码。两种情况都表明休眠。默认:技能 3 个月,规则 6 个月。

ROI 追踪:产生的摩擦(来自过于严格的执行、错误上下文触发)超过预防的摩擦的技能。信号:技能在摩擦事件的 active_skills 字段中出现的频率,多于在"已解决"事件中出现的频率。连续 4 周以上的负 ROI 是弹出候选项。

配置文件重叠:当规则出现在超过 80% 的个人开发者配置文件中时,它属于共享配置而非每个配置文件中。这是整合建议,而非弹出,但它减少了重复维护面。

弹出 vs. 归档

弹出不意味着删除。归档模式(第 8 节)建立了保留带退休说明的退役规则的制度记忆原因。弹出是应该归档的内容的自动检测。策划者标记候选项;人类做出最终决定,并将规则移至 CLAUDE-archive.md,附上日期和原因。

没有商业可观测性工具(Braintrust、Langfuse、Helicone、LangSmith)实现这种模式。它们追踪发生了什么;它们不追踪你的配置包含什么,也不建议删除其中造成伤害的部分。弹出机制是商业工具跳过的纪律,因为它需要了解你的配置 schema,而不仅仅是你的提示历史。


13. 宪法和自我一致性审计

在没有约束的情况下增长的配置最终会自相矛盾。规则 A 说"始终使用 ESLint 进行格式化"。规则 B 说"优选速度更快的 Biome"。新开发者读了两条都不做,因为规则冲突而系统没有发出信号。宪法审计在这种情况复合之前捕获它。

宪法审计

在每个策划者 PR 落地之前,针对两个目标运行约束检查:提议的更改 vs. 现有规则集,以及提议的更改 vs. 明确的 constitution.md

YAML
# .claude/constitution.md(示例)
invariants:
  - id: no-auto-commit
    rule: "未经明确用户请求,永不提交"
    rationale: "2024-事件:自动提交绕过了审查门"
  - id: no-destructive-without-confirm
    rule: "未经确认,永不运行 rm、DROP 或 force-push"
    rationale: "生产安全基线"
  - id: diff-before-merge
    rule: "在应用多文件更改之前,始终显示差异"
    rationale: "保持人工审查在循环中"

宪法检查是两个查询:提议的规则是否与任何不变量相矛盾,以及它是否与 .claude/rules/ 中的任何现有规则冲突?两个查询都可以作为 Claude 提示运行,以宪法和规则列表作为上下文。每次策划者运行花费几百 Token,并防止规则冲突悄悄积累。

这种模式的谱系是宪法 AI(Anthropic,2022 年)和 RLAIF:使用高层价值文档来约束低层生成过程。这里的转置是从输出对齐(检查模型的响应)到配置对齐(检查规则系统的内部一致性)。机制更简单,因为输入更短且完全确定。

自我一致性检查

自我修改的系统积累一种特定的失败模式:文档声称的状态不再与现实相符。在生产 ACE 安装中,文件 ace-improvement-loop.md 声称"技能版本控制于 2026-03-04 100% 完成"。六周后测量的实际状态是 114 个技能中有 20 个已版本控制(17%)。差距持续存在,因为没有人审计系统对自身所做的声明。

自我一致性检查每周单独运行,与策划者无关。它读取你的 ACE 文档中的声明,并根据测量状态进行验证:

声明类型如何验证
"N 条活跃规则"`find .claude/rules -name "*.md"
"技能版本控制 X% 完成"`grep -l "^version:" .claude/skills/*/SKILL.md
"上次策划者运行:日期"检查 git log 中最近的策划者 PR 创建日期
"摩擦趋于下降"比较信号存储中的 4 周移动平均

当声明与测量状态偏差超过 10% 时,检查在下一份策划者报告中追加"自我一致性违规"部分。这不是失败状态;这是系统在做它应该做的事。文档退化是正常的。每周捕获它不是。


14. 多开发者配置文件协调

基于配置文件的组装(第 5 节)通过给每位开发者一个个人配置文件,解决了 N 开发者 × M 工具碎片化问题。随着时间推移,一个新问题出现:个人配置文件产生分歧。开发者 A 的配置文件添加了防止直接访问生产数据库的规则。开发者 B 两周后添加了同一条规则,措辞略有不同。开发者 C 从未添加它。本应在共享配置中的规则最终被复制、不一致且无法执行。

这对于 Claude Code 的三层配置结构(用户 ~/.claude/CLAUDE.md + 项目 CLAUDE.md + 插件规则)等层级配置系统是特有的。没有商业 LLMOps 工具解决这个问题,因为没有一个在团队配置层级的单个规则文件粒度上操作。

检测

协调检查扫描所有活跃开发者配置文件,并识别出现在超过 50% 中的规则:

Bash
# profile-reconcile.sh
PROFILES_DIR="${1:-.claude/profiles}"
THRESHOLD="${2:-0.5}"

all_rules=$(find "$PROFILES_DIR" -name "*.yaml" -exec yq '.includes[]' {} \; | sort | uniq -c | sort -rn)
total_profiles=$(find "$PROFILES_DIR" -name "*.yaml" | wc -l)

while IFS= read -r line; do
  count=$(echo "$line" | awk '{print $1}')
  rule=$(echo "$line" | awk '{print $2}')
  ratio=$(echo "scale=2; $count / $total_profiles" | bc)
  if (( $(echo "$ratio >= $THRESHOLD" | bc -l) )); then
    echo "提升候选项($count/$total_profiles 个配置文件):$rule"
  fi
done <<< "$all_rules"

出现在 5 个开发者配置文件中的 4 个的规则,应该放在项目级 CLAUDE.md 中,而非 4 个独立的配置文件中。

保护

不是所有东西都应该被提升。个人偏好保持个人:语气设置、详细程度、首选解释深度、语言选择。协调检查区分行为规则(Claude 做什么)和偏好规则(Claude 如何沟通)。超过阈值的行为规则是提升候选项;偏好规则永远不会被触碰。

对于 5 人或以上的团队,每月运行协调检查。对于超过 10 人的团队,将其作为季度宪法审计的一部分运行。输出是包含共享配置提议差异的提升候选项列表;人类审查并应用。检查不会自动修改任何文件。


15. Token 审计工作流

上下文工程理论只有在你测量实际开销后才能转化为真实收益。大多数开发者发现,在任何用户任务开始之前,他们就加载了 4-6 万 Token 的固定上下文:配置文件、规则、Hook 输出、记忆文件和 Claude Code 系统提示都在叠加。本节提供一个可重复的审计工作流,耗时不到五分钟,并产生可执行的计划。

真实世界会话基准

在审计你的开销之前,先以从业者在真实代码库上观察到的数据进行校准。以下数据来自在 Max 200 计划上高强度使用 Opus 4.7 的用户。将它们作为上限参考:相同任务在 Sonnet 级别的使用量低 30-50%。

每轮(输入 + 输出合计)

任务类型典型范围
简单问题,1-2 次工具调用1-3 万 Token
带文件读取的定向编辑3-8 万 Token
带探索的功能实现10-30 万 Token
深度调查(MCP、多智能体、Datadog)30 万 -100 万+ Token

每次会话(完整对话)

会话类型典型范围
快速修复10-30 万 Token
完整 PR(含测试)50 万 -200 万 Token
带压缩的长会话500 万 -2000 万+ Token

主要成本驱动因素是输入 Token,而非输出。同一个 1,000 行文件在同一会话中重读 5 次,光这一项就会增加约 5 万个输入 Token。返回冗长 JSON 的 MCP 工具(Notion、Datadog、GitHub API 响应)会迅速叠加:单次 Datadog 查询就可以在模型处理数据之前将 2-5 万 Token 推入上下文。

团队视角:在 Claude Code 重度用户的 Slack 社区调查(2026 年 5 月)中,个人重度用户在复杂智能体工作流中每天报告 3-4.3 亿 Token;混合团队(简单和复杂任务混合)的中位数每请求约 4 万 Token,重度用户可达 8.5 万+。

子智能体改变了数学。每个子智能体在更短、更专注的上下文窗口中操作,因此每个智能体的 Token 成本更低。在复杂工作流中跨所有智能体的总成本通常高于单一长会话,因为你在产生许多智能体。改善的是质量和并行性,而非原始 Token 效率。

什么算作固定上下文

每次会话在 Claude 处理第一条用户消息之前,都从基线 Token 开始:

组件加载时机典型大小
~/.claude/CLAUDE.md + @imports始终5-15K Token
项目 CLAUDE.md始终2-8K Token
.claude/rules/*.md(自动加载)始终5-40K Token
MEMORY.md(项目记忆)始终1-3K Token
Claude Code 系统提示始终~7,500 Token
Hook 输出每次工具调用0.1-2K Token × 调用频率
.claude/commands/*.md仅在调用时默认 0
.claude/agents/*.md仅在调用时默认 0

关键区别:.claude/rules/ 在会话开始时加载每个 .md 文件,无论相关性如何。命令和智能体是懒加载的——直到调用才消耗成本。规则文件是意外开销最常见的来源。

步骤 1——测量各组件

从你的项目根目录运行这些命令,按组件获取分解:

Bash
# 项目 CLAUDE.md
echo "=== 项目 CLAUDE.md ===" && wc -c CLAUDE.md

# 按大小排序的规则文件(你最大的优化机会)
echo "=== 规则文件 ===" && find .claude/rules -name "*.md" 2>/dev/null \
  | xargs wc -c 2>/dev/null | sort -rn | head -20

# 全局配置文件
echo "=== 全局 ~/.claude ===" && ls -la ~/.claude/*.md 2>/dev/null \
  | awk '{print $5, $9}' | sort -rn

步骤 2——计算你的 Token 预算

Token ≈ 字符 ÷ 4(对于英语/代码混合内容,粗糙但可靠)。

Bash
# 完整预算估算
GLOBAL=$(cat ~/.claude/CLAUDE.md ~/.claude/*.md 2>/dev/null | wc -c)
PROJECT=$(wc -c < CLAUDE.md 2>/dev/null || echo 0)
RULES=$(find .claude/rules -name "*.md" 2>/dev/null | xargs cat | wc -c)
MEMORY=$(find ~/.claude/projects -name "MEMORY.md" -path "*$(pwd | tr '/' '-')*" \
  2>/dev/null | xargs cat 2>/dev/null | wc -c || echo 0)
TOTAL=$(( GLOBAL + PROJECT + RULES + MEMORY + 30000 ))

echo "全局 ~/.claude      : ~$(( GLOBAL / 4 )) Token"
echo "项目 CLAUDE.md      : ~$(( PROJECT / 4 )) Token"
echo "规则(自动加载)    : ~$(( RULES / 4 )) Token"
echo "MEMORY.md           : ~$(( MEMORY / 4 )) Token"
echo "系统提示            : ~7,500 Token(估算)"
echo "---"
echo "总计                : ~$(( TOTAL / 4 )) Token"

供参考:Claude 的窗口是 20 万 Token。6 万的固定开销意味着在任何工作开始前就消耗了 30%。对于通常使用额外 2-4 万 Token 的典型编码任务,实际输出可用的空间不到窗口的一半。

步骤 3——按使用频率分类规则

规则文件通常是节省空间最大的地方。对于 .claude/rules/ 中的每个文件,问一个问题:这在典型会话中有多频繁是相关的?

类别定义操作
始终关键适用于每个任务(编码规范、输出格式、安全规则)保持自动加载
有时需要在 20-40% 的会话中相关(调试方法、任务管理)如果小就保持自动加载;如果大考虑按需加载
很少需要在不到 10% 的会话中相关(Figma 工作流、Windows 兼容性、设计系统)从自动加载中移除
从不需要过时、在其他地方已覆盖,或与本项目无关删除或归档

将此分类作为提示运行:

Plain
读取 .claude/rules/ 中的每个文件。对每个文件,分类为:
- 始终:适用于典型会话中的大多数任务
- 有时:在 20-40% 的会话中适用
- 很少:在不到 10% 的会话中适用

输出一个表格:| 文件 | 大小(字符)| 类别 | 理由 |
在每个类别内按大小降序排序。
计算:如果排除"很少"文件,可以从自动加载中移除多少字符。

步骤 4——审计 Hook 开销

PreToolUsePostToolUse 上触发的 Hook 在每次工具调用时运行。每次调用将其 stdout 注入上下文。一个每次调用输出 500 字符、每次会话运行 150 次的 Hook,会向会话上下文添加 75,000 字符(约 1.9 万 Token)。

检查你的 Hooks:

Bash
# 列出所有 Hook 及其事件类型
cat ~/.claude/settings.json | python3 -c "
import json, sys
data = json.load(sys.stdin)
hooks = data.get('hooks', {})
for event, hook_list in hooks.items():
    for h in hook_list:
        cmd = h.get('command', h.get('hooks', [{}])[0].get('command', '?'))
        print(f'{event}: {cmd[:80]}')
"

对于每个 PreToolUsePostToolUse Hook,通过手动运行并测量 stdout 来估算其输出大小。乘以你每次会话的平均工具调用次数(在典型会话后检查 /cost 获取工具调用计数)。

要注意的高开销模式

  • 每次调用都 cat 文件或打印多行摘要的 Hook
  • 无条件运行 git statusgit log 的 Hook
  • 从未删除的用于调试的 echo 语句

步骤 5——构建行动计划

没有 RAG 或自定义基础设施的典型节省:

操作难度风险典型节省
从很少使用的规则中移除"自动加载"30 分钟5-20K Token
将大型规则文件拆分为核心 + 详细1-2 小时3-8K Token
将 Hook stdout 精简到必要字段1 小时2-10K Token
压缩冗长规则(见第 8 节)1-2 小时2-5K Token
归档过时的 MEMORY.md 条目30 分钟1-2K Token

第一轮现实的节省通常是固定上下文减少 30-50%,无需触碰任何需要基础设施的内容。

RAG 问题

你可能会遇到建议将规则文件移入向量数据库并动态检索(RAG)的建议。这是大规模情况下的有效优化——它将固定开销转换为按查询检索,并实现精确的懒加载。

在投入那些基础设施之前,请诚实地验证数学:

  • 你实际上能节省多少 Token?(先用步骤 1-3 测量)
  • 设置成本是多少?带自定义 MCP 服务器的 pgvector 或 Chroma 设置对于工作团队是 1-2 周的项目
  • 盈亏平衡点在哪里?如果简单清理后你的固定上下文已经在 2 万 Token 以下,RAG 增加的复杂性带来的边际收益很小

对于大多数个人开发者和小团队,基于分类的懒加载(从很少使用的文件中移除自动加载标记)以 2% 的基础设施成本实现了 80% 的收益。当你有 50+ 个规则文件并需要自动的、基于意图的加载时,RAG 才值得其复杂性。

审计提示模板

以下提示在项目内部运行时会生成完整的审计报告。根据需要替换路径变量:

Plain
# Token 审计——[项目名称]

审计此 Claude Code 项目配置的 Token 开销。
要系统全面,而非流于表面。

**步骤 1——清单**
列出会话开始时加载的每个文件:
- ~/.claude/CLAUDE.md 及所有 @imported 文件(带行数)
- ./CLAUDE.md(行数)
- .claude/rules/*.md(所有文件,按大小排序)
- 项目 MEMORY.md(行数)

对每个文件,注明:行数、近似 Token(字符 ÷ 4)以及一句话
描述其内容。

**步骤 2——预算计算**
计算:任何用户任务开始前的总固定上下文 Token 数。
按组件显示分解。以 Claude 20 万 Token 窗口的百分比表示。

**步骤 3——信号/噪声分类**
对每个规则文件,根据其在此项目典型会话中的适用频率,
分类为 始终/有时/很少。
标记任何分类为 有时 或 很少 但超过 5K 字符的文件。

**步骤 4——Hook 审计**
读取 .claude/settings.json(以及 ~/.claude/settings.json)。
对每个 Hook:事件类型、命令、每次调用的估算 stdout,以及
它是在每次工具调用时触发还是仅在会话边界触发。
标记每次 PreToolUse 或 PostToolUse 调用注入超过 200 字符的 Hook。

**步骤 5——行动计划**
生成一个优先级表:
| 操作 | 估算 Token 节省 | 难度 | 风险 |
按节省降序,然后难度升序排序。
只包括不需要外部基础设施(无 RAG、无向量数据库、无自定义 MCP 服务器)即可实现的操作。

**步骤 6——RAG 结论**
基于步骤 5 后的剩余节省,计算 RAG 是否值得:
估算剩余节省,估算以小时计的设置成本,并明确说明
基础设施投入是否合理。

16. 研究模式:文献表明什么

应用上下文工程从关于语言模型如何处理长输入的学术研究中获取知识。四个发现对你在生产智能体中构建上下文的方式有实践意义。

迷失在中间效应

来源:Liu 等人(2023 年),Stanford——"Lost in the Middle: How Language Models Use Long Contexts"

当相关信息被放置在长上下文窗口的中间时,检索和推理任务的性能会下降。模型在关键信息出现在上下文开头(首要性)或结尾(近因性)时表现最好,在埋在中间时表现最差。

这一效应在模型大小和上下文长度上是一致的。在一个 20 文档检索任务中,当答案在位置 1 时准确率约为 70%,在位置 10 时降至约 40%,在位置 20 时又回升至约 70%。

对智能体的实践意义

  • 将最关键的决策信息放在系统提示的开头或结尾,而非长 CLAUDE.md 的中间
  • 总结多个来源时,以最相关的发现开头,而非最近的
  • 如果你有一列工具结果,第一个和最后一个结果比中间的更可靠地被回忆
  • 对于 Claude 审查 N 个项目的评估任务,分成较小批次而非一次发送所有内容

这不是说你应该让上下文更短——而是上下文窗口中的位置是一个设计变量,而非偶然。

渐进式摘要的风险

压缩摘要的摘要的摘要流水线,以对模型不可见的方式丢失信息。每次压缩都会删除细节,但模型的置信度不会按比例降低。到第三或第四次压缩后,模型可以流畅地回答关于原始内容的问题,但答案可能不再准确——它在基于它保留的压缩模式的典型后续内容进行臆造。

具体风险

  • 事务性事实首先消失:具体的数字、日期、名称和条件在早期压缩中被抽象化,而叙事结构被保留
  • 置信度保持不变:模型不知道它在基于压缩信息工作;它以与拥有原始内容时同样的确定性回答
  • 没有检索信号:与 RAG 不同,检索失败是可见的,摘要失败是静默的——无论如何模型都会产生流畅的文本

缓解措施

Markdown
<!-- 研究/摘要智能体的 CLAUDE.md 内容 -->
## 摘要规则
- 在摘要中始终保留确切的数字、日期和专有名词——永不释义它们
- 用压缩级别标记摘要:[summary-level-1]、[summary-level-2]
- 如果你在你拥有的摘要中找不到特定事实,请说出来——不要从合理推断中重建

对于压缩上下文以保持在预算内的多步骤智能体,在回到源材料之前将链限制为 2 次压缩。

校准的分层抽样

在评估上下文工程设置是否有效时,随机抽样会遗漏系统性失败。从 100 项测试集中随机抽样可能显示 85% 的准确率——但如果 15 次失败集中在特定难度层(长文档、模糊指令、边缘情况),你不会检测到这种模式。

分层抽样按相关属性(文档长度、指令模糊度、来源质量)将评估集分成层,并独立测试每层。

特别针对上下文工程

为什么重要
短上下文(< 5K Token)基线——应接近 100%
中等上下文(5K–50K)大多数真实工作发生的地方
长上下文(50K+)退化首次出现的地方
位置关键(关键信息在中间)直接测试迷失在中间
高指令密度测试 150 条指令上限

如果你的长上下文层准确率比短上下文层低 20 个百分点,这是一个信号——添加基于位置的结构或实施分块处理。汇总指标会隐藏这个差距。

声明-来源映射(溯源追踪)

在从多个来源(网络搜索、文件读取、工具结果)综合信息的智能体中,最终输出中的声明应该可以追溯到其来源。没有溯源追踪,幻觉与准确综合无法区分,错误在智能体步骤中复合。

声明-来源映射的样子

与其让智能体生成没有来源归因的摘要,不如将中间表示结构化,保持声明与来源的链接:

Python
# 每个综合步骤保留溯源信息
claims = [
    {"claim": "API 速率限制是每分钟 1000 次请求", "source": "tool:get_api_docs", "confidence": "direct"},
    {"claim": "速率限制在 v2.3 中增加", "source": "web:release-notes-url", "confidence": "direct"},
    {"claim": "速率限制每 60 秒重置", "source": "inferred", "confidence": "inferred"},
]

标记为"推断"或缺少来源的声明在最终输出中应标记为不确定,而非以与直接来源声明相同的置信度呈现。

多步骤智能体的实现模式

Markdown
<!-- 研究智能体系统提示内容 -->
对于你在响应中包含的每个事实声明:
- 用来源标记它(工具名称、文件路径、URL 或"推断")
- 如果你无法识别来源,将声明标记为不确定
- 不要以与直接观察事实相同的确定性呈现推断结论

声明-来源映射作为质量保证机制与合规机制的区别:合规追踪问"我们是否使用了授权来源?",质量保证追踪问"这个具体声明准确吗?"——两者对于不同的失败模式都有价值。处理事实查询或生成报告的智能体需要质量保证版本。


17. 注意力机制与可靠性

Claude 的注意力在上下文窗口中并不均匀。在提示中的位置会显著影响信息是否被使用。本节涵盖其机制、背后的证据以及补偿策略。


迷失在中间的问题

Liu 等人(2023 年,arXiv:2307.03172)的研究检验了大型语言模型如何使用长上下文中不同位置的信息。发现:检索准确率呈 U 形曲线。放置在长上下文开头或结尾的信息比放置在中间的信息被回忆得更准确。

具体到 Claude,在 10 万 Token 上下文窗口上的 NIAH(针中草堆)基准测试显示,放置在开头或结尾的文档段落检索准确率为 98%,而放置在中间的降至 27%,相差 71 个百分点。后续模型版本改善了中间上下文的回忆能力,但 U 形偏差在大规模情况下仍然存在。

实践后果:模型需要可靠使用的任何信息都不应埋在长上下文的中间。


首要性和近因性定位

两个高注意力区域是上下文窗口的开头(首要性)和结尾(近因性)。三明治模式利用了两者:

Plain
[系统提示——持久约束、角色、关键规则]
[用户的长文档或检索到的上下文——中间区域]
[用户消息末尾——重申任务 + 必须满足的任何约束]

对于长到中间区域惩罚明显的文档(大致超过 20,000 Token),在两端都放置最关键的信息:

Python
def build_analysis_prompt(document: str, critical_facts: list[str]) -> str:
    facts_block = "\n".join(f"- {f}" for f in critical_facts)
    
    return f"""关键事实(分析全程参考):
{facts_block}

待分析文档:
{document}

提醒——在你的分析中应用这些关键事实:
{facts_block}

现在生成分析。"""

在末尾重复关键事实并非多余。它补偿了主文档的中间区域注意力下降。

非常长文档的逐章节处理

对于超过 5 万 Token 的文档,单次分析有遗漏中间章节内容的风险。逐章节 + 整合模式:

Python
def analyze_long_document(client, document: str, section_size: int = 8000) -> str:
    sections = split_into_sections(document, max_tokens=section_size)
    section_analyses = []
    
    for i, section in enumerate(sections):
        response = client.messages.create(
            model="claude-opus-4-5",
            max_tokens=1024,
            messages=[{
                "role": "user",
                "content": (
                    f"分析第 {i+1} 章节,共 {len(sections)} 章节:\n\n{section}\n\n"
                    f"关注关键事实、风险和义务。"
                    f"注意:这是较长文档的一个章节。"
                )
            }]
        )
        section_analyses.append(response.content[0].text)
    
    # 对所有章节摘要进行整合处理
    integration_prompt = "\n\n".join([
        f"第 {i+1} 章节分析:\n{analysis}"
        for i, analysis in enumerate(section_analyses)
    ])
    
    final_response = client.messages.create(
        model="claude-opus-4-5",
        max_tokens=2048,
        messages=[{
            "role": "user",
            "content": (
                f"你有同一文档的 {len(sections)} 个章节分析。"
                f"将它们综合成一个完整分析:\n\n{integration_prompt}"
            )
        }]
    )
    
    return final_response.content[0].text

每个章节分析都很短,并将相关内容保留在首要性位置。整合处理在摘要上而非完整文档上进行,将所有内容保持在高注意力范围内。


上下文窗口大小 vs. 注意力质量

更大的上下文窗口不意味着对大型输入的更好理解。注意力质量在上下文窗口填满之前就会退化。实践中:

  • Claude 3.5 Sonnet:有效内容约 50,000-70,000 Token 时开始出现明显质量退化
  • Claude 3 Opus:类似的退化阈值,中间区域惩罚更强
  • 100 万 Token 窗口的模型:窗口大小允许更多数据存在,不一定更好地使用这些数据

误解是将上下文窗口大小视为质量保证。20 万 Token 的输入不会获得与 1 万 Token 输入相同的每 Token 注意力质量。对于需要精确使用分散事实的任务,一个结构良好的 3 万 Token 提示通常优于一个原始倾倒的 20 万 Token 提示。

设计规则:让上下文适应任务,而非让任务适应上下文。


持久事实块

持久事实块是一个结构化部分,放置在系统提示的开头,包含模型在整个对话过程中必须引用的事实。与检索不同,它是逐字包含的:事实始终处于首要性位置,始终在范围内,与提示缓存兼容。

Python
PERSISTENT_FACTS = """
## 参考:公司背景

实体:Acme Corp(特拉华州 C 类公司,EIN:12-3456789)
财政年度结束:12 月 31 日
适用法律:特拉华州公司法、美国联邦法规
争议管辖:特拉华州大法官法院
授权股份:10,000,000 普通股 @ $0.001 面值
"""

system_prompt = f"""{PERSISTENT_FACTS}

你是合同分析助手。对所有实体引用使用上述公司背景。
[系统提示其余部分]
"""

持久事实块对提示缓存友好:因为它们以固定位置和静态内容出现,Anthropic 的提示缓存将在第一次调用后缓存它们,减少后续轮次的成本和延迟。

将持久事实块保持在 500 Token 以下。超过这个数,带重排序的检索更有效,因为块本身开始落入中间区域。


草稿纸模式

草稿纸模式在轮次间给模型持久的工作记忆,而无需依赖上下文积累。对话开头的合成助手消息保存结构化状态;编排器在每次轮次后以编程方式更新它。

Python
def initialize_scratchpad(task_spec: dict) -> str:
    return f"""<scratchpad>
<task>{task_spec['description']}</task>
<status>in_progress</status>
<completed_steps>[]</completed_steps>
<pending_steps>{json.dumps(task_spec['steps'])}</pending_steps>
<working_notes></working_notes>
</scratchpad>"""

def update_scratchpad(scratchpad: str, updates: dict) -> str:
    for key, value in updates.items():
        scratchpad = re.sub(
            f"<{key}>.*?</{key}>",
            f"<{key}>{value}</{key}>",
            scratchpad,
            flags=re.DOTALL
        )
    return scratchpad

# 对话结构:
messages = [
    {"role": "assistant", "content": initialize_scratchpad(task)},
    {"role": "user", "content": "从你的草稿纸继续任务。"}
]

# 每次轮次后,用新状态更新草稿纸
new_scratchpad = update_scratchpad(
    messages[0]["content"],
    {
        "completed_steps": json.dumps(completed),
        "pending_steps": json.dumps(remaining),
        "working_notes": latest_notes
    }
)
messages[0]["content"] = new_scratchpad

草稿纸在所有轮次中保持在首要性位置(它是第一条消息)。工作笔记积累在那里,而非增长对话历史。

草稿纸 vs. 滚动摘要:当你需要有结构化状态并进行编程更新时使用草稿纸。当积累的上下文是非结构化对话且需要压缩时使用滚动摘要。


滚动上下文摘要

随着对话历史增长,较旧的轮次失去相关性但消耗 Token。滚动上下文摘要在完成的阶段漂移到中间区域之前将其压缩成紧凑记录。

Python
SUMMARY_TRIGGER_RATIO = 0.65  # 当上下文达到 65% 容量时摘要

def maybe_summarize_history(
    client,
    messages: list[dict],
    context_limit: int,
    current_tokens: int
) -> list[dict]:
    if current_tokens / context_limit < SUMMARY_TRIGGER_RATIO:
        return messages
    
    # 分离要摘要的消息和要逐字保留的近期消息
    keep_recent = 4  # 逐字保留最后 4 轮
    to_summarize = messages[:-keep_recent]
    to_keep = messages[-keep_recent:]
    
    # 摘要前提取关键事实
    facts_response = client.messages.create(
        model="claude-haiku-4-5",
        max_tokens=512,
        messages=[
            {
                "role": "user",
                "content": (
                    f"将以下对话历史中的关键事实、决策和待解事项"
                    f"提取为紧凑列表:\n\n"
                    + "\n".join(f"{m['role']}: {m['content']}" for m in to_summarize)
                )
            }
        ]
    )
    key_facts = facts_response.content[0].text
    
    summary_message = {
        "role": "assistant",
        "content": f"<conversation_summary>\n{key_facts}\n</conversation_summary>"
    }
    
    return [summary_message] + to_keep

在摘要之前提取关键事实,而非从摘要中提取。摘要会丢失边缘情况和边界条件,而这些通常很重要。使用较小模型(Haiku)进行事实提取是廉价的,并保留更多信号。

在上下文限制的 65% 而非等待 80% 的自动压缩阈值时触发。主动压缩让你掌控保留什么。


18. Token 压缩工具

前面的章节关注在上下文中放什么。本节涵盖在流水线层级压缩进入上下文的内容的工具——在 Claude 处理之前减少 Token 数量。这些工具补充了 CLAUDE.md 的编写工作:良好的上下文工程在设计时减少噪声,压缩工具在运行时减少数量。

两个独立的工具在 Claude Code 工具流水线的不同层次上运行。


第一层——CLI 输出:RTK

RTK(Rust Token Killer)是一个 CLI 代理,它拦截 shell 命令输出并在 Claude 读取之前压缩它。它通过 PreToolUse Hook 运行,将 git log 等命令重写为 rtk git log

它压缩什么:git、cargo、npm、pnpm、tsc、vitest、playwright、docker、kubectl 等。测量节省:支持命令 60-90%。

它不压缩什么:文件读取、MCP 工具结果、任何不通过 Bash 工具调用的内容。

Bash
brew install rtk-ai/tap/rtk   # 或:cargo install rtk
rtk init --global              # 安装 PreToolUse Hook + settings.json 补丁
rtk gain                       # 仪表板:每个命令节省的 Token

交叉引用:完整命令参考和 TOML 过滤 DSL 在 「third-party-tools.md §RTK」。


第二层——文件读取和会话记忆:lean-ctx

lean-ctx 作为全局 MCP 服务器运行,在工具级别拦截 Read 调用和 Bash 调用,位于 RTK 的 shell Hook 之下。它使用 tree-sitter AST 解析来提取文件的相关结构,而非发送完整内容。

安装(一次性,全局):

Bash
curl -fsSL https://raw.githubusercontent.com/yvgude/lean-ctx/main/skills/lean-ctx/scripts/install.sh | bash
lean-ctx setup   # 在 ~/.claude.json 和 ~/.claude/settings.json 中注册 MCP 服务器 + Hooks

无需每个项目单独设置。

10 种读取模式

模式返回内容最适合
signatures仅类型和函数签名为上下文读取的大型 TypeScript/Rust 文件
map导出和导入依赖理解模块关系
auto系统根据文件类型和上下文使用情况选择默认,大多数情况下安全
full完整文件,已缓存即将编辑的文件
diff仅变更行编辑后重新读取文件
lines:N-M特定行范围定向检查
aggressive最大压缩,语法已剥除仅作参考的大型文件
entropy仅高熵片段扫描异常
task任务相关行已定义活跃任务集

规则:对即将编辑的文件使用 full。对为上下文读取的文件使用 signaturesmap。一个 2364 行文件的差异:full 约 19,000 Token,signatures 约 200 Token。

缓存:重新读取未更改的文件无论文件大小均约 13 Token。缓存通过文件 mtime 失效。

CCP(上下文连续性协议):会话结束时,lean-ctx 写入一个约 400 Token 的摘要,记录读取、发现和决策内容。下次会话自动加载它,消除重新读取先前上下文的冷启动成本。

测量基准(TypeScript/T3 monorepo,2455 个文件,7063 节点图)

指标
整体压缩率57.8%
ctx_read 节省率86%
一天节省的 Token130 万
schema.prisma 2364 行 → 签名~200 Token(99%)
文件重读(缓存命中)13 Token

监控你的效率

Bash
lean-ctx gain            # 整体仪表板
lean-ctx gain --daily    # 每日节省
lean-ctx cep             # CEP 分数 /100:压缩率、缓存命中率、一致性、模式多样性
lean-ctx sessions list   # 带 Token 计数的会话历史

/lean-ctx-audit 斜杠命令一次运行以上所有命令并综合报告。将其添加到 ~/.claude/commands/lean-ctx-audit.md,使其在每个项目中可用。


如何选择

RTK 和 lean-ctx 没有太多重叠。来自测量会话的实际节省分布:

来源工具占总节省的百分比
文件读取(AST)lean-ctx~85%
搜索结果lean-ctx~5%
Shell 输出RTK剩余部分
通过 lean-ctx 的 Shell 输出lean-ctx<1%(RTK 在此更好)

两者都安装。RTK 处理 CLI 输出;lean-ctx 处理文件读取和会话记忆。

lean-ctx 价值最大时:TypeScript、Rust、Python 项目,大型源文件被反复读取,会话运行时间足够长以致上下文在完成前填满,以及跨会话连续性很重要。

lean-ctx 价值较小时:以 Markdown 为主的文档仓库。AST 解析器在 Markdown 中找到嵌入的代码示例而非源结构。收益存在但低于以代码为主的项目。

交叉引用:完整工具介绍在 「third-party-tools.md §上下文压缩」。


交叉引用

  • 架构和项目结构模式:guide/core/architecture.md
  • AI 辅助开发的方法论框架:guide/core/methodologies.md
  • 用于上下文管理的 Hooks(钩子)和自动化:《Claude Code:从入门到大师》 第 5 节(Hooks)
  • 扩展上下文的 MCP 服务器集成:《Claude Code:从入门到大师》 第 7 节(MCP)
  • 上下文内容的安全注意事项:guide/security/
  • 路径范围模块示例:examples/ 目录
  • PRP 方法论(产品需求提示,5 层结构):Wirasm/Widing 的社区框架——guide/core/methodologies.md 查看完整摘要,或在指南中搜索"PRP"查看实际示例

Claude Code 终极指南的一部分。完整参考请见 《Claude Code:从入门到大师》。


来源:飞书 · AI Spark 知识库 | 原文(最新版):https://lcnniolukk80.feishu.cn/wiki/Lpl3wmddJiJUbdkj6WYcLMk5nqf | 归档:2026-06-04