← 返回信息流
Agent SkillLINUX DO · AI·57 分钟前

Codex首包卡死问题调查与解法

原标题:【拯救 5.6 Sol(1.5)】codex首包卡死问题调查和解法

速览

本文分析了Codex模型在HTTP/SSE模式下因响应头阻塞导致首包卡死的问题,指出OpenAI服务器可能延迟发送200状态码,并给出了四种优先级解法:推荐使用OAuth登录或WebSocket模式;其次通过配置websockets和first-event-timeout等参数实现超时重连。文章还解释了两种请求模式的差异,以及如何通过二次开发修改代理解决。

AI 深度解读

背景

近期不少 Codex 用户遇到一个棘手问题:模型在 "thinking" 阶段卡死,没有任何报错,界面看起来完全失去响应。使用某些反向代理(如 5.6 版本)时,模型只要执行完工具调用就会卡住;手动停止再继续则可以正常。这一问题严重影响了工作流效率,社区在 LINUX DO 上展开了深入调查,最终定位到 OpenAI API 的 HTTP/SSE 请求模式中的 "首包卡死" 现象,并给出了多种解法。

核心内容

问题现象

  • Codex 卡死在 "thinking" 状态,无报错,界面完全冻结。
  • 使用特定反代版本(如 5.6)时,模型执行完工具调用后卡住,但停止并重新继续对话即可恢复。
  • 本质是 OpenAI 收到请求后迟迟不返回任何响应数据。

请求模式对比

HTTP/SSE 模式(逐请求)
每一次模型调用都是一次全新的独立请求:重新建立 TCP 和 TLS 连接,把整段上下文发送给 OpenAI,等待响应头(首包),然后以 SSE 流式逐事件返回。下一次调用重复全部流程。

关键步骤(以 CPA 为例):

  1. TCP 三次握手
  2. TLS 握手(uTLS 伪装 Chrome 指纹)
  3. 发送 HTTP POST 请求(含完整上下文,可达 ~90K token)
  4. 等待响应头 —— 卡死点:OpenAI 已受理请求但后端迟迟不开始产出字节,健康请求数百毫秒,卡死可能持续几十分钟甚至永久不返回。
  5. 响应头到达,http.Do() 返回。
  6. SSE 流式返回事件(首包一到后续正常)。

WebSocket 模式(一段会话复用一条连接)
会话开始时只握手一次(TCP + TLS + HTTP Upgrade 为 WebSocket),连接升级为长期存活的双向管道。此后每一轮对话都只是往已有管道里发送轻量帧,应答也以帧返回。不再有 "新建 HTTP 请求 → 等响应头" 这一步骤。

卡死原因深入分析

以 CPA 为例,在 codex_executor.go:1121 处调用 httpClient.Do(httpReq),该调用发出请求并阻塞直到响应头到达。关键问题:这个 HTTP 客户端在等待响应头时没有任何超时设定

  • helps.NewUtlsHTTPClient(ctx, cfg, auth, 0) 构造时 timeout=0,未设置 client.Timeout
  • transport 层也未设 ResponseHeaderTimeout

因此 Do() 可在第④步无限期阻塞,上游卡多久就等多久。

日志跟踪发现两种可能形态:

  • header-stall(首包/响应头卡死):连 200 响应头都迟迟不发,卡在第④步。逐帧日志显示选定上游后有 8.5 分钟空白才进入读流阶段,证明卡在 http.Do 内部。
  • body-stall(响应体卡死):200 头秒到,SSE 流开了却卡在首个内容事件。但健康请求中 response.created 亚秒即到,无证据支持此为常见问题。

最终结论:开源在于 OpenAI 服务端收到请求后不干事,死活不发响应头

解法方案(按优先级)

  1. OAuth 登录(几乎一定能解决):直接使用 OpenAI 官方 OAuth 认证绕过反代层问题。
  2. 配置允许 WebSocket:若无法 OAuth,在 CPA 或 sub2api 中配置允许 WebSocket 连接。在 config.toml 的相应 provider 下添加 supports_websockets = true(几乎一定能解决)。
  3. new-api + CPA:new-api 至今不支持 WebSocket,需自行合并 PR #5062,然后开启 CPA 配置允许 WebSocket,并在 config.toml 中配置上述字段(几乎一定能解决)。
  4. 超时重连逻辑(不开 WebSocket 兜底):反代客户端需要实现「首包超时 → 同号重连」逻辑。具体方法:给第④步套一个预算 first-event-timeout-seconds(设为 6 秒),将 ExecuteStream 丢入 goroutine,超时则丢弃卡住请求,用同一账号重新发起(first-event-retries 设为 4)。健康请求首字节 1~3 秒到,6 秒充足;卡死则零字节。实测多数一次或两次重试即可恢复。此为不得已之方案,需二开 CPA 或 sub2api。

WebSocket 为何能绕过首包卡死

WebSocket 的本质是只握手一次,将 HTTP 连接升级为持久双向管道。在 HTTP/SSE 中,每一轮对话都要经历一次 "等响应头"(第④步),而 WebSocket 仅在会话开始时有一次握手(返回 101 很快),之后每轮对话只是帧的收发,不再有等待响应头这一步。因此直接规避了概率卡死点。

关键要点

  • 核心问题:OpenAI API 在 HTTP/SSE 模式下随机出现响应头延迟(首包卡死),导致 Codex 无限等待。
  • 根本原因:CPA 等反代客户端的 http.Do() 调用未设置任何超时,可无限期阻塞。
  • 最优解法:使用 WebSocket 模式(通过 OAuth 登录或配置 supports_websockets = true),将等响应头的步骤压缩为仅一次握手,后续对话不再触发。
  • 兜底解法:实现首包超时机制(6 秒超时 + 同账号重试 4 次),实测能绕过单次请求的坏运气。
  • WebSocket 模式下,连接复用显著减少延迟和卡死概率;生产观测中每个会话只记一条连接日志,而 HTTP/SSE 每轮请求都产生独立日志。
  • 无论 OAuth 还是 WebSocket 配置,几乎都能解决卡死问题;不开 WebSocket 则必须依赖超时重连机制,需要修改反代代码。

意义与影响

这次调查不仅解决了 Codex 用户的具体痛点,更揭示了 API 交互模式选择对 AI 工具稳定性的关键影响。研究清晰定位了 "首包卡死" 的根源在 OpenAI 服务端,并给出了从配置到代码层面的系统化解决方案。WebSocket 作为一种更高效的通信协议,在需要持续交互的 AI 编程助手中展现出明显优势——它消除了逐请求建立连接和等待响应头的开销与风险,提升了用户体验。同时,社区驱动的问题排查过程(日志追踪、时序图分析、代码定位)为类似问题提供了可复用的方法论。对于反代工具开发者来说,此案例也敲响了警钟:默认无超时的 HTTP 客户端配置在生产环境中极其危险,合理的超时和重试策略应当成为标配。未来,AI 工具链可能会更广泛地采用 WebSocket 或类似长连接协议,以规避此类阻塞问题。

查看原文 →linux.do