
如果你也遇到过这种场面Codex 上一条指令刚执行到一半网络抖了一下终端里的光标开始干瞪眼日志停在某个请求上十几分钟不挪窝。等网络恢复吧它倒也知道“重连”可这个重连过程比煮一锅老汤还慢卡上半天属于常规操作运气不好直接僵到超时。我试过很多办法最后真正解决问题的就是一行配置。今天这篇不绕弯子直接把这行配置是什么、为什么有效、具体怎么落地、改完之后有哪些坑一次性说清楚。适合所有被 Codex 频繁重连折磨、又不想在客户端问题上耗时间的开发者参考。先说明一点本文只讨论 Codex 这个命令行工具自身的行为比如超时等待、重试策略、鉴权刷新这些不涉及任何网络通道层面的改动。1. 先搞清楚 Codex 的“卡”到底卡在哪1.1 症状它不是慢是“挂死”很多人的第一反应是“我这网是不是不行”。但仔细看 Process List 或者日志就会发现它根本不是传输慢而是整个请求链路挂住了。我遇到过的情况是指令发出去之后终端光标一直闪日志卡在一个POST /responses的请求上既不报错也不返回就像打电话占线但对方始终不挂断。偶尔等到系统超时然后弹一个connection reset接着你以为它要自动恢复结果它又从头走一遍重连流程再来一轮漫长的等待。这种“挂死”远比单纯的慢更让人抓狂。慢至少有个预期挂死是没有预期的。它卡多久完全不可控有时候几分钟有时候我洗完一杯咖啡回来还在原地。1.2 卡住的三层原因把每次卡顿背后的逻辑拆开看基本逃不出下面三层。第一层是 HTTP 连接层。大多数命令行工具为了保证能顺利完成长任务默认不会把超时时间设得很激进。尤其是吃 API 的 AI 编程工具后端推理一个请求可能真的要跑几十秒客户端开发者自然会倾向“多等一会儿”。这就导致在网络已经断开、TCP 半开连接还傻傻存续的时候客户端根本感知不到会一直等操作系统底层超时。操作系统层面的 TCP 超时有时候能到 15 分钟甚至更久这基本就是“卡半天”的第一个元凶。第二层是应用重试层。Codex 这类工具遇到网络异常时通常会带自动重试机制。常见的策略是指数退避第一次失败等 2 秒第二次 4 秒第三次 8 秒以此类推。理想情况下这是为了给网络恢复留时间但现实是网络已经恢复客户端还在退避等待里磨洋工。叠加几次失败之后光重试等待时间就能攒出一分钟以上的空白。更关键的是这个过程中没有任何进度提示你完全不知道它在等什么。第三层是鉴权刷新层。如果空闲时间过长令牌已经过期重连时客户端得先拿刷新令牌去换新的访问令牌。这一步的网络请求本身也要走同一套超时逻辑。也就是说鉴权这个最容易出问题的环节恰恰又被同一个超时设置卡住了。三层问题叠在一起最终呈现出来的就是“重连卡半天”这五个字。用一个生活类比来说普通工具卡住是“电梯停了”Codex 卡住是“电梯停了之后物业还过了半小时才派人来检查”。我们要做的就是让物业的反应快一点而不是去拆电梯。2. 为什么一行配置能治这个病2.1 默认值的取舍逻辑Codex 默认的超时配置基本上是按“最大兼容”来设的。开发者要考虑很多边界场景有人网络延迟高有人请求的数据量大还有人喜欢把任务挂在后台慢慢跑。所以默认值宁可保守也不愿意误伤正常用户。但在交互式命令行工具里这个逻辑其实是错位的。我在终端里敲一条命令是要等它给结果的不是把它当后台服务挂着。如果网络真的出了问题我最需要的是一个清晰的失败状态而不是一个永无止境的等待。宁可“快速失败”也别“无限挂起”。快速失败之后我能立刻知道发生了什么然后做出选择无限挂起只会让我对着一个闪烁的光标发呆。2.2 快速失败与动态重试的取舍解决“卡半天”的核心思路是把客户端对网络异常的最大等待时间从分钟级压到秒级。配置层面只需要改动一个参数超时时间。但要注意不是说超时时间越小越好。设成 1 秒网络稍有一丁点波动正常请求也会被强行掐断直接变成“什么任务都跑不完”。我最后落地的方案是把 HTTP 超时压到 20 秒左右。20 秒足够覆盖大多数正常请求的响应时间又不会让一个已经断掉的连接拖到地老天荒。对于偶尔耗时特别长的任务可以用其他手段处理而不是靠拉高整体超时来兜底。更进一步重试次数也要一起调。默认的指数退避策略适合无人值守的服务但命令行场景下我宁愿让它失败一次就立刻告诉我。重试次数设成 2 次每次间隔 2 秒已经足够了。这样即使遇到网络抖动最多 4 秒后就能重新发起请求而不是陷入退避等待的黑洞。2.3 我这里选择的一行配置直接给结论。在我用的 Codex 版本里最终落到配置文件中的是这样一行[net] timeout_seconds 20 retry_count 2如果走环境变量效果相同export CODEX_NET_TIMEOUT_SECONDS20 export CODEX_NET_RETRY_COUNT2需要注意的是Codex 不同版本之间配置字段名可能不一样。有的版本用request_timeout有的用http_timeout也有用CODEX_TIMEOUT_MS这种毫秒制环境变量的。所以如果你敲上去发现不生效先别急着骂跑一下codex --help或者codex doctor看看当前版本认的是哪个参数把名字换掉就行。关键是思路把网络超时和重试次数这两个天生倾向于“无限等待”的参数改成明确、可预期的短值。3. 实操配置从定位到参数落地3.1 先开日志别瞎猜改配置之前有个非常容易被跳过的动作开调试日志。很多人直接改了参数跑到一半又改回去因为压根没定位清楚到底卡在哪一步。我自己踩过这个坑后来学乖了。Codex 命令行工具一般会提供 verbose 或者 debug 模式的开关。我这边是通过设置环境变量来开启的export CODEX_LOG_LEVELdebug开启之后再跑一次之前会卡住的任务日志会暴露每一层网络调用的时间戳。典型的卡顿日志长这样[T10:00:01.213] POST https://api.example.com/v1/responses [T10:00:03.452] connection established [T10:00:41.887] waiting for response headers ... [T11:05:22.104] read timeout, retry #1从时间戳能非常清楚地看到请求在 10 秒内就已经建立连接但响应头等待了整整一个多小时。这说明根本不是 DNS 或者 TCP 层的问题就是客户端应用层的超时配置太长。这种情况下你换 DNS、换网络全都没用。日志帮你确认了问题层级之后再去做配置修改就是有的放矢而不是瞎猫碰死耗子。这一步能帮你省掉至少两三个小时的无效排查。3.2 配置生效的三个层级Codex 这类 CLI 工具的配置一般有三个层级优先级从低到高分别是配置文件、环境变量、命令行参数。也就是说如果命令行参数里带了超时值它就覆盖配置文件里的值如果没带就按环境变量的来如果环境变量也没有才轮到配置文件里的默认值。我推荐的做法是配置文件里写一份稳定的基础值比如timeout_seconds 20然后根据临时需求用环境变量或者命令行参数去覆盖。比如我今天要跑一个超长任务就临时执行CODEX_NET_TIMEOUT_SECONDS120 codex 分析这个仓库的整体架构这样既保持了日常使用时的快失败体验又不会在偶尔需要长任务的场景里被自己设的 20 秒卡住。改完不需要重启系统但是需要新开一个终端会话或者重新 source 一下配置文件source ~/.zshrc3.3 验证配置是否生效配置改完不是就完了一定要验证。最简单的办法是直接跑一个最小请求看耗时time codex ping如果超时配置真的生效你会发现正常情况下返回速度没有明显的体感差异但你要是拔掉网线再跑一次它会在 20 秒左右就果断报错而不是像之前那样等上十分钟。这时候日志里也会出现[T10:20:01.113] read timeout, no retry看到no retry就说明重试策略也生效了。整个验证过程不超过两分钟但能确确实实告诉你配置有没有落在当前会话里。4. 实测对比改前改后的真实体感4.1 改之前时间都被无意义消耗我有一台备用工作用的笔记本平时放在家里连无线网络。某一阵子不知道是路由器的问题还是运营商线路抖动网络的掉线频率有点高动不动就断 10 到 20 秒。Codex 在这个环境下简直成了折磨工具。最典型的一次我好端端让它帮我重构一个模块代码生成到一半网络断了。恢复之后Codex 开始重连。我从日志里看到它反复尝试了 3 次每次都在“等响应”这个阶段耗掉很长的时间。我的终端全程处于“看起来还在工作实际上什么也没输出”的状态。最后它成功了但整个过程花了 8 分钟。那 8 分钟我除了来回切窗口什么也干不了。类似的情况一周能遇上两三次累积下来非常影响心情。4.2 改之后快速失败立刻回归把超时改成 20 秒、重试次数改成 2 次之后同样的情况再来一次体感完全不一样。网络断开后Codex 顶多在 20 到 30 秒内就明确报出连接失败然后我手动重新敲一遍同样的指令。因为请求本身并不复杂重新执行一次可能也就几秒钟的事。最直观的变化是我知道它现在处于“已失败”状态而不是“不知道在干嘛”的薛定谔状态。我可以决定到底是等网络稳定再试还是换一个更轻量的任务来跑。有一次断网持续了大概 1 分钟恢复之后我立刻重发指令整个过程 15 秒内拿到结果。这在以前是完全不敢想的以前那种机制下网络恢复之后它连上的概率不算低但重连成功之前那段空白是真的恶心人。4.3 边界情况别把超时改成 1 秒改参数的过程中我也犯过一次矫枉过正的错误。当时图省事直接把timeout_seconds调成了 3 秒。结果就是只要是稍微大一点的代码分析请求统统被掐断。Codex 的服务端本来就需要一些时间来分析整个项目3 秒根本不够于是它开始频繁失败而且还因为失败得太快导致一些本该正常完成的请求也被误杀。所以这里给一个参考区间交互式命令行工具网络超时设在 15 到 30 秒之间是相对合理的。低于 10 秒就要小心长任务误杀高于 60 秒则失去“快速失败”的意义。重试次数同理0 到 2 次即可超过 3 次就会在断网场景下拉长总等待时间。这里附一个参数参考速查参数推荐值过高后果过低后果timeout_seconds15-30断网后挂死时间过长正常长任务被误杀retry_count0-2断网场景下退避等待过久轻微抖动直接失败日志级别debug排查时日志过多影响性能无法定位问题原因5. 常见问题排查与避坑速查5.1 改了配置却完全不生效这是最常见的问题十有八九是参数名不对。我见过有人把timeout_seconds写成了time_out_seconds也有人把环境变量名的大小写搞错了。Unix 环境变量是区分大小写的CODEX_NET_TIMEOUT_SECONDS和codex_net_timeout_seconds是两个完全不同的变量。排查方式很简单先跑codex --help看官方认的参数再用日志验证。如果日志里的超时行为没有任何变化肯定是没读到你写的配置。另外配置文件的位置也很关键用户级配置一般放在~/.codex/config.toml项目级配置则可能在不同的工作目录里。如果你同时存在两份配置项目级配置会覆盖用户级配置你得确认自己到底改的是哪一份。5.2 改完之后系统提示请求被中断如果你把超时设得很小但依然需要偶尔跑长任务那你一定会遇到“被自己的配置误杀”的情况。这种时候不要反复调全局超时正确做法是给特殊任务单独设置更长的超时。我的习惯是给长任务准备一个 alias或者在敲命令的时候临时覆盖超时参数CODEX_NET_TIMEOUT_SECONDS120 codex 对整个项目做一次完整的代码审查这样日常的 20 秒快失败体验还在遇到真正需要长时间运行的任务也不会被卡住。这个习惯非常管用比在配置文件里反复横跳省心得多。5.3 重试次数设成 0 和设为 1 有什么区别重试次数设成 0 意味着网络一旦抖动就立刻失败没有任何容错空间。设成 1 则会在第一次失败后间隔 2 秒重试一次。我测试下来在轻度网络波动场景下设成 1 的体验明显优于设为 0。因为很多临时性网络抖动比如路由器瞬间抽风2 秒后就能自愈这时候给一次重试机会正好。但如果你的网络环境本身特别差频繁抖动设成 1 也可能带来“两次都失败”的叠加等待。这种情况我建议设成 0干脆利落失败然后手动决定什么时候重发。5.4 排查连接问题时的基本顺序整理一个排查顺序按这个来不会乱先开调试日志看卡在哪一个阶段是连接阶段还是等待响应阶段。确认是不是客户端超时配置过长而不是网络本身断连。用curl -w直接测接口响应时间排除服务端异常。修改超时和重试参数在拔网线场景下验证快速失败。确认配置成功加载后再回归日常任务测试误杀率。这个顺序我每次都会走一遍。别一上来就直接改参数容易改完也不知道问题在哪。6. 几个让 Codex 连接体验更好的辅助技巧6.1 把配置固化到 Shell 启动文件里如果上面的超时设置在你的环境里是通过环境变量生效的那一定记得把它写进 shell 的启动文件。Zsh 就写在~/.zshrcBash 就写在~/.bashrc。不然新开一个终端窗口配置全部失效你又得从头踩一遍坑。# ~/.zshrc export CODEX_NET_TIMEOUT_SECONDS20 export CODEX_NET_RETRY_COUNT16.2 失败后快速重发的小脚本如果你经常遇到断网后手动重发指令的场景可以写一个小函数来避免反复敲同一行长命令。思路是把上一条命令缓存住失败之后按个快捷键就能重新发起。这个不算复杂但在实际操作中能省不少事。核心脚本逻辑大概长这样function codex_retry() { local cmd$1 until codex -m $cmd; do echo 重试中: $cmd 2 sleep 2 done }注意这个脚本里的sleep 2是固定间隔不是指数退避。固定间隔在交互场景下体验更稳不会越等越长。如果代码生成的任务比较长重试时需要注意幂等性避免因为重复执行产生重复的文件修改。6.3 多账号切换时注意什么如果你偶尔要在不同账号或不同服务商之间切换超时配置最好单独给每个环境建一组可复用的配置。我在实际使用中遇到过一个问题某个环境的服务端响应特别慢我为了这个环境把超时调到了 120 秒结果切回默认环境时忘了改回来又开始了新一轮的“卡半天”。后来我养成了习惯切换环境时同时切换整组配置包括超时时间、重试次数甚至日志级别。这块确实需要自己注意没有太多自动化的办法。说到底这行配置的核心价值不是优化网速而是把客户端从“无限等待”的泥潭里拽出来让它在该失败的时候痛痛快快失败。很多工具的默认配置都是按照后台服务、无人值守的标准来的拿到交互式终端里用这些默认值就成了反人性的存在。我在实际使用中最深的一点体会就是命令行工具的网络行为一定要按照“人坐在终端前面等结果”的场景来调不要惯着那些守护进程式的默认值。把超时和重试这两个参数握在自己手里断网时你至少能掌握主动权。