Skip to main content

上下文窗口:200K 不是全部

Claude Code 的默认上下文窗口为 200K tokens(MODEL_CONTEXT_WINDOW_DEFAULT = 200_000),但实际可用于对话的空间远小于此:
getContextWindowForModel()src/utils/context.ts:51)按 5 级优先级解析窗口大小:
  1. CLAUDE_CODE_MAX_CONTEXT_TOKENS 环境变量覆盖
  2. 模型名含 [1m] 后缀 → 1M tokens
  3. getModelCapability(model).max_input_tokens
  4. 1M beta header + 支持的模型(claude-sonnet-4, opus-4-6)
  5. 兜底:200K
有效上下文 = 窗口大小 - min(maxOutputTokens, 20K),因为压缩摘要需要预留输出空间。

Token 计数:近似 vs 精确

系统使用两级 token 计数策略:

近似估算(毫秒级)

对不同内容类型有特殊处理:
  • JSON/JSONLbytesPerToken = 2(密集的 {, :, , 符号,每个仅 1-2 token)
  • 图片/文档:固定 2000 tokens(基于 2000×2000px 上限的保守估计)
  • thinking block:按实际文本长度 / 4
  • tool_use:序列化 name + JSON.stringify(input) 后 / 4

精确计数(API 调用)

使用 Anthropic 的 beta.messages.countTokens 端点。在不同 provider 上有不同路径: 精确计数在关键决策点使用(压缩前后对比、warning 判断),近似估算在热路径使用(每轮循环的 shouldAutoCompact 检查)。

3P Provider 的 Token 计数差异

不同 Provider 的精确 token 计数实现方式不同,部分 provider 甚至不支持精确计数: OpenAI 和 Gemini 兼容层不支持精确 token 计数,系统会退回到近似估算。这会影响:
  • 自动压缩触发时机:可能略有偏差
  • 压缩前后 token 对比:仅为估算值,非精确
  • Warning/Error 阈值判断:基于估算而非精确计数
源码路径:src/services/tokenEstimation.ts

自动压缩的触发阈值

以 200K 窗口为例:
  • ~167K:warning 闪烁,用户看到建议压缩的提示
  • ~180K:自动压缩触发(200K - 20K 输出预留 = 180K 有效,再 - 13K buffer)
  • ~197K:达到 blocking limit,新消息被阻止
shouldAutoCompact() 有多个逃逸条件:
  • compact / session_memory 来源的查询永不触发(防递归死锁)
  • DISABLE_COMPACT / DISABLE_AUTO_COMPACT 环境变量
  • 用户配置 autoCompactEnabled = false
  • Context Collapse 模式激活时抑制(collapse 自己管理上下文)
  • Reactive Compact 实验模式下抑制主动压缩
  • 超过连续失败上限(circuit breaker)

Micro-Compact:工具结果的渐进式压缩

在触发全量压缩之前,系统先尝试 micro-compact——只压缩旧的工具调用结果:
策略基于时间:
  • 超过一定时间(由 timeBasedMCConfig 控制)的工具结果被替换为简短占位符
  • 图片/文档结果替换为 [image] / [document] 文本
  • 每次替换释放 tokens,可能推迟全量压缩
工具本身也有 maxResultSizeChars(通常 100K)硬限制,超长结果在写入消息前就被截断。

全量压缩的完整流程

Prompt Cache Sharing

压缩 API 调用是整个会话中最昂贵的操作之一。系统通过 runForkedAgent 复用主线程的缓存前缀(system prompt + tools + context messages),将缓存命中率从 2% 提升到接近 100%。这个优化单独节省了舰队级约 0.76% 的 cache_creation tokens。

输出 Token 的 Slot 优化

一个经常被忽视的优化:maxOutputTokens 的动态调整
为什么?因为 API 的 slot 机制按 max_tokens 预留推理容量。BQ p99 输出仅 4,911 tokens,32K 默认值浪费了 8-16 倍的 slot 容量。降到 8K 后,不到 1% 的请求被截断——这些请求会自动获得一次 64K 的 clean retry。 这个优化对 token 预算的影响是间接的:更多的 slot 容量意味着更少的排队延迟,间接减少了超时和重试。

Partial Compact:选择性地压缩

除了全量压缩,用户还可以在消息历史中选择某个位置,只压缩该位置之前或之后的内容:
  • up_to 方向:压缩选中消息之前的内容,保留最近的对话
  • from 方向:压缩选中消息之后的内容,保留早期的对话
from 方向保留 prompt cache(前缀不变),up_to 方向则破坏 cache(摘要插在保留内容之前)。 两种方向的 PTL(prompt-too-long)重试策略相同:从最老的 API 轮次开始删除,确保至少保留一组消息供摘要。