3.9 反应式压缩
当 Prompt-Too-Long(PTL)错误发生时,反应式压缩作为最后手段触发:
// src/query.ts — PTL 恢复的第二阶段
// reactiveCompact 由 feature('REACTIVE_COMPACT') 门控 require,
// 实现位于 services/compact/reactiveCompact.ts(本源码快照中缺失)
if ((isWithheld413 || isWithheldMedia) && reactiveCompact) {
const compacted = await reactiveCompact.tryReactiveCompact({
hasAttempted: hasAttemptedReactiveCompact,
querySource,
aborted: toolUseContext.abortController.signal.aborted,
messages: messagesForQuery,
cacheSafeParams: {
systemPrompt, userContext, systemContext,
toolUseContext, forkContextMessages: messagesForQuery,
},
})
if (compacted) {
// 构建压缩后消息,继续循环
state.transition = { reason: 'reactive_compact_retry' }
}
}在正常运行中,autocompact 应该在上下文利用率达到阈值时主动触发(约 83%~90% 总窗口,取决于模型的 reservedTokensForSummary),防止 PTL 错误发生。反应式压缩只在以下情况下需要:
- Autocompact 被禁用或跳过
- 单次工具结果异常大,一步跳过了 autocompact 阈值
- Context Collapse 排水释放的 Token 不够
设计决策:压缩阈值是怎么确定的?
自动压缩的触发公式是
tokens >= effectiveContextWindow - AUTOCOMPACT_BUFFER_TOKENS,其中AUTOCOMPACT_BUFFER_TOKENS = 13,000(src/services/compact/autoCompact.ts)。effectiveContextWindow本身 =contextWindow - Math.min(getMaxOutputTokensForModel(model), 20_000),即扣除了压缩摘要的输出预留(MAX_OUTPUT_TOKENS_FOR_SUMMARY = 20,000,基于 p99.99 的压缩摘要输出为 17,387 tokens)。对于 200K 上下文窗口,阈值相对 effectiveWindow 约 92.8%(167K/180K),相对总窗口约 83.5%~89.5%(取决于reservedTokensForSummary是 20K 还是 8K)。13K buffer 确保触发压缩时还有足够空间完成当前工具执行和生成摘要。与此相关的还有WARNING_THRESHOLD_BUFFER_TOKENS = 20,000——警告阈值 = autocompact 阈值 − 20,000,即在自动压缩触发前 20K tokens 就开始向用户显示警告(autocompact 关闭时则相对effectiveWindow减 20K)。
设计决策:为什么 max_output_tokens 默认只用 8K 而不是 32K?
CAPPED_DEFAULT_MAX_TOKENS = 8,000(src/utils/context.ts)。源码注释解释了原因:"BQ p99 output = 4,911 tokens, so 32k/64k defaults over-reserve 8-16× slot capacity." API 服务端会根据max_output_tokens预留计算资源(slot),如果每个请求都声明 32K 但实际只用 5K,服务端的资源利用率极低。8K 作为默认值覆盖了 99% 的实际需求。当模型确实因为max_tokens截断时,系统自动升级到ESCALATED_MAX_TOKENS = 64,000并清洁重试——这就是 MOT(Max Output Tokens)恢复机制。