9Router 架构深度解析:本地 AI 路由网关的 API 兼容层、翻译核心与容错回退设计
【免费下载链接】9routerUnlimited FREE AI coding. Connect Claude Code, Codex, Cursor, Cline, Copilot, Antigravity to FREE Claude/GPT/Gemini via 40+ providers. Auto-fallback, RTK -40% tokens, never hit limits.项目地址: https://gitcode.com/GitHub_Trending/9r/9router
9Router 是一个基于 Next.js 的本地 AI 路由网关与可视化面板,对外暴露统一的 OpenAI 兼容端点(/v1/*),在内部完成上游提供商调度、多格式请求/响应翻译、模型组合与账户级回退、令牌刷新与用量统计。本文以仓库 docs/ARCHITECTURE.md 为骨架,结合 src/sse/handlers/chat.js、open-sse/handlers/chatCore.js、open-sse/services/accountFallback.js 等源码,完整还原其分层架构、请求生命周期、数据模型与故障韧性设计,帮助读者理解"一个本地进程如何把 Claude Code / Codex CLI / Cursor 等客户端路由到数十种上游 AI 服务"的完整机制。
系统定位与核心能力
9Router 的运行模型非常清晰:Next.js 应用路由同时承载面板管理 API 与兼容 API,一个共享的 SSE/路由核心负责提供商执行、翻译、流式转发、回退与用量统计。核心能力包括:
- 面向 CLI/工具的 OpenAI 兼容 API 表面(
/v1/*); - 跨提供商格式的请求/响应翻译;
- 模型 Combo 回退(多模型序列);
- 账户级回退(同提供商多账户);
- OAuth 与 API Key 两种提供商连接管理;
- 提供商、密钥、别名、Combo、设置、定价的本地持久化;
- 用量/成本追踪与请求日志;
- 可选的云端同步,支撑多设备状态同步。
范围边界:本仓库涵盖本地网关运行时、面板管理 API、提供商鉴权与令牌刷新、请求翻译与 SSE 流式转发、本地状态与用量持久化、云端同步编排;而NEXT_PUBLIC_CLOUD_URL背后的云端服务实现、本地进程之外的提供商控制面,以及 Claude CLI、Codex CLI 等外部 CLI 二进制本身,均不在本仓库范围内。
高层系统上下文
从整体数据流看(对应 docs/ARCHITECTURE.md 中的 mermaid 图):
- 客户端侧:Claude Code、Codex CLI、OpenClaw/Droid/Cline/Continue/Roo 等工具,以及任意 OpenAI 兼容客户端,全部接入
/v1/*;浏览器面板接入/api/*管理 API。 - 9Router 本地进程:
/v1/*兼容 API 与/api/*面板 API 汇入 SSE + 翻译核心(open-sse+src/sse),状态写入db.json,用量写入usage.json与log.txt。 - 上游提供商:三类——OAuth 类(Claude/Codex/Gemini/Qwen/iFlow/GitHub/Kiro/Cursor/Antigravity)、API Key 类(OpenAI/Anthropic/OpenRouter/GLM/Kimi/MiniMax)、兼容节点类(OpenAI 兼容 / Anthropic 兼容的自建端点)。
- 可选云端:通过
NEXT_PUBLIC_CLOUD_URL指向的云端同步端点完成多设备同步。
这一上下文说明了一个关键设计:所有客户端看到的都是单一本地端点,所有上游差异都被收敛在翻译核心内部,这正是"路由网关"的核心价值。
分层架构:从 API 路由到持久化
1) API 与路由层(Next.js App Routes)
兼容 API 与管理 API 都实现在 Next.js App Router 下:
- 兼容 API:
src/app/api/v1/*、src/app/api/v1beta/*; - 管理/配置 API:
src/app/api/*; - 路由重写:
next.config.mjs中的 rewrites 把/v1/*映射到/api/v1/*。
从 next.config.mjs 可以看到完整的重写表:除了/v1/:path*外,还处理了/v1/v1/:path*(防止客户端重复拼接前缀)、/codex/:path*与/responses(映射到/api/v1/responses)、/v1beta/:path*等。这意味着 Codex CLI 等以/responses为根路径的客户端也可以直接指向本网关。
关键兼容路由(可在src/app/api/v1/下找到对应route.js):
src/app/api/v1/chat/completions/route.js:OpenAI Chat Completions;src/app/api/v1/messages/route.js:Claude Messages;src/app/api/v1/responses/route.js:OpenAI Responses API;src/app/api/v1/models/route.js与src/app/api/v1beta/models/route.js、src/app/api/v1beta/models/[...path]/route.js:模型列表与路径化查询;src/app/api/v1/messages/count_tokens/route.js:令牌计数。
管理域则按领域划分:鉴权与设置(src/app/api/auth/*、src/app/api/settings/*)、提供商与连接(src/app/api/providers*)、自定义兼容节点(src/app/api/provider-nodes*)、OAuth(src/app/api/oauth/*)、密钥/别名/Combo/定价(src/app/api/keys*、src/app/api/models/alias、src/app/api/combos*、src/app/api/pricing)、用量(src/app/api/usage/*)、同步/云端(src/app/api/sync/*、src/app/api/cloud/*)、CLI 工具辅助(src/app/api/cli-tools/*)。
2) SSE + 翻译核心
这是网关的心脏,主流程模块:
- 入口:
src/sse/handlers/chat.js; - 核心编排:
open-sse/handlers/chatCore.js; - 提供商执行适配器:
open-sse/executors/*; - 格式检测与提供商配置:
open-sse/services/provider.js; - 模型解析与解析:
src/sse/services/model.js、open-sse/services/model.js; - 账户回退逻辑:
open-sse/services/accountFallback.js; - 翻译注册表:
open-sse/translator/index.js; - 流转换:
open-sse/utils/stream.js、open-sse/utils/streamHandler.js; - 用量提取与归一化:
open-sse/utils/usageTracking.js。
3) 持久化层
- 主状态库:src/lib/localDb.js(注意:当前它已退化为 shim,实际实现重导出自
src/lib/db/下的 SQLite 层),物理文件为${DATA_DIR}/db.json(未设置DATA_DIR时回退到~/.9router/db.json),实体包括 providerConnections、providerNodes、modelAliases、combos、apiKeys、settings、pricing; - 用量库:src/lib/usageDb.js,文件为
~/.9router/usage.json、~/.9router/log.txt,目前独立于DATA_DIR。
4) 鉴权与安全面
- 面板 Cookie 鉴权:
src/proxy.js、src/app/api/auth/login/route.js; - API Key 生成与校验:
src/shared/utils/apiKey.js; - 提供商密钥持久化在 providerConnections 条目中;
- 上游代理支持:通过环境代理变量(见 open-sse/utils/proxyFetch.js)。
5) 云端同步
- 调度器初始化:
src/lib/initCloudSync.js、src/shared/services/initializeCloudSync.js; - 周期任务:
src/shared/services/cloudSyncScheduler.js; - 控制路由:
src/app/api/sync/cloud/route.js。
请求生命周期:/v1/chat/completions一次完整往返
以POST /v1/chat/completions为例(时序图见 docs/ARCHITECTURE.md),完整链路为:
- 客户端 POST 到
/v1/chat/completions,rewrite 落到src/app/api/v1/chat/completions/route.js; - 路由调用 src/sse/handlers/chat.js 的
handleChat(request); handleChat解析/解析模型字符串(getModelInfo/getComboModels)——如果是 Combo 名称则进入handleComboChat/handleFusionChat逐模型迭代;- 通过
getProviderCredentials(provider)选择账户与令牌; - 进入
open-sse/handlers/chatCore.js的handleChatCore(body, modelInfo, credentials); - 核心内部:检测源格式 → 翻译为目标格式 → 调用
executor.execute(provider, transformedBody)发起上游请求; - 收到 SSE/JSON 响应后,按需处理 401/403(
refreshCredentials()刷新后重试); - 把上游流翻译/归一化为客户端格式,SSE 分块或 JSON 返回;
- 用量提取并持久化到 usageDb。
从源码看,open-sse/handlers/chatCore.js 中格式决策的优先级是:sourceFormatOverride(如 "openai-responses")→detectFormat(body)检测源格式 →getModelTargetFormat(alias, model)取目标格式 → 对多端点提供商用resolveTransport(provider, sourceFormat)选择零翻译的原生通道。同时核心会做一系列"净化"工作:能力裁剪(stripUnsupportedModalities移除模型不支持的图片/音频)、远程图片预取(prefetchRemoteImages)、thinking 配置归一化、工具去重(dedupeTools,Claude 客户端下消除内建工具与等价 MCP 工具的重复)。
一个值得注意的实现细节是native passthrough 通道:当检测到客户端工具与提供商属于同一生态(如 Claude Code → Claude)时,跳过全部翻译,仅替换 model 与 Bearer,实现无损直通(见 open-sse/handlers/chatCore.js)。
令牌节省器(Token Saver)管线
在最终 dispatch 之前,chatCore 会依次应用一组可选的令牌节省器(每个都可用请求头X-Token-Saver: off整体关闭,见TOKEN_SAVER_HEADER):
- RTK:压缩 tool_result 内容(
compressMessages); - Headroom:可选的外部代理压缩,代理不可用时 fail-open 不阻塞请求,并检测"幻影节省"(outbound JSON 缩小不足 5% 时告警);
- Caveman / Ponytail:注入简洁风格或"懒惰资深工程师"风格 system prompt(
rtk/caveman.js、rtk/ponytail.js); - PXPIPE:针对 Claude 格式请求压缩大图片上下文,作为 dispatch 前的最后一环。
这些能力均位于open-sse/rtk/目录,体现了网关在"省 token"上的纵深设计。
Combo 模型序列 + 账户回退:永不因单点失败中断
回退决策流程
对应 docs/ARCHITECTURE.md 的流程图,逻辑如下:
- 请求携带的模型字符串如果是 Combo 名,则加载该 Combo 的模型序列(
string[] models),否则走单模型路径; - 逐个尝试模型:解析 provider/model → 选择账户凭据;
- 无凭据则返回 provider unavailable;
- 执行失败时判断是否属于"可回退错误"(fallback-eligible):
- 不是 → 直接返回错误;
- 是 → 给该账户标记不可用冷却(cooldown),尝试该提供商的下一个账户;
- 账户耗尽 → 若处于 Combo 中则尝试下一个模型;
- 全部耗尽 → 返回 all unavailable。
该决策由 open-sse/services/accountFallback.js 驱动,依据状态码 + 错误消息启发式规则判断。
错误分类规则的源码细节
open-sse/config/errorConfig.js 定义了精确的规则表ERROR_RULES,自上而下匹配,文本规则优先于状态码规则:
| 规则类型 | 匹配条件 | 冷却/行为 |
|---|---|---|
| 文本 | 包含no credentials/improperly formed request | 2 分钟冷却 |
| 文本 | 包含request not allowed | 5 秒冷却 |
| 文本 | 包含rate limit/too many requests/quota exceeded/capacity/overloaded | 指数退避 |
| 状态码 | 401 / 402 / 403 / 404 | 2 分钟冷却 |
| 状态码 | 429 | 指数退避 |
| 兜底 | 其他未匹配错误 | 30 秒瞬态冷却 |
指数退避配置BACKOFF_CONFIG = { base: 2000, max: 5min, maxLevel: 15 },即 2s → 4s → 8s … 封顶 5 分钟(getQuotaCooldown实现,见 open-sse/services/accountFallback.js)。此外,提供商自报的限流冷却(如 codex 的resets_at可能长达 5-6 小时)会被MAX_RATE_LIMIT_COOLDOWN_MS = 30min硬性封顶,避免账户长时间不可用。
模型锁与账户锁
accountFallback 还实现了模型级锁定:通过连接记录上的扁平字段modelLock_${model}(无模型时使用modelLock___all)标记某个账户在指定模型上的冷却状态,避免把"刚被限流的模型"立刻重新路由到同一账户。
Combo 策略:fallback 与 fusion
src/sse/handlers/chat.js 显示 Combo 支持两种顶层策略:
- fallback(默认):按顺序尝试模型序列,前一个失败才进入下一个;
- fusion:将多个模型的结果做融合输出,可选
judgeModel裁判模型与fusionTuning调参(实现见open-sse/services/combo.js)。
策略优先级为 Combo 专属配置comboStrategies[modelStr].fallbackStrategy> 全局settings.comboStrategy,并且支持comboStickyRoundRobinLimit(粘性轮询阈值)控制同一会话内对某模型的复用倾向。
OAuth 接入与令牌刷新生命周期
对应 docs/ARCHITECTURE.md 的时序图,OAuth 类提供商(Claude/Codex/Gemini/Qwen/iFlow/GitHub/Kiro/Cursor/Antigravity)的接入流程为:
- 面板 UI 发起
GET /api/oauth/[provider]/[action](authorize 或 device-code 流); - OAuth 路由在提供商鉴权服务器创建授权/设备流,返回 auth URL 或设备码载荷;
- UI 随后
POST exchange或轮询,完成令牌交换; - 成功后
createProviderConnection(oauth data)写入 localDb; - 面板调用
POST /api/providers/[id]/test验证凭据(可触发一次刷新),更新状态/令牌/错误信息。
流量中的令牌刷新则在 open-sse/handlers/chatCore.js 内通过 executor 的refreshCredentials()完成:401/403 时先刷新再重试,无需用户介入。
云端同步生命周期(启用 / 同步 / 禁用)
对应 docs/ARCHITECTURE.md 的时序图:
- enable:UI 提交
POST /api/sync/cloud(action=enable)→ 设置cloudEnabled=true、确保 API Key 存在 →POST /sync/{machineId}上传 providers/aliases/combos/keys →GET /{machineId}/v1/verify校验 → 返回启用状态; - sync:拉取远端数据,将更新的本地令牌/状态写回 DB;
- disable:
cloudEnabled=false,DELETE /sync/{machineId},必要时把ANTHROPIC_BASE_URL切回本地。
启用后由CloudSyncScheduler周期触发同步(src/shared/services/cloudSyncScheduler.js)。
数据模型与存储映射
对应 docs/ARCHITECTURE.md 的 ER 图,核心实体包括:
- SETTINGS:
cloudEnabled、stickyRoundRobinLimit、requireLogin、password_hash; - PROVIDER_CONNECTION:
id、provider、authType、name、priority、isActive、apiKey、accessToken、refreshToken、expiresAt、testStatus、lastError、rateLimitedUntil、providerSpecificData(JSON); - PROVIDER_NODE:自定义兼容节点的
id、type、name、prefix、apiType、baseUrl; - MODEL_ALIAS:
alias→targetModel; - COMBO:
id、name、models(字符串数组); - API_KEY:
id、name、key、machineId、isActive; - USAGE_ENTRY:
provider、model、prompt_tokens、completion_tokens、connectionId、timestamp。
物理存储文件:
- 主状态:
${DATA_DIR}/db.json(或~/.9router/db.json); - 用量统计:
~/.9router/usage.json; - 请求日志行:
~/.9router/log.txt; - 可选的翻译/请求调试会话:
<repo>/logs/...(需开启ENABLE_REQUEST_LOGS=true)。
从实现看,当前主状态库已迁移到 SQLite 层(src/lib/localDb.js 仅为兼容 shim,重导出 src/lib/db/index.js),package.json中better-sqlite3放在 optionalDependencies(无构建工具的环境回退到sql.js),这是部署健壮性的一个细节。
部署拓扑
对应 docs/ARCHITECTURE.md 的拓扑图:开发者主机上的 CLI 工具与浏览器面板都指向 9Router 运行时(Next.js 服务,PORT=20128),SSE 核心与 executors 负责上游调用,主库db.json与用量库usage.json/log.txt就近持久化,外部只依赖 AI 提供商与可选的云端同步服务。注意 package.json 中dev/start默认端口为 20127,文档验证清单使用PORT=20128,部署时以实际环境变量为准。
模块映射(决策关键)
路由与 API 模块
src/app/api/v1/*、src/app/api/v1beta/*:兼容 API;src/app/api/providers*:提供商 CRUD、校验、测试;src/app/api/provider-nodes*:自定义兼容节点管理;src/app/api/oauth/*:OAuth/设备码流;src/app/api/keys*:本地 API Key 生命周期;src/app/api/models/alias:别名管理;src/app/api/combos*:回退 Combo 管理;src/app/api/pricing:成本计算用的定价覆盖;src/app/api/usage/*:用量与日志 API;src/app/api/sync/*+src/app/api/cloud/*:云端同步与云端辅助;src/app/api/cli-tools/*:本地 CLI 配置写入/检查。
路由与执行核心
src/sse/handlers/chat.js:请求解析、Combo 处理、账户选择循环;open-sse/handlers/chatCore.js:翻译、executor 分发、重试/刷新、流建立;open-sse/executors/*:各提供商的网络与格式行为。
翻译注册表与格式转换器
open-sse/translator/index.js:翻译注册表与编排(register(from, to, requestFn, responseFn)注册制,各格式模块以副作用导入方式自注册,规避循环依赖);- 请求翻译器:
open-sse/translator/request/*; - 响应翻译器:
open-sse/translator/response/*; - 格式常量:
open-sse/translator/formats.js。
持久化
src/lib/localDb.js:持久化配置/状态(shim →src/lib/db/);src/lib/usageDb.js:用量历史与滚动请求日志。
提供商执行器覆盖
专用 executor(见 open-sse/executors/index.js)覆盖antigravity、gemini-cli、github、kiro、codex、cursor,此外还包含azure、iflow、qoder、vertex、qwen、opencode、grok-cli、grok-web、perplexity-web、ollama-local、commandcode、xiaomi-tokenplan、mimo-free、codebuddy-cn/intl、trae、zed、windsurf、devin-cli等,并提供了便捷别名(cu→cursor、gcli/gb→grok-cli、mmf→mimo-free)。其余所有提供商(含兼容节点)统一走open-sse/executors/default.js,通过getExecutor(provider)惰性实例化并缓存。
格式翻译覆盖
检测的源格式(detectFormat/detectFormatByEndpoint依据请求载荷形态判定):openai、openai-responses、claude、gemini。目标格式:OpenAI chat/Responses、Claude、Gemini/Gemini-CLI/Antigravity envelope、Kiro、Cursor。翻译对(from→to)依据源载荷形态与提供商目标格式在运行时动态选择;当客户端工具与提供商同生态时走 native passthrough 零翻译直通。
失败模式与韧性设计
1) 账户/提供商可用性
- 对瞬态/限流/鉴权错误触发账户冷却;
- 请求失败前先做账户回退;
- 当前模型/提供商路径耗尽时做 Combo 模型回退。
2) 令牌过期
- 可刷新提供商在请求前预检并刷新,失败重试;
- 核心路径中 401/403 在刷新尝试后重试。
3) 流安全
- 断线感知的流控制器(
createStreamController,见 open-sse/handlers/chatCore.js); - 翻译流带流结束 flush 与
[DONE]处理; - 提供商未返回用量元数据时使用用量估算回退。
4) 云端同步降级
- 同步错误会暴露,但本地运行不受影响;
- 调度器具备可重试逻辑,但周期执行默认单次尝试。
5) 数据完整性
- DB 结构迁移/缺失键修复;
- localDb 与 usageDb 的损坏 JSON 重置保护。
可观测性与运维信号
运行时可见性来源:
- 控制台日志:src/sse/utils/logger.js;
usage.json中的按请求用量聚合;log.txt中的文本请求状态日志;ENABLE_REQUEST_LOGS=true时logs/下的深度请求/翻译日志;- 面板用量端点
/api/usage/*供 UI 消费。
安全敏感边界
JWT_SECRET:面板会话 Cookie 的签名/校验;INITIAL_PASSWORD:初始密码回退值(默认123456,真实部署必须修改);API_KEY_SECRET:本地 API Key 格式的 HMAC 密钥;- 提供商密钥(API Key/令牌)持久化在本地 DB,应在文件系统层面保护;
- 云端同步端点依赖 API Key 鉴权 + machineId 语义。
环境变量与运行时矩阵
代码中实际使用的环境变量:
| 类别 | 变量 |
|---|---|
| 应用/鉴权 | JWT_SECRET、INITIAL_PASSWORD |
| 存储 | DATA_DIR |
| 安全哈希 | API_KEY_SECRET、MACHINE_ID_SALT |
| 日志 | ENABLE_REQUEST_LOGS |
| 同步/云端 URL | NEXT_PUBLIC_BASE_URL、NEXT_PUBLIC_CLOUD_URL |
| 出站代理 | HTTP_PROXY、HTTPS_PROXY、ALL_PROXY、NO_PROXY及小写变体 |
| 平台/运行时辅助 | APPDATA、NODE_ENV、PORT、HOSTNAME |
已知架构注意事项
usageDb当前存于~/.9router,不遵循DATA_DIR;/api/v1/route.js返回静态模型列表,并非/v1/models的主要模型来源;- 请求日志开启后会写完整请求头/体,
logs/目录应按敏感数据对待; - 云端行为依赖正确的
NEXT_PUBLIC_BASE_URL与云端端点可达性。
运维验证清单
构建与启动(命令中的路径按实际仓库位置替换):
npm run build # 从源码构建 docker build -t 9router . # 构建 Docker 镜像启动后验证:
curl http://<host>:20128/api/settings # 面板设置可用 curl http://<host>:20128/api/v1/models # 兼容模型列表可用CLI 工具的 base URL 应指向http://<host>:20128/v1(当PORT=20128时)。完整的架构演进与历史变更可进一步查阅 CHANGELOG.md,运行方式与容器化部署参考 DOCKER.md。
综上,9Router 的架构本质是一个"兼容面 + 翻译核心 + 回退矩阵 + 本地持久化"四层结构:对外用最少的兼容端点覆盖最多客户端,对内用翻译注册表吸收上游格式差异,用账户冷却/模型锁/Combo 序列保证高可用,用db.json/usage.json支撑离线可运行的本地优先体验——这是理解其所有功能特性的总纲。
【免费下载链接】9routerUnlimited FREE AI coding. Connect Claude Code, Codex, Cursor, Cline, Copilot, Antigravity to FREE Claude/GPT/Gemini via 40+ providers. Auto-fallback, RTK -40% tokens, never hit limits.项目地址: https://gitcode.com/GitHub_Trending/9r/9router
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考