Codex 的上下文压缩策略
Codex 的上下文压缩策略
学习目标
- 理解 Codex 的三种上下文溢出处理策略
- 掌握 Codex 基于 user turn boundary 的截断算法
- 分析 Codex 的本地压缩和远程压缩实现
前置知识
本章涉及上下文压缩的通用原理,建议先阅读:
下文假设你已理解上述概念,直接聚焦 Codex 的具体实现。
项目实践
上下文管理架构
Codex 的上下文管理分布在多个模块:
core/src/compact.rs:本地压缩core/src/compact_remote.rs/compact_remote_v2.rs:远程压缩core/src/thread_rollout_truncation.rs:截断message-history/src/lib.rs:消息历史
基于 user turn boundary 的截断
thread_rollout_truncation.rs 实现了智能截断:
// 保留前 N 个用户轮次之前的内容fn truncate_rollout_before_nth_user_message_from_start( rollout: &mut Rollout, n: usize,);
// 保留最后 N 个 fork turnsfn truncate_rollout_to_last_n_fork_turns( rollout: &mut Rollout, n: usize,);截断原则:
- 以
user turn为最小单位,不拆分单个回合 - 保留系统提示和最近的完整回合
- 支持多分支对话的 fork turn 保留
动态调整:ThreadRolledBack 事件会动态调整 user turn 索引,确保截断逻辑与当前对话状态一致。
本地压缩
core/src/compact.rs 实现本地压缩:
压缩流程:
- 将完整历史发送给模型,请求生成摘要
- 用
CompactedItem替换历史:保留所有用户消息 + 摘要前缀 - 发送
ContextCompactedEvent通知客户端
关键设计:
InitialContextInjection::BeforeLastUserMessage:确保 mid-turn 压缩后初始上下文不丢失SUMMARY_PREFIX标记摘要内容,便于模型识别
远程压缩
core/src/compact_remote.rs 和 compact_remote_v2.rs 实现远程压缩:
- 将压缩任务 offload 到 OpenAI 服务端
- 客户端无需实现压缩逻辑
- 支持版本迭代(v1 → v2)
消息历史管理
message-history/src/lib.rs 管理用户输入历史:
存储:~/.codex/history.jsonl
{"session_id":"uuid","ts":1234567890,"text":"给这个项目添加 README"}{"session_id":"uuid","ts":1234567891,"text":"运行测试"}截断策略:
- 硬上限:
max_bytes(配置项) - 软上限:
0.8 * max_bytes - 从头部(最旧)删除,保留尾部(最新)
并发安全:
- advisory file lock 保护读写
- 单行写入使用单
write(2)+O_APPEND - 依赖 POSIX
PIPE_BUF原子性
问题与规避
压缩导致的信息丢失
Codex 的策略:
- 保留所有用户消息(用户消息通常包含关键指令)
- 对工具结果选择性保留(保留文件路径但截断内容)
- 摘要前缀明确标记,模型可区分原始内容和摘要
频繁压缩的性能开销
Codex 的缓解措施:
- 软上限机制:在 80% 处触发预警,减少压缩频率
- 远程压缩优先:如果提供商支持,使用服务端压缩减少客户端开销
设计取舍
为什么同时支持本地和远程压缩?
Codex 提供双路径压缩:
- 远程压缩:优先使用(如果提供商支持),质量高、客户端无负担
- 本地压缩:通用回退,所有模型都可用
代价是维护两套压缩代码,增加了复杂度。
为什么消息历史与对话上下文分离?
Codex 将用户输入历史(message-history)与对话上下文(rollout)分开存储:
- 历史用于命令行补全和输入建议,跨 Session 可用
- 上下文用于模型推理,包含完整的工具结果
这种分离使得历史管理更简单(纯追加 JSONL),而上下文管理更复杂(结构化、可压缩)。