Local Compact 与 Remote Compact 产生摘要的路径不同,但最终都必须收敛到同一个 replacement checkpoint;差异集中在输入裁剪、输出过滤、重试和上下文注入位置。
本节解决的不是“把对象存一下”这种抽象问题,而是把问题限定在:对比两种 compactor 的历史构造与故障路径,强调共同安装边界。输入:当前 Context History、压缩原因/阶段、模型 Provider 能力与 InitialContextInjection;状态所有者:compact.rs / compact_remote.rs,最终由 Session 安装;成功结果:清洗后的 replacement history 和统一 CompactedHistoryMetadata。先把这些边界钉住,后面的顺序、失败和恢复才不会混成一句“持久化失败后重试”。
状态边界
| 问题 | 本节答案 |
|---|---|
| 输入 | 当前 Context History、压缩原因/阶段、模型 Provider 能力与 InitialContextInjection |
| 状态所有者 | compact.rs / compact_remote.rs,最终由 Session 安装 |
| 成功产物 | 清洗后的 replacement history 和统一 CompactedHistoryMetadata |
| 研究范围 | 对比两种 compactor 的历史构造与故障路径,强调共同安装边界 |
正常路径:先看顺序点
这条路径可以压缩成五步:
- 按 Provider 选择实现。
- 构造/发送压缩输入。
- 获得 summary 或 compacted transcript。
- 过滤并插入必要初始上下文。
- 调用同一 replace_compacted_history。
图中的箭头不是“可能调用”的依赖图,而是源码中决定可见性和所有权转移的先后关系。前一步没有确认时,后一步不能替它作出更强的成功承诺。
源码机制拆解
Local 路径把压缩当模型 Turn
它把 summarization prompt 加入 history,通过普通 Responses 流 drain 到 completed,从最后 assistant message 取摘要,再用保留的 user messages + SUMMARY_PREFIX 构造 replacement。
Local 对 context overflow 逐项裁剪
压缩请求自己也可能超窗。若输入多于一项,就从最旧项开始删除,同时 remove_first_item 会连带移除 Call/Output 配对;普通流错误按 provider retry/backoff。
Remote 路径接收结构化 transcript
supports_remote_compaction 的 Provider 调 compact endpoint,结果可包含多条 message。安装前丢弃 stale developer message、非真实 user wrapper、Reasoning/Tool Call 等不在 compact retention shape 的项。
Remote 可以按错误类型做 model fallback
自动压缩可携带 fallback StepContext;只有 should_retry_with_current_model 的错误才用当前模型再试,并记录 fallback telemetry。原始错误与 fallback 结果按实现规则选择返回。
两种路径共享 InitialContextInjection
Standalone/pre-turn 使用 DoNotInject,让下次正常 Turn 重建完整上下文;mid-turn 使用 BeforeLastUserMessage,把当前初始上下文插到最后真实 user/summary 之前,并建立 reference/world-state baseline。
Python 风格伪代码
下面的伪代码只保留设计职责、状态和失败顺序;它不逐行翻译 Rust,也不借 Python 语法虚构源码中不存在的事务:
async def compact(session, step, mode, injection):
await run_pre_compact_hooks()
try:
if mode == LOCAL:
working = session.history.clone()
while True:
try:
summary = await responses_summarize(working)
replacement = build_local_history(user_messages(working), summary)
break
except ContextWindowExceeded:
working.remove_oldest_with_call_pair()
else:
transcript = await remote_compact_endpoint(prompt_history(session))
replacement = [i for i in transcript if keep_remote_compacted_item(i)]
initial, world = await build_compaction_initial_context(injection)
replacement = insert_before_last_real_user_or_summary(replacement, initial)
reference = step.turn_context if injection.is_mid_turn else None
await session.install_checkpoint(replacement, reference, world,
session.advance_window())
finally:
await run_post_compact_hooks_if_success()
阅读时要特别看三处:哪个对象拥有可变状态,哪一个 await 是可观察屏障,以及失败后保留的是已提交前缀、未提交后缀,还是完全独立的外部副作用。
失败、取消与恢复
| 故障点 | 已留下的状态 | 可观察结果 | 恢复责任 |
|---|---|---|---|
| Local 压缩输入仍超窗 | 尚未生成 replacement | 删除最旧完整 item/pair 后重试 | 只剩一项仍超窗则失败 |
| Remote 返回旧 developer 指令 | 可能重复或过期 | 过滤掉 | 重新注入当前 canonical context |
| Remote 主模型可回退错误 | 首 attempt 失败 | 用 fallback StepContext 重试 | 记录模型切换结果 |
| Pre/Post Hook 阻断 | checkpoint 尚未或已经生成边界不同 | 返回 TurnAborted | 只有成功安装后运行 post hook |
这里没有统一的“回滚一切”。内存状态、日志行、SQLite 投影、父子拓扑和工具造成的文件/网络变化分别有自己的提交点。恢复代码只能根据已经存在的权威证据继续,不能用较弱的投影替较强的事实背书。
必须保持的不变量
- 两种实现最终都调用统一 checkpoint installer
- Remote 输出不能成为旧 developer 指令权威
- Mid-turn replacement 中初始上下文位于最后真实用户消息之前
- 压缩失败不替换当前 history
这些不变量比“最终能 Resume”更严格:正常路径要成立,Writer 竞争、任务取消、坏尾行、投影落后和旧格式兼容时也必须成立。
设计取舍
Local 路径可在普通 Responses 能力上工作、逻辑可控;Remote 路径可由专用模型优化压缩质量。统一 checkpoint 格式隔离了生成策略差异。
源码可以直接证明字段、分支、调用顺序和测试期望;“为什么这样设计”的表述是基于这些事实作出的工程归纳,不把它包装成未公开的产品承诺。
Mini Codex 复刻
定义 Compactor Protocol 返回 candidate history;Local/Remote adapter 各自清洗,CheckpointInstaller 负责所有持久化和 baseline 不变量。
复刻时先验证协议不变量,再补性能优化。一个能在故障注入下说明“留下了什么”的小实现,比一个只在正常路径调用 save() 的演示更接近真实 Runtime。
源码导航
- codex-rs/core/src/compact.rs:Local summarization、裁剪、initial context 与 checkpoint
- codex-rs/core/src/compact_remote.rs:Remote 调用、过滤、fallback 和统一安装
- codex-rs/core/src/compact_remote_v2.rs:新版 remote retention shape 与 trace checkpoint
相邻测试也很重要:
- codex-rs/core/src/compact.rs:模块内 history 构造、插入位置和截断测试
- codex-rs/core/src/compact_remote_v2.rs:remote 输出过滤、截断和图片保留计数
- codex-rs/core/src/client_tests.rs:compact conversation 请求路径
本节结论
Local Compact 与 Remote Compact 产生摘要的路径不同,但最终都必须收敛到同一个 replacement checkpoint;差异集中在输入裁剪、输出过滤、重试和上下文注入位置。
评论
登录后即可评论