保留 Claude Code 的工具链,用本机代理转换协议、独立管理订阅授权,并验证模型、推理档位和上下文配置。
我平时同时使用 Codex 和 Claude Code。Codex 通过 ChatGPT 账号登录,使用订阅提供的模型额度;Claude Code 则有另一套习惯用的工具、命令和工作流。
这次想解决的问题很具体:保留 Claude Code 的交互和工具执行方式,把模型请求交给 OpenAI 官方 GPT,并使用自己的订阅授权。
实际接通之后,最值得分享的并不是某个环境变量,而是几个容易混淆的边界:客户端与模型、订阅 OAuth 与 API Key、Claude 的 effort 与 GPT 的推理级别,以及开机启动与网络就绪。
本次实测环境为 macOS、Claude Code 2.1.310、CLIProxyAPI 8.0.35。验证时间为 2026 年 10 月,后续版本需要重新核对配置与模型能力。
先分清 Claude Code 和模型的职责
Claude Code 不只是一个聊天窗口。它负责读取项目、调用工具、执行命令、管理子代理和组织上下文。模型负责根据这些上下文决定接下来做什么、生成什么内容。
因此,换成 GPT 之后,运行的仍是 Claude Code 的工作流。它不会自动变成 Codex CLI,同一模型在两个客户端中的行为也不必完全相同。
这次采用的链路是:
Claude Code
│ Anthropic Messages 格式
▼
本机 CLIProxyAPI
│ 协议转换与独立 OAuth 认证
▼
OpenAI 官方 GPT 模型CLIProxyAPI 在本机提供 Anthropic 兼容接口,负责将请求、工具调用和流式响应转换成上游需要的形式。模型请求随后使用代理自己取得的 OpenAI OAuth 凭据发送。
需要区分技术可行性与厂商支持范围。Anthropic 的网关文档明确说明,不支持通过网关将 Claude Code 路由到非 Claude 模型。这个组合的兼容性,要靠所用代理版本和实际验证来确认。官方说明
已有 Codex 桥接为什么不能直接接上 Claude Code
这台机器原本已经有一个本地桥接,让 Codex 在 GPT 订阅和 DeepSeek 官方 API 之间分流。
乍看之下,似乎可以把 Claude Code 的地址改到同一个桥接上。但阅读代码之后发现,原桥接只处理 Responses 请求,GPT 登录态由 Codex 客户端随请求传入。桥接本身既不执行 GPT 登录,也不负责刷新 OAuth。
Claude Code 发出的协议不同,手里也没有 Codex 的登录凭据。只改地址,缺少的两项能力仍然缺少。
最后没有在原链路上继续叠代理,而是让两个入口独立运行:
Codex → 原有桥接 → GPT 订阅或 DeepSeek API
Claude Code → 新的本地代理 → GPT 订阅
Claude Code 的 DeepSeek 入口 → DeepSeek 官方兼容接口原来的 Codex 工作方式继续使用。新代理只处理新增的 Claude Code GPT 入口,排查问题时也能明确知道请求经过哪一层。
代理有自己的凭据,不复制 Codex 的 Token
订阅 OAuth 与 OpenAI Platform API Key 是两种认证和计费路径。Codex 能使用 ChatGPT 订阅,不意味着把它的 Token 填进任意 API Key 字段,就能获得同样的能力。Codex 认证文档
本次使用 CLIProxyAPI 的 Codex OAuth 登录流程,让它单独完成浏览器授权,使用同一个 ChatGPT 订阅账号。没有复制现有 Codex 的 auth.json,也没有让两个程序共同管理同一份 refresh token。
Claude Code 访问本机代理时,使用的是另一把本地访问密钥。这把密钥只负责保护本机接口,不是 OpenAI API Key,也不是订阅的 OAuth Token。
安装与版本配置
先安装依赖:
brew install cliproxyapi代理配置使用 v8 格式,核心结构如下。示例中的占位符需要自行替换,不能直接作为生产配置使用。
config-version: 8
server:
host: "127.0.0.1"
port: 18317
access:
api-keys:
- "<随机生成的本地访问密钥>"
oauth:
auth-dir: "~/.local/share/ccdx/auth"
management:
allow-remote: false
secret-key: ""
disable-control-panel: true
observability:
logs:
debug: false
request-log: false这里有一个版本坑:旧教程中的顶层 api-keys 列表,在 v8 中对应 access.api-keys。把不同版本的片段拼到一起,可能得到一份看似合理、实际含义已经变化的配置。
独立登录与凭据保存
准备好配置后,执行独立登录:
cliproxyapi -config /path/to/private/proxy.yaml -codex-login浏览器使用已经登录的订阅账号完成授权即可。配置和 OAuth 文件放在私有目录,运行目录权限设为 700,私有配置文件设为 600。
我的封装另外保留了一份私有 JSON 作为配置真源,再生成代理配置,避免修改密钥时还要到多个脚本里找副本。
OpenAI 也有面向第三方应用的 Sign in with ChatGPT 订阅授权机制。它有自己的注册、授权和接口要求,不能把任意代理的 Codex 登录都笼统视为采用了这一流程。正式接入文档
用独立命令启动,保留原来的 Claude Code 配置
我没有把 GPT 代理地址写进 Claude Code 的全局配置,而是封装了一个叫 ccdx 的 shell 函数。
ccdx 是这次自己定义的入口,不是 Claude Code 或 CLIProxyAPI 自带的命令。shell 函数只调用启动脚本,启动脚本读取私有配置,构造本次 Claude 子进程的环境:
route_model = f"{model}({effort})"
child_env = os.environ.copy()
child_env.pop("ANTHROPIC_API_KEY", None)
child_env.update({
"ANTHROPIC_BASE_URL": local_gateway_url,
"ANTHROPIC_AUTH_TOKEN": local_gateway_key,
"ANTHROPIC_MODEL": route_model,
"CLAUDE_CODE_SUBAGENT_MODEL": route_model,
})
os.execvpe("claude", ["claude", *claude_args], child_env)这段展示的是封装的核心逻辑。实际入口还统一设置了模型家族别名、轻量模型入口和上下文预算,避免主会话切到了 GPT,子代理却仍请求另一个模型。
这样,新命令的变量只随这次 Claude 进程生效,普通 claude 和其他模型入口继续使用各自的设置。密钥也不需要直接写在 shell 函数里。
还有一个值得提前检查的细节:Claude Code 的 settings 文件可以覆盖 shell 中的环境变量。安装前应检查已有的模型地址与认证配置,启动后再用 /status 确认实际生效的地址和模型,而不是只看函数里写了什么。网关配置文档
权限参数应单独决定。使用 GPT 不要求开启 --dangerously-skip-permissions;它控制的是本地执行权限,与模型和订阅认证无关。
GPT 推理级别要检查上游收到的值
这次没有照搬另一个模型入口的 max、[1m] 和压缩参数,而是分别验证 GPT 的推理级别与窗口。
本次入口开放五档:
low / medium / high / xhigh / maxClaude Code 的 effort 设置与 GPT 的 reasoning.effort 不完全对应。尤其是 xhigh,不能因为某个客户端只显示 high,就假定两者相同。
用模型后缀传递推理档位
所用 CLIProxyAPI 版本支持模型后缀,将明确选择的推理档位转换到上游。例如:
代理收到:gpt-6-astra(xhigh)
上游请求:
model = gpt-6-astra
reasoning.effort = xhigh括号后缀是代理约定,不是 OpenAI 官方模型 ID 的一部分。启动器负责验证参数,再统一生成它。
在启动时选择,在完成响应中验收
封装后的使用方式是:
ccdx
ccdx --model gpt-6-astra --effort xhigh
ccdx --model gpt-6-luna --effort low
ccdx --model gpt-6-sol --effort max默认使用 GPT-6.1 Sol、high。主会话和子代理使用同一次启动选定的模型与推理级别。
这个实现将推理级别固定在启动参数中,因此会话内的 /effort 不会覆盖模型后缀。需要调整时,重新运行对应的命令。Codex 客户端里出现的 ultra,本次代理后缀没有支持,所以入口没有开放这一档。
验证时检查的是上游完成响应中的模型和 reasoning.effort,而不是让模型口头回答“你现在用了什么档位”。官方支持范围还要结合模型、接口与账号权限核对。OpenAI 推理设置
上下文预算不能直接复制另一个入口
另一个模型支持大窗口,不代表把同样的 [1m] 后缀加到 GPT 入口就合理。Claude Code 对无法识别的模型 ID 还可能默认按 200K 窗口处理。
本次采用明确的客户端预算,并让提前压缩窗口统一取预算的 80%:
| 模型 | 客户端上下文预算 | 提前压缩窗口 |
|---|---|---|
| GPT-6.1 Sol | 500,000 | 400,000 |
| GPT-6 Astra | 272,000 | 217,600 |
| GPT-6 Sol | 272,000 | 217,600 |
| GPT-6 Luna | 272,000 | 217,600 |
这些预算参考了本机 Codex 模型目录的默认窗口,不是对各模型最大容量的声明。Claude Code 执行后的 modelUsage.contextWindow 回读与这些值一致;这验证了客户端配置生效,没有证明填满整个窗口时的表现。
模型预算集中保存在一处,压缩窗口通过计算生成。切换模型时一起更新窗口,避免仅改变模型名称,仍沿用上一个模型的参数。Claude Code 自定义窗口说明
开机启动与上游可用是两个条件
本地服务启动
后台运行不等于登录前启动。macOS 的 LaunchAgent 通常依赖用户登录;需要让本地代理在会话之前启动,应使用系统 LaunchDaemon,再指定普通用户身份运行程序。
本次服务使用 RunAtLoad 和 KeepAlive,独立监听回环地址。它与原有桥接采用不同的服务标识和端口,安装、重启和停用都只针对自己。
启动时使用内置模型目录,避免先下载远程目录才开始监听。隔离测试中,即使没有账号凭据、上游代理不可用,本地接口仍能启动并响应。
网络出口就绪
不过,如果上游流量需要经过一个登录后才启动的本机网络代理,真正的模型请求仍要等网络代理就绪。LaunchDaemon 可以解决本地服务启动时机,不能让另一个依赖提前可用。
使用 Surge 一类工具时,还要确认回环地址不会被进程规则送到远端代理。相关规则应排在需要它们兜底的进程规则之前:
IP-CIDR,127.0.0.0/8,DIRECT,no-resolve
DOMAIN,localhost,DIRECT这次现有规则已经满足要求,所以没有修改网络配置。确需修改时,先备份,再验证实际命中的路由。
系统级服务已经安装并运行,登录前启动所需的配置也已核对;本次没有通过实际重启电脑完成一次完整开机验收。
用真实工具调用验证接入结果
能返回一句话,只能证明链路的一小部分。编程场景还需要工具调用、子代理以及连续多轮交互正常。
测试范围与验收条件
本次对四个常用 GPT 模型分别进行了验证:
| 模型 | 五档推理 | Anthropic 流式响应 | Claude 文件操作与子代理 |
|---|---|---|---|
| GPT-6.1 Sol | 通过 | 通过 | 通过 |
| GPT-6 Astra | 通过,含一次复测 | 通过 | 通过 |
| GPT-6 Sol | 通过,含一次复测 | 通过 | 通过 |
| GPT-6 Luna | 通过 | 通过 | 通过 |
推理测试要求收到 response.completed,返回模型与请求一致,返回的 reasoning.effort 与选定档位一致,并得到固定文本。Anthropic 格式测试则检查固定文本及 message_stop,避免把流中途断开当成成功。
Claude Code 测试使用临时目录:主会话读取文件,调用一个子代理复制内容,随后执行 Bash 比较文件并生成结果。验证脚本检查实际文件、工具调用记录和执行结果,不依赖模型一句“已经完成”。
首次失败与复测
首次共执行 28 项检查,其中 26 项通过,两项返回 HTTP 500。日志中的错误是 TLS 握手 EOF;原失败项单独复测后,均返回正确模型、正确推理级别和完整结束事件。
这两次错误没有被当成参数不兼容,也没有通过静默换模型掩盖。首次失败与复测结果分别保留。偶发网络断流可以重试,但一次复测成功不代表出口从此不会中断。
此外,新开终端能够自动加载 ccdx,默认模型真实调用成功。原有 Codex 配置和登录文件的内容哈希保持一致,现有桥接也继续通过健康检查。
这次实践最终形成了一个独立入口:模型、推理级别和窗口由启动器统一选择,认证由本地代理管理,工具执行继续交给 Claude Code。后续升级客户端或代理时,可以重复同一套可执行验证,判断变化到底发生在模型、协议转换、工具调用还是网络边界。