让 Codex CLI 跑上 DeepSeek:用 LiteLLM 做 Responses↔Chat 协议翻译

结论先行:Codex CLI(实测 v0.143.0)只认 OpenAI 的 Responses API ,而 DeepSeek 只提供 Chat Completions。两者协议对不上,直接配必然 404。中间架一层 LiteLLM 代理做协议翻译,Codex 说 Responses、LiteLLM 转成 Chat 转发给 DeepSeek,链路就通了。本文给出的配置和命令可以直接照抄,所有踩过的坑都标在第四节。


一、为什么不能直接配

我一开始也以为改改 config.toml 就行,结果在三个坑里来回跳:

  1. 协议层面不兼容 。Codex v0.143.0 把 wire_api 写死为 responses,它发出的每个请求都是 POST /v1/responses。DeepSeek 的 OpenAI 兼容端点里只有 /v1/chat/completions,没有 /v1/responses。直连必 404。
  2. wire_api 没有可调空间 。新版 Codex 已经把 chat 选项删了,你设 chat 它直接拒绝加载配置;设 responses 配置能加载,但请求照样 404。翻这个字段纯属浪费时间。
  3. key 的字段名也有坑(见 4.1)。

所以根因不是配置写错,是两边协议不兼容,必须有个翻译层。


二、架构

flowchart LR A[Codex CLI<br/>wire_api=responses] -->|Responses<br/>/v1/responses| B[LiteLLM 本地代理<br/>127.0.0.1:8787] B -->|Chat<br/>/v1/chat/completions| C[DeepSeek API] C -->|Chat 响应 / SSE| B B -->|Responses 响应 / SSE| A

Codex 全程只跟本地 127.0.0.1:8787 通信,它以为自己在调 OpenAI;LiteLLM 负责把 Responses 语义翻成 Chat,再把 DeepSeek 的回答翻回 Responses。流式(SSE)和工具调用(function_call,Codex 跑命令/改文件就靠这个)都在翻译范围内。


三、前置条件

  • 一个本地空闲端口,本文用 8787
  • Python 3.12+(用 venv 隔离,别污染全局环境)。
  • 一个 DeepSeek API key(下文记为 YOUR_DEEPSEEK_API_KEY)。
  • Codex CLI 任意只支持 responses 的版本(v0.143.0 实测通过)。

四、踩坑记录(重点,先看这个再动手)

这些是我实际踩过的,照着避能省至少三轮往返:

4.1 env_key 不是 api_key Codex 的 config.toml 里,把整串 key 写进 env_key 是错的------Codex 会把它当环境变量名getenv(),于是报 Missing environment variable: sk-...。正确做法是直接内联:api_key = "sk-..."

4.2 wire_api = "chat" 已被新版 Codex 移除 设成 chat 会直接报 wire_api = "chat" is no longer supported,配置根本加载不了。别试。

4.3 wire_api = "responses" 直连 DeepSeek 必 404 这是协议根因,不是配置问题。设了能加载,但每个请求都打到 DeepSeek 不存在的 /v1/responses,全部 404。翻 wire_api 无解。

4.4 LiteLLM 模型前缀必须用 deepseek/,不能用 openai/ 这是最阴的一个坑。如果你在 litellm_config.yaml 里写 model: openai/deepseek-v4-flash,LiteLLM 会认为这是个"原生支持 Responses 的 OpenAI 兼容端点",直接把 /responses 原样转发给 DeepSeek → 又 404。只有写 model: deepseek/deepseek-v4-flash,LiteLLM 才会启用 Responses→Chat 的翻译逻辑。差一个前缀,天差地别。

4.5 只装 litellm 不够,要 litellm[proxy] 启动代理时若只 pip install litellm,会缺 backoff 等依赖而启动失败。务必装 extras:pip install "litellm[proxy]"

4.6 Codex 启动时的 Model metadata not found 警告无害 每次启动都会看到 ⚠ Model metadata for deepseek-v4-flash not found. Defaulting to fallback metadata。这是因为 deepseek-v4-flash 是自定义模型,不在 Codex 内置清单里,它走兜底元数据。聊天场景实际无影响,直接忽略。

4.7 代理是常驻后台进程 代理一旦退出(比如启动它的终端/会话关闭),Codex 就 404。下次用 Codex 前先手动把代理拉起来(见 3.3 的启动命令)。


五、实操步骤

5.1 建隔离环境并装依赖

bash 复制代码
python -m venv ~/.codex-proxy/venv

# Windows
~/.codex-proxy/venv/Scripts/pip install "litellm[proxy]" uvicorn

# macOS / Linux
~/.codex-proxy/venv/bin/pip install "litellm[proxy]" uvicorn

注意:Windows 的 venv 可执行文件在 Scripts/ 下(如 Scripts/litellm.exeScripts/pip.exe);类 Unix 在 bin/ 下。下文脚本按 Windows 路径写,类 Unix 自行替换。

5.2 写 LiteLLM 配置

~/.codex-proxy/litellm_config.yaml

yaml 复制代码
model_list:
  - model_name: deepseek-v4-flash
    litellm_params:
      model: deepseek/deepseek-v4-flash   # 关键:必须是 deepseek/ 前缀,见 4.4
      api_key: YOUR_DEEPSEEK_API_KEY
      drop_params: true

general_settings:
  disable_auth: true     # 本地代理,关掉鉴权
  telemetry: false

model_name 是暴露给 Codex 的模型名,要和 Codex 配置里的 model 对齐。drop_params: true 让 LiteLLM 丢掉 DeepSeek 不认识的参数,避免偶发报错。

5.3 启动代理

写个启动脚本 ~/.codex-proxy/start-proxy.sh(Windows 版):

bash 复制代码
#!/usr/bin/env bash
set -e
VENV="$HOME/.codex-proxy/venv"
CFG="$HOME/.codex-proxy/litellm_config.yaml"
exec "$VENV/Scripts/litellm.exe" --config "$CFG" --port 8787 --host 127.0.0.1

后台启动:

bash 复制代码
nohup bash ~/.codex-proxy/start-proxy.sh > ~/.codex-proxy/proxy.log 2>&1 &

等代理就绪(健康接口返回 200 即代表起来了,最多等 40 秒):

bash 复制代码
for i in $(seq 1 40); do
  code=$(curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:8787/health/liveliness 2>/dev/null)
  [ "$code" = "200" ] && { echo "proxy up after ${i}s"; break; }
  sleep 1
done

停掉代理:taskkill /IM litellm.exe /F(Windows)或 pkill -f litellm(类 Unix)。

5.4 改 Codex 配置指向代理

~/.codex/config.toml 里 provider 段改成:

toml 复制代码
model = "deepseek-v4-flash"
model_provider = "deepseek"
preferred_auth_method = "apikey"

[model_providers.deepseek]
name = "DeepSeek via local proxy"
base_url = "http://127.0.0.1:8787/v1"   # 指向本地代理,不是 DeepSeek 官网
api_key = "YOUR_DEEPSEEK_API_KEY"        # 代理关了鉴权,这里随便填也能通,但留着不碍事
wire_api = "responses"

base_url 从 DeepSeek 官网换成 127.0.0.1:8787/v1 是核心改动;wire_api 保持 responses 不动。


六、验证(三条 curl 全过才算通)

基础补全------应返回标准 Responses 结构(output 内含 message + output_text):

bash 复制代码
curl -s -X POST http://127.0.0.1:8787/v1/responses \
  -H "Content-Type: application/json" \
  -d '{"model":"deepseek-v4-flash","input":"Reply with exactly one word: hi"}'

工具调用------应返回 function_call(name 为 ping),这是 Codex 执行命令的关键能力:

bash 复制代码
curl -s -X POST http://127.0.0.1:8787/v1/responses \
  -H "Content-Type: application/json" \
  -d '{"model":"deepseek-v4-flash","tools":[{"type":"function","name":"ping","description":"return pong","parameters":{"type":"object","properties":{}}}],"input":"call the ping tool"}'

流式(SSE)------应看到 response.createdoutput_item.addedcontent_part.added 事件流:

bash 复制代码
curl -s -N -X POST http://127.0.0.1:8787/v1/responses \
  -H "Content-Type: application/json" \
  -d '{"model":"deepseek-v4-flash","input":"say hello in 3 words","stream":true}'

三条都正常,直接 codex 就能用 DeepSeek 干活了。


七、为什么是 LiteLLM 而不是降级 Codex

我也考虑过降级到还支持 wire_api = "chat" 的旧版 Codex,但那条路等于把自己锁死在旧版本上,以后 Codex 出新能力都用不了。LiteLLM 这层代理的好处是:Codex 永远用最新版,翻译逻辑独立演进,DeepSeek 换成别的 Chat 协议供应商也只是改一行 model 映射。代价是多跑一个本地进程------对这个场景来说,划算。

相关推荐
JavaGuide1 小时前
又一个画图 Skill 开源,再见手动画 draw.io!
前端·后端·github
吾诺1 小时前
2026大直径紧固件双叠自锁垫圈怎么选型
后端
tyung2 小时前
zhenyi-base zqueue 基准测试分享
后端·go
time展天12 小时前
Dddify:给 ASP.NET Core 项目一套轻量、清晰、可落地的 DDD 基础设施
数据库·后端·asp.net
SamDeepThinking2 小时前
为什么我会主动给一个年轻程序员加薪、晋升?
java·后端·程序员
tonydf2 小时前
接入服务商模式的支付宝支付
后端·支付宝
八苦2 小时前
MCP 企业托管授权(Enterprise-Managed Authorization)
后端
用户938515635072 小时前
Node.js 路径与文件系统:从基础到异步进化之路
javascript·后端·面试
一tiao咸鱼3 小时前
前端转 agent # 03 - Pydantic v2 数据校验深入
前端·后端