Codex 控制浏览器时怎样避免 Node 再次吃满 Mac CPU

4次阅读
没有评论

前一次排查里,我遇到过 Codex 浏览器控制进程持续占用约 140% CPU、内存接近 1.5 GiB,并反复读取一份约 735 MiB 会话日志的情况。Mac 温度一度接近 100℃,只杀掉最下面的 Node 子进程后,它还会被父控制器重新拉起。

事故原因和硬件检查过程已经写在上一篇:Mac 跑 Codex 跑到 100℃还有糊味:Node 140% 背后的原因和处理顺序。这篇不再重复现场,而是回答另一个更实际的问题:怎样给 Codex、浏览器控制和 Node 建一套不过度严格、又能在失控前停下来的资源规则?

不要先把所有 Node 都限速

Node 短时间占满一个 CPU 核心并不罕见。Vite 构建、TypeScript 类型检查、单元测试、压缩图片和启动开发服务,都可能在几十秒内出现较高占用。如果只看到 node 这个进程名就统一限速或批量结束,很容易误伤正常工作。

我最后采用的边界是:正常构建、测试和开发服务不受额外限制,重点约束浏览器控制、截图、页面恢复、失败重试和巨型会话日志。判断异常也不只看一个 CPU 数字,而是同时观察持续时间、内存、日志体积、任务是否仍在推进,以及机器有没有出现热警告。

全局设置 NODE_OPTIONSUV_THREADPOOL_SIZE 之类的环境变量也不是好办法。它们未必能限制浏览器 V8 主线程的 CPU,反而可能让所有 Node 项目的构建和测试行为发生变化。

一次只让一个任务控制同一个浏览器

浏览器自动化最容易出问题的场景,不是打开一个页面,而是多个任务同时抢同一个 Chrome:一个任务在跳转,另一个任务在截图,第三个任务又尝试重新附着调试器。失败后如果各自继续重连,资源消耗和会话日志会一起放大。

比较稳的做法是:

  • 同一时间只让一个 Codex 任务控制同一个浏览器实例。
  • 浏览器页签按需创建,通常复用两个工作页签就够了。
  • 页面操作串行执行,静态代码检查、接口请求和文档核对可以并行,但不要并行操控浏览器。
  • 临时页签用完就关闭,不碰用户原本打开的无关页面。

这不是限制 Codex 的分析能力,而是避免多个控制器争抢同一份外部状态。

浏览器只主动重连一次

浏览器插件、调试器附着或页面控制超时时,允许主动重连一次是合理的。第二次仍然失败,而且 Chrome、插件、锁屏状态和目标页都没有变化,就应该停止当前恢复路径。

真正的外部状态发生变化后,例如用户重新打开目标页、系统解锁、插件完成重启或失效页签绑定已经回收,才值得重新建立控制。反复重启同一个组件、不断新建空白页签,并不能把同一次失败变成新的机会,只会产生更多进程、日志和截图。

我现在采用的原则很简单:同一外部状态下连续两次失败就停,记录阻塞原因,把恢复动作交给明确的状态变化。

截图、DOM 和工具输出不要塞满聊天

浏览器任务里的大头不只是图片文件,还包括 DOM 快照、控制台输出、工具返回值、失败堆栈和重复截图。如果这些内容不断回显到同一个对话,任务恢复时就可能反复扫描越来越大的历史。

更合适的证据管理方式是:

  • 只截关键验收状态,不对同一页面重复截图。
  • 默认使用普通视口截图,不随手做整页长截图。
  • 图片、DOM 快照和大型报告落到项目文件,聊天里只记录路径和结论。
  • 控制台日志先按错误、时间和业务动作缩小范围,不直接复制全集。
  • 一个页面或阶段完成后立即写短摘要,不把几十轮工具调用当成唯一交接材料。

这样既保留了可追溯证据,也能让下一次恢复只读取真正需要的信息。

会话达到 100 MiB 就该开始收口

我给会话日志设了两道提醒线。达到约 100 MiB 时,不必立刻停工,但应该减少截图和浏览器调用,在当前小阶段结束后判断是否换新任务。达到约 200 MiB,而且后面仍有大量浏览器操作时,就停止继续堆叠,写一份简短交接后从新任务继续。

这两个数字不是系统极限,而是经验性的保护线。真正要避免的是让一个已经很长的浏览器会话继续派生更多任务,再把新的 DOM、截图和子任务结果全部写回同一份历史。

长期状态应该放在代码、测试、审计报告和项目文档里;聊天适合协调当前动作,不适合无限承载整个项目历史。

CPU 高并不等于立刻停,但持续增长要停

macOS 活动监视器里的 100% 通常表示占满一个逻辑核心。构建过程中短时超过 100% 很正常,因此我没有采用“超过 100% 就杀”的机械规则。

更合理的分级方式是:

  • 浏览器 Node 达到约 120% CPU 持续 30 秒,或内存接近 1 GiB:减少浏览器动作,观察任务是否仍在推进。
  • 约 120% CPU 持续 60 秒,同时内存或日志继续增长:停止浏览器控制,写交接并换新任务。
  • 温度长时间接近 100℃、出现系统热警告、异味、明显卡死,或子进程反复重建后仍然高占用:立即停止任务并完全退出 ChatGPT/Codex,冷却后再排查。

关键是“持续高占用 + 内存或日志增长 + 没有有效进展”的组合,而不是孤立的瞬时峰值。

子 Node 自动重建时,不要继续追着杀

如果结束一个浏览器 Node 后,新 PID 很快又出现,通常说明上层控制器仍认为它应该存在。继续按进程名结束,只会进入“杀掉、自动重建、再次升温”的循环。

此时应该停止对应浏览器任务,优先让控制器正常释放资源;确认任务已停但进程仍残留时,再完全退出 ChatGPT/Codex。不要使用按名称批量结束所有 Node 的命令,因为同一台机器上可能还有正常的前端服务、测试或构建任务。

Playwright 默认单 worker、最多重试一次

真实浏览器测试也遵循同样思路。个人 Mac 上的 Playwright、Puppeteer 验收可以默认使用单 worker,失败最多重试一次。项目已经证明并行配置稳定、机器状态正常,而且并行确实能缩短耗时时,再合理增加 worker。

浏览器测试也不要和交互式浏览器控制同时运行。测试失败后先区分产品回归、服务没有启动、网络问题和调试器连接失败,不能靠增加并发或无限重试掩盖根因。

把规则放进工作区入口,而不是只靠记忆

口头约定很容易在新任务里丢失。更可靠的方式是在工作区根目录维护一份简短的 AGENTS.md,把详细资源规则链接到唯一治理文档,再让各项目继承;Claude、Gemini、Cursor、Copilot 等工具如果不会自动读取同一个入口,就各自放一个很短的引用文件,不复制整份长规则。

一个最小版本可以只写这些硬底线:

同一时间只有一个任务控制同一个浏览器实例。
默认复用两个页签,页面操作串行执行。
同一外部状态连续两次连接失败就停止。
大图片、DOM 和日志写入文件,聊天只保留摘要。
会话约 100 MiB 开始收口,约 200 MiB 且仍需大量浏览器操作时换新任务。
持续高 CPU 并伴随内存或日志增长时停止;出现热警告或异味立即退出应用。
不要设置影响所有项目的全局 Node 限速变量。

AGENTS.md 的通用格式可以参考 agents.md;Codex 的具体读取方式可参考 OpenAI Codex AGENTS.md 文档

这套规则会不会太严格

如果把每次 Node 短时峰值都当故障,当然会过于严格。现在这套规则故意把“正常高负载”和“持续失控”分开:普通构建、测试和开发服务照常运行;浏览器任务也允许一次恢复、少量页签和合理的短时 CPU 峰值。

真正触发停止的是连续失败、会话持续膨胀、内存与 CPU 同时上涨、任务没有进展,或者机器已经出现明显热异常。这些条件都属于应该止损的信号,不是为了让工具看起来安静而牺牲正常效率。

最后的做法

Codex 控制浏览器本身并不可怕,Node 也不是天然比 Java 更危险。问题往往来自任务太长、证据太大、多个控制器抢同一个浏览器,以及失败后没有边界的恢复循环。

我最终保留的优化不是一个“限制 CPU”的开关,而是一套工作方式:先静态检查,再做少量真实浏览器验收;一次一个控制器;两次失败就停;证据落文件;长任务及时交接;持续增长才触发停止;出现热异常立即退出应用。

这些规则不会消灭所有异常,但能在会话长到数百 MiB、机器升到 100℃之前,给人和工具都留下一条清晰的刹车线。

正文完
 0
bdspAdmin
版权声明:本站原创文章,由 bdspAdmin 于2026-07-21发表,共计3199字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)