ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Codex 接入 DeepSeek 实战:协议适配与本地代理配置指南

Codex 接入 DeepSeek 实战:协议适配与本地代理配置指南 1. 为什么要在 Codex 里接 DeepSeek而不是继续用默认模型先把结论摆在前面Codex 这个命令行工具本身并不绑定任何一家模型服务它读取的是~/.codex/config.toml里的 provider 配置。默认情况下它走的是官方托管的模型通道但只要你把 provider 指向 DeepSeek 的 OpenAI 兼容接口就能用 DeepSeek 的模型来驱动整个 Codex 的对话、代码补全和工具调用流程。这件事的价值不在于省钱两个字那么简单而在于三点第一DeepSeek 的 API 价格相比主流闭源模型低一个数量级长上下文任务跑起来心理负担小很多第二DeepSeek 的deepseek-chat和deepseek-reasoner在代码理解和推理任务上表现相当能打尤其是带思维链的 reasoner 版本处理复杂重构和多步推理时思路清晰第三本地可控性强你可以随时切换模型、调整参数不用等官方灰度。但这里有个前提必须说清楚Codex 走的是Responses API风格的工具调用协议而 DeepSeek 对外暴露的是标准的Chat Completions API。这两者不是一回事。Responses API 是 OpenAI 后来推的一套新接口请求体结构和返回结构跟 Chat Completions 有差异尤其是工具调用tool calls和流式事件的格式。所以接入这件事的核心难点不是填个 API Key 就完事而是要解决协议适配问题。这也是为什么很多人照着网上教程改完config.toml之后会遇到cc switch local proxy failed while handling codex endpoint /responses这类报错——因为请求打到了 DeepSeek 的/chat/completions但 Codex 期望的是/responses的响应格式中间缺了一层转换。我自己的做法是在 Codex 和 DeepSeek 之间加一个轻量的本地代理层把 Codex 发出的 Responses 格式请求翻译成 DeepSeek 能吃的 Chat Completions 格式再把 DeepSeek 的返回翻译回 Responses 格式。这样 Codex 那边完全无感知DeepSeek 那边也只需要标准 API。下面我会把这个方案的每个环节拆开讲包括配置文件怎么写、代理怎么搭、报错怎么排。提示本文所有操作基于 Codex CLI 的通用配置逻辑不同版本的具体字段名可能有细微差异遇到codex is ignoring 1 unrecognized configuration setting这类警告时先确认你的 Codex 版本对应的配置字段名不要盲目照抄。适合读这篇的人已经在用 Codex、想换成 DeepSeek 后端但被协议问题卡住的刚装好 Codex、想一步到位配好 DeepSeek 的以及遇到 401、400、config.toml 加载失败等报错想找根因的。下面从环境准备开始一步步来。2. 动手前的环境盘点与 DeepSeek 侧准备2.1 Codex 的安装与版本确认Codex 的安装方式取决于你的平台。macOS 和 Linux 上通常通过包管理器或者官方提供的安装脚本获取Windows 上则多是通过 npm 全局安装或者下载独立安装包。不管你用哪种方式装完之后第一件事是确认版本因为不同版本的配置文件字段名和 API 协议支持程度不一样。在终端里执行codex --version如果这条命令报command not found说明安装路径没进 PATH或者根本没装成功。Windows 上常见的情况是 npm 全局目录没加到环境变量里这时候你需要手动把 npm 的全局 bin 目录加进去。确认版本之后再执行一次codex --help看看它支持哪些子命令和参数这一步能帮你判断当前版本是否支持自定义 provider。接下来是配置目录。Codex 默认读取用户主目录下的.codex文件夹配置文件是config.toml。在 Windows 上路径类似C:\Users\你的用户名\.codex\config.tomlmacOS 和 Linux 上是~/.codex/config.toml。如果这个文件不存在Codex 首次运行时会尝试生成一个默认配置但有时候生成失败或者权限不对就会导致chatgpt 无法加载 config.toml 因此此对话串无法继续这种问题。我的建议是不要等它自动生成直接手动创建这个文件内容从最小可用配置开始写。2.2 DeepSeek API Key 的获取与验证DeepSeek 的 API Key 在官方平台的控制台里创建创建时会给你一串以sk-开头的字符串。这里有个高频坑很多人复制 Key 的时候带上了多余的空格或者换行导致请求时出现unexpected status 401 unauthorized: incorrect api key provided: sk-svcac****。注意看这个报错里的sk-svcac前缀——这其实不是 DeepSeek 的 Key 格式DeepSeek 的 Key 通常是sk-后面跟一串字符没有svcac这种段。如果你看到这个报错八成是把别的平台的 Key 填进来了或者 Key 被截断了。拿到 Key 之后先别急着往 Codex 里填用 curl 单独验证一下这个 Key 能不能正常调用 DeepSeekcurl https://api.deepseek.com/chat/completions \ -H Content-Type: application/json \ -H Authorization: Bearer sk-你的Key \ -d { model: deepseek-chat, messages: [{role: user, content: 你好}], stream: false }如果返回正常的 JSON 且里面有choices字段说明 Key 和网络都没问题。如果返回 401检查 Key 是否复制完整如果返回 400 且提示this organization has been disabled那是账号层面的问题跟配置无关如果返回this models maximum context length is 1048576 tokens这类错误说明你请求的上下文超了模型上限需要精简输入。这一步单独验证非常关键因为它能把Key 的问题和Codex 配置的问题彻底分开省得后面排查时两头猜。2.3 确认 DeepSeek 的接口地址与模型名DeepSeek 的 OpenAI 兼容接口基地址是https://api.deepseek.comChat Completions 的完整路径是/chat/completions。模型名主要有两个deepseek-chat对应通用对话模型deepseek-reasoner对应带推理链的模型。这两个名字必须写准确写错了会返回模型不存在的错误。有些教程里会提到deepseek-coder那是早期版本现在统一用deepseek-chat就行。另外要注意DeepSeek 的接口是标准 Chat Completions 格式不是 Responses 格式。这意味着如果你直接把 Codex 的 provider base URL 指向https://api.deepseek.comCodex 会往/responses发请求而 DeepSeek 那边根本没有这个端点结果就是 404 或者代理层报cc switch local proxy failed while handling codex endpoint /responses。所以我们必须引入代理层这一点在下一节详细展开。3. 核心症结Responses API 与 Chat Completions 的协议鸿沟3.1 两套协议到底差在哪要理解为什么不能直连得先搞清楚 Responses API 和 Chat Completions 的结构差异。Chat Completions 是大家最熟悉的格式请求体里有model、messages、tools、stream这些字段返回体里是choices[].message工具调用放在message.tool_calls里。而 Responses API 是 OpenAI 后来推的一套更有状态的接口它的请求体里用input代替messages用instructions代替 system 消息工具调用的结构也变成了output数组里的一系列 item每个 item 有自己的type比如function_call、message、reasoning等。这个差异带来的直接后果是Codex 发出的请求体DeepSeek 看不懂DeepSeek 返回的响应体Codex 也解析不了。具体表现就是各种报错——要么是unexpected status 401其实是请求根本没到对的地方要么是api error: 400请求体字段对不上要么是代理层直接抛failed while handling codex endpoint /responses。所以代理层的核心任务就一个做双向翻译。3.2 代理层要处理的四类转换我在实际搭建代理时把转换逻辑拆成四块这样排查问题时能快速定位是哪一块出了岔子转换类型Codex 侧ResponsesDeepSeek 侧Chat Completions请求体字段input数组messages数组系统指令instructions字段messages里 role 为 system 的项工具定义tools里的type: functiontools里的type: function结构略有不同响应解析output数组里的 itemchoices[].message第一块是请求体字段映射。Codex 发过来的input可能是一个字符串也可能是一个消息数组代理层要把它统一转成messages格式。如果input是字符串就包成一条 user 消息如果是数组就逐项转换注意 role 的映射关系。第二块是系统指令。Responses API 把系统提示单独放在instructions字段里而 Chat Completions 要求系统提示作为messages数组的第一项role 为system。代理层要把instructions取出来插到messages最前面。第三块是工具定义。两边都支持 function 类型的工具但字段名有差异。Responses 里工具的参数 schema 放在parameters下Chat Completions 也是parameters但外层结构不同。这块要仔细对照否则模型会收不到工具定义导致该调用工具的时候不调用。第四块是响应解析也是最容易出问题的一块。DeepSeek 返回的是choices[].message里面可能有content和tool_calls。代理层要把它转成 Responses 格式的output数组文本内容转成type: message的 item工具调用转成type: function_call的 item。如果这一步转错了Codex 会认为模型没返回任何有效内容表现为对话卡住或者工具不执行。3.3 流式响应的特殊处理如果开启了流式stream事情会更复杂一点。Chat Completions 的流式返回是一系列data:开头的 SSE 事件每个事件里是 delta 增量。Responses API 的流式事件类型更多有response.output_text.delta、response.function_call_arguments.delta等等。代理层需要把 DeepSeek 的 delta 事件重新包装成 Responses 的事件类型并且维护好事件顺序和结束标记。我的经验是第一次搭建时先用非流式跑通确认请求和响应转换都正确再开流式。因为流式一旦出错报错信息往往很模糊你很难判断是转换逻辑错了还是事件顺序错了。非流式跑通之后流式的调试就有了基准。4. 配置文件 config.toml 的逐字段拆解4.1 最小可用配置的结构Codex 的config.toml用的是 TOML 格式结构上分几个层级。最外层是全局设置然后是model_providers表定义各个 provider最后是profiles或者直接指定当前使用的 provider。一个指向本地代理的最小配置大概长这样model deepseek-chat model_provider deepseek-proxy [model_providers.deepseek-proxy] name DeepSeek via local proxy base_url http://127.0.0.1:8787/v1 wire_api responses env_key DEEPSEEK_API_KEY [profiles.default] model deepseek-chat model_provider deepseek-proxy这里几个字段要重点解释。base_url指向你的本地代理地址注意末尾的/v1Codex 会在这个基础上拼/responses所以你的代理要能处理/v1/responses这个路径。wire_api指定用哪种协议这里写responses因为 Codex 内部就是按 Responses 协议发请求的代理层负责转成 Chat Completions。env_key指定从哪个环境变量读 API Key这样 Key 不用明文写在配置文件里更安全。4.2 那些容易写错导致被忽略的字段热词里有个很典型的报错codex is ignoring 1 unrecognized configuration setting. check for typos or deprecated settings. user (c:\users\丁子洋.codex\config.toml): mcp_servers.node_repl.type is ignored.这个报错的意思是配置文件里有个字段 Codex 不认识被忽略了。具体到这个例子是mcp_servers.node_repl.type这个字段在当前版本里不被支持。这类问题的根源通常是你抄的教程对应的是旧版本字段名在新版本里改了或者这个字段根本不属于当前配置层级。处理这类问题的原则是先删掉不认识的字段让配置能跑起来再逐个加回来验证。不要一次性写一大堆字段然后指望它全对。我一般会先用最小配置跑通确认能对话之后再一项一项加功能每加一项测一次。这样出问题时能立刻定位到是哪一项导致的。另外要注意 TOML 的语法细节字符串要用引号包起来布尔值是小写true/false表头用[方括号]。缩进不影响解析但影响可读性。如果 TOML 语法错了Codex 会直接报加载失败也就是chatgpt 无法加载 config.toml 因此此对话串无法继续。这时候用在线 TOML 校验器过一遍能快速找到语法错误。4.3 环境变量与 Key 的注入方式把 API Key 放在环境变量里比写在配置文件里安全得多。设置方式因平台而异# macOS / Linux export DEEPSEEK_API_KEYsk-你的Key # Windows PowerShell $env:DEEPSEEK_API_KEYsk-你的Key # Windows CMD set DEEPSEEK_API_KEYsk-你的Key注意这种方式设置的环境变量只在当前终端会话有效。要让 Codex 每次启动都能读到需要写进 shell 的配置文件如.bashrc、.zshrc或者 Windows 的系统环境变量里。我踩过的坑是在 PowerShell 里set了一下然后换了个终端窗口跑 Codex结果 Key 读不到报 401。后来统一写进系统环境变量才稳定。还有一个细节env_key里写的变量名必须和实际设置的环境变量名完全一致大小写敏感。写DEEPSEEK_API_KEY但环境变量设的是deepseek_api_key照样读不到。5. 本地代理的搭建与请求转发逻辑5.1 为什么必须自己搭代理有人会问能不能不搭代理直接把 Codex 的 base_url 指向 DeepSeek答案是不能原因在第三节已经说了——协议不兼容。DeepSeek 没有/responses端点Codex 也不会说 Chat Completions。所以中间必须有一层做翻译。这层代理可以是一个简单的 Python 脚本用 FastAPI 或者 Flask 起一个本地服务监听某个端口接收 Codex 的请求转换后转发给 DeepSeek再把响应转回来。代理的另一个好处是你可以在这一层做日志记录、请求重试、参数调整。比如 DeepSeek 的temperature默认值和 Codex 期望的不一样你可以在代理层统一改掉。又比如你想在请求里加一些固定的系统提示也可以在代理层注入不用改 Codex 的配置。5.2 代理的核心转发代码结构下面是一个最小代理的骨架用 Python 的 FastAPI 写重点是展示转换逻辑的位置from fastapi import FastAPI, Request import httpx app FastAPI() DEEPSEEK_URL https://api.deepseek.com/chat/completions app.post(/v1/responses) async def handle_responses(request: Request): body await request.json() # 第一步把 Responses 请求转成 Chat Completions 请求 chat_body convert_request(body) # 第二步转发给 DeepSeek async with httpx.AsyncClient(timeout120) as client: resp await client.post( DEEPSEEK_URL, jsonchat_body, headers{Authorization: fBearer {get_api_key()}} ) # 第三步把 Chat Completions 响应转回 Responses 格式 responses_body convert_response(resp.json()) return responses_bodyconvert_request函数负责字段映射把input转成messages把instructions插到最前面把tools的结构对齐。convert_response负责反向转换把choices[].message转成output数组。这两个函数是代理的核心也是最容易出 bug 的地方。我的建议是给这两个函数写单元测试用真实的请求和响应样本做输入输出验证这样改代码时不怕改坏。5.3 处理 401 和 400 的排查路径代理跑起来之后最常见的两类报错是 401 和 400。401 基本是 Key 的问题排查顺序是先确认环境变量有没有设对再确认代理转发时有没有把 Authorization 头带上最后确认 Key 本身有没有过期或被禁用。热词里那个incorrect api key provided: sk-svcac****就是典型的 Key 不对注意sk-svcac这个前缀不是 DeepSeek 的格式说明填错了 Key。400 的原因更多样。可能是请求体字段缺失比如messages为空可能是模型名写错比如写成了deepseek-coder可能是上下文超限报maximum context length is 1048576 tokens也可能是账号层面的问题报this organization has been disabled。排查 400 的关键是看返回体里的error.message它会告诉你具体哪里不对。代理层最好把 DeepSeek 的原始错误信息透传回来不要吞掉否则你只能看到 Codex 侧的模糊报错。6. 联调实测从报错到跑通的完整链路6.1 第一次启动的预期与意外配置写完、代理起好之后第一次跑 Codex 大概率不会一次成功。我的经验是准备好面对三类问题配置加载问题、网络连接问题、协议转换问题。配置加载问题表现为 Codex 启动就报无法加载 config.toml这时候用 TOML 校验器查语法。网络连接问题表现为代理收不到请求或者代理转发超时这时候先用 curl 直接测代理端口通不通。协议转换问题表现为 Codex 能连上代理但对话没反应或者报格式错误这时候看代理的日志对比转换前后的请求体。我建议在代理层加详细的日志把收到的原始请求和转换后的请求都打出来响应也一样。这样出问题时能一眼看出是哪一步转换错了。日志级别先开到 DEBUG跑通之后再调回 INFO。6.2 工具调用不生效的排查工具调用是 Codex 的核心能力如果代理层转换工具定义时出了错模型就收不到工具表现为该调用工具的时候直接返回文本。排查方法是在代理日志里确认转发给 DeepSeek 的请求体里有没有tools字段结构对不对。然后看 DeepSeek 的返回里有没有tool_calls如果有但 Codex 没执行说明是响应转换的问题。还有一个隐蔽的坑DeepSeek 的deepseek-reasoner模型在工具调用上的行为和deepseek-chat不完全一样reasoner 会先输出一段推理内容再决定是否调用工具。代理层要能正确处理这种带推理内容的响应把推理内容转成 Responses 的reasoningitem否则 Codex 可能会把推理内容当成普通文本显示出来看起来很奇怪。6.3 长上下文任务的稳定性观察DeepSeek 的上下文窗口很大但实际使用中我发现当对话轮次很多、上下文很长时响应延迟会明显增加偶尔还会超时。代理层的超时设置要相应调大我一般设 120 秒。另外Codex 在长对话里会不断累积历史消息如果代理层不做裁剪请求体会越来越大最终可能触发上下文超限。一个实用的做法是在代理层加一个简单的历史裁剪逻辑保留最近 N 轮对话或者按 token 数估算裁剪。7. 几个高频报错的根因与处置7.1 config.toml 加载失败chatgpt 无法加载 config.toml 因此此对话串无法继续这个报错根因通常是三种文件不存在、语法错误、权限不足。文件不存在就手动创建语法错误用 TOML 校验器查权限不足在 Windows 上比较常见检查文件是否被其他程序占用或者当前用户有没有读权限。还有一种情况是文件编码不对TOML 要求 UTF-8如果文件是 GBK 编码中文注释会导致解析失败。7.2 模型不支持的报错the gpt-5.6-sol model is not supported when using codex with a...这类报错说明 Codex 配置里指定的模型名不被当前 provider 支持。如果你接的是 DeepSeek模型名必须是deepseek-chat或deepseek-reasoner不能写 OpenAI 的模型名。检查config.toml里model字段和profiles里的model字段确保都改成了 DeepSeek 的模型名。7.3 代理端点处理失败cc switch local proxy failed while handling codex endpoint /responses这个报错说明代理层在处理/responses请求时抛异常了。可能的原因包括请求体解析失败、转换函数报错、转发超时。排查方法是看代理的异常堆栈定位到具体是哪一行代码抛的。如果是转换函数报错检查输入数据的结构是否符合预期加一些防御性判断比如字段不存在时给默认值。8. 跑通之后的调优与日常维护8.1 参数调优的取舍跑通之后可以开始调参数。temperature影响输出的随机性代码任务建议设低一点0.2 到 0.5 之间比较稳。max_tokens控制单次输出长度设太小会导致回答被截断设太大浪费额度根据任务类型调整。top_p一般保持默认就行。这些参数可以在代理层统一设置也可以在 Codex 的配置里透传看你的偏好。8.2 代理的稳定性维护代理是个长期运行的服务要考虑它的稳定性。我的做法是用 systemd 或者 supervisor 把它托管起来挂了自动重启。日志要定期轮转避免占满磁盘。如果代理跑在本机注意端口不要和别的服务冲突。如果想让局域网内其他机器也能用把监听地址从127.0.0.1改成0.0.0.0但要注意做好访问控制别让不相干的人蹭你的 Key。8.3 版本升级时的注意事项Codex 和 DeepSeek 的接口都可能升级。Codex 升级后Responses API 的字段可能有变化代理层的转换逻辑要跟着改。DeepSeek 升级后Chat Completions 的返回结构也可能微调。所以升级前先看更新日志升级后跑一遍回归测试。我一般会保留一份能跑通的配置和代理代码作为备份升级出问题时能快速回滚。最后分享一个我自己的习惯把config.toml和代理代码都放进版本控制每次改动都提交。这样出问题时能 diff 出改了什么也能随时回到上一个可用版本。这个习惯帮我省了好几次重头排查的时间。
返回列表