雨天小六

读懂 Codex(5.23):Context Length 错误与 Compact/Fallback

· 更新于 2026-08-02 · 专栏:读懂 Codex

#Codex#Agent Runtime#Responses API#流式协议#软件架构

服务端返回 ContextWindowExceeded 时,Codex 不在同一失败请求上盲目重试,也不会立即把半成品历史就地压缩后续跑。它先把 token 状态标记为已满并结束当前 Turn;下一次采样前的压缩检查才使用完整压缩流程。

具体问题与启用条件

本节区分主动压缩、服务端超窗错误和 previous-model compaction 的模型 fallback。压缩算法本身已在第四章 4.22—4.24 说明。

条件来源决定字段或状态对本机制的影响
主动阈值auto compact limit/full context window采样前或需要 follow-up 时压缩
服务端错误context_length_exceeded映射 ContextWindowExceeded
模型切换压缩comp_hash 变化或窗口缩小可先用 previous model 压缩

协议、类型与状态所有权

状态或协议所有者生命周期关键不变量
token usage/full markerSession state跨 Turn服务端超窗后强制视为已满
compaction decisionrun_turn/run_pre_sampling_compact采样边界不在传输重试函数内
fallback StepContextprevious-model compaction一次压缩尝试失败时可用当前模型重试压缩

机制调用链如下:

正常路径:token status 达阈值
→ run_auto_compact(local/remote/token-budget)
→ 安装 replacement history
→ 下一采样

错误路径:response.failed context_length_exceeded
→ CodexErr::ContextWindowExceeded
→ set_total_tokens_full
→ 不可 retry → Error/结束当前 Turn
→ 后续 Turn pre-sampling token check
→ run_auto_compact
服务端上下文超限到后续压缩的恢复流程
图 5.23-1:失败请求不盲重发;full 标记把后续入口导向统一压缩。

机制怎样工作

Context error在 SSE mapper成为专用 ApiError,再映射为不可重试 CodexErr。run_sampling_request 捕获后调用 set_total_tokens_full 并立刻返回;外层 Turn 发错误事件后允许用户继续会话。这样传输重试不会反复发送同一过长请求。下一 Turn 的 run_pre_sampling_compact 读取 token status,发现已达限制后捕获新的 StepContext 并运行统一压缩。

正常情况下压缩更早发生:Turn 前检查阈值,同一 Turn 某次 Completed 后若仍需 follow-up 且达到限制,则在下一采样前做 mid-turn compact。模型切换还有另一种 fallback:若需要用 previous model 做兼容压缩,且该尝试出现 InvalidRequest、ContextWindow、Usage、Overloaded、Internal、RetryLimit 等模型相关错误,可用当前模型的 fallback StepContext 重试压缩。这个“模型 fallback”与 WebSocket→HTTP 传输 fallback 完全不同。

Python 风格伪代码

这段伪代码保留生产实现中会改变结果的状态、分支和异步边界;认证 SDK、遥测字段和 Rust 所有权样板被折叠为明确的领域对象。

async def sample_once_or_mark_full(turn: TurnContext):
    try:
        return await run_sampling_request(turn)
    except ContextWindowExceeded:
        await session.tokens.mark_full(turn.model.context_window)
        raise  # 当前 Turn 结束,不做同请求 retry

async def before_sampling(turn: TurnContext, client: ModelTurnSession):
    status = await session.context_window_status(turn)
    if status.token_limit_reached:
        step = await session.capture_step_context(turn, turn.cancel)
        await run_auto_compact(step, client, phase="pre_turn")

async def compact_previous_model(previous_step, current_step):
    try:
        await compact(previous_step)
    except CodexError as error:
        if current_step is None or not model_specific_compaction_error(error):
            raise
        await compact(current_step)  # 模型 fallback,不是传输 fallback

失败、取消与恢复

模型切换时压缩模型 fallback 的边界
图 5.23-2:Fallback 更换的是压缩模型,不是 Responses 的网络传输。
故障或边界已发生的状态对上层的结果能否直接重试恢复动作
服务端 context error请求未完成,token 标记 full当前 Turn Error后续 Turn 先 compact
主动 compact 失败旧 History 仍是权威压缩错误/Turn 结束依错误不安装半成品历史
previous-model compact 失败且可回退尚未替换 History当前模型再压缩一次一次模型 fallback记录 telemetry
压缩后仍过长replacement history 可能仍超限再次错误不盲重试新线程或更强裁剪

设计取舍与验证

不在 context error 后自动原地压缩并重跑当前 Turn,牺牲一次无感恢复机会,却避免对已产生部分 Done Item、工具副作用和动态 StepContext 做隐式重写。主动阈值负责大多数正常场景;错误标记保证下一次入口会走受测试的压缩流程。

可验证契约证据方式预期结果
context_length_exceeded 不走普通 retry错误分类与请求计数同 Turn 只发一次过长采样
token full 触发下一入口压缩构造满窗口 Session采样前出现 compact
previous model 失败可用 current model fallbackcompact model fallback 测试第二模型尝试并记录 outcome

Mini Codex 对照

Mini Codex 应把 ContextWindowExceeded 从 RetryPolicy 排除,并让压缩属于 Agent Loop 而非 HTTP client。可选择自动在同一 Turn 重试,但那是不同产品策略,必须先定义已完成 Item 与工具副作用怎样处理。

本节边界

本节只解释触发与恢复时机,不重复压缩内容算法。5.24 讨论另一个常被误写的边界:Output Schema 约束由谁验证。

评论


← 返回文章列表