OpenAI 的 Codex CLI 是一个强大的终端 AI 助手,但它默认只能使用 OpenAI 的模型。本文介绍如何通过自建 AI Gateway,让 Codex CLI 无需修改任何代码即可使用 DeepSeek、通义千问、Kimi、智谱 GLM 等国产大模型,并完整保留推理过程展示、工具调用、网络搜索等高级功能。
背景:为什么需要一个网关?
Codex CLI 是 OpenAI 推出的命令行 AI 编程助手,类似于 Claude Code,但它基于 OpenAI 的 Responses API 协议与模型通信。国产大模型普遍兼容的是 Chat Completions API,两者在请求格式、响应结构、流式事件、工具调用约定上存在显著差异。
让 Codex 直接调用国产模型?不行------协议不兼容。Fork Codex 改代码?维护成本太高。
最优雅的方案是中间加一层网关:Codex 以为自己在跟 OpenAI 通信,国产模型以为自己在服务一个普通的 Chat Completions 客户端。网关负责协议的双向翻译。
零侵入接入:只需两个环境变量
这是整个方案最优雅的地方------不需要 fork Codex,不需要改一行代码,只需要设置两个环境变量:
bash
# 把 Codex 的请求指向本地网关
export OPENAI_BASE_URL=http://localhost:3000
# 用网关的 API Key 替代 OpenAI 的 Key
export OPENAI_API_KEY=gateway-your-api-key
# 正常启动 Codex
codex
Codex CLI 会把所有 /v1/responses 请求发到本地网关,网关翻译后转发给国产模型,再把响应翻译回 Responses API 格式返回。整个过程对 Codex 完全透明。
核心架构:协议翻译层
请求方向:Responses API → Chat Completions API
当 Codex 发送一个 Responses API 请求时,网关需要做以下转换:
| Responses API 概念 | Chat Completions API 概念 |
|---|---|
instructions 字段 |
system 角色消息 |
input 字符串 |
user 角色消息 |
input 结构化数组 |
按类型逐项映射为 messages |
function_call_output 输入项 |
role: "tool" 消息 |
previous_response_id |
从数据库查询历史消息并拼接 |
tools[].type: "function" |
直接透传 |
web_search_preview 工具 |
拦截,触发网关内置搜索逻辑 |
computer_use_preview 工具 |
拦截,注入 Prompt 引导 |
其中 input 的结构化数组是最复杂的部分。Codex 的多轮对话会把历史消息以结构化数组的形式发送,网关需要正确识别每条消息的类型(用户消息、助手回复、工具调用、工具结果)并映射为对应的 Chat Completions 角色。
响应方向:Chat Completions API → Responses API
国产模型返回的响应同样需要翻译:
| Chat Completions 字段 | Responses API 字段 |
|---|---|
message.content |
output 中的 output_text 项 |
message.reasoning_content |
output 中的 reasoning 项(含 summary) |
message.tool_calls |
output 中的 function_call 项 |
usage.prompt_tokens |
usage.input_tokens |
usage.completion_tokens |
usage.output_tokens |
流式响应的翻译
流式处理是最精巧的部分。Codex 期望的 SSE 事件序列与 Chat Completions 的 chunk 格式完全不同,网关需要实时翻译:
vbscript
Chat Completions SSE chunk → Responses API SSE event
─────────────────────────────────────────────────────────────────
开始接收 → response.created
→ response.output_item.added
delta.reasoning_content 出现 → response.reasoning_summary_text.delta
delta.content 出现 → response.output_text.delta
delta.tool_calls 出现 → response.function_call_arguments.delta
单个输出项完成 → response.output_item.done
流结束 → response.completed(含 token 统计)
通过 ReadableStream 逐 chunk 读取、翻译、推送,实现了零延迟的流式透传。Codex 终端上能看到和国产模型原生 API 一样的逐字输出效果,包括推理过程的实时展示。
推理模型的特殊处理
DeepSeek R1 等推理模型会在响应中返回 reasoning_content 字段,记录模型的思考链路。网关的 DeepSeekAdapter 会:
- 非流式 :将
reasoning_content提取为独立的reasoning输出项,包含 summary 文本 - 流式 :检测每个 chunk 中的
delta.reasoning_content,翻译为response.reasoning_summary_text.delta事件
这样在 Codex 终端中就能看到模型"正在思考"的过程,体验与使用 OpenAI o-series 模型一致。
高级功能:不只是翻译
Web Search 代理
Codex 支持 web_search_preview 工具来搜索网络,但国产模型不一定支持这个能力。网关的做法是拦截并代理:
- 检测请求中包含
web_search_preview工具 - 通过 MCP SDK 调用智谱的
webSearchPrime搜索服务 - 取 Top 3 搜索结果,格式化后注入用户消息上下文
- 从工具列表中移除
web_search_preview(避免模型困惑) - 将增强后的请求正常转发给国产模型
搜索结果带有 5 分钟内存缓存,相同的查询不会重复调用搜索服务。搜索失败也不阻塞请求------网关会优雅降级,直接将原始请求转发给模型。
Computer Use 桥接
Codex 的 computer_use_preview 工具允许模型操作用户电脑(截图、点击、输入等),但国产模型没有原生的 Computer Use API。网关采用 Prompt 注入法 桥接:
- 检测到
computer_use_preview工具时,向 system 消息追加一段操作规范 Prompt - Prompt 定义了 screenshot、click、scroll、type、key、wait 六种操作的 JSON 格式
- 国产模型按 Prompt 指示输出 JSON 格式的操作指令
- 网关通过正则检测模型输出中的操作 JSON,包装为 Responses API 的
computer_call格式 - Codex 在本地执行操作后,将结果通过新的请求回传
这个方案虽然不如原生 API 优雅,但在实践中证明是可行的------国产模型的指令遵循能力足以支撑这种结构化输出。
工具调用的双向透传
Codex 的 function tools(如 exec_command 等)可以直接透传给国产模型,因为国产模型普遍支持 Chat Completions 的 function calling 格式:
scss
Codex 发送 tools (Responses API 格式)
→ 网关透传 tools (Chat Completions 格式)
→ 国产模型返回 tool_calls
→ 网关翻译为 function_call (Responses API 格式)
→ Codex 执行工具,发送 function_call_output
→ 网关翻译为 role: "tool" 消息
→ 国产模型继续生成
整个循环对 Codex 和国产模型都是透明的,双方都以为自己正在与原生 API 通信。
多模型负载均衡与故障转移
生产环境中,单一模型提供商可能不够可靠。网关内置了加权负载均衡 + 自动故障转移:
负载均衡 :在网关的管理后台中,可以为同一个 OpenAI 模型名配置多个国产模型映射。例如 gpt-4o 可以映射到 DeepSeek、通义千问和 Kimi,各自带有权重。网关按权重加权随机选择。
故障转移:网关维护每个 provider 的健康状态。在 60 秒窗口内连续失败 3 次的 provider 会被标记为不健康,自动排除在候选列表外。窗口过期后自动恢复。
markdown
请求 → 获取所有候选 provider
→ 过滤掉不健康的
→ 按优先级逐个尝试
→ 成功:记录成功,返回响应
→ 失败:记录失败,尝试下一个
→ 全部失败:返回 502
这意味着即使某个国产模型 API 完全不可用,Codex 用户也不会感知到任何中断。
会话管理
Codex 通过 previous_response_id 实现多轮对话。网关在 SQLite 中维护会话表:
- 每次请求完成后,将 response_id 和完整消息列表存入数据库
- 下次请求携带
previous_response_id时,网关查询历史消息并拼接到上下文中 - 会话默认 7 天过期
- 服务重启后会话数据不丢失(SQLite 持久化)
这个设计让网关无状态化------网关本身不持有对话状态,所有持久化都交给数据库,方便水平扩展。
技术实现细节
整个网关用 TypeScript 编写,基于 Bun 运行时和 Elysia Web 框架:
- Bun:提供高性能 HTTP 服务和内置 SQLite,单二进制部署
- Elysia:类型安全的路由定义,自动请求体验证
- OpenAI SDK:用 OpenAI 自家的 SDK 调用国产模型(因为它们兼容 Chat Completions API),省去自己写 HTTP 客户端
- Zod:请求体校验,确保进入网关的 Responses API 请求格式正确
- SQLite:零额外依赖的持久化,单文件部署
前端管理后台用 React + Bun HTML bundler,不需要单独的构建步骤。通过管理后台可以可视化管理提供商、模型映射、API Key 和查看调用日志。
部署与使用
bash
# 1. 启动网关
pnpm dev
# 2. 初始化数据库
bun src/db/migrate.ts
# 3. 通过管理后台添加提供商和模型映射
# 打开 http://localhost:3000
# 4. 配置 Codex
export OPENAI_BASE_URL=http://localhost:3000
export OPENAI_API_KEY=your-gateway-key
codex
总结
这个 AI Gateway 的核心价值在于:用协议翻译替代代码修改。通过在 Codex CLI 和国产模型之间加一层智能翻译层,实现了:
- 零侵入:Codex 不需要任何改动
- 全功能:推理过程、工具调用、流式输出、网络搜索、Computer Use 全部支持
- 高可用:多模型负载均衡 + 自动故障转移
- 可观测:调用日志、token 统计、管理后台
对于想用国产模型但又被 Codex 等 OpenAI 生态工具绑定的开发者来说,这是一个实用且低成本的方案。同样的思路也可以应用到 Cursor、Copilot 等其他基于 OpenAI API 的工具上------只需把 OPENAI_BASE_URL 指向网关即可。
代码仓库 : github.com/BoltDoggy/a...