发布于 2026 年 10 月 22 日· 13 分钟阅读

Codex CLI 接入 DeepSeek:用 Moon Bridge 给 ChatGPT 账号加一个模型(2026)

Codex CLIDeepSeekMoon BridgeAI 编程ChatGPT反向代理

起因

Codex CLI 的 /model 只给你选 OpenAI 自家的几个型号。想要"同一个 CLI、同一套上下文,临时切到 DeepSeek 跑一段",官方是没有这个选项的。

解法不难想:起一个本地反向代理,Codex 以为自己在跟 OpenAI 说话,代理在后面按你选的模型分流——GPT 系列继续走你自己的 ChatGPT 账号,DeepSeek 系列走你自己的 DeepSeek key,两笔账互不影响,Codex 的模型选择器里就能看到两家的型号并排放着。

做这层代理不用自己写,Moon Bridge 已经是个现成的开源协议转换网关:对外伪装成 OpenAI 的 Responses API,对内认 Anthropic Messages、Google GenAI、OpenAI Chat Completions 等好几种上游协议,自带一个 web console。接进 Codex 只用改两个配置文件。

配起来那天晚上顺手写了个脚本,从 Codex 官方文档抓最新模型列表同步进 Moon Bridge 的配置——写的时候没测全,几周后一次很平常的 /review 直接把当时用的 Claude Code 会话打断了,报错长这样:

{"detail":"The 'openai.gpt-5.5' model is not supported when using Codex with a ChatGPT account."}

这篇把"怎么接" + "这个报错到底是怎么回事"一起记一下。如果你是直接搜这行报错来的,可以直接跳到下面"根因"那一节。

关键词(可以展开学习): 反向代理、协议转换、OpenAI Responses API、model alias 路由


适用前提

  • Codex CLI 已经装好,用 ChatGPT 账号(Plus / Pro / Business)登录能正常用——本文不改这部分
  • 有一个 DeepSeek(或其他 Anthropic 协议兼容服务)的 API key
  • 愿意在本机(或一台常开的机器)跑一个常驻的本地服务,端口默认 38440

接起来

1. 装 Moon Bridge,起一个最小配置

moonbridge --config ~/.codex/moonbridge/config.yml

配置文件核心是三块:models(元数据)、providers(上游怎么连)、routes(外部看到的别名 → 内部用哪个 provider 的哪个模型):

providers:
  # 真正的 ChatGPT/Codex 后端。requires_openai_auth(下面 Codex 那边配)会让
  # Codex 把自己的 ChatGPT OAuth token 转发过来,Moon Bridge 原样透传——
  # api_key: INBOUND_AUTH 的意思就是"用客户端发来的那个 token,不是我自己的 key"。
  openai:
    protocol: openai-response
    base_url: https://chatgpt.com/backend-api/codex/responses
    api_key: INBOUND_AUTH
    offers:
      - model: gpt-5.6
      - model: gpt-5.5
  # 你自己的 DeepSeek key,独立计费,跟上面那个账号无关
  deepseek:
    protocol: anthropic
    base_url: https://api.deepseek.com/anthropic
    api_key: sk-your-deepseek-api-key
    offers:
      - model: deepseek-v4-pro

routes:
  gpt-5.6:
    model: gpt-5.6
    provider: openai
  ds-v4-pro:
    model: deepseek-v4-pro
    provider: deepseek

完整版(含 cache、web_search、可选的 web console 持久化)见文末仓库里的 config.example.yml。

2. 告诉 Codex 走这个代理

~/.codex/config.toml:

model = "gpt-5.6"
model_provider = "moonbridge"

[model_provider_by_model]
"gpt-5.6" = "moonbridge"
"gpt-5.5" = "moonbridge"
"ds-v4-pro" = "moonbridge"

[model_providers.moonbridge]
name = "Moon Bridge"
base_url = "http://127.0.0.1:38440/v1"
requires_openai_auth = true
wire_api = "responses"

重开 Codex,/model 里就能同时看到 gpt-5.6 和 ds-v4-pro——选哪个,账单就走哪家。

3. 让它常驻

用 launchd(macOS)或 systemd(Linux)管理,进程挂了自动拉起。Linux 上一个最简单的 systemd unit:

[Unit]
Description=Moon Bridge

[Service]
ExecStart=/path/to/moonbridge --config /home/you/.codex/moonbridge/config.yml
Restart=always

[Install]
WantedBy=default.target

到这一步已经能用了。后面记的是三个真实踩过、而且不太直觉的坑。


根因:一个从 AWS 文档里抄错的前缀

配好几周后的某天,/review 跑到一半直接断了,报错是文章开头那行:

{"detail":"The 'openai.gpt-5.5' model is not supported when using Codex with a ChatGPT account."}

先排除的是"网络问题""token 过期"这些常规嫌疑——都正常。可疑点在报错里那个模型名本身:openai.gpt-5.5。Codex 官方模型名从来不带 openai. 这个前缀,这个字符串是从哪冒出来的?

Moon Bridge 是开源的,本地就有源码,直接读。一路跟:

  1. routes.<alias>.model 里存的这个值(当时配置文件里就是 openai.gpt-5.5),会被当成"要在 provider 的 offer 列表里查的 key"
  2. 如果没查到匹配的 offer(没查到——offer 列表里当时存的是不带前缀的 gpt-5.5),就直接把这个原始字符串原样当成最终请求体里的 model 字段发出去
  3. 发给谁?https://chatgpt.com/backend-api/codex/responses——真正的 ChatGPT 账号后端,它只认裸模型名

也就是说,这个 openai. 前缀不是哪层加密/编码,就是个错字符串,被原样送到了不认识它的地方。

那这个前缀最早是哪来的?追配置生成脚本,发现它是从 Codex 官方文档里"扫描反引号包起来的字符串,凡是像模型名的都收进来"这么个粗暴的正则抓出来的。回去翻官方文档原文,还真找到了这两个字符串:

openai.gpt-5.5
openai.gpt-5.4

但上下文是 AWS Bedrock 那一节——Bedrock 给第三方模型加厂商前缀是它自己的命名习惯,跟 ChatGPT 账号走的那条线路完全是两码事。脚本的正则没有区分"这行是在讲 Bedrock"还是"这行是在讲 ChatGPT 账号",两种命名法混进了同一份模型列表,然后被无差别用在了所有地方。

修法就是把这个人为加的前缀去掉,让 alias 和真正发给上游的模型名保持一致:不加前缀、不做任何转换,gpt-5.5 就是 gpt-5.5。

怎么确认自己是不是也中招:把配置文件(不管是不是用 Moon Bridge,任何做协议转换的本地代理都可能有类似设计)里 routes 部分实际发给上游的那个字段单独挑出来看一眼,跟官方文档给的模型名逐字比对,尤其警惕任何"看起来像是加了层命名空间"的前缀。


坑二:持久化打开后,YAML 改了也不生效

修完上面那个之后,想顺便看看 Moon Bridge 自带的 web console(http://127.0.0.1:38440/console/)——打开发现啥数据都没有。

配置里当时确实没开持久化,补上:

persistence:
  active_provider: db_sqlite
extensions:
  db_sqlite:
    enabled: true
    config:
      path: ./data/moonbridge.db

重启,console 有数据了。但紧接着又冒出一个更隐蔽的问题:改完 routes 里的某个模型映射,重启后 API 查出来的还是改之前的值——文件明明改了,读到的却是旧的。

翻源码才发现:这个"持久化存储"只在第一次启动、发现存储是空的时候才从 YAML 播种数据;只要种过一次,它就变成权威数据源,后续所有请求(包括真实转发请求,不只是管理 API)都读它,YAML 文件之后再怎么改都不会被重新加载。

这个设计单独看是合理的——web console 本来就是让你在线改配置、不希望被外部文件覆盖。但如果你的习惯是靠脚本改 YAML 再重启生效(比如下一节的模型同步脚本),这两个功能会互相打架,改了跟没改一样,而且没有任何报错提示你。

解法:要么两个功能二选一(要么只用脚本管 YAML、不开持久化;要么开了持久化之后只通过 console/API 改配置,不再碰 YAML),要么每次用脚本改完之后,手动删掉持久化的数据库文件,让它在下次启动时重新从 YAML 播种(会丢掉之前的用量统计,但配置最简单可靠)。


坑三:脚本重启时和进程守护抢端口

如果 Moon Bridge 是被 launchd / systemd 这类守护进程管着的(设了 KeepAlive / Restart=always),手动写的重启逻辑通常是"杀掉旧进程 → 自己重新拉起一个"。这一步在守护进程存在时会出问题:你杀掉的那个瞬间,守护进程自己也会去拉起一个新的,两边同时抢同一个端口,日志里会看到一条 address already in use。

不影响最终结果(守护进程那边总能抢到),但会在日志里留一堆误导性的报错。改法是杀掉旧进程后先等一下、检查端口是不是已经有人重新监听了,只有确实没人接管才自己动手拉起——这样不管有没有守护进程都能正确工作,不用额外配置去区分两种情况。


怎么确认现在用的到底是哪个模型

踩完上面三个坑之后,多留了个心眼:Codex 界面上显示的"当前模型",只是它自己读的配置文件里那个别名,不代表请求真的发到了哪——毕竟这次的三个坑,两个都是"配置说的和实际发生的不一样"。

靠谱的做法是看代理自己转发完之后打的日志。Moon Bridge 每次转发完一个请求,会记一行日志,里面同时有你选的别名、真正发给上游的模型名、和用了哪个 provider:

model=gpt-5.5 actual_model=gpt-5.5 provider=openai ...
model=ds-v4-pro actual_model=deepseek-v4-pro provider=deepseek ...

盯着这行看,比信任界面上的文字标签靠谱得多:

tail -f ~/.codex/logs/moonbridge.log | grep --line-buffered 请求完成

发一条消息,立刻就知道这次到底花的是哪份钱、用的是哪个模型——model 和 actual_model 对不上、或者 provider 不是你以为的那个,就是配置出了问题的信号。


一张表看清整条链路

你在 Codex /model 里选的 Moon Bridge 实际转发的模型 走的 provider 花谁的钱
gpt-5.6 gpt-5.6 openai(真实 ChatGPT 后端) 你的 ChatGPT 订阅额度
gpt-5.5 gpt-5.5 openai 你的 ChatGPT 订阅额度
ds-v4-pro deepseek-v4-pro deepseek 你的 DeepSeek API key

只要中间那列跟第一列对不上,就是本文前两个坑里说的那种情况——先去看日志,再回头查配置。


模型同步脚本

Codex 官方模型列表会更新(比如某天多了个 gpt-5.7),手动改配置麻烦。写了个 Ruby 脚本自动比对官方文档和本地配置:

# 拉官方文档,对比本地配置,交互式确认要新增/删除哪些模型
codex-sync-model

# 手动加一个模型(比如新的 DeepSeek 型号)
codex-sync-model deepseek-v5-pro deepseek --keep-default

脚本、完整的 config.example.yml,连同本文踩过的三个坑对应的修复,都放在 GitHub 上:

→ github.com/baiyedev/baiye-scripts/codex-moonbridge


常见问题

这样用会不会违反 ChatGPT 的服务条款? 这层代理只是本地转发:GPT 系列请求原样带着你自己的 ChatGPT OAuth token 发给官方后端,走的还是你自己账号的正常额度,代理不缓存、不转售、不共享给别人。DeepSeek 请求完全走独立的 DeepSeek key,跟 ChatGPT 账号没有任何关系。是不是符合你所在地区/账号类型的条款,建议自己核对一遍 OpenAI 的使用协议。

一定要用 DeepSeek 吗,能换别的模型吗? Moon Bridge 支持 Anthropic Messages、Google GenAI、OpenAI Chat Completions 好几种上游协议,理论上只要能用其中一种协议访问的模型都能接进来,配置方式跟本文的 DeepSeek 例子一样,换个 base_url 和 api_key。

Moon Bridge 一定要跑在装 Codex 的这台机器上吗? 不用,base_url 换成公网可达的地址(自己套一层认证)就行。如果本来就有一台常开的 VPS,跑在那上面比本地更省心——参考《用 $6 VPS 搭建你的第一个服务器》。


下一步