# 10. 上下文窗口
# 10.1 原理
上下文窗口包含输入和模型将要生成的输出。真正可用的输入预算为:
input_budget = model_window // 从模型声明的总上下文窗口开始计算
- reserved_output_tokens // 预留模型生成最终答案和 ToolCall 的输出空间
- system/developer instructions // 扣除必须始终存在的高优先级指令
- tool schemas // 扣除当前 Step 可见工具定义占用的 Token
- safety margin // 预留分词误差、动态附件和提供方差异的安全余量
还要考虑 Prompt Cache:缓存优化希望前缀稳定;上下文优化希望内容相关。工程上应把稳定内容放前面,把频繁变化的 world state、用户输入和工具结果放后面;避免无意义地重排工具列表或系统段落导致 cache miss。
窗口管理要有硬上限:每种注入片段、单个工具输出、图片/音频、Skill、Memory 都应有独立预算。只设置总 Token 限制会让一个异常资源挤掉全部任务上下文。
# 10.2 源码细节
Codex 在 Turn 开始、调用模型之前预先检查是否需要压缩(pre-sampling compact);源码注释明确指出,还应估算即将注入的 Context 变化和用户输入。采样后,系统同时计算当前有效输入、自动压缩阈值范围、预填充 Token 和完整窗口上限:turn.rs:159、turn.rs:375。
Claude Code 的压缩路径会记录 cache read/create/input token,并尝试复用主对话的 system/tools/context 前缀;压缩恢复 Skill 时另设独立 Token Budget:compact.ts:1154、compact.ts:1497。
# 10.3 预算伪代码
function allocateContext(model, candidates): // 为当前模型 Step 分配各类上下文预算
total = model.contextWindow - reserveOutput - safetyMargin // 计算扣除输出和安全余量后的可用输入上限
budgets = { // 为不同来源设置独立上限,防止单类内容挤占窗口
policy: fixedAndMandatory, // 系统和开发者策略固定保留且不可被候选淘汰
toolSchemas: capped(15%), // 工具 Schema 最多使用总输入预算的约 15%
recentConversation: capped(30%), // 近期原始对话保留细节但设置约 30% 上限
taskState: capped(15%), // 计划、任务和状态投影占用约 15% 预算
retrievedCode: capped(25%), // 与当前步骤最相关的代码最多占约 25%
memoryAndSkills: capped(15%) // 记忆与 Skill 共享约 15%,避免知识包膨胀
} // 结束分类预算定义
selected = priorityKnapsack(candidates, budgets, // 在分类约束下选择总价值最高的候选组合
value = relevance * authority * freshness / tokenCost) // 用相关性、权威、新鲜度和 Token 成本计算价值
return stablePrefixOrder(selected) // 按稳定前缀顺序输出,提升 Prompt Cache 命中率
# 10.4 面试题
问:模型窗口很大,为什么仍然要压缩?
大窗口仍有成本、延迟、注意力稀释和 cache miss 问题;历史中的错误结论和重复日志会降低决策质量。窗口变大只是提高上限,不能替代 Context Engineering。
# 10.5 原理深化:Context Window 需要多层预算记录
窗口管理至少要区分四个数:模型声明的 context_window、本次请求可用的 input_limit、必须预留的 output_limit、当前活跃历史的 active_context_tokens。此外还有跨多次压缩窗口累计的 Session/rollout budget,用来防止 Agent 虽未单次超窗却无限消耗成本。把这些数合成一个 remaining_tokens 会导致错误决策:例如 cache-read Token 影响计费和指标,但不一定等价于新写入上下文的物理占用。
源码也表明预算不是一次性静态分配。Codex 在 Turn 前预检查是否需要压缩,在采样后根据实际用量更新 Token 统计;压缩事件记录压缩前后的有效输入量以及缓存读写量,并通过独立的 window_id 关联多次压缩:turn.rs:159、compact.rs:397、token_budget_context.rs:16。因此预算器应同时读取模型能力、历史估算和 Provider 返回的真实用量,并用安全余量吸收分词和多模态估算误差。
分类预算也应有“硬保留”和“可竞争”之分:系统策略、当前用户目标和未完成调用配对不可淘汰;代码片段、Memory、Skill 和旧 ToolResult 在各自上限内按边际价值竞争。稳定前缀不是为了好看,而是降低重复 prefill 和 cache write;动态世界状态应追加 diff,只有基线丢失或压缩恢复时才全量重注入。