# 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:159turn.rs:375

Claude Code 的压缩路径会记录 cache read/create/input token,并尝试复用主对话的 system/tools/context 前缀;压缩恢复 Skill 时另设独立 Token Budget:compact.ts:1154compact.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:159compact.rs:397token_budget_context.rs:16。因此预算器应同时读取模型能力、历史估算和 Provider 返回的真实用量,并用安全余量吸收分词和多模态估算误差。

分类预算也应有“硬保留”和“可竞争”之分:系统策略、当前用户目标和未完成调用配对不可淘汰;代码片段、Memory、Skill 和旧 ToolResult 在各自上限内按边际价值竞争。稳定前缀不是为了好看,而是降低重复 prefill 和 cache write;动态世界状态应追加 diff,只有基线丢失或压缩恢复时才全量重注入。

最后更新: 2026/8/5 22:05:21