Skip to content

13.3 上下文生命周期与释放机制

进入上下文的东西,不会因为「用完了」而自动消失。

读完你能做什么:说清每一类内容何时进入上下文、是否常驻、唯一的退出路径是什么;理解 Skill 被读取后为什么不会被释放,以及有哪些手段可以真正把它请出去。


一、一条铁律

凡是进入对话历史的内容,在同一会话内不会被释放。没有 unload 这个动作。

这是很多人的第一个认知缺口。直觉上会觉得:Claude 读了一个 Skill,用完了,下一步不需要了,那它应该被回收。

实际不是。对话历史是一个只追加的日志。模型每一轮都收到完整的日志。「不再需要」不是日志里能表达的状态 —— 客户端没有把某条消息从中间挖掉的机制,因为那会让后面所有内容的缓存前缀全部失效(见 13.4)。

所以准确的说法是:

  • 加载是渐进式的(不用就不进来)
  • 卸载是批量式的(要么整段丢弃,要么整段摘要,没有单点回收)

二、内容生命周期全表

以 Claude Code 会话为例:

内容何时进入上下文会话内是否常驻唯一退出方式
系统提示词、工具定义启动重启(不受压缩影响)
输出风格(output style)启动重启或 /clear
项目根 CLAUDE.md、无作用域 rules启动压缩后从磁盘重新注入
Auto memory(MEMORY.md启动压缩后从磁盘重新注入
Skill 的 name + description启动从磁盘移除该 Skill,或设 disable-model-invocation: true
MCP 工具名(延迟加载时)启动断开该 server
MCP 工具完整 schemaClaude 首次搜索到该工具时/clear/compact
Skill 正文(SKILL.md body)被调用时/clear/compact(受配额截断)、/rewind
Skill 目录下的其他文件被显式读取时同上
文件读取结果Read / Grep / Glob 返回时同上
工具与 Bash 输出工具返回时同上;API 侧可用 tool result clearing
paths: 的 rules首次读到匹配文件时压缩后丢失,直到再次读到匹配文件
子目录里的嵌套 CLAUDE.md首次读到该目录下文件时同上
思考块生成时取决于模型Opus 4.5+ / Sonnet 4.6+ 默认保留;更早模型与 Haiku 自动剥离
Hooks从不进入上下文Hook 是代码,只有它的输出会进上下文
脚本文件本身从不进入上下文只有 stdout / stderr 进上下文

最后两行是本章最重要的杠杆,13.5 会展开。


三、Skill 的完整生命周期

这是提问最集中的地方,单独拆开讲。

3.1 三级加载模型

层级何时加载Token 成本内容
L1 元数据启动时,全部 Skill每个约 100 tokenfrontmatter 的 namedescription
L2 正文被调用时建议控制在 5k 以内SKILL.md 的 markdown 正文
L3 资源被显式读取时未读取时为 0其他 .md、模板、数据文件
L3 脚本被执行时只有输出计入scripts/ 下的可执行文件

descriptionwhen_to_use 合并后,在 Skill 清单里按 1,536 字符截断,以控制常驻成本。所以关键用途要写在最前面。

3.2 调用之后发生了什么

官方对这一段的描述是精确的:

当你或 Claude 调用一个 Skill 时,渲染后的 SKILL.md 内容作为一条消息进入对话,并在本次会话的剩余时间里一直留在那里。Claude Code 不会在后续轮次重新读取该 Skill 文件。

三条直接推论:

  1. 用完不释放。 下一个流程不需要它了,它依然占着上下文,并在之后每一轮被重新计费(缓存命中时按 0.1× 价)。
  2. 正文里的每一行都是重复成本。 所以写 Skill 时要用写 CLAUDE.md 的标准:说「做什么」,不说「为什么」和「怎么想的」。
  3. 要写成常驻指令,不要写成一次性步骤。 因为它会一直在场,措辞应该是「本任务中始终遵守 X」,而不是「现在请做 X」。

3.3 重复调用会不会叠加

会去重,但有条件:

情形行为
再次调用,渲染结果与已在上下文中的副本完全相同只追加一句「该 Skill 已加载」的提示,不重复正文
渲染结果不同(参数变了,或 !`command` 动态注入的输出变了)追加完整正文,形成第二份副本

需 Claude Code v2.1.202+。更早版本每次重新调用都会追加一份完整正文。

3.4 压缩之后会发生什么

/compact 是唯一能「部分释放」Skill 的机制,规则很具体:

text
压缩时:
  1. 对话历史被摘要替换
  2. 摘要之后,重新附着每个 Skill 的最近一次调用内容
  3. 每个 Skill 只保留前 5,000 token(从文件开头截断)
  4. 所有重新附着的 Skill 共享 25,000 token 总预算
  5. 从最近调用的 Skill 开始填预算,超出后老的 Skill 被整个丢弃

三条实操结论:

  • 最重要的指令写在 SKILL.md 靠前位置,因为截断保留的是开头。
  • 一次会话里别调用太多 Skill。调用了 8 个各 4k token 的 Skill,压缩后只有最近 6 个能进 25k 预算,最早的 2 个消失。
  • 如果某个 Skill 在压缩后「失效」了,先确认是被丢弃还是模型只是没在用它 —— 前者重新调用一次即可恢复。

3.5 让 Skill 用完即走的两个办法

办法一:context: fork Skill 内容成为子代理的提示词,在独立上下文里执行,只有结果回到主会话。

yaml
---
name: deep-research
description: 深入调研一个主题并返回结论
context: fork
agent: Explore
---

调研 $ARGUMENTS:

1. 用 Glob / Grep 定位相关文件
2. 阅读并分析
3. 输出带文件引用的结论

主会话为此付出的上下文成本,只有最后那份摘要。

办法二:disable-model-invocation: truedescription 都不进常驻上下文,只有你手动敲 /skill-name 时才加载。适合有副作用、需要你控制时机的流程(部署、提交、发消息)。

frontmatter你能调用Claude 能自动调用常驻成本
(默认)description 常驻
disable-model-invocation: true零常驻
user-invocable: falsedescription 常驻

四、四种真正的释放手段

手段释放范围自身成本缓存影响适用时机
/clear全部对话历史(不发请求)从头重建切换到无关任务
/compact历史 → 摘要一次完整请求对话层击穿,系统层保留需要保留连续性的长任务
/rewind截断回到某一轮命中已有缓存走错路,想退回分叉点
子代理 / context: fork内容从不进入主上下文子代理自身的启动开销主会话缓存不受影响大量读取、探索、验证

4.1 /clear/compact 的选择

text
需要延续之前的工作状态吗?
├─ 不需要 → /clear(零成本,且新会话上下文最干净)
└─ 需要   → 走错了路吗?
            ├─ 是 → /rewind(缓存友好,比 compact 便宜得多)
            └─ 否 → /compact,并且带上指令:
                    /compact 重点保留鉴权模块的改动与未解决的测试失败

/compact 的时机同样重要:在任务边界主动执行,而不是等它在任务中途自动触发。自动压缩会按它的判断取舍,而你在任务边界执行时可以指定保留重点。

也可以把压缩偏好固化到 CLAUDE.md

markdown
# Compact instructions

压缩时优先保留:未解决的失败用例、已确定的架构决策、正在修改的文件路径。
可以丢弃:已通过的测试输出、成功的构建日志、已完成步骤的复述。

4.2 API 侧的精细释放

在自己写 Agent 时,有比 /compact 更外科手术式的选项(需 beta 头 context-management-2025-06-27):

python
response = client.beta.messages.create(
    model="claude-opus-5",
    max_tokens=4096,
    messages=messages,
    tools=tools,
    betas=["context-management-2025-06-27"],
    context_management={
        "edits": [
            {
                "type": "clear_tool_uses_20250919",
                "trigger": {"type": "input_tokens", "value": 30000},   # 超过此阈值才动手
                "keep": {"type": "tool_uses", "value": 3},             # 保留最近 3 组工具调用
                "clear_at_least": {"type": "input_tokens", "value": 5000},  # 清不够这么多就不清
                "exclude_tools": ["web_search"],                       # 这些工具的结果永不清除
            }
        ]
    },
)
策略清什么关键权衡
clear_tool_uses_20250919旧的工具结果(可选连工具调用一起清)会击穿缓存前缀,所以要用 clear_at_least 保证清得够本
clear_thinking_20251015历史思考块,可指定保留最近 N 轮保留思考块可维持缓存命中,清除则在清除点击穿
compact_20260112整段历史 → 摘要(服务端)额外一次采样,账单要遍历 usage.iterations

关键设计点:context editing 在服务端执行,客户端不需要同步。 你本地照常维护完整历史,服务端在请求到达模型之前做裁剪。


五、「我这句话去了哪里」的时序

一个具体例子,帮助建立直觉:

text
T0  启动
    → 系统提示词 4.2k / auto memory 0.7k / 环境信息 0.3k
    → MCP 工具名 0.12k / Skill 描述 0.45k
    → 用户 CLAUDE.md 0.32k / 项目 CLAUDE.md 1.8k
    常驻小计 ≈ 7.9k,这部分每一轮都要重新计费

T1  你:"给鉴权加上 refresh token"        +0.045k
T2  Read src/api/auth.ts                  +2.4k   ← 永久留下
T3  Read src/lib/tokens.ts                +1.1k   ← 永久留下
T4  自动加载 rules/api-conventions.md      +0.38k  ← 压缩后会丢失
T5  Edit auth.ts                          +0.4k
T6  PostToolUse hook 跑 prettier           +0.12k  ← 只有输出进来,脚本本身没有
T7  npm test 输出                          +1.2k   ← 未过滤的话可能是 12k
T8  Claude 总结                            +0.4k

此刻窗口占用 ≈ 13.9k
第 8 轮的计费输入 ≈ 13.9k(其中约 12k 走缓存读)

T9  你派一个子代理去调研                    +0.08k
    子代理内部读了 3 个文件共 8k token      → 全部留在子代理的上下文里
    子代理返回摘要                          +0.42k  ← 主会话只付这 0.42k

T10 /compact
    → T1~T9 被摘要替换
    → 项目 CLAUDE.md、auto memory 从磁盘重新注入
    → rules/api-conventions.md 丢失,直到下次读到匹配文件
    → 已调用的 Skill 按 5k/个、25k 总额重新附着

T9 那一步是整段流程里性价比最高的动作:8k token 的读取,主会话只承担 0.5k。


六、常见误解清单

误解实际情况
Skill 用完会被卸载不会。会话内一直在,直到 /clear/compact
只要不再提它,它就不占钱了每一轮都重新计费,缓存命中时按 0.1×
装了 30 个 Skill,上下文会爆未触发时每个约 100 token,30 个约 3k,可接受
中途改 CLAUDE.md 会立刻生效不会。项目根与用户级 CLAUDE.md 在会话启动时读入并驻留内存,改动要等 /clear/compact 或重启
中途改 CLAUDE.md 会击穿缓存不会,因为它压根没重新读
/compact/clear 省钱相反。/clear 零成本,/compact 是一次完整请求
Hook 脚本会占上下文不会。Hook 是代码,只有它的输出进上下文
子代理读的文件会进主上下文不会。只有它返回的摘要会进来

延伸阅读

基于 VitePress 构建 · 内容采用原作者授权