乐于分享
好东西不私藏

Claude Code源码解构:压缩提示工程(一)

Claude Code源码解构:压缩提示工程(一)

Claude Code源码解构:压缩提示工程(一):上下文经济学与令牌预算

摘要:借着泄露的 Claude Code 代码,深度解读其”上下文经济学”的核心实现。本文将揭示 Claude Code 如何通过自动压缩流水线,在有限的上下文窗口里制造出”无限上下文”的错觉。

引言:上下文经济学的诞生

在这里插入图片描述

我在 prompts.ts 文件的 function getSimpleSystemSection() 中看到了这样一段描述:

当对话接近上下文限制时,系统会自动压缩对话中之前的消息。这意味着你与用户的对话不受上下文窗口的限制。

这句话背后的工程实现非常有意思。写过 AI Agent 的人都知道,”Token 溢出”和”模型失忆”是实际开发中的死穴。当你让 AI 处理一个跨越几十个文件、几千行代码的复杂重构时,对话历史会像吹气球一样膨胀。以前我们只能粗暴地截断历史,但这会导致 AI 瞬间变傻。

Claude Code 换了个思路:它把上下文当成一种昂贵的”运行时预算”,通过一套自动压缩(Compact)流水线,在有限的窗口里制造出一种”无限上下文”的错觉。

这篇文章旨在通过源码深度解读,正面回答以下几个核心问题:

  • • 如何界定上下文限制?(触发机制是什么)
  • • 系统如何自动压缩?(具体的脱水策略)
  • • 为什么做了自动压缩就可以不受上下文窗口的限制了?(状态重注入的逻辑)

一、痛点所在:Token 的失控与”噪音”污染

在真实重构场景下,Token 消耗的速度快得惊人。这主要来自三个方面:

  • • 无节制的大海捞针:一次 grep 或 find 吐出来的搜索结果可能有好几万 Token
  • • 反复摩擦的迭代:改代码 → 报错 → 再改 → 再报错,每一轮都在往上下文里塞垃圾
  • • 冗余的中间态:你可能读取了 20 个文件,但真正决定逻辑的只有那两三行

如果不治理,模型很快就会因为”噪音”太多开始胡言乱语,或者直接报 prompt_too_long 挂掉。

“这块本质上不只是 prompt 细节,它更像:Anthropic 对 AI 工程师行为规范的制度化表达。”

1.1 代码实现逻辑

参考源码中的 compact.ts,其核心步骤可以简化为以下伪代码:

/** * 揭秘 Claude Code 的核心压缩逻辑 * 源码位置:src/services/compact/compact.ts:L387 */export async function compactConversation(messages: Message[]) {  // 1. 监控触发与预留安全缓冲  // 核心参数:AUTOCOMPACT_BUFFER_TOKENS = 13,000 (约 13k Token)  // 当 contextUsage >= (effectiveWindow - 13,000) 时强制触发  const currentTokens = tokenCountWithEstimation(messages);  if (currentTokens < getAutoCompactThreshold(model)) return messages;  // 2. 预处理 (Context Collapse)  const cleanMessages = stripImagesFromMessages(messages);  // 3. 生成"深度摘要" (The Heart of Compact)  const summaryResponse = await streamCompactSummary(cleanMessages);  const summary = getAssistantMessageText(summaryResponse);  // 4. 状态保留 (State Re-injection) - 核心精髓!  // 压缩并非简单的"删除",而是"有选择的遗忘"  const preservedState = [    await createPostCompactFileAttachments(), // 重新加载最近读取的 5 个文件内容    await createPlanModeAttachmentIfNeeded(), // 恢复 /plan 任务树的执行状态    await createSkillAttachmentIfNeeded()     // 重新激活已加载的 Skill 规则  ];  // 5. 标记边界  return [    createCompactBoundaryMessage(),    createUserMessage({ content: getCompactUserSummaryMessage(summary) }),    ...preservedState  ];}

字段深度解读:

getAutoCompactThreshold:安全缓冲通过 AUTOCOMPACT_BUFFER_TOKENS 设置为 13,000 Token。这个缓冲至关重要:它预留了足够的空间让模型能够生成完整的摘要(摘要本身可能长达数千 Token),并处理压缩过程中的系统指令,避免因空间不足导致压缩任务本身崩溃。

二、令牌预算追踪

令牌管理不仅是被动的压缩触发,还包含主动的预算规划和预警系统。

2.1 多级警告状态

calculateTokenWarningState 函数计算当前令牌使用状态,返回多个布尔标志:

标志
触发条件
UI 行为
isAboveWarningThreshold
令牌用量 >= 阈值 – 20,000
显示黄色警告
isAboveErrorThreshold
令牌用量 >= 阈值 – 20,000
显示红色警告
isAboveAutoCompactThreshold
令牌用量 >= 自动压缩阈值
触发自动压缩
isAtBlockingLimit
令牌用量 >= 有效窗口 – 3,000
阻止新请求

这些标志驱动 UI 层面的警告展示和压缩行为的触发。percentLeft 字段向用户展示剩余空间的百分比。

2.2 压缩后的令牌预算

压缩完成后,系统并非简单地释放所有空间。压缩模块定义了严格的令牌预算常量:

常量
用途
POST_COMPACT_MAX_FILES_TO_RESTORE
5
最多恢复的文件数量
POST_COMPACT_TOKEN_BUDGET
50,000
总令牌预算上限
POST_COMPACT_MAX_TOKENS_PER_FILE
5,000
每个文件的令牌上限
POST_COMPACT_MAX_TOKENS_PER_SKILL
5,000
每个技能的令牌上限
POST_COMPACT_SKILLS_TOKEN_BUDGET
25,000
技能独立预算

这些预算限制了压缩后重新注入上下文的内容量,确保了压缩不会因为过度注入附件而立即再次触发压缩。

反模式警告: 常见的错误是在压缩后立即重新加载所有之前读取的文件。这样做会迅速耗尽令牌预算,导致在几轮对话后再次触发压缩,形成”压缩 – 膨胀 – 再压缩”的恶性循环。正确做法是只重新加载当前任务需要的文件。


下一篇(二):深度摘要的防幻觉策略 + 状态保留与场景复现


参考文献:

  1. 1. Claude Code源码:compact.ts
  2. 2. Anthropic Claude API Documentation