Codex控不了浏览器?模型层到执行层的全链路排查指南

发布时间:2026/9/26 17:56:17
Codex控不了浏览器?模型层到执行层的全链路排查指南 最近好几个同学跑来问同一个问题Codex 明明能正常聊天、能写代码、能调工具但只要涉及“控制浏览器”就歇菜——让它打开个网页、点个按钮、截个图它要么装死要么报一串看不懂的错。“Codex 控不了浏览器求排查思路”这个高频问题背后多数时候根本不是 Codex 本身坏了而是你根本没给它搭好一条能通到浏览器的路。这篇文章我就按实际排查的顺序把从模型层到工具层再到浏览器执行层的整条链路全部过一遍记录我自己踩过的坑和验证过的方法给遇到同样问题的人一条可以直接照着抄的排查路径。1. 先说清楚“控不了浏览器”到底是哪一层在出问题1.1 Codex 控制浏览器的完整调用链Codex 本身不是一个“浏览器自动化工具”它是一个编程智能体你给它一个任务它把任务拆解成代码或工具调用然后调用本地的 CLI、脚本、MCP 工具去执行。如果想要它控制浏览器完整链路是这样的你在 Codex 里输入“打开 example.com 并截图”Codex 把这句话连同上下文发给配置好的模型端点OpenAI 官方端点或者你用 cc-switch 之类的本地转发服务指到的第三方模型网关模型返回的响应中携带工具调用tool call指令Codex 解析出“调用 playwright 的 MCP 工具”这个动作Codex 通过 MCP 协议把工具请求发到本地某个 MCP server如 playwright/mcp、chrome-devtools-mcpMCP server 再通过 Chrome DevTools ProtocolCDP或 WebSocket 桥接到一个真实运行的 Chrome/Edge 实例浏览器执行指定动作把结果页面标题、DOM、截图原路返回给 Codex。任何一个环节断掉表现都是“Codex 控不了浏览器”。而最迷惑人的地方在于前 3 步没通时Codex 根本就不会表现出“想控制浏览器”的样子它只会像没听见一样不调用任何工具第 5 步之后断了才会看到工具调用报错、超时、空结果。1.2 三层故障模型我习惯把所有“控不了浏览器”的问题归成三层排查时逐层过不跳级模型层请求没到模型或者模型没正确返回工具调用。表现为 Codex 没有反应、报认证错误、报模型不支持、报本地转发服务失败。工具层模型有响应但 Codex 找不到 MCP server、MCP server 启动失败、浏览器扩展里的 MCP 连接没打开。表现为“尝试调用 playwright 工具但失败”、工具列表为空。执行层工具调用成功了但浏览器端的动作没执行成功。表现为打开页面超时、元素找不到、iframe 点不进去、截图是空白。排查的第一原则就是先定位层再动手改。否则你调半天 MCP 配置结果问题其实出在模型端点的认证上纯属浪费。1.3 用一张症状表快速对号入座症状最可能出问题的层优先查看Codex 完全没有工具调用迹象回复一句“无法完成”模型层模型端点、认证、日志报 local proxy failed / responses 端点错误模型层cc-switch 配置、本地转发服务的转发日志报 auth token is unavailable模型层~/.codex/auth.json、环境变量报 model is not supported模型层模型名映射报 MCP server 启动失败 / 未注册工具层config.toml 的 mcp_servers 段扩展已装但 Codex 连不上 Chrome工具层扩展 MCP 连接开关、9222 端口工具调用成功但页面空白 / 元素找不到 / 超时执行层页面等待策略、iframe、headless 设置这张表是我每次接手这类问题第一个打开的东西。下面几节就按这张表一层一层切。2. 第一刀本地转发服务与 /responses 端点的报错现场2.1 报错信息到底在说什么很多人的 Codex 不是直连 OpenAI而是通过 cc-switch 这类工具在多个模型配置之间切换。cc-switch 可以起一个本地 API 转发服务local proxy把 Codex 发出的请求转发到你选择的模型网关比如自建的 API 网关、第三方兼容端点、DeepSeek 这类模型服务。社区里那条高频报错可以拆成三截看“codex endpoint /responses”Codex 默认调用的是 OpenAI 的 Responses API也就是 POST /v1/responses“local proxy failed while handling”本地转发服务在处理这个 /responses 请求时报错“provi...” 大概率是 provider 或者 provided 相关字段被截断暗示问题出在 provider 配置上。这意味着请求已经到达了本地转发服务但它没能把手上的请求顺利转到目标模型服务。常见根因有三个。第一目标模型服务不支持 /responses 协议。很多 OpenAI 兼容端点只实现了 /v1/chat/completions并不实现 /v1/responses。Codex 发过来的请求是 Responses API 格式你的转发服务必须把它翻译成 Chat Completions 格式再转发。如果只做了透传没做协议转换就会在处理 /responses 时直接失败。第二模型名映射问题。Codex 在请求里带的 model 字段是你在配置里写的模型名比如那类报错里出现的 gpt-5.6-sol。如果这个名字在目标网关里不存在、或者目标网关没有注册这个名字网关会拒绝或转发失败进而抛出 “the gpt-5.6-sol model is not supported when using codex with a...” 这类错误。第三认证头没有透传或注入。Codex 本地配置里通常不直接存第三方 API key而是通过配置文件里的环境变量占位符引用。如果转发服务转发时没有把 Authorization 头补上目标网关会回 401转发服务再把失败原样抛回给 Codex。2.2 顺着配置逐项核对如果你也用 cc-switch建议按下面的顺序查不要跳过打开 cc-switch 的配置面板确认当前激活的配置里 base_url 指向的是哪个地址。base_url 通常需要以 /v1 结尾大部分兼容网关例如 https://api.deepseek.com/v1。如果地址缺了 /v1Codex 拼接 /responses 时就会拼出一个不存在的 URL。确认该配置的 model 字段。Codex 发送的模型名就是这里写的值。如果你本意是接 DeepSeek但模型名写成某个测试模型或官方专属模型名目标网关不认识就会报 model not supported。解决办法是把模型名改成目标网关里真实存在的模型 ID比如 deepseek-chat或者把本地转发服务的模型映射表改一下让请求里的模型名在转发时自动替换成目标模型。确认 API key 来源。检查配置文件里 env_key 对应的环境变量是否真的设置了值。不要在终端里直接 echo 出来可以用下面这种方式验证变量是否存在python -c import os; print(bool(os.getenv(DEEPSEEK_API_KEY)))看本地转发服务的运行日志。cc-switch 的本地转发服务一般会把每个请求的 method、path、status 打出来。如果日志里显示 POST /v1/responses 返回 404那就是协议转换没做如果返回 401那是认证问题如果超时那是目标网关不可达或限流。2.3 auth token unavailable 与模型名不支持的连带问题“codex auth token is unavailable” 这个错经常和本地转发一起出现因为它不一定指 OpenAI 官方 token而是指你配置的 provider 需要的凭证取不到。排查时两件事必做检查 ~/.codex/auth.jsonLinux/macOS或 %USERPROFILE%.codex\auth.jsonWindows里有没有有效 token。如果用的是官方账号登录重新执行 codex login 刷新 token。如果走的是自定义 provider打开 ~/.codex/config.toml找到对应的 [providers.xxx]看 env_key 字段。Codex 会从该环境变量读取 token环境变量没导出、拼写不一致、shell 没重载都会导致 auth token is unavailable。我见过一个特别坑的情况用户在 .bashrc 里 export 了变量但是 Codex 是从桌面应用或者另一个 shell 启动的那个进程压根没继承环境变量。所以排查环境变量时最好在启动 Codex 的同一个终端里先确认变量存在再启动 Codex。顺便说一句配置文件里 provider 的写法可以参照下面这种wire_api 决定了 Codex 走 /responses 还是 /chat/completionsmodel deepseek-chat model_provider deepseek [providers.deepseek] name DeepSeek base_url https://api.deepseek.com/v1 env_key DEEPSEEK_API_KEY wire_api chat如果你把 wire_api 设成 chatCodex 会走 /chat/completions很多兼容端点反而能直接通。2.4 给模型层做一次“体检”的命令集与其反复猜不如直接模拟 Codex 的请求。下面这套命令可以帮你快速判断模型层是否健康# 确认本地转发服务监听正常端口按你的实际配置 curl -v http://127.0.0.1:PORT/v1/models \ -H Authorization: Bearer $YOUR_API_KEY # 模拟 Codex 的 Responses 请求本地转发必须能处理 curl -v http://127.0.0.1:PORT/v1/responses \ -H Content-Type: application/json \ -H Authorization: Bearer $YOUR_API_KEY \ -d {model:YOUR_MODEL,input:say hi} # 模拟 Chat Completions 请求对照是否有协议转换问题 curl -v http://127.0.0.1:PORT/v1/chat/completions \ -H Content-Type: application/json \ -H Authorization: Bearer $YOUR_API_KEY \ -d {model:YOUR_MODEL,messages:[{role:user,content:say hi}]}如果第 1 步都过不了说明本地转发服务根本没起来或端口不对去查 cc-switch 的进程状态。如果第 2 步失败但第 3 步成功那就是典型的协议转换问题。模型层通了之后“Codex 像聋子一样没有任何反应”的现象基本就消失了。接下来才轮到工具层。3. 第二刀MCP 工具链与浏览器桥接是否真正就绪3.1 Codex 侧 MCP server 注册检查模型层通了之后Codex 至少会“听得懂人话”。这时候再试“打开 example.com”如果还是没有任何工具调用迹象就要查 Codex 侧有没有正确注册浏览器 MCP server。Codex 的配置文件是 ~/.codex/config.tomlWindows 下是 %USERPROFILE%.codex\config.tomlMCP server 注册写在文件末尾[mcp_servers.playwright] command npx args [-y, playwright/mcplatest] [mcp_servers.chrome-devtools] command npx args [-y, chrome-devtools-mcplatest]这里有几个特别容易被忽略的点command 必须能被 PATH 解析到。如果你用 npx先确认 npx 在哪个目录、Codex 进程能不能访问到。有些桌面版 Codex 启动时 PATH 不完整npx 找不到MCP server 自然起不来。不要迷信“装了 MCP 包就行”。npx 第一次运行要现场下载包可能因为网络源问题卡住。建议先单独在终端跑一遍 npx -y playwright/mcplatest确认能进入正常等待状态不报错退出再把它交给 Codex。Windows 下写 MCP 命令注意 .cmd 后缀问题。有时候直接写 command npx 会找不到需要写成 command npx.cmd。3.2 浏览器扩展里的“MCP 连接”开关与被忽略的本地桥现在比较流行的方案是在 Chrome/Edge 里装一个 MCP 桥接扩展让 Codex 通过扩展直接操作当前浏览器窗口。这类扩展通常要求你在扩展设置里手动打开一个开关一般叫“启用 MCP 连接”或“Allow MCP”打开后扩展会在本地监听一个 WebSocket 端口。很多人卡在这一步扩展装好了但开关没开Codex 自然连不上。检查方法很简单地址栏访问 chrome://extensions找到对应扩展进入详情页确认“MCP 连接 / 从本地应用访问”这类选项处于启用状态看扩展图标有没有变成“已连接”状态大部分桥接扩展在未连接时会显示灰色角标打开扩展的详情面板看它实际监听的端口号是多少这个端口号要和 Codex 侧 MCP server 配置里的端口一致否则连不上。另外如果 Codex 是通过 chrome-devtools-mcp 直连浏览器它还要依赖 Chrome 的远程调试接口这正好引出下一层也是我遇到频率最高的一层坑。3.3 最小可复现测试让 Codex 打开一个页面工具层到底通没通不要直接上复杂任务先用一个无副作用的最小测试“请使用 playwright 工具打开 https://example.com等待页面完全加载然后返回页面的标题文字。”然后观察 Codex 的行为如果 Codex 开始调用工具但立刻报 tool execution failed说明工具层已经通了问题在执行层去下一节查如果 Codex 一直在“思考”但就是不调用工具说明模型可能返回了纯文本而不是工具调用问题大概率出在模型能力或上下文里工具描述缺失可以换个更强的模型再试如果 Codex 连工具名都提不出来说明 MCP server 没注册成功回到 3.1 重新检查。这一步是整个排查链路里最关键的分水岭。强烈建议把这次测试的输入输出截图或存日志因为后面每改动一个配置都要用它来验证效果。4. 第三刀被“托管”的浏览器与失效的远程调试端口4.1 浏览器被企业策略托管时的症状前面几层都查完Codex 也调用了工具但浏览器就是不响应这时候十有八九是浏览器进程本身的问题。第一个要查的就是你的 Chrome/Edge 是不是被“托管”了。怎么判断地址栏输入 chrome://management。如果页面显示“您的浏览器由 XX 组织管理”说明它被企业策略托管。托管状态下你可能遇到chrome://extensions 里的某些扩展被禁用无法手动启用远程调试端口被策略禁用--remote-debugging-port 参数不生效扩展内“MCP 连接”这类开关被置灰点不了某些网站的权限被锁死改不了就是大家常搜的“为什么我谷歌浏览器某个网站里面的权限没办法更改是被禁用的”这类问题。被托管的 Chrome 基本不能用作 Codex 的自动化载体。解决思路是准备一个独立的、非托管的浏览器实例最好是专门给自动化用的、干净的 user-data-dir不要和日常浏览器混用。4.2 --remote-debugging-port 为什么不生效这是执行层最常见的坑。Chrome 通过 CDP 暴露控制接口默认并不开着需要启动时带远程调试参数。标准写法是这样# Windows chrome.exe --remote-debugging-port9222 --user-data-dirC:\codex-chrome-profile # macOS /Applications/Google Chrome.app/Contents/MacOS/Google Chrome \ --remote-debugging-port9222 \ --user-data-dir/tmp/codex-chrome-profile # Linux google-chrome --remote-debugging-port9222 --user-data-dir/tmp/codex-chrome-profile但很多人发现带了这个参数Chrome 起来之后 9222 端口照样连不上。原因通常有两个用了默认用户目录。新版 Chrome 出于安全考虑如果检测到已有实例在运行新启动的进程会把命令转发给旧实例旧实例没有开调试端口所以你的参数没有生效。解决办法是必须指定一个全新的 --user-data-dir并且等上一个 Chrome 进程完全退出后再启动。端口被占用了。9222 是个很常见的端口可能有别的程序占着。验证方法curl http://127.0.0.1:9222/json/version如果返回 JSON 字符串里面有 webSocketDebuggerUrl说明调试端口正常。如果报连接拒绝继续往上查进程列表确认 Chrome 是否真的在跑以及启动命令里的端口号是不是 9222。另外新版 Chrome 还有一个细节headless 模式分为旧版 --headlessold 和新版 --headlessnew。新版 headless 是支持远程调试的但有些老脚本会强制旧 headless导致 CDP 行为异常。如果遇到 headless 下截图空白或者操作不响应可以试一下去掉 headless 参数用有头模式跑直观很多。4.3 用户目录、防火墙与多实例的连带问题就算端口开着还有几个连带问题经常让人抓狂防火墙拦截本地回环。某些安全软件会拦截 127.0.0.1 的流量Chrome 的调试接口连不上。测试时可以临时关掉安全软件确认也可以看防火墙日志里有没有本地回环拦截记录。浏览器是在子进程里被拉起的没继承启动参数。如果 Chrome 是从任务栏已运行实例或某些工具拉起启动参数里的调试端口无效。自动化专用的浏览器实例最好自己用命令行启动不要依赖默认快捷方式。MCP server 连接的是哪个端口。如果你用 chrome-devtools-mcp它的配置里默认连 9222。如果你改了端口MCP server 配置也要同步改两边不一致就会一直连接失败。执行层这块我吃过最大的亏就是“参数不生效”。后来我形成了习惯每次排查端口问题先杀掉所有相关浏览器进程再手工带参数启动一次等 curl 通了再做下一步。这能把变量降到最少。5. 执行层的真实故障现场打开页面之后的事5.1 无头模式、iframe、懒加载的经典陷阱到了这一层Codex 已经能通过工具控制浏览器了但任务依然可能失败。最常见的几种执行层故障元素找不到页面是动态渲染的数据从接口回来后才填到 DOM 里。如果你等待时间不够工具报 timeoutCodex 就会说“找不到元素”。解决办法是让工具等待更明确的出现条件比如等待某个文本或某个 selector 出现而不是固定 sleep 几秒。iframe 陷阱页面嵌了 iframe外层定位不到内层元素。Playwright 有 frame_locator但 Codex 不一定自动处理 iframe。如果你让它点一个在 iframe 里的按钮它会一直报无此元素。排查时可以给 Codex 追加提示“目标元素可能位于 iframe 内请先定位 iframe 再操作”。懒加载长列表页面上滚动才会加载更多Codex 如果不滚动直接找元素当然找不到。让它在查找前先滚动到底部或使用支持滚动加载的工具。无头模式headless 浏览器对某些站点可能表现异常比如被检测、视频无法播放。如果不需要无头优先用有头模式跑自动化人还能实时看到出了什么问题。5.2 登录态、弹窗和验证码的地狱控制浏览器还有一个让人崩溃的场景目标网站需要登录。Codex 自己不会帮你填验证码如果站点有复杂的人机验证指望它自动登录基本不现实。我的实践经验是不要指望 Codex 现场处理登录提前把登录态准备好。方案有三个用你日常浏览器登录好站点把 cookie 导出然后在自动化脚本里注入。这种方式最省事但注意 cookie 有时效过期了要重新导出。用一个专门给自动化准备的 user-data-dir手动登录一次之后启动浏览器都指向这个目录登录态就一直在。配合 --user-data-dir 使用效果很好。如果站点只有简单账号密码、没有复杂人机验证让 Codex 模拟输入账号密码偶尔也可以但别抱太大期望出错了还得回到前两个方案。弹窗方面要明确告诉 Codex遇到 dialog 弹窗时要主动处理接受或拒绝必须二选一。否则页面被弹窗卡住后续所有操作都会超时。5.3 等待策略与重试机制给自动化留点容错最后说一个影响体验的大问题Codex 驱动浏览器时单个动作失败往往直接导致整个任务失败。我常用的做法是在提示词里给足上下文“请逐步执行每一步操作后等待页面响应完成再执行下一步。如果元素查找超时先截图并调用控制台查看当前页面状态再决定是否重试。”给 Codex 一个“先截图再重试”的兜底策略它的成功率会明显提升因为很多失败其实是页面加载慢造成的瞬时问题。截图还能让你一眼看出执行到了哪一步排查时信息量非常大。另外如果频繁遇到页面加载慢可以考虑把浏览器 MCP server 配置里的超时参数调大。别小看这个细节我处理过的很多“Codex 控不了浏览器”最终都是超时时间太短导致的误报页面其实好好的只是工具等得不耐烦了。6. 最后聊聊我的排查习惯写到这里我想总结一个实操习惯而不是讲概念永远从最小通路开始验证。我第一次遇到这类问题时在工具层折腾了两天最后发现是模型层的本地转发服务没把 /responses 翻译过来Codex 压根没进入工具调用阶段。从那以后我的顺序永远是curl 通模型端点确认模型层通让 Codex 完成一次纯文本对话确认会话正常让 Codex 调用一个无副作用的 MCP 工具确认工具层通让 Codex 打开 example.com 截图确认执行层通再上真实目标页面处理登录态、iframe、等待策略等问题。每一步都有明确的通过标准没过就停在当前层解决不外溢。这个习惯帮我省掉了大量无效排查。如果你现在正被“Codex 控不了浏览器”折磨建议先把链路在脑子里过一遍然后用最小测试一刀一刀切别一上来就改配置。排查这种事稳比快重要日志比记忆靠谱。