摘要
很多团队开始同时使用 Claude Code、Codex CLI、Gemini CLI、OpenClaw 这类 AI 编程工具后,会遇到一个很现实的问题:每个工具都有自己的协议、配置文件、凭证来源和模型命名方式。工具越多,配置越散;账户、API Key、本地模型和日志也越难统一管理。
CliGate 的思路不是再做一个远端中转服务,而是在本机启动一个 localhost:8081 的本地 AI gateway:上游可以是 ChatGPT / Claude 账户池、各类 provider API Key,也可以是本地运行时;下游则统一接住 Claude Code、Codex CLI、Gemini CLI、OpenClaw、Web Chat 和渠道会话。
这篇文章不写安装流水账,而是结合项目源码和文档,拆一下 CliGate 里比较有意思的几个工程点:协议转换、应用识别、凭证路由、能力感知降级,以及为什么它和普通 API proxy 的侧重点不太一样。
1. 问题不是“能不能代理”,而是“能不能稳定地接住多个客户端”
单个 OpenAI-compatible 代理并不复杂:客户端打到 /v1/chat/completions,服务端转发到上游,再把响应返回即可。
但 AI 编程 CLI 的问题更碎:
- Claude Code 和 OpenClaw 常走 Anthropic Messages:
POST /v1/messages - Codex CLI 会走 OpenAI Responses 或它自己的内部接口:
POST /v1/responses、POST /backend-api/codex/responses - Gemini CLI 使用 Gemini API 风格:
POST /v1beta/models/* - 不同客户端对 tool call、流式事件、图片输入、文件输入、reasoning/thinking 的表达都不一样
- 账户池、API Key 池和本地模型都可能是可用上游,但可支持能力不同
CliGate 的 README_CN 和 docs/ARCHITECTURE.md 里把定位说得比较直接:它是一个本地控制平面,而不是单一路由转发器。对应到代码里,主要入口分布在:
src/routes/messages-route.js:Anthropic Messages 入口,服务 Claude Code / OpenClaw 等src/routes/responses-route.js、src/routes/codex-route.js:OpenAI Responses / Codex 相关入口src/routes/gemini-api-route.js:Gemini CLI 入口src/app-routing.js:识别请求来源,并处理应用级绑定src/translators/:协议转换与 normalizersrc/api-key-manager.js、src/account-rotation/:API Key 与账户池调度
换句话说,CliGate 要解决的是“多个 AI 编程客户端如何共用一套本地能力面”的问题。
2. 北向协议统一:先把客户端差异收进来
从 API 面看,CliGate 暴露了几组关键入口:
| 入口 | 典型客户端 | 说明 |
|---|---|---|
POST /v1/messages | Claude Code / OpenClaw | Anthropic Messages 兼容入口 |
POST /v1/chat/completions | OpenAI-compatible 客户端 | Chat Completions 兼容入口 |
POST /v1/responses | Codex CLI | OpenAI Responses 兼容入口 |
POST /backend-api/codex/responses | Codex CLI | Codex 内部兼容入口 |
POST /v1beta/models/* | Gemini CLI | Gemini API 兼容入口 |
这层的关键不是“端点多”,而是每个入口后面都要保留客户端语义。
以 /v1/messages 为例,Claude Code 请求里可能包含:
systemmessagestoolstool_choicestream- 图片 / 文件 content block
- thinking block 或 signature
如果上游刚好是 Anthropic 或 Claude 账户,可以接近直通;但如果上游是 ChatGPT account、OpenAI Responses、Azure OpenAI、Gemini、Vertex AI,就必须转换。
CliGate 在 src/translators/request/anthropic-to-openai-responses.js 中把 Anthropic Messages 转成 OpenAI Responses:
system会被提取成instructions- Anthropic message content 会被规范化成 Responses
input - Anthropic tools 会转成 Responses tools
max_tokens、temperature、top_p、stop_sequences、metadata、user等参数会按 provider 能力保留- 图片、文件、
tool_result富内容会交给 normalizer 处理
项目没有把这些逻辑塞在一个巨大 if/else 里,而是拆到了 normalizers,例如:
normalizers/anthropic-messages.jsnormalizers/multimodal.jsnormalizers/tools.jsnormalizers/responses-request.jsnormalizers/responses-events.js
这对 AI CLI 很重要。工具调用、流式增量、reasoning/thinking 的兼容问题通常不是“字段名改一下”能解决的,必须有一层专门处理语义保真和可接受降级。
3. 南向不是单一 provider:账户池、API Key、本地模型都可以成为上游
CliGate 的另一个特点是,上游不是单一 API Key。
根据 README_CN、docs/product-manual.zh-CN.md 和源码,当前可管理的上游大致包括:
- ChatGPT 账户池
- Claude 账户池
- Antigravity 账户池
- OpenAI / Azure OpenAI / Anthropic / Gemini / Vertex AI / MiniMax / Moonshot / ZhipuAI / DeepSeek 等 API Key
- 可选本地运行时,例如 Ollama
- Kilo free route 一类免费模型路径
src/api-key-manager.js 里,API Key 会按 provider 类型实例化成不同 provider class,并维护启用状态、请求数、错误数、token 与成本统计。选择 Key 时不是固定拿第一条,而是从可用 Key 中按请求数做简单负载均衡。
账户池则由 src/account-rotation/ 管理,支持 sequential / random 等策略,并维护 rate limit、invalid 状态和冷却时间。对于 Claude 账户,messages-route.js 中还能看到模型维度的 cooldown:某个账号在某个模型上被限流后,不会马上继续打同一个模型,而是先让后续请求尝试其他账户或其他上游。
这类设计的意义在于:开发工具侧只看到一个本地入口,实际请求可以按策略落到不同凭证来源上。
4. 应用级路由:不是所有客户端都应该走同一个凭证
很多代理工具只有“账户优先”或“Key 优先”这种全局策略。CliGate 进一步做了 app-assigned routing。
src/app-routing.js 里定义了几类 app id:
codexclaude-codegemini-cliopenclawunknown-openai-clientunknown-anthropic-client
请求来源主要通过 path、header、User-Agent 和约定 token 来识别。例如:
/backend-api/codex/*、/v1/responses识别为 Codex/v1beta/models/*识别为 Gemini CLI/v1/messages下通过x-proxypool-client、User-Agent 或代理 token 区分 Claude Code / OpenClaw
在 docs/APP_ROUTING.md 的设计里,路由模式分成两种:
automatic:保持默认自动路由逻辑app-assigned:先看应用是否绑定了指定凭证,如果绑定不可用,再根据配置决定是否 fallback 到默认逻辑
这带来的体验差异很明显:
- Codex 可以固定走某个 ChatGPT 账户
- Claude Code 可以固定走某个 Claude 账户或 Anthropic Key
- OpenClaw 可以走另一个 provider key
- Gemini CLI 可以绑定 Gemini / Vertex / 本地模型
对个人开发者来说,这是“少改配置”;对小团队来说,这是“不同工具、不同用途、不同成本来源”可以被表达出来。
5. 能力感知路由:会根据请求内容挑更合适的 provider
比较值得展开的是 /v1/messages 的兼容 provider 排序。
在 src/translators/request-features.js 中,CliGate 会先分析 Anthropic 请求特征:
- 是否包含图片输入
- 是否包含文件输入
tool_result是否包含图片、文件等结构化内容- 是否使用 hosted tools
然后在 src/translators/provider-capabilities.js 中为 provider 打分:
- 原生 Anthropic 支持 hosted tools、图片、文件、结构化 tool_result,分数更高
- OpenAI / Azure OpenAI / DeepSeek 走 Responses bridge,支持能力取决于 capability profile
- Gemini / Vertex 有自己的多模态与工具限制
- Vertex Claude rawPredict 这类链路对 hosted tools 更合适
这不是为了“绝对智能”,而是避免明显不兼容的 bridge 被排在最前面。比如一个请求带了 web_search 这类 hosted tool,普通 OpenAI bridge 并不能完整承接;能力感知排序会让更合适的 provider 先尝试。
项目还提供了 strictTranslatorCompatibility。默认情况下,一些转换降级会继续兼容返回并在 header / 日志里记录;开启严格模式后,工具降级、tool_choice 降级可以直接变成 400 invalid_request_error。
这个开关很实用:个人使用时可以“尽量跑通”,生产或团队场景则可以“宁愿失败也不要静默丢语义”。
6. 多模态与 thinking:真正麻烦的是细节保真
docs/TRANSLATOR_CAPABILITY_MATRIX.md 里有一段很能说明问题:项目当前已经覆盖 Anthropic image block 到 OpenAI Responses input_image,Anthropic document/file 到 Responses input_file,以及 tool_result 中 text + image + document 混合内容的保留。
这些看起来是格式转换,实际更像“协议间语义迁移”。
举几个源码里的细节:
normalizers/anthropic-messages.js会把 assistant 的tool_use转成 Responsesfunction_call,同时处理 tool id 映射。- thinking / signature 不只是文本,项目会通过
thinking.js相关逻辑做缓存和恢复,避免工具调用链断掉。 providers/gemini.js中明确处理了 Gemini 的限制:当 tool path 需要兼容时,会禁用 thinking budget;当tool_result带图片等多模态内容时,部分情况下会降级为 user multimodal parts。
这也是 CliGate 和普通 API proxy 的重要差异。普通 proxy 更多关心“请求能不能发出去”;CliGate 关心的是“Claude Code / Codex 这类强协议客户端在换 provider 后还能不能保持可预期行为”。
7. 可观测性:本地工具也需要日志、用量和成本
如果只是个人临时代理,请求日志可能不重要。但一旦把多个 CLI、多个账户和多个 provider 放到一起,没有观测能力就很难排查:
- 这个请求到底从哪个客户端来?
- 最终走了哪个账户或 API Key?
- 模型名有没有被映射?
- 是上游限流、凭证失效,还是协议转换降级?
- 这段时间哪个 provider 成本更高?
CliGate 在产品层提供了 Usage、Pricing、Request Logs、Logs、API Explorer 等页面;API 文档里也有对应接口,例如:
/api/usage/overview/api/request-logs/api/pricing/api/model-mappings/api/agent-runtimes/sessions
这说明它的定位更接近“本地控制台”,而不是只在终端里跑一个代理进程。
8. 和同类方案的差异
如果和常见 API 代理或统一中转服务对比,CliGate 的差异大致在四点:
第一,它更偏本地优先。默认跑在 localhost:8081,文档中也强调不依赖托管中转服务。这降低了把个人 CLI 凭证、OAuth 账户和本地运行时暴露到远端的需求。
第二,它面向具体 AI 编程工具做集成,而不只是提供 OpenAI-compatible 入口。Claude Code、Codex CLI、Gemini CLI、OpenClaw 都有配置助手或专门路由。
第三,它把账户池和 API Key 池放在同一套路由层里。很多工具只处理 API Key,而 CliGate 同时考虑 ChatGPT / Claude / Antigravity OAuth 账户、provider key、本地模型和免费模型路径。
第四,它承认 provider 能力不一致,并把降级显式化。多模态、hosted tools、thinking、tool_result 这些能力不是所有上游都能等价支持,项目通过 capability matrix、provider capabilities、strict mode 和测试基线来维护这些差异。
当然,它也不是万能的。当前架构更像产品型单体:Web UI、配置助手、代理路由、账户管理都在同一个 Node.js / Express 项目里。这让开箱体验更好,但如果要作为平台底座长期扩展,后续可能还需要继续抽象 translator pipeline、认证调度和配置治理。
9. 适合什么场景
我觉得 CliGate 适合这几类使用场景:
- 同时使用 Claude Code、Codex CLI、Gemini CLI,希望配置入口统一
- 有多个 ChatGPT / Claude 账户,想做本地账户池与轮换
- 同时有 API Key 和账户订阅,希望按工具或按模型路由
- 希望在 Web UI 里看请求日志、用量、成本和模型映射
- 想把本地模型或免费模型路径作为部分请求的 fallback
- 想从 Telegram / 飞书等渠道继续使用 Codex / Claude Code runtime session
不太适合的场景也很明确:
- 只需要一个极简 OpenAI-compatible 转发器
- 希望部署成多租户、中心化、强治理的平台服务
- 不想在本地保存任何 OAuth 账户或工具配置
小结
CliGate 最有意思的地方,不在于“又做了一个 AI API proxy”,而在于它把 AI 编程工具的几个真实痛点放到了一起处理:
- 北向接住多种 CLI 协议
- 中间做协议转换和 capability-aware routing
- 南向统一账户池、API Key 池和本地模型
- 产品层提供仪表盘、日志、用量、配置助手和渠道会话
当你只用一个工具、一个 API Key 时,这些能力可能显得有点重;但当 Claude Code、Codex CLI、Gemini CLI、OpenClaw、多个账户和多个 provider 同时出现时,一个本地控制平面的价值就很明显了。
从工程角度看,CliGate 的经验也比较值得借鉴:AI 工具链的兼容不只是“适配一个接口”,而是要认真处理协议语义、工具调用、多模态、reasoning、凭证状态和降级策略。否则表面上接通了,真正跑复杂任务时还是会在细节上掉链子。