Codex CLI 接入 DeepSeek:用 Moon Bridge 给 ChatGPT 账号加一个模型(2026)
起因
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 是开源的,本地就有源码,直接读。一路跟:
routes.<alias>.model里存的这个值(当时配置文件里就是openai.gpt-5.5),会被当成"要在 provider 的 offer 列表里查的 key"- 如果没查到匹配的 offer(没查到——offer 列表里当时存的是不带前缀的
gpt-5.5),就直接把这个原始字符串原样当成最终请求体里的model字段发出去 - 发给谁?
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 搭建你的第一个服务器》。
下一步
- 想知道 Codex CLI 日常怎么用、context 怎么管理 → 《Claude Code 实际工作流》,Claude Code 和 Codex CLI 是同一类工具,工作流思路可以互相借鉴
- 还没解决怎么稳定访问 ChatGPT/Claude → 《如何稳定访问 ChatGPT 和 Claude》
- 想更进一步,把模型完全部署在自己的服务器上,不依赖任何第三方 API → 《在 VPS 上跑 Ollama,搭一个私有大模型 API》
- 需要一台稳定跑常驻服务的机器 → 《用 $6 VPS 搭建你的第一个服务器》