Featured image of post 我把 Qoder 接进了 Codex:OpenCodex 接入、提速与图片串题排查

我把 Qoder 接进了 Codex:OpenCodex 接入、提速与图片串题排查

一次 Windows 实机折腾记录:我用 OpenCodex 接入 Qoder CN,从 502、工具标签和黑框,修到会话续接、空会话预启动、国内接口直连,以及历史图片的轮次归属。附真实截图与首字延迟对照。

我最开始只想做一件事:继续用 Codex 的界面和工具,但模型可以切到我的 Qoder CN 账号,最好连模型列表和额度也一起交给 OpenCodex 管。

登录并不难。真正花时间的是后面:先是 502,接着模型把 <tool_call> 当正文发出来,好不容易能对话了,每次又弹一个黑框。修完这些,同样一句“你好”,两条 DeepSeek 路线的响应速度又差了好几秒。

最后还有一个很容易漏掉的问题:我这一轮明明没发图,它却说“你发的两张图我也看了”。这篇把整个过程连起来,也写清楚哪些已经验证,哪些不能下结论。

本文记录的是 2026 年 9 月 20 日至 22 日这台 Windows 电脑上的适配。OpenCodex 的本地修改按当时安装的 2.60.0 核对。它包含自定义桥接代码和本地补丁,不是装完官方软件就自动具备的功能,也不是一键安装教程。升级软件后需要重新验收。

封面根据实际界面截图生成,标了“界面参考合成图”;正文使用真实的模型菜单和额度截图。登录链接、令牌等认证信息没有公开。

Codex、OpenCodex 和 Qoder 各自做什么

Codex 是聊天和执行任务的入口。OpenCodex 是本机的提供商代理与管理界面。Qoder CN 则是另一套账号、CLI 和模型服务。

这套方案连接的是 Qoder CLI,通过 SDK 交换消息,不涉及嵌入 Qoder IDE。Qoder 的 SDK 架构文档也明确区分了本地 CLI 进程和远端模型服务。

请求路径如下:

Codex 界面、任务历史、工具执行与审批
    ↓ Responses 请求
OpenCodex · 127.0.0.1:10100
    ├─ deepseek → DeepSeek API
    ├─ xiaomi   → 小米 MiMo API
    └─ qoder-cn → 本地桥接器 · 127.0.0.1:3000
                       ↓ Qoder Agent SDK
                   Qoder CN CLI
                       ↓
                   Qoder 模型服务

这里需要区分两种“代理”:OpenCodex 和桥接器是本机协议转发;HTTP_PROXY 指向的则是网络出站代理。让国内接口直连,取消的是后者,不是把 OpenCodex 从链路中移除。

OpenCodex 项目提供多提供商接入和管理面板。本文里的 Qoder 会话桥接、启动联动等是这次本机适配的部分,不能据此认为上游发行版已经包含全部修改。

登录成功,只是第一步

先在 PowerShell 登录 CLI:

qodercn login

浏览器授权后,终端报告登录成功。但这只说明 CLI 拿到了账号状态,并不能证明 Codex 到模型服务的整条链路都通了。

当时 Codex 报过这样的错误:

502 Bad Gateway: Provider unreachable: Unable to connect
url: http://127.0.0.1:10100/v1/responses

遇到这个错误,先确认本地服务是否启动、端口是否监听。本文使用了下面两个地址,其他环境应按实际配置替换:

Invoke-RestMethod http://127.0.0.1:10100/healthz
Invoke-RestMethod http://127.0.0.1:3000/health
Get-NetTCPConnection -LocalPort 10100,3000 -State Listen

健康检查只能确认服务是否响应。接下来还要发一条真实消息、调用一个简单工具,并检查工具结果回来后能否继续回答。

能说话以后,为什么工具还是用不了

早期最明显的症状,是模型直接输出了一段像这样的文字:

<tool_call>
  {"name":"read_file","arguments":{"path":"<临时提示词文件>"}}
</tool_call>

界面没有真的读取文件,回答就在这里结束了。另一种表现是它说“我先读取这张图片”,然后没有下文。

这段文字没有触发工具执行。Codex 需要结构化的工具事件,还需要调用 ID、参数和结果能正确对应。只把普通文本接通,离完整的编程代理还差一段。

修复后,桥接器使用 SDK 的结构化消息:普通文字按流式片段转发,工具请求转换成客户端能识别的调用,Codex 执行和审批后,再把真实结果送回原会话。桥接层不伪造成功结果,也不在另一边偷偷执行一遍同名工具。

修复后,界面开始连续显示“已读取聊天”“运行了命令”“已读取技能”和文件读取记录。一次网站检查可以连续做下去,不必每次等它停住后再补一句“继续”。

图片也是同一个道理。把图片变成一句“用户上传了截图”,不能代替图片像素。桥接器需要传递实际图像内容;遇到不支持图片的模型,应明确报错,而不是让它猜。

每次弹出的黑框,来自 CLI 子进程

对话接通后,每发一条消息都会冒出一个黑色控制台。窗口标题指向 Qoder CN 的 CLI 可执行文件。

处理位置在启动子进程的代码里。下面是关键选项示意,不能单独替代整个桥接器:

spawn(command, args, {
  cwd,
  env,
  windowsHide: true,
  shell: false,
  stdio: ['pipe', 'pipe', 'pipe'],
});

这个改动只隐藏控制台窗口,输入输出管道、取消信号和进程清理都保留。SDK 请求、额度查询和其他 CLI 启动路径也要检查,否则很容易修好对话,却在刷新额度时又弹一次。

两条 DeepSeek 路线,首字差了多久

两条路线都输入“你好”,响应时间却不同。模型菜单里的名字相近,也不能据此认定请求完全一样。

这次对照让两条路线同时发出同样的短问候,使用相同的低推理设置和各自独立的任务 ID。每一对请求的发出时间相差约 1 毫秒,两边都返回了“你好!”。计时从请求发出开始,直到收到第一段非空答案文字。

请求DeepSeek API 路线首字Qoder DeepSeek 路线首字
首次使用0.600 秒6.714 秒
续聊一1.016 秒1.958 秒
续聊二0.912 秒2.240 秒

这只是三组本机样本,不是性能排名。直连路线当时配置的是 V4 Flash,Qoder 目录给出的标识是 DeepSeek-Flash,目前无法确认它们是否使用完全相同的底层模型版本。提供商内部提示、排队和推理过程也可能不同。

进一步查看桥接内部的计时,三轮中,本地 HTTP 转发层相对 SDK 首段文字只多出约 24、20、18 毫秒。因此,当时剩下的几秒等待,不能再解释成“本地攒完答案才显示”。

计时按 OpenAI 流式响应文档区分答案增量和其他事件:连接建立、心跳、思考状态都不算首字。也没有通过提前显示一句假话,让数字看起来更快。

续用会话,减少重复初始化

早期每轮新开 CLI 进程,都要重新初始化,还可能重发整段历史。界面虽然留在同一个聊天框,后端却又从头开始处理。

现在桥接器会把 Codex 任务与 Qoder 会话对应起来。匹配时续用现有会话,只补发新增消息;需要恢复时,使用对应的会话 ID,而不是随意接到“最近一次聊天”。

不同任务的会话保持隔离。账号、模型、工作目录、系统指令、工具定义和推理参数发生变化,都可能要求隔离或重建。省掉启动时间,不能以串任务、串账号为代价。

切换模型时保留 Codex 的可见历史,但不同模型的缓存不能视为通用。实测从 Kimi 切到 Qoder DeepSeek,需要给新会话初始历史;切回仍可复用的 Kimi 时,则补入中间新增的消息。历史被编辑或配置不匹配时,要安全重建。

这减少了重复传输,却不能直接换算成“省了多少计费 Token”。当时部分 SDK 用量字段返回零,无法据此计算可靠的计费节省比例。旧历史留在模型上下文里,也不意味着后续推理免费。

预启动空会话,随 OpenCodex 一起退出

最终采用的办法是提前初始化一个空会话,等真正提问时再使用。它不发送“你好”之类的占位请求,也不为了测试连通性偷偷跑一次推理。

预启动只准备一个配置匹配的空会话,不会把所有模型一起打开。第一次安装使用的默认配置,未必匹配下一次任务;模型、工具或系统指令不一致时,仍需正常初始化。

启动和退出按以下规则处理:

  • 启动 OpenCodex 服务,才启动和准备 Qoder 桥接。
  • 普通 Codex 的启动方式不变,不因此额外启动 Qoder。
  • 退出 OpenCodex 服务,关闭桥接器及它自己启动的 CLI 子进程。
  • 只关闭管理网页,不等于停止后台 OpenCodex 服务。

测试退出时还发现了一个 Windows 时序问题:SDK 的 close() 返回,子进程未必已经完全退出。因此,清理流程增加了等待子进程退出的步骤。隔离测试用的是模拟服务进程,没有为了验收把正在用的 Codex 桌面强行关掉。

一次预启动命中的短问候,首字约 3 秒;模拟拥有者退出后,桥接器和 CLI 约 1.3 秒内结束。另一轮后续测试分别是 2.619 秒和 0.562 秒。这些是不同轮次的小样本,不能拿来承诺每次都一样。

空会话仍占内存,所以热会话数量有上限,空闲后也会回收。保留时间从 60 秒改到 5 分钟,减少读完回答再打字时触发冷启动的次数;代价是内存占用持续得更久。

配置国内直连,继续排查其他等待

排查时确实发现 OpenCodex 配了 127.0.0.1:7897,却没有国内接口绕过列表。环境里也有 HTTP_PROXY、HTTPS_PROXY 和 ALL_PROXY。

已配置的 DeepSeek、小米 MiMo 和本地接口加入了 noProxy。下面是局部配置示意,应合并进原配置,别拿它覆盖账号和提供商内容:

{
  "noProxy": [
    "localhost", "127.0.0.1", "::1",
    "api.deepseek.com", "api.xiaomimimo.com",
    "qoder.cn", ".qoder.cn"
  ]
}

Qoder CN 子进程则清除了继承的出站代理变量,并单独设置绕过环境;没有改整台电脑的代理,也没有改变国外接口的路由。用 OpenCodex 自己的路由解析器检查,国内配置地址返回 direct,ChatGPT 地址仍返回 proxy。

排查时曾把继承代理变量当成请求走代理的证据,这个判断不够严谨。Qoder CN TypeScript SDK 文档写明:没有显式配置 proxy 时,SDK 集成的 CLI 自身默认直连;继承的代理变量仍可能影响工具和子进程。不同版本还要以实际运行行为为准。

这次确认的是应用层路由策略已修正,真实请求也能成功;后续提速不能全归因于去掉代理。系统级 TUN/VPN 是否转发到代理节点,也不由这段 noProxy 配置保证。

慢请求的日志里还出现过多轮工具调用,以及一次约 1.42 MB 的历史重传。任务总耗时、单轮首字时间、模型思考和工具运行,必须分开看。后面网站检查显示的 4 分 47 秒包含连续操作,不能拿它当一次纯聊天的首字延迟。

本轮没发图,回答却提起旧截图

还有一次,最后一条消息只问了功能和 UI 建议,没有附图,回答却又讨论起以前的截图。

检查记录显示,那一轮调用的是读取网站样式和模板的命令,没有调用读图工具。它说“我看了两张图”,不能作为本轮确实发生读图的证据。

桥接代码也存在歧义:重建会话时,会把历史消息中的图片收集起来,附在同一次 SDK 输入后面。正文虽然保留了引用,但模型看到的是同一批附件,容易把旧图误认为本轮刚上传的图。

直接丢掉历史图片会影响后续追问,例如“刚才那张图呢”。这次保留了图片,改为逐张标注来源:

[Image m1:p2; source_role=user; scope=HISTORICAL_IMAGE]
[Image m5:p2; source_role=user; scope=CURRENT_USER_ATTACHMENT]
[Image m6:p1; source_role=tool; scope=TOOL_OUTPUT_IMAGE]

这里的编号对应原始消息和内容块。每张实际图片旁边都有同一份标签,历史正文也使用它。本轮没有附件时,输入会明确说明“本轮没有新图片”;历史图片仍然保留,但只有与当前问题相关时才应该分析。工具返回的图片也不能冒充用户新上传的附件。

同一个有效会话续聊时,沿用已有的增量输入机制,不重复发送旧像素;切换模型或重建会话时保留图片及其来源。完全没有图片历史的纯文字对话,不添加这段额外图片说明。

Kimi 的四轮实测结果如下:

操作实际回答
发一张模型菜单图,要求只回复收到收到
同会话问本条是否附带新图片没有
强制新建会话并带回历史,再问本条是否附带新图片没有
追问最早那张图里的一个 Kimi 模型标识qoder-cn/kimi-k3

这次修改后,自动测试共 189 项通过,包含新旧图混合、工具图像、增量续聊和重建标记。四轮真实请求也通过,但它只是一次功能验收,不代表从此不会再跑题。

这样既能区分图片来源,也保留了追问旧图的能力,正常续聊无需重复传图。代价是桥接代码更复杂,有图片历史时多了一小段说明;重建仍可能需要传回旧图。尚未做足够的对照测试,暂时无法量化 Token 和耗时的变化。

模型列表和额度终于放到一起了

Codex 中实际显示的 Qoder CN 模型菜单

实际运行的模型菜单。目录能显示,不等于每个模型的文字、图片、工具调用都已逐项验收。

接入完成后,Codex 的模型菜单可以选择 Qoder CN 路线,OpenCodex 可以查看 Qoder 的套餐、组织资源包和其他提供商的用量。额度读取用到了 SDK 的 getUsageInfo(),不是用本地对话 Token 估算账户余额。

OpenCodex 的 Qoder CN 提供方页面,显示连接状态、套餐额度和组织资源包

截图时的额度快照。右侧同时显示读取来源 qoder-sdk · getUsageInfo 和配额更新时间,数值不代表当前余额。

查看额度时要留意“上次检查”的时间。界面显示两小时前检查,就代表缓存结果,不能把它当成实时账单。接口返回未知或不支持,也不应该画成“还剩零额度”。

当时不是每个模型都通过了验收:Kimi 的工具往返和续聊有真实测试;Qwen3.8 Flash 出现过超时,不能因为出现在菜单里就写成“全模型完美兼容”。图片能力也需要单独测试。

接入后的检查清单

一句“你好”只能验证最基本的对话。完整接入还需要检查这些情况:

  1. CLI 登录和模型请求分别测试,先确认账号与服务。
  2. 检查 OpenCodex 和桥接器的监听端口,再测整条路线。
  3. 先验证文字增量,再验证真实工具调用与结果续接。
  4. 单独测试图片,以及“这一轮没图、上一轮有图”。
  5. 测同任务续聊、切模型、切回、历史编辑和会话恢复。
  6. 分开记录冷启动、热会话首字、工具耗时和任务总时间。
  7. 检查应用层代理规则,不用“国内模型”四个字推断实际网络路径。
  8. 退出 OpenCodex 后检查子进程,升级后重新检查本地补丁。

桥接器应只监听回环地址,不向外部网络开放;本机接口按需要配置认证。账号文件、令牌、完整请求历史和本地预启动配置都不应该跟着教程上传。使用远端模型时,任务所需的提示、文件片段和工具结果仍可能发往提供商,“本地桥接”不等于数据全在本机。

目前,这套接入已经能在 Codex 中使用 Qoder CN 模型,完成带工具和图片的连续任务。大历史重建的成本、不同模型的长任务稳定性,仍需要在实际使用中继续观察。

评论

聊聊你的想法,使用 GitHub 账号参与讨论。

在 GitHub 查看

滚动到此处时加载评论。