一、上下文窗口里到底装了什么

很多开发者认为 Claude Code 的上下文就是"我说了什么、它回了什么"。实际上,对话历史只是七类内容之一。理解这个认知偏差,是用好 Claude Code 的第一步。

完整的七类上下文内容

# 类型 大概占比 压缩优先级 生命周期
1 系统指令(框架规则+工具定义) 5-10% 永不清除 会话全程
2 CLAUDE.md(持久化规则) 1-3% 压缩后重新注入 跨会话
3 对话历史(用户交互记录) 40-70% 优先压缩 会话全程
4 文件内容(Claude 读取/编辑过的源码) 15-30% 清除优先级第2 按需存在
5 命令输出(终端执行结果) 10-25% 清除优先级第1 单轮有效
6 Skills(按需加载的指令文本) 2-8% 压缩后保留前5000 token 触发后常驻
7 自动记忆(跨会话学习记录) <1% 不压缩 跨会话

经验数据:一个典型的 10 轮对话,对话历史+文件内容占到上下文窗口的 70% 以上。这也是为什么上下文管理如此关键——token 消耗的大头永远是"说了什么和读了什么"。

压缩的两步策略

Claude Code 的压缩不是简单的"删旧消息",而是一个有优先级排序的过程:

第一步:移除命令输出和文件读取结果
  这两类内容有几个共同特征:
  - 占用大(一个文件几千 token、一条命令几百 token)
  - 时效性低(第十轮时第五轮的 ls 结果已经没用)
  - 可替代(需要时重新执行命令就行)
​
第二步:摘要压缩对话历史
  具体过程:
  - 把具体措辞浓缩为语义概要
  - 把数值和代码片段替换为占位描述
  - 把多轮讨论折叠为单轮总结
  
  示例:
  压缩前:
    "第五轮时我们讨论过 PostgreSQL 连接池配置,
     最终决定 maxPoolSize=10, connectionTimeout=5000ms,
     连接串是 jdbc:postgresql://127.0.0.1:5432/ragent"
  
  压缩后:
    "数据库连接池配置已确定(具体参数见代码)"

信息丢失的代价与应对

压缩必然丢失信息。丢失的不是随机的,而是细节——具体数值、精确措辞、代码片段都被浓缩掉了。

丢失模式:

内容类型 压缩后状态 影响程度
跨轮强调的规则 可能变成模糊总结 ★★★★★
讨论过的架构决策 决策在,原因丢失 ★★★★
具体参数/配置值 精确值丢失,变成"已配置" ★★★★
代码实现细节 思路在,代码丢失 ★★★
文件结构发现 目录关系在,文件名模糊 ★★

三个递进式的解决方案

方案 A(入门):规则写进 CLAUDE.md

CLAUDE.md 每次压缩后都会被重新注入上下文,不受摘要影响。可以把 CLAUDE.md 理解为"不会被遗忘的提醒便签"。

# CLAUDE.md 里的压缩指令示例
​
## 对话压缩时必须保留的信息
- 数据库使用 PostgreSQL,连接池 maxPoolSize=10, connectionTimeout=5000ms
- RocketMQ nameserver 是 127.0.0.1:9876
- 项目不使用 Spring AI,所有 AI 调用走自建的 infra-ai 层

方案 B(进阶):主动触发 /compact 并指定焦点

普通压缩是自动触发的,Claude 自行判断保留什么。/compact 让你介入决策:

/compact 我只关心数据库 schema 的变更历史,其他的都可以压缩
/compact 聚焦在 API 接口签名上,把具体实现细节压缩掉
/compact 保留所有配置相关的讨论,省略代码实现

方案 C(系统级):善用自动记忆的"长尾存储"

自动记忆由 Claude 自己写入,存在 ~/.claude/projects/ 目录。每次新会话启动时加载 MEMORY.md 的前 200 行(或 25KB)。

它和 CLAUDE.md 的本质区别:

维度 CLAUDE.md 自动记忆
写入者 Claude 自己
加载量 全部 前 200 行/25KB
适合内容 核心规则、硬性约束 细碎经验、模式偏好
稳定性 高(人不删就不会变) 中(Claude 会重写)

实战心法:CLAUDE.md 放"假设立场"(用什么库、遵循什么规范),自动记忆放"历史教案"(之前吃过什么亏、发现什么规律)。


二、CLAUDE.md 的加载机制:层级叠加的陷阱

Claude Code 启动时从四个位置按顺序查找并加载 CLAUDE.md:

层级 位置 共享范围
托管策略 /Library/Application Support/ClaudeCode/CLAUDE.md 组织内所有用户
用户级 ~/.claude/CLAUDE.md 所有项目通用
项目级 ./CLAUDE.md./.claude/CLAUDE.md 通过 Git 共享给团队
本地级 ./CLAUDE.local.md(被 gitignore) 仅限本机使用

拼接而非覆盖:最大设计细节

四层文件不是覆盖关系,而是从根目录到当前工作目录全部串联注入

这意味着一个常见陷阱——层级间规则冲突

~/.claude/CLAUDE.md:          使用 four 空格缩进
./.claude/CLAUDE.md:          使用 two 空格缩进

两条规则同时存在于上下文里,Claude 自行判断优先级——结果可能符合预期,也可能不符合。这是很多"规则不生效"投诉的根本原因。

排查清单:

规则 X 没生效?
├── 检查各层级 CLAUDE.md 是否有冲突规则
│   find . -name "CLAUDE.md" -o -name "CLAUDE.local.md" | xargs grep "规则关键词"
├── 检查规则是否超过 200 行限制
│   (官方建议每个文件 < 200 行,超出的执行力度会下降)
├── 检查规则是否足够具体
│   "写好好代码" ❌(不可验证)
│   "方法不超过 30 行" ✅(可验证)
└── 检查是不是只写在对话里
    (对话里的规则不跨压缩)→ 改写到 CLAUDE.md

CLAUDE.md 最佳实践结构

一个经过实战检验的 CLAUDE.md 模板:

# 项目速览
- 项目名: ragent
- 框架: Spring Boot 3.5.7 / Java 17
- 向量DB: Milvus 2.6.6 + pgvector
- 消息队列: RocketMQ
​
# 编码约定
- 代码风格: Google Java Format (Spotless)
- 命名规范: 大驼峰类名 / 小驼峰方法+变量
- 方法长度: < 30 行
- 必须用中文注释公共方法
​
# 不可违反的架构边界
- 所有 AI 调用走 infra-ai 包,严禁直接 import OpenAI SDK
- 数据库访问走 MyBatis-Plus Mapper,禁止写裸 SQL
- MCP 相关代码集中在 rag/core/mcp/ 包
​
# 压缩时必须保留的配置信息
- DB: postgresql://127.0.0.1:5432/ragent, pool=10
- Redis: 127.0.0.1:6379
- RocketMQ nameserver: 127.0.0.1:9876
- Milvus: http://localhost:19530

三、Hooks:CLAUDE.md 管规则,Hooks 管纪律

CLAUDE.md 的"建议式管理"有一个短板:Claude 读了会参考,但不保证每次都遵守——它有自己的判断,有时认为规则在当前场景不适用就跳过去了。

如果是必须执行的规则(如提交前跑 lint、禁止 force push、所有写入必须审批),就需要 Hooks 来"物理拦截"。

五种处理器类型详解

类型 退出码控制 典型场景
command 0=通过, 2=阻断 跑 lint、校验格式、安全扫描
http 0=通过, 2=阻断 触发 CI 流水线、通知外部系统
mcp_tool 0=通过, 2=阻断 把 MCP 能力引入生命周期钩子
prompt Claude 模型判断 需要语义理解的动态审查(如"这次提交有没有看起来像密码的东西")
agent Subagent 多步判断 复杂校验(实验性,如 Subagent 跑完完整代码审查链路)

Hook 事件矩阵

Hooks 覆盖了 Claude Code 的完整生命周期,核心事件:

会话开始
  └── SessionStart → 可以用来注入动态信息、连接工具
  
用户输入
  └── UserPromptSubmit → 校验输入内容(比如阻止含敏感信息的提交)
  
工具调用前(最重要)
  └── PreToolUse → 可以 allow/deny/ask/defer,还可能篡改参数
  
工具调用后
  └── PostToolUse → 记录日志、触发后续流程
  
压缩前
  └── PreCompact → 在压缩前把关键信息写入 CLAUDE.md 或记忆文件
​
Claude 回复完毕
  └── Stop → 校验输出内容、触发代码审查
​
会话结束
  └── SessionEnd → 清理资源、生成会话摘要

PreToolUse Hook 实战示例

PreToolUse 是最常用的 Hook 类型。它有两个杀手级能力:

能力 1 — 权限控制,通过 permissionDecision 字段:

{
  "matcher": "Bash",
  "hook": [{
    "type": "command",
    "command": "check-command.sh",
    "async": false
  }]
}

permissionDecision 取值:

  • allow:直接放行,不弹确认

  • deny:禁止操作

  • ask:弹确认框让用户决定

  • defer:交给默认逻辑

能力 2 — 参数篡改,通过 updatedInput

# PreToolUse Hook 改写所有 git commit 命令,强制加签名
PreToolUse:
  - matcher: "Bash"
    hooks:
      - command: |
          if [[ "$COMMIT_CMD" == *"git commit"* ]] && [[ "$COMMIT_CMD" != *"-S"* ]]; then
            echo "${COMMIT_CMD/-m /-S -m }" > /tmp/modified.sh
          fi

可以用来:强制 git commit 加签名、文件写入前追加版权声明、给敏感命令加审计日志等。

Hook 配置位置与超时

配置位置与 CLAUDE.md 层级体系完全对齐:

  • 用户级~/.claude/settings.json

  • 项目级.claude/settings.json(Git 共享,团队统一)

  • 本地级.claude/settings.local.md(本机专用)

默认超时:command/http/mcp_tool 类型 600 秒,prompt 30 秒,agent 60 秒。

异步 Hook:不阻塞的后台校验

在配置中加 "async": true,Hook 会在后台执行:

{
  "matcher": "Write",
  "hook": [{
    "type": "command",
    "command": "python scripts/security_scan.py",
    "async": true,
    "asyncRewake": true
  }]
}

asyncRewake: true 的含义:后台 Hook 返回退出码 2(阻断)时,会向 Claude 发送系统提醒,通知它异步校验未通过。适合耗时较长的检查,如远程代码扫描、大规模依赖漏洞检查。

设计哲学:CLAUDE.md = 规则手册(员工看了参考着做),Hooks = 安检门(过不了就别想走)。


四、Skills 机制:按需加载的专业知识模块

CLAUDE.md 每次都加载(常驻),Skills 是被触发才进入上下文(按需)。这是两者的核心分界线。

Skills 的存在价值

为什么不能把所有规则都塞进 CLAUDE.md?
↓
因为 CLAUDE.md 有 200 行限制,而且每次都注入
↓
想象一下把"修 MySQL""修 Redis""修网络""写前端""写算法"的全套手册都塞进去
↓
Claude 辩认当前该用哪部分的信息成本极高
→ 这就是 Skills 存在的意义:按任务运行时动加载对应的"专业手册"

三步触发引擎

三步决定了一个 Skill 能否被成功激活:

Step 1 — 启动时构造索引

Claude Code 收集所有 Skill 的名称和描述文本,拼成一张索引清单注入上下文。关键限制:

  • 整张清单总占用不过上下文窗口的 1%

  • 单个 Skill 描述最多 1536 个字符

Step 2 — 逐轮决策

每轮对话时,Claude 扫一遍清单。此时它只能看到描述文本,看不到 SKILL.md 正文。

Step 3 — 触发加载

匹配上了,SKILL.md 全文才被加载进上下文。匹配过程没有"试探",没有"预览",要么全加载,要么不加载。

超高门槛:1536 字符的军备竞赛

1536 字符是什么概念?大约是一个段再多两三行。你必须在这么短的篇幅内说清楚:

  1. 这个 Skill 是干什么的(一句话)

  2. 什么时候该用(触发条件)

  3. 什么时候不该用(排除条件)

  4. 和同领域其他 Skill 怎么区分

写详细了?被截断。写简略了?Claude 不知道该不该用。

描述优化对比

# ❌ 糟糕的描述(Claude 不知道怎么触发)
description: Spring 相关的帮助

# ✅ 精准的描述(告诉模型具体边界)
description: |
  处理 Spring Boot 配置问题。
  WHEN: 用户修改 application.yaml/pom.xml 或遇到 Bean 初始化错误时。
  WHEN NOT: @RestController/@Service 注解的开发或 HTTP 接口层代码编写。

Skill 的上下文驻留规则

被触发加载后,Skill 的内容会一直留在当前会话——不会因为开启新一轮对话而消失。

但上下文压缩时 Skill 也会被处理:

压缩后的 Skill 保留策略:
- 每个 Skill 保留前 5000 个 token
- 所有 Skill 的重新注入总预算 25000 个 token
- 超出总量时,按 LRU(最近最少使用)淘汰

实战启示:
- 同时生效的 Skill 总量不要超过 4-5 个
- 核心 Skill 保持在最近使用位置以免被淘汰

Skill 目录结构与 frontmatter 全字段

my-skill/
  SKILL.md           # 主文件
  references/        # 参考资料(加载时自动引入)
  scripts/           # 可执行脚本供 Claude 调用
  examples/          # 示例输出(展示正确用法)

SKILL.md frontmatter 字段一览:

---
name: my-skill
description: 精准描述(1536字符内)
version: 1.0.0

# 控制加载行为
disable-model-invocation: false      # true = 只能手动触发
allowed-tools: Read, Bash, Grep      # 免确认工具
model: sonnet                        # 覆盖当前模型
context: fork                        # 在独立子agent 运行

# 权限控制
allowed-tools: Read, Write, Edit, Bash, Grep
---

动态内容:让 Skill 感知运行时

!`command` 语法在加载时执行 Shell 命令,输出直接替换到正文里。

应用:

## 当前项目状态
生成时间:!`date "+%Y-%m-%d %H:%M:%S"`

数据库连接状态:!`psql -h 127.0.0.1 -U postgres -c "SELECT 1" 2>&1 | tail -1`

最近 3 次提交:!`git log --oneline -3`

服务健康检查:!`curl -s -o /dev/null -w "%{http_code}" http://localhost:9090/api/ragent/health`

每次加载时自动刷新这些信息,不需要手动编辑 SKILL.md。

五、Subagent 的上下文隔离:让主线程轻装上阵

Subagent = 独立上下文窗口 = 独立记忆 + 独立工具权限 + 独立 token 预算。

为什么需要 Subagent

没有 Subagent 的多步任务是怎样的?

"帮我分析哪个 Service 最复杂,然后重构它"

主 Claude:
1. 读取 20 个 Service 文件    → 消耗 15000 token
2. 分析复杂度               → 消耗 5000 token
3. 根据分析结果重构目标文件   → 消耗 20000 token
总计:40000 token,主上下文被"分析过程"的中间数据塞满

有 Subagent 时:

主 Claude:
1. 派 Subagent 去分析 + 重构 → 几乎不消耗主上下文 token
2. Subagent 返回一份精简报告   → 消耗 2000 token
3. 主 Claude 根据报告决策下一步 → 轻装上阵

对比:主上下文节省了 95% 的消耗

三种内置 Subagent 的定位

类型 模型 工具权限 CLAUDE.md 适用场景 响应速度
Explore Haiku(快速模型) 只读(Glob/Grep/Read) 不加载 搜索文件、浏览代码结构 最快(2-5秒)
Plan 继承主模型 只读(Glob/Grep/Read) 不加载 架构分析、方案设计 中等
General-purpose 继承主模型 全部(含编辑和执行) 完整加载 复杂修改、多步任务 最慢

Explore 和 Plan 跳过 CLAUDE.md 的原因

它们主要做信息收集和分析,不需要遵守项目的代码规范和提交约束,跳过 CLAUDE.md 可以让上下文更干净、响应更快。General-purpose 要改代码,必须了解项目规则才能产出合格结果。

自定义 Subagent 的定义

.claude/agents/ 目录下创建 Markdown 文件:

# .claude/agents/performance-analyst.md
---
name: performance-analyst
description: Analyzes code performance and suggests optimizations
tools: Read, Glob, Grep, Bash
model: sonnet
skills: sql-optimization, redis-best-practices
mcpServers:
  - name: metrics-server
    url: http://localhost:9999/mcp
---

You are a performance analysis specialist.
Focus on:
1. Database query patterns and N+1 issues
2. Memory allocation and GC pressure
3. Network call batching opportunities
4. Caching strategy optimization

When analyzing, always provide:
- Quantitative metrics (current vs proposed)
- Risk assessment (what could break)
- Incremental adoption plan

定义后主 Agent 在需要性能分析时自动委派——专属工具集、独立的 MCP 服务器(只在启动时连接,结束时不占主上下文)。

Fork Subagent:继承上下文的快捷方式

通过 /fork 命令或 context: fork 启用。

和普通 Subagent 的本质区别:

维度 普通 Subagent Fork Subagent
上下文 空白开始 继承完整对话历史
适用场景 全新独立任务 在当前讨论基础上深入探索
Prompt 缓存 独立计算 共享主 Agent 缓存
典型用法 "帮我调研 X 方案" "基于刚才的讨论,再评估一下它的可行性"

六、五大机制的协作关系

这五个机制不是孤岛,它们在每一轮对话里动态协作。一个典型的多机制协作流程:

用户: "帮我重构 UserService"

1. [CLAUDE.md] 命中规则:
   - "Service 层代码放在 rag/service/ 包下" ✓
   - "重构需先跑全部测试" ✓

2. [System Prompt] 提供工具定义:
   - Read, Edit, Bash 等可用工具

3. [Skills 索引] 逐轮扫描:
   - 发现 "refactoring" Skill 可能相关
   - 读取描述文本判断...

4. [Skill 加载] 触发 "refactoring" Skill:
   - SKILL.md 完整指令进入上下文
   - 包含重构检查清单、最佳实践、常见陷阱

5. [PreToolUse Hook] 执行 Edit 前触发:
   - 校验目标文件路径合规
   - 检查是否已跑测试

6. [Subagent] 执行重构分析(可选):
   - Explore 子 Agent 搜索所有调用方
   - 结果返回给主 Agent

7. [对话历史] 累积上下文:
   - 用户原始请求 + Claude 的中间决策

8. [自动记忆] Claude 边做边记:
   - "发现 UserService 有 3 个未处理的 null pointer 风险"
   - "项目偏好保留 Service 接口,只改实现"

协作关系总结:

CLAUDE.md ──────────────────────────────────────────────────────────
      │                                          │
      │ 提供"项目级知识"                          │ 提供"全局架构规则"
      ▼                                          ▼
System Prompt                              Context Window
      │                                          │
      │ 注入框架规则                              │ 接受四项内容
      │ 注入工具定义                              │ ① 对话历史 ② 文件
      │                                          │ ③ 命令输出 ④ 记忆
      ▼                                          │
Skills ──────────── Subagent ──────────── Hooks  │
  │                   │                   │      │
 │提供专业知识       │提供隔离执行环境    │强制执行 │
 │按需加载           │防止过程数据溢出    │权限控制 │
 ▼                   ▼                   ▼      │
Skills ──────── Subagent ──────── Hooks ────────┘
     (压缩后保留前5000 token)  (独立token预算)   (退出码决策)

七、实战调优清单

规则层问题的排查思路

症状:Claude 不遵守规则
│
├── 规则放在哪?
│   ├── 对话里 → 对话被压缩就忘,改写到 CLAUDE.md
│   ├── CLAUDE.md 但超过 200 行 → 删除冗余规则
│   └── 多层级冲突 → 检查各层 CLAUDE.md 的一致性
│
├── 规则写得不够具体?
│   └── "写好好的代码" → "方法不超过 30 行,圈复杂度不超过 10"
│
└── 规则必须强制执行?
    └── 改 Hooks(带退出码 0/2 拦截)

上下文污染的排查思路

症状:Claude 注意力不集中,回答偏离预期
│
├── 太多无关文件被读取?
│   └── 在 .gitignore 里排除 /target, /node_modules
│
├── 太长的不相关命令输出?
│   └── 在 CLAUDE.md 加"压缩必须保留"清单
│
├── 同时激活太多 Skill?
│   └── 合并相关 Skill,确保总数 < 5
│
└── 主上下文数据量不够隔离?
    └── 用 Subagent 隔离分析过程(特别是代码搜索+分析)

Subagent 选型指南

任务类型 → 推荐 Subagent 类型 → 原因
─────────────────────────────────────────────
"搜索/浏览"           → Explore                    → 快、便宜、只读
"分析方案/架构规划"   → Plan                       → 继承主模型推理能力
"实际改代码"          → General-purpose            → 需要完整工具权限
"需要项目规则约束"    → General-purpose            → 加载 CLAUDE.md
"在当前讨论基础上深入" → Fork Subagent             → 共享对话历史

总结

Claude Code 的上下文窗口有七个维度,其中有五个是最具工程价值的:

机制 角色 关键词 本质
CLAUDE.md 方向 持久化规则 每次压缩后重新注入
Hooks 纪律 强制拦截 退出码决定行止
Skills 专业 按需加载 1536 字符触发窗口
Subagent 分工 隔离执行 独立 token 预算
Context Compression 记忆 有损压缩 细节丢失的根因

一句话总结:CLAUDE.md 管方向,Hooks 管纪律,Skills 管专业,Subagent 管分工,压缩机制管理记忆。

我们在 Prompt 优化上花了很多时间,但决定 Claude Code 能不能持续稳定产出的,从来不是某一句话写得多巧妙,而是对这五个机制的配置质量和理解深度。

把时间花在理解工具的运作方式上,比花在猜测工具的脾气上,回报大得多。


Logo

小龙虾开发者社区是 CSDN 旗下专注 OpenClaw 生态的官方阵地,聚焦技能开发、插件实践与部署教程,为开发者提供可直接落地的方案、工具与交流平台,助力高效构建与落地 AI 应用

更多推荐