news 2026/10/20 22:24:12

Codex一直显示“正在思考”?WebSocket握手失败与HTTPS回退全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Codex一直显示“正在思考”?WebSocket握手失败与HTTPS回退全解析

最近被 Codex 折腾了一把——对话框里一直显示“正在思考”,转圈转到天荒地老,既没有报错也没有结果。这不是网络慢的问题,排查到最后发现,真正的原因藏在 WebSocket 连接上:客户端尝试用 WebSocket 做流式请求,连接建立阶段就失败了,而它又没能按预期回退到 HTTPS 通道,于是一直卡在等待状态。这篇排障记录就把整个过程拆开讲讲,从日志定位到原理分析再到配置修复,给同样遇到 Codex“思考中”的朋友一条可复现的排查路径,也顺便说清楚 WebSocket 和 HTTPS 这两种传输方式在 AI 编程工具里到底是怎么协作的。

1. 故障现象与第一反应

1.1 “正在思考”的三种常见情况

先说结论:Codex 界面里的“正在思考”是个很模糊的状态,至少能拆成三种完全不同的情况。

第一种是正常推理。模型确实在处理请求,需要一定时间生成内容,这时候多等几秒就好。第二种是响应中断。请求已经发出去了,传输链路在半路断了,客户端没收到结束信号,于是界面就停在“思考中”一直不往前走。第三种是客户端内部状态卡死。任务本身已经失败,但 UI 状态没有同步更新,看起来就像在思考,实际上后台已经没有任何活动了。

我这次遇到的情况属于第二种和第三种之间的灰色地带——请求发出去了,链路断了,客户端没有感知,于是就一直挂着。区分这三种情况有个土办法:看任务有没有明确报错,以及重新发一条更短的请求试一下。如果新请求能正常返回,说明客户端本身是活的,问题多半出在会话上下文或者传输通道上;如果新请求也卡住,基本可以确定是连接层出问题,这时候就该考虑重启客户端或者抓日志看底层报错。

1.2 动手之前先把现场信息存下来

出现故障的第一件事不是乱点,而是把现场固化下来。我给自己定了一个固定动作,按顺序做四件事:

第一,把当前 Codex 的版本号记下来。命令行版直接执行版本查询命令,桌面版在设置页里能看。第二,复制最近一次出错的提示信息,包括界面上所有可见的报错文本,哪怕看起来无关的也复制一份。第三,把客户端日志目录里的当天日志文件归档一份,避免后续排查过程中日志被滚动覆盖。第四,记录出现故障的时间点和当时大概的网络状况。

这次运气不错,日志里留下了一句关键的报错信息,大意是“本地连接层在处理 /responses 接口时失败”,并且明确提到了协议切换失败。看到这句基本就锁定了方向——问题不在模型输出,而在客户端与服务的传输链路。

2. 拉日志:找到卡住的真正原因

2.1 日志里的关键线索

Codex 的日志文件默认会记录请求的完整生命周期,包括接口路径、连接建立状态、响应阶段和耗时时间。日常使用中你可能完全注意不到这个文件的存在,但排障时它就是最重要的线索来源。

我翻到当天的日志,发现请求确实发出去了,也走到了 /responses 这个接口,但在连接建立那一栏出现了异常标记,紧接着就是那句关键报错。这句话拆开看,“local”指客户端本机的传输组件,“failed”表示连接建立失败,“endpoint”说明卡在具体接口上。注意它说的是 /responses,这是 Codex 对话补全的底层接口,不是别的辅助接口,所以直接影响对话框里的正常聊天。

找到这句话之后,我立刻把日志里对应时间戳前后的内容也导出来看了一遍。前面几行是请求参数,后面几行是连接状态变化。整个链条非常清楚:请求发起、尝试建立长连接、握手阶段失败、进入等待、最终没有等到恢复信号。这个时间线帮我确认了问题就出在连接建立环节,跟模型推理速度、上下文长度这些都没有关系。

2.2 WebSocket 在 Codex 里的角色

Codex 默认为了让回答像打字机一样逐字蹦出来,走的是流式传输,底层用的是 WebSocket。要理解这个设计,得先明白流式输出对传输通道的要求。

普通 HTTP 请求是一个“请求-响应”的回合制游戏:客户端发一个请求,服务端处理完,一次性把完整结果返回,然后连接就结束了。这对短任务没问题,但对 AI 对话这种动辄生成几百上千 token 的场景来说,用户体验会很差——你发一条消息,然后干等着,直到全部生成完才能看到内容,中间完全没有反馈。

WebSocket 的好处是双向通信,握手完成后一条连接可以持续收发数据。服务端每生成一个 token,就可以立刻推给客户端,不用每次都重新建立连接。这就是界面上能看到逐字输出效果的原因,也是现代 AI 编程工具普遍采用流式传输的根本逻辑。

但 WebSocket 有个隐藏门槛:它要先完成握手,握手成功后才能进入数据传输阶段。握手依赖于正常的 HTTP Upgrade 流程,客户端发送 Upgrade 请求,服务端返回 101 状态码表示“切换协议成功”。这个 101 响应的返回速度,直接决定了用户感受到的“首字延迟”。如果这一步被卡住,客户端就会一直停在“等待连接”的状态,表现出来正是那句经典的“正在思考”。

2.3 “正在思考”和“连接失败”为什么长得一样

这是我踩坑最多的点,也是这次排障最有价值的地方。

WebSocket 握手失败后,很多客户端不会立刻抛错,而是先等一个超时窗口。在这个窗口里,UI 没有任何异常提示,转圈动画照常播放,给人感觉就是“模型在思考”。一旦超时,有的客户端会回退到普通 HTTP 请求,有的就直接放弃,取决于客户端的具体实现。

Codex 这次的表现属于“没有及时回退”的那一类。于是我以为它在思考,其实它已经断在半路了。这也是为什么排查类似问题时要优先看日志而不是看界面——界面给的是体验层信息,日志才是传输层的真相。界面只会告诉你“我在等”,日志才会告诉你“我在等什么”。

3. 原理拆解:为什么要回退、怎么回退

3.1 流式传输与普通请求的本质区别

流式传输和普通请求的差异,表面上是“逐字输出”和“整段返回”的区别,本质上却是连接生命周期管理的区别。

流式请求需要维持一条长连接,服务端在这条连接上持续推送数据,直到完整响应结束。这种方式对网络质量要求更高,连接中断会导致半截响应——用户看到一部分内容,然后卡住不动。普通请求是一次性返回的,相当于发一个 HTTP 请求,等到完整响应后关闭连接。它牺牲了一部分体验,但换来的是简单可靠的传输逻辑。

Codex 在正常状态下优先走 WebSocket 的流式通道,这与流式输出的产品体验强相关。但底层接口在设计时通常都会兼容普通 HTTP 模式,只要把流式开关关掉,就会走标准的“请求-响应”模式。回退 HTTPS 的意思,就是放弃 WebSocket 这条专用连接,改用普通的 HTTPS 接口发送同样的请求。虽然不能逐字显示输出,但能保证请求完整送达、响应正常返回,不至于卡死。

换句话说,HTTPS 回退是保底方案,WebSocket 是体验方案。保底方案存在的意义,就是在体验方案失效的时候兜住整个请求流程。

3.2 为什么客户端没有自动回退

理论上客户端应该能在 WebSocket 失败的瞬间自动降级,但实际工程里这个降级判断往往做得不够完善。我梳理了三种常见原因。

第一种,握手阶段的异常没有被归类为致命错误。客户端收到的是“连接暂时不可用”之类的信号,以为等一下就能好,于是进入重试等待,而不是立刻切换通道。第二种,超时时间设得偏长。回退逻辑在超时之后才触发,但超时阈值如果按秒级甚至分钟级设置,用户感知就是“卡了很久没反应”。第三种,回退开关本身被某种配置隐式关闭。比如在非流式模式未被允许的情况下,客户端强行走流式通道,一旦失败就无处可退。

我这次的配置就典型地踩中了第三个原因。客户端配置里明确启用了流式模式,但同时本地的传输环境又不能让 WebSocket 握手成功,两边一拍即合,直接卡死。这个场景特别坑,因为表面上看配置没有问题,网络似乎也没有问题,实际是“流式模式强制开启”和“握手链路不可用”之间的矛盾。

3.3 换个思路理解回退机制

回退机制在很多系统里都有影子。比如视频播放器,优先走高清流,拉不到就自动切标清;比如下载工具,一种协议连不上就换另一种协议。核心逻辑都是“优先方案失败后,自动寻找备选通道”。

但前提是有一个明确的状态切换信号。WebSocket 握手失败时其实是有明确信号的——HTTP 响应的状态码不是 101,或者连接直接被关闭。问题是 Codex 这次没有把这个信号映射成“立刻回退”的动作,而是等到了自己的超时时间。等超时到了,请求可能已经被服务端判定为废弃,再发一次又要重新走流程。

理解了这一点,解法就清晰了:既然是等待超时后才处理,那就从两个方向下手。要么缩短等待时间,让它早点失败、早点切换;要么干脆跳过 WebSocket 直接走 HTTPS,从根上绕过握手环节。我选了后者,因为从产品使用角度来说,稳定优先,逐字输出是加分项而不是必需项。

4. 实操:强制走 HTTPS 回退通道

4.1 找到配置文件并备份

不同安装方式的 Codex 配置文件位置不一样。命令行版一般在用户专属目录下的 .config 目录里,桌面版一般有自己的应用配置目录。定位方式很简单,看日志里的“配置加载路径”字段,那行会明明白白写着当前正在使用的是哪个目录,不需要瞎猜。

进目录后先别急着改,把原文件备份一份。我习惯用命令行复制一份带日期的备份文件,比如 config.json.bak-20250614 这种格式。万一改错了能秒回滚。这一步看着多余,但改配置翻车的时候就知道多重要了。

配置文件通常是 JSON 格式,编辑时注意不要破坏 JSON 结构。逗号、引号、花括号都要检查到位。很多小白翻车就翻在改完配置后 JSON 解析失败,Codex 直接起不来,报错又看不懂,最后还要靠备份文件恢复。

4.2 关闭流式传输开关

找到配置里的传输相关字段,把流式开关设为 false。不同版本的字段名可能不一样,有的叫 streamMode,有的叫 streaming,有的藏在传输配置的子对象里。但语义都是同一个:是否使用流式响应。

改完之后保存文件,重启 Codex 进程,让配置重新加载。这一步做完,Codex 和服务端的通信就会改用普通 HTTPS 请求,不再尝试 WebSocket 握手。实测下来,回答速度会从逐字输出变成整段返回,延迟感知上略差一点,但稳定性明显提升,至少不会再出现“思考十分钟没结果”的情况。

这里有个细节:如果客户端界面里还有单独的“流式显示”选项,记得也关掉。否则 UI 层会等待流式数据,后端明明已经返回了完整响应,界面却依然不显示。这个坑很容易被忽略,因为配置层面的流式开关和界面层面的显示开关是两套独立的控制逻辑。

4.3 用环境变量做双保险

除了配置文件,我还建议检查环境变量。有些版本支持通过环境变量覆盖传输模式,优先级比配置文件更高。设置方式是在启动 Codex 前导入一个环境变量,把值设为非流式,然后从当前终端启动。

这个做法特别适合排查场景:不确定配置文件里具体字段叫什么的时候,直接环境变量覆盖最省事。不需要翻文档猜字段名,一条命令就能验证“如果不用 WebSocket,问题是否解决”。命令行的语法很简单,设置变量然后启动进程就行。

改完之后用一条简单请求做验证。如果配置生效,请求会快速返回完整结果,日志里也能看到对应的传输模式标记。验证通过后,我通常再观察几轮会话,确认没有异常再删除环境变量,回到配置文件的路线上来。这样可以避免留下一个只能靠环境变量才能启动的隐性地雷,毕竟环境变量不像配置文件那么直观,换个终端就可能忘记设置。

5. 常见问题与排查技巧实录

5.1 其他用户常遇到的同类现象

我在社区里看到不少人和我一样卡在“正在思考”,但原因五花八门。整理一下,大概有这几类:

现象可能原因排查方向
一直思考,无输出WebSocket 握手失败,未回退检查传输模式配置,禁用流式
一直思考,日志有 /responses 报错模型不支持指定参数检查请求参数模型配置
一直思考,日志有组织设置报错组织信息加载失败检查登录状态和组织配置
一直思考,请求根本没发出去安装不完整或本地组件异常重装客户端或检查安装目录

这些场景和 WebSocket 无关,但现象完全一样,都是卡在思考中。排查时一定要区分:先看日志有没有明确报错,再看接口路径是什么。如果日志里出现类似 /responses 的接口报错,才往传输层排查;如果日志里只是配置加载错误,那改传输模式也没用,得先把配置修好。

5.2 一套可复用的排查顺序

经过这次折腾,我总结了一个固定的排查顺序,分享出来供参考:

第一步,重启客户端,排除一次性卡死状态。很多偶发问题重启就好了,不值得深究。第二步,看日志,定位真正的报错位置。重点看有没有连接建立失败的痕迹,比如握手异常、状态码异常、超时记录。第三步,判断是否传输层问题。这一步最关键,判断依据是日志里是否有 WebSocket 相关条目,以及报错是否出现在接口调用阶段。第四步,临时禁用流式模式,验证是否能恢复正常。这一步是验证性的,如果禁用了流式就正常,那基本坐实传输通道的问题。第五步,恢复正常后复盘配置,把临时改动固化为正式配置。第六步,记录变更,方便下次出问题时快速回滚。

这套顺序对很多“界面卡住但看不到原因”的客户端问题都适用,不局限于 Codex。核心思路是先绕开问题通道,再诊断问题原因,比直接对着界面瞎猜高效得多。

5.3 几个日志阅读的实用技巧

最后分享几个日志阅读的实用技巧,能帮你快速从日志里捞出关键信息。

一是按时间过滤。故障发生时段的日志才有价值,别从头到尾看。直接定位到报错时间点附近几十条记录即可。二是关注状态码变化。WebSocket 握手正常会看到 101,如果看到 200 或 4xx、5xx,说明握手没成功。三是看请求生命周期标记。正常完成的请求有明确的开始和结束标记,只有开始没有结束的,大概率就是卡住的请求。

四是注意日志里的“重试”字段。如果连续出现多次重试记录,说明客户端一直在尝试恢复连接,但每次都失败了,这时候不是客户端的问题,而是底层传输链路的问题。五是别忽略启动日志。每次启动时日志都会显示当前生效的配置和传输模式,改完配置后看一眼启动日志,能确认改动是否真的生效。

我个人在实际操作中的体会是:Codex“正在思考”这个提示其实是个黑盒,它把连接层、传输层、处理层的所有状态都吞进去了,对外只显示一个转圈动画。遇到这种问题,不要盯着界面干着急,把日志打开、把传输模式切换一下,通常十分钟内就能定位。我后来养成了习惯,每次改完配置都顺手看一眼启动日志,确认传输模式符合预期,之后这类问题基本没有再困扰过我。日志就是排障的地图,线头找到了,剩下的路就好走了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/17 18:39:55

MediaGo流媒体下载:从HLS分段解析到30MB/s极速保存

流媒体时代,很多人都有这样的经历:刷到一个收藏级的公开课视频,看完一遍不过瘾,想转存到本地慢慢品;或者自己付费订阅了某个在线学习平台,会员到期之前想把课程备份下来,却发现平台只给了"…

作者头像 李华
网站建设 2026/10/16 11:44:51

Notepad++ 写 Markdown 实战:插件安装、预览配置与避坑指南

简介:这份资源面向使用 Notepad 编写 Markdown 文档的开发者与技术人员,解决编辑器原生不支持 Markdown 语法高亮与实时预览的问题。压缩包内共 2 个文件,包含 1 个 dll 插件核心组件与 1 个 xml 语言配置文件,整体约 228KB&#…

作者头像 李华
网站建设 2026/10/20 3:31:43

mysqld --initialize --console 初始化失败原因排查与解决清单

很多人在装 MySQL 5.7 或 8.0 时,第一次执行mysqld --initialize --console就卡住了,要么窗口一闪而过,要么终端里抛出一堆[ERROR],翻来覆去查不清楚到底是哪里的问题。这条命令本身不复杂,但它牵扯到目录状态、文件权…

作者头像 李华
网站建设 2026/10/16 14:50:05

国产化数据库深度运维:从基线体检到故障排查实战指南

1. 接手一套国产化数据库,第一件事不是调SQL而是改认知上个月某客户的核心交易系统在业务高峰突然“卡死”,应用侧日志全是连接超时,数据库侧查看活跃会话,发现几百个会话堆积在同一条SQL上。当时的运维同事第一反应是重启数据库&…

作者头像 李华
网站建设 2026/10/19 22:46:04

PCA9422+STM32L041C6低功耗电源管理方案设计与落地

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/13 8:38:01

OpenSandbox:为大模型生成代码构建安全隔离执行环境

这两年做大模型应用,特别是做 Agent 或代码解释器相关功能的人,应该都遇到过同一个尴尬:模型写代码的能力越来越强,但把代码真正跑起来的那一刻,心是悬着的。它可能只是想算一道题,但生成出来的代码如果要读…

作者头像 李华