我给 Qoder 接了一个 Codex 调度层:模型、额度、代理、异步 Agent 和生图一次解决
环境:Windows + WSL2 Ubuntu + Qoder CN + ChatGPT Plus + Codex CLI
记录时间:2026 年 9 月
目标:让 Qoder 负责理解与调度,优先把实际开发工作交给 ChatGPT Plus 中的 Codex;Codex 不可用时,再回退到 Qoder 自身能力。
前言
最开始,我只是想解决一个看起来很简单的问题:
我已经有 ChatGPT Plus,能不能直接在 Qoder CN 里调用 Codex 干活?
最后却一路搭成了这样一套系统:
Qoder CN
↓
用户级 Rule
↓
用户级 codex-work Skill
↓
Codex Router MCP
↓
Codex app-server
↓
ChatGPT Plus
├─ Coding
└─ built-in image_gen
而网络又单独隔离成:
Qoder CN
→ Windows 直连
Codex
→ WSL
→ GOST
→ SOCKS5
→ OpenAI
最后得到的体验已经不只是"在 IDE 里调用另一个模型"。
更接近:
Qoder 是项目总管,Codex 是默认执行 Agent。
它能自己查额度、选模型、后台执行、判断任务是不是卡住,甚至能直接生成图片并把图片放进项目。
一、最终想实现什么
理想状态是:
我:
"帮我把这个 Bug 修了"
Qoder:
↓
判断这是开发任务
↓
检查 Codex 额度
↓
选择合适模型
↓
取得当前项目 cwd
↓
启动 Codex
↓
后台等待
↓
拿到结果
↓
告诉我改了什么
如果问题很简单:
Luna Max
解决不了再逐级:
Luna Max
↓
Terra Max
↓
Sol High
↓
Sol Max
↓
Astra High
↓
Astra XHigh
↓
Astra Max
如果 Codex 额度耗尽:
Codex 不可用
↓
Qoder 接管
如果我要做一个页面:
"把这个 Landing Page 做完整,缺的 Hero 图自己生成"
则希望:
Codex
├─ 分析代码
├─ 生成 Hero 图
├─ 保存图片
├─ 修改前端
├─ 接入图片
└─ 最终检查
这就是整套方案最后的目标。
二、第一个问题:Qoder CN 和 OpenAI 网络冲突
Qoder CN 在 Windows 直连环境中正常工作。
但是 Codex / ChatGPT 无法直接连接 OpenAI。
最简单的想法当然是:
打开 Windows VPN
但实际变成:
OpenAI ✓
Qoder CN ×
Qoder 自己的网络反而被影响。
所以最终没有让整个 Windows 走 VPN。
而是把网络拆开。
三、网络隔离:只让 WSL 里的 Codex 走代理
最终架构:
Windows
│
├─ Qoder CN
│ └─ 正常直连
│
└─ WSL2 Ubuntu
│
└─ Codex
↓
GOST
↓
SOCKS5
↓
OpenAI
这样:
Qoder
完全不知道 VPN 的存在。
只有:
Codex
访问 OpenAI 时才经过代理。
这是整套系统最重要的基础设计之一。
四、为什么最终选择 GOST
中间试过:
OpenVPN
IKEv2 / strongSwan
Privoxy
但都有各种兼容问题。
最后选择 GOST,把:
带用户名密码认证的 SOCKS5
转换成:
WSL 本地 HTTP Proxy
最终 Codex 只需要访问:
http://127.0.0.1:8118
上游认证全部由 GOST 接管。
五、GOST 作为 systemd 后台服务
GOST 最终被做成:
gost-codex.service
监听:
127.0.0.1:8118
上游 SOCKS5 的账号密码放在:
/etc/gost/gost.json
权限限制:
sudo chown root:gost /etc/gost
sudo chmod 750 /etc/gost
sudo chown root:gost /etc/gost/gost.json
sudo chmod 640 /etc/gost/gost.json
这样正常用户无法直接读取代理密码。
测试:
curl -x http://127.0.0.1:8118 \
-I --max-time 15 \
https://chatgpt.com
如果首先看到:
HTTP/1.1 200 Connection established
Proxy-Agent: gost/3.0
就说明:
WSL
→ GOST
→ SOCKS5
→ OpenAI
已经建立连接。
之后即使 Cloudflare 对 curl 返回 403 challenge,也不代表代理失败。
六、Codex 使用 ChatGPT Plus 登录,不使用 API Key
Codex 安装完成后:
codex login --device-auth
浏览器完成设备授权。
检查:
codex login status
返回:
Logged in using ChatGPT
这里没有使用:
OPENAI_API_KEY
而是直接使用 ChatGPT Plus 登录态。
七、给 Codex 做统一代理入口
为了避免每次启动都手工导出代理环境变量,创建:
~/.local/bin/codex-proxy
核心逻辑:
#!/usr/bin/env bash
export HTTP_PROXY="http://127.0.0.1:8118"
export HTTPS_PROXY="http://127.0.0.1:8118"
export http_proxy="$HTTP_PROXY"
export https_proxy="$HTTPS_PROXY"
export NO_PROXY="127.0.0.1,localhost"
export no_proxy="$NO_PROXY"
exec "$HOME/.local/bin/codex" "$@"
从此所有 Codex 启动都统一经过:
codex-proxy
八、第一次在 Qoder 中调用 Codex
最开始直接用了 Codex 自带的:
codex mcp-server
Qoder 通过 STDIO MCP 启动:
{
"mcpServers": {
"codex": {
"type": "stdio",
"command": "C:\\Windows\\System32\\wsl.exe",
"args": [
"-d",
"Ubuntu",
"--",
"/home/<USER>/.local/bin/codex-proxy",
"mcp-server"
]
}
}
}
测试:
只回复 CODEX_MCP_OK
成功得到:
CODEX_MCP_OK
至此最基础链路跑通:
Qoder
→ wsl.exe
→ Ubuntu
→ Codex
→ GOST
→ SOCKS5
→ OpenAI
九、第二个坑:Codex 默认 cwd 是 Qoder 安装目录
连接成功后马上发现一个非常重要的问题。
Codex 的:
pwd
不是当前项目。
而是类似:
/mnt/d/Users/.../Qoder CN/.qoder-versions/...
也就是:
Qoder 自己的安装目录。
当前项目实际是:
D:\ComfyUI-H3
对应 WSL:
/mnt/d/ComfyUI-H3
显式传:
cwd: /mnt/d/ComfyUI-H3
之后 Codex 才能正确看到:
studio/
ComfyUI/
_tools/
...
因此得出一个非常重要的结论:
所有 Codex 调用必须显式传 cwd。
十、Windows → WSL cwd 转换
规则很简单。
Windows:
C:\Users\Test\App
对应:
/mnt/c/Users/Test/App
Windows:
D:\Projects\Demo
对应:
/mnt/d/Projects/Demo
算法:
盘符转小写
↓
/mnt/<盘符>/
↓
\ 转 /
例如:
E:\AI\Project
变成:
/mnt/e/AI/Project
后来这个转换规则被直接放进了全局 Rule。
十一、为什么后来不再依赖 codex mcp-server
codex mcp-server 能用,但它已经提示:
deprecated
更重要的是,我需要的东西更多:
查模型
查额度
指定 reasoning
后台执行
任务状态监控
超时处理
取消任务
fallback
于是最终改成:
Qoder
↓
自定义 Codex Router MCP
↓
Codex app-server
十二、Codex app-server 能做什么
首先测试:
model/list
实际拿到了当前 Plus 账号的模型目录:
GPT-6-Astra
GPT-5.6-Sol
GPT-5.6-Terra
GPT-5.6-Luna
GPT-5.5
GPT-5.4-Mini
而且不是只返回模型名。
还包括:
defaultReasoningEffort
supportedReasoningEfforts
例如:
GPT-6-Astra
low
medium
high
xhigh
max
ultra
因此 Router 无需把模型写死。
十三、直接读取 Codex 剩余额度
调用:
account/rateLimits/read
还能得到当前 ChatGPT Plus 的 Codex 使用情况。
某次测试实际返回:
planType: plus
5 小时窗口:
usedPercent: 2
remainingPercent: 98
windowDurationMins: 300
周窗口:
usedPercent: 46
remainingPercent: 54
windowDurationMins: 10080
以及:
resetCreditsAvailable: 3
于是 Router 增加:
codex_usage
以后 Qoder 可以直接问:
Codex 还剩多少额度?
不需要猜。
十四、Router 第一阶段
最初 Router 只有:
codex_models
codex_usage
旧 MCP:
codex
继续保留。
于是:
codex
→ 旧官方 MCP
codex-router
→ 新 Router
这一步没有直接删除旧方案。
事实证明这种"旧方案保底、新方案并行测试"的方式非常稳。
十五、同步 codex_run 为什么失败
随后加入:
codex_run
逻辑:
Qoder
→ codex_run
→ thread/start
→ turn/start
→ 等 Codex 完成
→ 返回
在 WSL 里直接调用:
ROUTER_DIRECT_OK
完全正常。
但 Qoder 中却报:
MCP_PROTOCOL_REQUEST_TIMEOUT
一度怀疑:
代理坏了?
VPN 掉了?
OpenAI 网络不通?
但绕过 Qoder 直接调用 Router:
ok: true
status: completed
durationMs: 15325
问题终于明确:
Codex 花了 15 秒。
对于 MCP 客户端来说,一个 Tool Call 一直不返回几十秒甚至几分钟,很容易被判超时。
十六、关键设计:Codex 必须异步执行
于是同步:
codex_run
被废弃。
换成:
codex_start
codex_job_status
codex_job_result
codex_cancel
流程变成:
Qoder
↓
codex_start
↓
立即返回 jobId
后台:
Codex 持续工作
Qoder
↓
codex_job_status
↓
检查进度
完成
↓
codex_job_result
这一下彻底解决长任务 MCP timeout。
十七、怎么知道 Codex 还在工作,还是已经卡死
单纯:
running
远远不够。
于是每个 Job 都保存:
state
health
stage
processAlive
startedAt
lastActivityAt
elapsedSeconds
secondsSinceLastActivity
lastEvent
threadId
turnId
任务 state:
queued
starting
running
completed
failed
timeout
cancelled
health:
healthy
stale
suspect
dead
例如:
state = running
health = healthy
lastActivity = 3 秒前
说明:
Codex 正常工作。
如果:
running
stale
表示:
worker 仍存活,但暂时没新事件。
如果:
running
suspect
表示:
已经长时间没有 Codex 活动,需要警惕。
如果:
dead
则表示:
后台 worker 已退出。
这样 Qoder 不会再出现:
一直转圈,但没人知道到底发生了什么。
十八、异步链路实测
测试:
只回复 ASYNC_CODEX_OK
实际 Job:
starting
↓
running
↓
working
↓
completed
最终:
ASYNC_CODEX_OK
任务总时间约:
40 秒
没有再触发 Qoder 的 MCP timeout。
至此异步架构正式成立。
十九、最终 Router 提供的 6 个工具
当前:
codex_models
codex_usage
codex_start
codex_job_status
codex_job_result
codex_cancel
分别负责:
codex_models
→ 查询当前可用 Codex 模型
codex_usage
→ 查询 Plus Codex 剩余额度
codex_start
→ 启动后台 Codex Job
codex_job_status
→ 查看 Job 是否正常
codex_job_result
→ 获取最终结果
codex_cancel
→ 真正终止后台任务
旧:
codex
MCP 现在已经关闭,但配置仍保留作为备用。
日常只开启:
codex-router
二十、模型路由策略
最终没有让 Qoder 随便"凭感觉"选模型。
而是定了一套明确路线。
Luna Max:默认主力
gpt-5.6-luna
max
负责:
查文件
搜代码
读项目
看日志
单文件分析
普通 Bug
一般功能
普通 UI
Python / JS / TS
测试
大多数日常开发
也就是:
大部分任务先给 Luna Max。
Terra Max:中间升级
gpt-5.6-terra
max
主要用于:
明显多文件
多模块联动
前后端共同排查
中型重构
复杂工具链
Luna 已认真尝试但没解决
Sol:复杂开发
默认:
gpt-5.6-sol
high
更难:
gpt-5.6-sol
max
负责:
困难跨模块 Bug
大型重构
性能问题
并发问题
复杂架构
Terra 无法解决的问题
Astra:最终王牌
默认:
gpt-6-astra
high
再升级:
xhigh
max
主要用于:
极复杂端到端问题
大范围 Repo 分析
系统级架构
Sol 仍无法解决
用户明确要求最强
自动路由默认不使用:
ultra
最终顺序:
Luna Max
↓
Terra Max
↓
Sol High
↓
Sol Max
↓
Astra High
↓
Astra XHigh
↓
Astra Max
二十一、为什么还需要 Skill 和 Rule
只有 MCP 还不够。
否则每次都必须说:
请调用 codex-router
cwd=/mnt/d/...
sandbox=...
model=...
这显然不是理想体验。
所以最终增加两层:
用户级 Skill
+
用户级 Always-On Rule
二十二、用户级 Skill:codex-work
codex-work 是整个 Codex 工作流的大脑。
它负责:
判断任务
↓
codex_usage
↓
决定是否能使用 Codex
↓
codex_models
↓
选模型
↓
codex_start
↓
jobId
↓
codex_job_status
↓
codex_job_result
↓
整理结果
同时负责:
模型升级
额度判断
异常 fallback
Job 取消
图片生成
图片编辑
代码 + 图片混合任务
也可以手动:
/codex-work
强制走 Codex 工作流。
二十三、用户级 Always-On Rule:codex-routing
Rule 则负责:
"什么时候自动使用 codex-work?"
它是:
用户级
全局
always-on
所以打开任何 Qoder 项目都会自动加载。
它负责:
判断是不是开发任务
↓
获取当前 workspace
↓
Windows → WSL 路径转换
↓
决定权限
↓
调用 codex-work
因此平时直接说:
帮我修这个 Bug
就够了。
不需要:
/codex-work
二十四、Rule 的 cwd 是动态的
因为是全局 Rule,所以绝不能写死某个项目:
/mnt/d/ComfyUI-H3
而是动态获取 Qoder 当前 workspace。
例如:
D:\ComfyUI-H3
变:
/mnt/d/ComfyUI-H3
换项目:
E:\Projects\TestApp
自动:
/mnt/e/Projects/TestApp
因此整套配置真正做到:
一次设置,所有项目使用。
二十五、权限也交给 Rule 自动判断
如果用户说:
看看为什么出错
分析一下
检查代码
Review 一下
自动:
sandbox = read-only
approval_policy = never
如果用户说:
帮我修了
直接改好
实现这个功能
重构这里
自动:
sandbox = workspace-write
approval_policy = never
用户不用关心这些参数。
二十六、后来发现:Codex 还能直接生图
这是今天最意外的一部分。
测试让 Codex 使用:
$imagegen
并明确要求:
built-in image_gen
结果:
成功。
而且完全没有:
OPENAI_API_KEY
图片真实落盘到:
D:\ComfyUI-H3\_tools\imagegen-test.png
对应 WSL:
/mnt/d/ComfyUI-H3/_tools/imagegen-test.png
实际文件:
PNG
1254 × 1254
约 1.56 MB
内容与提示一致。
这不是 Codex "说它生成了"。
而是实际在文件系统中验证了文件存在。
二十七、生图链路
实际链路:
Qoder
↓
codex-routing Rule
↓
codex-work Skill
↓
codex-router
↓
codex_start
↓
Codex
↓
$imagegen
↓
built-in image_gen
↓
PNG
↓
当前 workspace
没有 API Key。
没有单独 API 调用。
使用的是当前 Codex / ChatGPT Plus 登录环境。
二十八、图片任务也纳入 Skill
后来进一步把图片能力正式写进:
codex-work
现在 Skill 支持三类任务。
1. 纯代码
Bug
功能
重构
测试
分析
2. 纯图片
背景图
插画
Hero
产品图
海报素材
封面
UI 配图
视觉资产
图片编辑
优先:
$imagegen
+
built-in image_gen
明确禁止自动 fallback 到需要:
OPENAI_API_KEY
的方案。
3. 代码 + 图片混合任务
例如:
把这个页面做完整,缺的 Hero 图自己生成
允许 Codex 在同一个工作流中:
理解代码
↓
生成图片
↓
保存图片
↓
修改代码
↓
引用图片
↓
检查结果
这已经非常接近网页端 Work 的实际体验。
二十九、图片任务为什么也必须异步
图片生成可能需要:
1 分钟
2 分钟
甚至更长
实际一次 Luna Max + imagegen 测试:
约 135 秒
如果同步 MCP Tool Call:
大概率超时。
所以图片任务仍然严格使用:
codex_start
↓
codex_job_status
↓
codex_job_result
而不是重新做同步工具。
三十、图片任务的权限规则
如果只是:
设计提示词
规划视觉
分析风格
则:
read-only
如果实际:
生成图片
保存图片
编辑图片
则:
workspace-write
生成内容默认只能落在当前 workspace。
三十一、Rule 也增加了图片自动路由
全局 codex-routing Rule 后来同样升级。
现在这些请求:
生成图片
做个背景
做封面
生成 Hero
做一张插画
修改这张图
给页面补图
都会自动判断为:
视觉任务
然后进入:
codex-work
→ Codex imagegen
用户不需要手动写:
调用 imagegen
三十二、混合任务是最有意思的
例如直接对 Qoder 说:
把这个产品页面做完整,缺的视觉素材自己生成。
理论工作流:
Qoder
↓
Rule 判断为开发 + 视觉
↓
codex-work
↓
检查 quota
↓
选择模型
↓
Codex
├─ 阅读页面
├─ 设计素材
├─ imagegen 生图
├─ 保存图片
├─ 修改组件
├─ 接入图片
└─ 检查结果
这已经不再像:
"一个代码补全工具"。
而更像:
"一个能交付结果的 Agent"。
三十三、最终整体架构
用户
│
▼
┌───────────┐
│ Qoder CN │
└─────┬─────┘
│
User Always-On Rule
│
codex-routing
│
▼
codex-work
User Skill
│
┌────────────────┼───────────────┐
│ │ │
▼ ▼ ▼
Code Routing Image Routing Quota / Model
│ │ │
└────────────────┼───────────────┘
│
▼
Codex Router MCP
│
┌───────────────┼───────────────┐
│ │ │
▼ ▼ ▼
models usage async jobs
│
codex_start
│
job_status
│
job_result
│
▼
Codex app-server
│
┌────────────┴────────────┐
│ │
▼ ▼
Coding $imagegen
│
▼
built-in image_gen
│
▼
codex-proxy
│
▼
GOST
│
▼
SOCKS5
│
▼
OpenAI
三十四、最终使用体验
现在我基本不需要考虑:
MCP
cwd
WSL
proxy
model
quota
sandbox
jobId
imagegen
只需要说:
帮我看看这个 Bug。
或者:
直接帮我修好。
或者:
把这个页面做完整,缺的图片也一起生成。
剩下的:
网络
模型
额度
路径
权限
后台任务
图片生成
全部由规则和 Skill 处理。
三十五、这次实践里最值得记住的经验
1. 网络应该按 Agent 隔离
不要为了一个 AI 工具,把整个操作系统流量一起代理。
WSL 是一个非常好的隔离层。
2. MCP Server 的 cwd 不能想当然
"当前 IDE 项目"并不意味着 MCP 子进程的 cwd 也是当前项目。
必须显式处理。
3. 长任务必须异步
AI Agent 不是普通函数。
它可能运行几十秒、几分钟甚至更久。
因此:
start
status
result
比:
call → 等到结束
稳得多。
4. running 不等于健康
真正成熟的 Agent Job 至少需要:
state
health
heartbeat
lastEvent
processAlive
elapsed
5. 模型应该路由,而不是一刀切
不是所有问题都要 Astra。
合理路线应该是:
便宜模型承担大多数工作
↓
失败或复杂度上升
↓
逐级升级
6. IDE Agent 不应该只会写代码
真正有意思的是:
代码
+
图片
+
文件
+
项目上下文
+
任务状态
组合在一起。
当 Codex 能自己生成素材再写进项目时,Agent 的价值会明显提升一个层级。
三十六、目前还差什么
目前最值得继续补的是:
codex_continue
现在每个:
codex_start
仍然会创建新 thread。
后面如果支持:
codex_continue(threadId, prompt)
就可以做到:
第一轮:
分析问题
↓
threadId
第二轮:
继续修
↓
同一个 thread
第三轮:
测试失败,再继续
↓
仍然同一个 thread
这样复杂任务的连续性会更好。
之后还可以继续增加:
codex_jobs
查看历史任务,以及:
模型使用统计
平均耗时
成功率
升级次数
甚至把 Router 整理成真正独立的项目。
结语
今天最开始的问题只是:
"能不能让 Qoder 用我的 ChatGPT Plus Codex?"
最后却变成了一套:
IDE
+
MCP Router
+
WSL
+
Proxy Isolation
+
ChatGPT Login
+
Quota Awareness
+
Model Routing
+
Async Agent Jobs
+
Health Monitoring
+
Image Generation
+
Global Skill
+
Global Rule
真正让我觉得这件事有价值的地方,是最终每层职责都非常清楚:
Qoder 负责理解与调度。
Rule 负责自动判断什么时候该走 Codex。
Skill 负责定义完整工作流。
Router 负责模型、额度和后台任务。
Codex 负责真正执行代码与视觉工作。
GOST + WSL 负责把网络问题隔离掉。
最终用户真正看到的,却只剩一句:
"帮我把这个东西做完。"
剩下的系统自己处理。