拆解 24K Star 的 CLIProxyAPI:Go 实现多模型代理与负载均衡
9 个月 24000 Star,日更到 v6.9.19。这篇文章从源码层面拆解它的架构。
它到底做了什么
一句话:CLIProxyAPI 是一个用 Go 写的反向代理,把 Claude Code、Gemini CLI、OpenAI Codex、Qwen Code 这些 AI CLI 工具的 OAuth 订阅,包装成标准的 OpenAI / Claude / Gemini 兼容 API。
开发者只需启动这个代理,配置好自己的账号,就能在 Cursor、Cline、自己写的脚本里,通过标准 API 格式调用这些模型。多个账号还能自动负载均衡和故障转移。
听起来简单,实际不简单。Claude 的 Messages API、Gemini 的 GenerateContent API、OpenAI 的 Chat Completions API 格式各不相同,还要处理 OAuth 刷新、流式响应、多账号调度。
下面从源码拆解它是怎么做的。
整体架构
先看目录结构,项目的分层非常清晰:
CLIProxyAPI/
├── cmd/server/ # 入口
├── internal/ # 应用层(不可外部引用)
│ ├── auth/ # 各 Provider 的 OAuth 认证
│ ├── runtime/executor/# 各 Provider 的执行器
│ ├── translator/ # 格式转换器(NxN 矩阵)
│ ├── config/ # 配置管理
│ ├── watcher/ # 文件监听 & 热重载
│ ├── store/ # 持久化(文件/PG/Git/S3)
│ ├── tui/ # 终端管理界面
│ └── api/ # HTTP 路由 & Middleware
├── sdk/ # SDK 层(可嵌入其他 Go 项目)
│ ├── cliproxy/ # 核心服务
│ │ ├── auth/ # 调度器 & 账号管理
│ │ ├── executor/ # 执行器接口
│ │ └── usage/ # 用量统计
│ ├── translator/ # 翻译管线 & 注册表
│ ├── auth/ # 认证抽象
│ └── api/handlers/ # API Handler
└── config.example.yaml # 配置模板
核心有三层:
- Executor 层:每个 AI Provider 一个执行器,负责实际调用上游 API
- Translator 层:N×N 格式转换矩阵,在 OpenAI / Claude / Gemini 等协议间互转
- Scheduler 层:多账号调度,负载均衡 + 故障转移 + 冷却恢复
一个请求的完整生命周期:客户端请求 → API Handler → 格式转换 → Scheduler 选账号 → Executor 调上游 → 格式转回 → 返回客户端。
核心设计一:Executor 模式
每个 AI Provider 都有自己的 Executor,实现统一的 ProviderExecutor 接口:
type ProviderExecutor interface {
Identifier() string
Execute(ctx context.Context, auth *Auth, req Request, opts Options) (Response, error)
ExecuteStream(ctx context.Context, auth *Auth, req Request, opts Options) (*StreamResult, error)
Refresh(ctx context.Context, auth *Auth) (*Auth, error)
CountTokens(ctx context.Context, auth *Auth, req Request, opts Options) (Response, error)
HttpRequest(ctx context.Context, auth *Auth, req *http.Request) (*http.Response, error)
}
源码里能看到这些 Executor 实现:
| Executor | 对应服务 | 认证方式 |
|---|---|---|
claude_executor | Claude Code | OAuth + PKCE |
gemini_executor | Gemini API | API Key |
gemini_cli_executor | Gemini CLI | Google OAuth |
codex_executor | OpenAI Codex | OAuth |
codex_websockets_executor | Codex WebSocket | OAuth |
qwen_executor | Qwen Code | OAuth |
iflow_executor | iFlow | Cookie/OAuth |
antigravity_executor | Antigravity | OAuth |
kimi_executor | Kimi | OAuth |
openai_compat_executor | OpenAI 兼容上游 | API Key |
每个 Executor 内部处理自己 Provider 的特殊逻辑。比如 claude_executor 里有 claude_signing.go 处理 Claude 特有的请求签名,codex_websockets_executor 处理 OpenAI Codex 的 WebSocket 协议。
这种设计的好处:新增一个 Provider,只需实现 ProviderExecutor 接口,注册到 Manager 里。调度逻辑、翻译逻辑、API 路由都不用动。
核心设计二:N×N 格式转换矩阵
这是整个项目最精巧的部分。
各家 AI 的 API 格式完全不同:
- OpenAI:
/v1/chat/completions,JSON 格式 - Claude:
/v1/messages,自己的 JSON 格式 - Gemini:
/v1beta/models/xxx:generateContent,又是另一种格式
CLIProxyAPI 需要在这些格式之间互相转换。它的做法是建一个 翻译注册表(Translator Registry):
type Registry struct {
requests map[Format]map[Format]RequestTransform
responses map[Format]map[Format]ResponseTransform
}
一个二维 Map。requests[OpenAI][Claude] 存的就是"把 OpenAI 格式的请求翻译成 Claude 格式"的函数。
看源码里的 translator 目录结构就能明白这个矩阵有多大:
internal/translator/
├── claude/gemini/ # Claude → Gemini
├── claude/openai/chat-completions/ # Claude → OpenAI
├── claude/openai/responses/ # Claude → OpenAI Responses
├── codex/claude/ # Codex → Claude
├── codex/gemini/ # Codex → Gemini
├── codex/openai/chat-completions/ # Codex → OpenAI
├── gemini/claude/ # Gemini → Claude
├── gemini/openai/chat-completions/ # Gemini → OpenAI
├── openai/claude/ # OpenAI → Claude
├── openai/gemini/ # OpenAI → Gemini
└── ... (更多组合)
每种组合都有 init.go,通过 Go 的 init() 函数在启动时自动注册到全局 Registry。
翻译过程被封装成 Pipeline,支持中间件:
func (p *Pipeline) TranslateRequest(ctx context.Context, from, to Format, req RequestEnvelope) (RequestEnvelope, error) {
terminal := func(ctx context.Context, input RequestEnvelope) (RequestEnvelope, error) {
translated := p.registry.TranslateRequest(from, to, input.Model, input.Body, input.Stream)
input.Body = translated
return input, nil
}
// 从后往前包裹中间件
handler := terminal
for i := len(p.requestMiddleware) - 1; i >= 0; i-- {
mw := p.requestMiddleware[i]
next := handler
handler = func(ctx context.Context, r RequestEnvelope) (RequestEnvelope, error) {
return mw(ctx, r, next)
}
}
return handler(ctx, req)
}
洋葱模型。翻译函数是核心,外围可以套任意数量的中间件来处理通用逻辑(日志、metrics 等)。
为什么用 NxN 而不是星型(所有格式先转中间格式)?
因为不同格式之间的直接转换更高效。星型需要 2N 个转换器,但每次转换要经过两步,精度有损。NxN 需要 N² 个转换器,但一步到位。对于 API 格式转换这种场景,N 目前只有 6-7 种,N² 可控,精度更重要。
核心设计三:多级调度器
多账号负载均衡是 CLIProxyAPI 的杀手级功能。源码里的实现分了三层:
第一层:Provider 级别
Manager 维护一个 providerOffsets Map,追踪每个模型跨 Provider 的轮询状态。比如 claude-sonnet-4 可能在 Claude Provider 和 Gemini Provider 上都有对应模型,Manager 会在这两个 Provider 之间轮询。
第二层:Provider 内的 Auth 轮询
每个 Provider 可以有多个认证(多个 Google 账号、多个 Claude 订阅)。providerScheduler 管理这些 Auth:
type providerScheduler struct {
providerKey string
auths map[string]*scheduledAuthMeta
modelShards map[string]*modelScheduler
}
按模型分片。每个模型有自己的 modelScheduler。
第三层:Priority Bucket + 冷却队列
最精巧的部分。每个 modelScheduler 把 Auth 按 优先级(priority) 分桶:
type modelScheduler struct {
modelKey string
entries map[string]*scheduledAuth
priorityOrder []int
readyByPriority map[int]*readyBucket // 按优先级分桶
blocked cooldownQueue // 冷却队列
}
- Ready 状态:可以使用的 Auth,放在
readyBucket里按优先级分组 - Cooldown 状态:刚遇到 429 或错误的 Auth,放入冷却队列,等
nextRetryAt到了再恢复 - Blocked/Disabled 状态:彻底不可用
调度策略支持两种:
routing:
strategy: "round-robin" # 或 "fill-first"
- round-robin:在同优先级的 Auth 之间均匀轮询
- fill-first:优先用同一个 Auth 直到限速,再切下一个
还有一个细节:Gemini 的虚拟 Auth(一个 Google 账号下多个 Project)通过 virtualParent 机制做子账号分组轮询,避免全部请求打到一个 Project 上。
认证:OAuth 流程的工程化处理
每个 Provider 的 OAuth 流程都不一样,CLIProxyAPI 对每个做了单独处理。以 Claude 为例:
internal/auth/claude/
├── anthropic.go # Anthropic 认证常量
├── anthropic_auth.go # OAuth 认证逻辑
├── pkce.go # PKCE 流程
├── oauth_server.go # 本地回调服务器
├── token.go # Token 存储 & 刷新
├── utls_transport.go # TLS 指纹伪装
└── errors.go # 错误处理
几个有意思的工程细节:
PKCE(Proof Key for Code Exchange):Claude 的 OAuth 用了 PKCE 增强安全性,源码里的 pkce.go 生成 code_verifier 和 code_challenge。
本地回调服务器:oauth_server.go 起一个临时 HTTP 服务器接收 OAuth 回调,拿到 authorization code 后交换 token。
TLS 指纹:utls_transport.go 用 uTLS 库模拟浏览器 TLS 指纹,避免被上游检测为非浏览器流量。
Token 持久化:Token 支持四种存储后端——本地文件、PostgreSQL、Git 仓库、对象存储(S3 兼容)。通过统一的 TokenStore 接口抽象:
// 注册方式(main.go 里):
if usePostgresStore {
sdkAuth.RegisterTokenStore(pgStoreInst)
} else if useGitStore {
sdkAuth.RegisterTokenStore(gitStoreInst)
} else {
sdkAuth.RegisterTokenStore(sdkAuth.NewFileTokenStore())
}
这意味着你可以把 Token 存在 Git 仓库里做多机同步,或者存 PostgreSQL 做集群部署。
热重载 & 运维友好
internal/watcher/ 实现了配置文件的热重载。修改 config.yaml 后不用重启服务,Watcher 会检测变更,触发 config_diff → auth_diff → 调度器重建。
还有个 TUI 模式:
cliproxyapi -tui # 终端管理界面
cliproxyapi -tui -standalone # 内嵌服务 + TUI
TUI 用 Bubble Tea 框架(Go 生态的 TUI 标准),有 Dashboard、Auth 管理、配置编辑、日志查看、用量统计等 Tab。
SDK 化:可以嵌入你的 Go 项目
项目分了 internal/ 和 sdk/ 两个包。sdk/ 是可被外部引用的:
import "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy"
官方文档里给了嵌入式用法:
svc := cliproxy.NewService(cfg,
cliproxy.WithTokenProvider(myProvider),
cliproxy.WithAPIKeyProvider(myKeyProvider),
)
svc.RegisterUsagePlugin(myPlugin) // 监控用量
svc.Run(ctx)
这意味着你可以把 CLIProxyAPI 作为库嵌入到自己的项目里,不需要单独部署一个进程。
架构总结
CLIProxyAPI 的架构可以用一张图概括:
客户端请求 (OpenAI/Claude/Gemini 格式)
│
▼
┌─────────────────────────────────────────┐
│ API Handler Layer │
│ /v1/chat/completions │
│ /v1/messages │
│ /v1beta/models/...:generateContent │
└──────────────┬──────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ Translator Pipeline │
│ 客户端格式 → Provider 格式 (NxN 矩阵) │
└──────────────┬──────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ Scheduler Layer │
│ Provider 轮询 → Auth 轮询 → Priority │
│ Bucket → Cooldown Queue │
└──────────────┬──────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ Executor Layer │
│ Claude | Gemini | Codex | Qwen | ... │
│ OAuth → 上游 API → 响应 │
└──────────────┬──────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ Translator Pipeline (返回) │
│ Provider 格式 → 客户端格式 │
└─────────────────────────────────────────┘
四个核心设计决策:
- 接口抽象:
ProviderExecutor统一了所有 Provider 的调用方式,扩展性极强 - NxN 翻译矩阵:直接转换比星型中间格式精度更高,用
init()自动注册简化维护 - 三级调度:Provider → Auth → Priority Bucket,层层递进,兼顾负载均衡和故障恢复
- SDK 化:
sdk/包独立可嵌入,支持二次开发
这套架构不只能做 AI CLI 代理。任何需要"多上游、多格式、多账号"的代理场景,都能参考这个模式。