雨天小六

读懂 Codex(5.12):HTTP/SSE 回退触发条件

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

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

WebSocket 是性能路径,不是完成任务的前提。握手返回 426 会立即回退;普通可重试流错误耗尽预算后也会回退。回退标志属于 ModelClient Session,后续 Turn 都直接使用 HTTP SSE。

具体问题与启用条件

本节只说明传输降级,不把 Provider 错误、模型错误和重试等待混在一起;完整错误分类见 5.21—5.23。

条件来源决定字段或状态对本机制的影响
初始条件Provider 支持 WS 且 fallback 未激活优先尝试 WS
直接信号HTTP 426 Upgrade Required不消耗流重试预算,立即 HTTP
累计信号retryable error 且 retries >= stream_max_retries激活 Session 级回退

协议、类型与状态所有权

状态或协议所有者生命周期关键不变量
disable_websockets AtomicBoolModelClientState整个 Session首次激活后保持 true
retry counterrun_sampling_request一次逻辑采样/传输阶段回退后重置为 0
WebsocketSessionModelClientSessionTurn/可转移回退时整体重置

机制调用链如下:

优先 WS
→ 426:FallbackToHttp
→ 或 retryable stream error
→ 在预算内退避重试 WS
→ 预算耗尽 try_switch_fallback_transport
→ 原子禁用 WS + 清连接/previous 基线
→ 重置 retries
→ 同一 Prompt 改走 HTTP SSE
WebSocket 从直接 426 或重试耗尽回退 SSE
图 5.12-1:426 跳过无效重试,其他暂态错误先使用 Provider 预算。

机制怎样工作

握手 426 表示端点不接受当前 WebSocket 升级,客户端立即返回 FallbackToHttp,不再做注定相同的 WS 重试。其余可重试错误先由外层循环按 stream_max_retries 处理;达到上限时,try_switch_fallback_transport 调用 Session 级 force_http_fallback,并用空 WebsocketSession 替换当前状态。

回退后,采样循环将重试计数置零并返回继续,下一次 stream()responses_websocket_enabled() 为 false 直接建立 SSE。这个原子标志位于共享 ModelClientState,因而新 Turn 也继承降级决定。回退时发 Warning;release 构建通常隐藏第一次瞬态 WS retry 的 StreamError,减少无意义闪烁,但不会隐藏真正的回退通知。

Python 风格伪代码

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

async def open_model_stream(client: ModelTurnSession, prompt: Prompt):
    if client.parent.websocket_enabled:
        try:
            return await client.open_websocket_stream(prompt)
        except UpgradeRequired:
            client.activate_http_fallback()
    return await client.open_sse_stream(prompt)

async def retry_or_fallback(state: RetryState, error: Exception):
    if state.retries >= state.max_retries and state.client.can_activate_fallback():
        await state.ui.warning(f"Falling back to HTTPS: {error}")
        state.client.activate_http_fallback()  # 清 WS 与 previous state
        state.retries = 0
        return
    if state.retries < state.max_retries:
        state.retries += 1
        await asyncio.sleep(error.retry_after or jittered_backoff(state.retries))
        return
    raise error

失败、取消与恢复

HTTP 回退在后续 Turn 中保持
图 5.12-2:新 Turn 重置 turn_state,但不会重置 Session 级传输回退标志。
故障或边界已发生的状态对上层的结果能否直接重试恢复动作
426 握手拒绝未发送模型请求立即 HTTP不重试 WS激活粘滞回退
WS 可重试错误未耗尽连接可能已重置仍在 WS 路径退避后重连/重发
WS 重试耗尽重试预算用完Warning + HTTP按 HTTP 新预算清 previous 基线
HTTP 也失败fallback 已保持返回分类错误依 HTTP 错误不会自动恢复 WS

设计取舍与验证

Session 级粘滞回退避免每个 Turn 重复支付失败握手与重试成本,代价是网络条件恢复后本 Session 不会自动升级回 WS。更激进的探测恢复需要冷却计时、并发探针和重复请求保护,当前实现选择稳定完成任务。

可验证契约证据方式预期结果
426 只尝试一次 WS启动预热 + 首 Turn 请求计数一次 GET、一次 HTTP POST
预算耗尽后同请求转 HTTP故意让 WS 失败初次+N 次 WS 后一个 POST
第二 Turn 不再试 WS连续两个 TurnWS 次数只来自第一 Turn

Mini Codex 对照

Mini Codex 应让 fallback flag 属于长期客户端,并在降级时清除连接、last request 和 last response。可以不隐藏首个重试通知,但不能在每个 Turn 重置回退。

本节边界

传输层现在能稳定交付统一事件。5.13 开始进入事件语义:Created、Item、Delta 与 Completed 怎样组成一次响应。

评论


← 返回文章列表