)
1. 从一次 413 报错说起Claude Code 上下文管理到底在管什么如果你用 Claude Code 跑过一个稍微大点的重构任务大概率见过这个报错Prompt is too long或者 HTTP 413。这不是网络问题是上下文窗口被塞满了。Claude Code 的默认上下文窗口是 200,000 tokens听起来很大但读几十个源文件、跑几轮命令输出之后消息历史就能吃掉大半个窗口。Claude Code 上下文管理机制的核心就是解决窗口不够用这件事。它不是一个简单的截断逻辑而是一套分层的调度系统入口处拦截超大工具结果存量部分按代价递进做三层清理同时还要绕开 Prompt Cache 的约束。压缩策略的设计难点在于——你不能随便删消息因为删了可能破坏缓存前缀导致每次请求成本翻十倍你也不能随便摘要因为摘要会丢细节模型可能忘记关键约束。这套机制适合谁看适合正在做 LLM 应用开发、想理解上下文窗口如何被调度与回收的程序员。不管你是用 Claude Code 本身还是在自己的 Agent 项目里要复现类似的压缩逻辑理解它的工程实现都有直接参考价值。下面我会按上下文怎么拼装 → 为什么要压缩 → 三层压缩怎么协作 → 怎么本地验证的顺序拆开讲每一步都给可复制的配置和命令。2. 上下文不是对话框五层拼装结构与 Prompt Cache 约束每次 Claude Code 向 API 发请求发出去的不是一个简单的对话历史而是一个由五层拼装而成的结构体。理解这个结构是理解它为什么需要压缩的前提。第一层是系统提示它本身是一个数组由最多四个块按顺序拼接归因头包含版本号等变化信息不参与缓存、身份前缀固定文本所有请求共享、静态指令任务指导、工具规范、代码风格缓存命中率最高、动态上下文工作目录、Git 状态、CLAUDE.md、日期等每次会话可能不同。代码里有一个明确的边界标记// src/constants/prompts.ts export const SYSTEM_PROMPT_DYNAMIC_BOUNDARY __SYSTEM_PROMPT_DYNAMIC_BOUNDARY__边界之前的内容可以跨请求复用缓存边界之后每次重新处理。第二层是工具定义作为独立的tools字段传给 API。工具列表的最后一个工具会被标记cache_control: { type: ephemeral }缓存点打在工具列表末尾意味着系统提示静态部分 所有工具定义这整个前缀都可以被缓存。第三层是 userContext。CLAUDE.md 和当前日期不是放在系统提示里而是作为消息历史的第一条消息注入。为什么不放系统提示因为 CLAUDE.md 可能在会话中途变化而系统提示在会话开始后就被缓存了。放在消息历史开头可以在不破坏系统提示缓存的情况下更新。第四层是消息历史这是增长最快的部分。每一轮对话追加用户消息、助手回复、tool_use 块、tool_result 块。文件内容本身会被完整放入 tool_result 块这是膨胀的主要原因。第五层是 Attachments每次请求前动态生成注入到消息历史末尾包括 提及的文件、Todo 列表、Plan 文件、记忆片段等。Prompt Cache 是整个压缩设计的核心约束。它的原理是 API 把输入的 KV Cache 保存下来下次请求如果前缀完全一致就复用费用降到正常处理的 10%。但缓存粒度是前缀——必须从请求开头连续匹配。你在系统提示中间插一个字符后面全部失效。缓存 TTL 是 5 分钟超时缓存过期。这意味着所有压缩操作都必须绕开这个约束不能随意改动消息历史开头否则省下的空间会被增加的 token 处理成本抵消。3. 可复制配置三层压缩策略与 Hook 干预片段Claude Code 的压缩分三层代价递进。第一层 MicroCompact 在每次请求前静默清理旧工具结果零 API 调用第二层 Session Memory Compact 在上下文接近阈值时优先用已有记忆文件压缩零额外 API 调用第三层 AutoCompact 在前两者都不适用时调用 Claude 生成摘要一次额外 API 调用。入口管控先于这三层工具结果在入库时检查大小超限直接持久化到磁盘。// src/constants/toolLimits.ts export const DEFAULT_MAX_RESULT_SIZE_CHARS 50_000 // 单个工具结果上限5 万字符 export const MAX_TOOL_RESULTS_PER_MESSAGE_CHARS 200_000 // 单条消息内所有工具结果合计上限20 万字符超限后上下文里只保留 2000 字节预览 文件路径模型可按需用 FileRead 读取完整内容。替换决策一旦做出就冻结后续每轮用完全相同的预览字符串重新注入保证前缀字节一致缓存不失效。MicroCompact 只针对一次性消费型工具的结果做清理核心工具是cache_editsAPI——允许在不改变消息历史语义的情况下用缓存版本替换消息内容。但它有个前提缓存必须还是热的。如果距离上次请求超过 5 分钟TTL 到期就切换到直接清空路径// 时间触发路径缓存已冷直接清空内容 return { ...block, content: TIME_BASED_MC_CLEARED_MESSAGE } // cache_edits 路径缓存仍热通过 API 层替换 pendingCacheEdits cacheEditsSession Memory Compact 复用会话过程中持续提取的 10 段结构化记忆作为压缩摘要跳过 AI 摘要调用。压缩后保留的消息由配置决定export const DEFAULT_SM_COMPACT_CONFIG { minTokens: 10_000, // 压缩后至少保留 10K tokens 的近期消息 minTextBlockMessages: 5, // 至少保留 5 条有文本内容的消息 maxTokens: 40_000, // 保留消息的硬上限 }AutoCompact 的触发阈值是动态计算的export const AUTOCOMPACT_BUFFER_TOKENS 13_000 export function getAutoCompactThreshold(model: string): number { const effectiveContextWindow getEffectiveContextWindowSize(model) return effectiveContextWindow - AUTOCOMPACT_BUFFER_TOKENS }对于 200K 上下文的模型实际触发阈值大约在 167K tokens 左右。摘要严格按九个章节生成其中所有用户消息会逐条列出确保用户意图不丢失。Hook 干预机制允许你在压缩前后注入自定义逻辑。在settings.json中配置{ hooks: { PreCompact: [{ hooks: [{ type: command, command: python3 /path/to/pre_compact.py }] }], PostCompact: [{ hooks: [{ type: command, command: python3 /path/to/post_compact.py }] }] } }PreCompact 在生成摘要前执行脚本的标准输出会作为自定义摘要指令与用户指定的 customInstructions 合并用户指令在前Hook 指令追加在后。PostCompact 在摘要完成后执行接收完整的压缩摘要文本可用于记录事件或同步到外部系统。如果你要把这套逻辑接到自己的项目里需要准备三件套Base URL、API Key、Model ID。Base URL 填https://taotoken.net/apiKey 在控制台生成Model ID 按你实际使用的模型填。这三项在 Cline MCP 配置、Codex 的 auth.json、或者 Claude Code 的环境变量里都要对齐缺一个就会报 401 或 model not found。4. 本地验证观察 token 变化与压缩触发光看源码不够得实际跑一遍才能确认压缩逻辑真的生效。下面是我验证时用的步骤。第一步确认当前会话的 token 消耗。Claude Code 在交互模式下可以用/cost查看本次会话的 token 使用情况。如果你想更细粒度地观察可以在请求前后对比 API 返回的 usage 字段。第二步制造一个会触发工具结果持久化的场景。让 Claude Code 读取一个大文件比如一个几千行的日志文件# 生成一个测试用的大文件 python3 -c print(line content here\n * 20000) /tmp/big_test_file.txt wc -c /tmp/big_test_file.txt # 输出约 400000 字节超过 5 万字符上限然后在 Claude Code 里让它读取这个文件。读取后观察上下文里的 tool_result 块应该会被替换成persisted-output格式包含文件路径和 2000 字节预览。第三步验证 MicroCompact 的清理效果。连续进行多轮对话让旧的文件读取结果被清理。你可以在请求日志里看到[Old tool result content cleared]占位符出现。第四步触发 AutoCompact。持续对话直到 token 接近阈值或者手动执行/compact。手动路径有独立的 PTL 重试逻辑const MAX_PTL_RETRIES 3 for (;;) { summaryResponse await streamCompactSummary({ messages: messagesToSummarize, ... }) if (!summary?.startsWith(PROMPT_TOO_LONG_ERROR_MESSAGE)) break messagesToSummarize truncateHeadForPTLRetry(messagesToSummarize, summaryResponse) }压缩完成后你会看到摘要消息里包含一行提示指向完整的会话转录文件路径。如果模型需要压缩前的具体细节可以主动读取这个文件。第五步验证压缩后的状态恢复。AutoCompact 完成后会自动恢复最多 5 个最近修改的文件export const POST_COMPACT_MAX_FILES_TO_RESTORE 5 export const POST_COMPACT_TOKEN_BUDGET 50_000 export const POST_COMPACT_MAX_TOKENS_PER_FILE 5_000此外还会重新注入本次会话使用过的 Skills、重新执行 SessionStart Hook 加载 CLAUDE.md、重新注入工具和 MCP 指令。实测下来从触发压缩到恢复完成整个流程在几秒内结束模型能立即继续工作。你可以通过对比压缩前后的/cost输出确认 token 数量确实下降了。5. 常见报错排查401、local proxy failed 与 reading choices在配置和验证过程中最容易踩的坑集中在几个报错上。下面按真实报错逐个排查。401 Unauthorized最常见的原因是 API Key 没配对或者 Base URL 和 Key 不匹配。检查你的配置里 Base URL 是不是https://taotoken.net/apiKey 是不是从对应控制台生成的。如果你用的是 Cline MCP 或 Codex 的 auth.json确认三件套Base URL Key Model ID都填了缺一个就会 401。local proxy failed / connection refused这个报错通常出现在你配置了本地代理但代理没启动或者端口填错了。检查你的环境变量里有没有残留的代理配置。如果你在 settings.json 里配了自定义 endpoint确认地址可达。Error reading choices / invalid response format这个报错说明请求发出去了但返回的数据格式不符合预期。常见原因是 Model ID 填错了或者你用的模型不支持当前的 API 格式。确认 Model ID 和你的接入方式匹配。如果你在 Claude Code 里用Model ID 要填 Claude 系列如果你在 Cline 里用按 Cline 的要求填。OAuth token expired / authentication failed如果你用的是 OAuth 方式接入token 过期后会报这个。重新走一遍授权流程或者换成 API Key 方式。API Key 方式更稳定不会因为 token 过期中断。Prompt is too long (413)这个不是配置问题是上下文真的满了。检查 AutoCompact 有没有正常触发。如果连续失败 3 次熔断器会停止尝试const MAX_CONSECUTIVE_AUTOCOMPACT_FAILURES 3 if (tracking?.consecutiveFailures MAX_CONSECUTIVE_AUTOCOMPACT_FAILURES) { return { wasCompacted: false } }这时候你可以手动执行/compact手动路径有独立的 PTL 重试逻辑会逐步截断最旧的消息组直到压缩请求成功。缓存命中率低 / 费用异常高检查你是不是在会话中途修改了系统提示或工具定义。频繁切换 MCP 服务器会改变工具定义导致 Prompt Cache 失效。如果不需要某个 MCP 服务器在会话开始前就断开而不是在会话中途操作。排障时如果确认是接入配置问题可以去 API Keys 页面重新生成 Key对照接入文档检查每一项配置。文档里有各客户端的完整配置示例照着填基本不会出错。6. 把压缩逻辑用起来从观察到定制理解这套机制之后你能做的事情比想象中多。CLAUDE.md 要保持简洁。它通过 userContext 注入到消息历史开头不参与系统提示的缓存每次请求都会消耗 token。只放真正需要每次都提醒模型的内容不要把它当知识库用。我见过有人把整个项目的架构文档塞进 CLAUDE.md结果每次请求都多花几千 token压缩触发得特别早。AutoCompact 触发后早期对话的细节会变成摘要。摘要会保留所有用户消息但中间的工具调用细节会被压缩。对于长任务在关键节点明确告诉模型记住这个约束或者把重要约束写进 CLAUDE.md比依赖模型的记忆更可靠。压缩后模型会自动恢复最近 5 个文件。如果任务涉及很多文件压缩后模型可能无法立即看到所有文件的当前状态。在压缩后的第一轮可以明确告诉模型需要重新读取哪些文件。可以用 PreCompact Hook 定制摘要重点。如果你的项目有特定的信息需要在压缩时重点保留比如某个架构约束、某个正在进行的重构方向写一个 PreCompact 脚本在每次压缩时自动注入这些指令不需要每次手动指定。脚本的标准输出会被合并到摘要指令里你可以在脚本里根据当前 Git 分支或环境变量动态生成不同的保留规则。频繁切换 MCP 服务器会增加成本。连接和断开 MCP 服务器会改变工具定义导致 Prompt Cache 失效。如果不需要某个 MCP 服务器在会话开始前就断开。如果你在做一个自己的 Agent 项目想复现类似的压缩效果建议先从入口管控做起——在工具结果入库前检查大小超限就持久化。这一步代价最低效果最明显。然后再加 MicroCompact 式的旧结果清理最后才考虑 AutoCompact 式的摘要压缩。每一步都先验证 token 变化确认收益之后再往上加。想验证不同模型在压缩前后的表现差异可以在模型对话页面直接对比。如果你要长期跑编码任务或 Agent 流程Coding Plan 的额度更适合持续使用不用每次担心 token 消耗。