技术栈简化: - 前端:One-API 网页管理 → OpenWebUI 极简界面(WEBUI_AUTH=False 无需登录,自动连接 LiteLLM) - 网关:One-API(数据库驱动)→ LiteLLM(纯 YAML 配置驱动) - 数据存储:MySQL(持久化) → LiteLLM 无状态(配置即代码) 配置简化: - 不需要注册账号、不需要点网页添加渠道 - 所有 5 个渠道的定义、模型映射、优先级、Failover 规则写在 litellm-config.yaml - API Key 填在 .env(占位符在 config.yaml 中引用) - 用户体验:打开网页 → 输入问题 → 自动用最合适的模型回答 已部署到 NAS,openwebui 正在初始化数据库(首次启动需要 1-2 分钟)
NexusAI — 自建多模态 AI 网关系统(AI Gateway System)
聚合多家免费多模态 AI API 的统一对话网关:Nginx + LobeChat + One-API + MySQL 容器化部署。 单一渠道不可用时自动故障转移(Failover),用户无感知。 最后更新:2026-08-23(文档入库,尚未部署)
1. 项目简介与设计目标
公司内网直连 NVIDIA NIM、Gemini 等免费大模型 API 不稳定,本系统部署在 NAS 本机(Docker Compose),聚合 5 家免费多模态 API(Gemini、NVIDIA NIM、Groq、OpenRouter、Mistral),通过 One-API 网关统一路由,实现"一个渠道不可用自动切换下一个"的高可用能力,并提供接近 Gemini 首页体验的 Web 对话界面(LobeChat),支持图片 + 文字混合输入问答;对外由甲骨文服务器(129.146.203.203)反向代理端口提供访问(与 FAM 项目 frp 方案一致)。
1.1 核心价值
- 聚合免费额度 + 高可用:用户只需在统一界面提问/上传图片,无需关心调用哪家模型
- 配置驱动:渠道增删、优先级调整通过 One-API 后台界面完成,不改代码
- 全部免费:仅使用各平台免费额度,不产生 API 调用费用
1.2 文档
2. 系统架构
浏览器 / 移动端浏览器(公司内网)
│ HTTPS (3210)
▼
甲骨文服务器(129.146.203.203)—— frp 隧道专属端口 3210(对外统一入口)
│ 反代 / 隧道(frp)
▼
NAS 本机(Docker 宿主机)
├─ Nginx(HTTPS 终止,流式透传)──► LobeChat(:3210)──► One-API(:3000 仅内网)──► MySQL
│
▼ NAS 出站直连海外 API
Gemini · NVIDIA NIM · Groq · OpenRouter · Mistral
| 层级 | 组件 | 职责 |
|---|---|---|
| 反代层 | 甲骨文服务器 | frp/nginx 反向代理端口,对外统一访问入口 |
| 接入层 | Nginx | HTTPS 终止、反向代理(NAS 本机) |
| 表现层 | LobeChat | 对话界面渲染、图片上传、Access Code 鉴权、对话历史(IndexedDB) |
| 网关层 | One-API | 协议转换、渠道路由、Failover、用量统计 |
| 数据层 | MySQL | 渠道配置 / Token / 调用日志持久化 |
2.1 渠道设计(优先级数字越小越优先)
| 渠道名 | Base URL | 优先级 | 支持视觉 |
|---|---|---|---|
| nvidia-nim | integrate.api.nvidia.com/v1 | 1 | 部分模型 |
| gemini | generativelanguage.googleapis.com/v1beta | 1 | 是 |
| openrouter | openrouter.ai/api/v1 | 2 | 部分模型(:free) |
| groq | api.groq.com/openai/v1 | 2 | 有限 |
| mistral | api.mistral.ai/v1 | 3 | Pixtral 系列 |
2.2 模型映射(前端只感知两个聚合模型名)
| 对外模型名 | 用途 | 关联渠道 |
|---|---|---|
auto-vision |
图文混合问答 | Gemini、NVIDIA(视觉模型)、OpenRouter(视觉模型)、Mistral(Pixtral) |
auto-text |
纯文本问答 | 上述全部 + Groq |
2.3 故障转移策略
- 请求按
auto-vision/auto-text匹配渠道池,按优先级从高到低尝试 - 渠道返回 429/5xx/超时 → 标记本次失败,自动尝试下一渠道
- 连续失败超阈值 → 渠道进入"冷却禁用",冷却期后自动恢复
- 全部渠道失败 → 向前端返回明确错误提示(不静默失败)
3. 部署概览
- 部署位置:NAS 本机(Synology DSM 7,Docker Compose / ContainerManager)
- 对外访问:甲骨文服务器(129.146.203.203)frp 隧道(frps 已运行,
frpc.toml追加nexusai代理)转发公网专属端口 3210 → NAS 的 Nginx 容器(NAS 本机端口 8443),即https://129.146.203.203:3210 - 方式:所有服务在同一
ai-network内;NAS 本机 80/443/3000 已被 Gitea/DSM 等既有服务占用,AI 网关改用 8443(nginx)与 3001(one-api,仅 127.0.0.1) - 目录结构:
docker-compose.yml/.env(敏感变量,不入库)/nginx.conf/certs//one-api-data//mysql-data/ - 详细部署步骤:见 SDD 第 6 章
3.1 容器服务
| 服务 | 镜像 | 端口(NAS 本机) | 说明 |
|---|---|---|---|
| nginx | nginx:alpine | 8443→443(经 frp 隧道对外) | HTTPS 终止、反向代理 |
| lobe-chat | lobehub/lobe-chat:latest | 未发布主机端口,仅容器内网 3210 | 前端对话界面 |
| one-api | justsong/one-api:latest | 192.168.50.64:3001→3000(仅局域网) | 网关聚合层 |
| mysql | mysql:8.0 | 未发布主机端口,仅容器内网 3306 | One-API 数据存储 |
NAS 本机 80/443/3000 已被 Gitea(3000)与其他既有服务(80/443)占用,故 AI 网关容器改用上表端口;甲骨文侧无需改动,frps 按
frpc.toml中新增的[[proxies]] name="nexusai"动态接受 remotePort 3210。
3.2 安全要点
- 对外仅暴露甲骨文服务器 frp 隧道转发的 3210 端口(专属端口,非默认 443,与 Gitea:3000/WordPress:8500/FAM-core:8000 同一约定),NAS 本机不直接暴露公网
- One-API 后台端口只绑定 NAS 局域网 IP
192.168.50.64:3001,不接入 frp 隧道,不暴露公网;NAS sshd 已关闭AllowTcpForwarding,SSH 隧道方案不可行,故改为局域网直连管理:浏览器打开http://192.168.50.64:3001(需与 NAS 同一局域网) - 所有 API Key 通过服务器
.env注入容器环境变量,.env不入库(见 SDD 第 5 章) - 前端强制
ACCESS_CODE校验;Nginx 全站 HTTPS
4. 服务器访问信息
4.1 Synology NAS(AI 网关部署目标 + FAM + Gitea)
| 项目 | 值 |
|---|---|
| IP | 192.168.50.64 |
| SSH 端口 | 2222(scp 禁用,用 stdin 管道传文件) |
| SSH 用户 / 密码 | ericwyuan / iLoveJava5 |
| 系统 | Synology DS220+ (Geminilake), DSM 7 |
| Tailscale IP | 100.70.234.39(userspace 模式,端口不通待修) |
| 登录命令 | ssh -p 2222 ericwyuan@192.168.50.64 |
4.2 MariaDB(NAS)
| 项目 | 值 |
|---|---|
| 版本 | MariaDB 10.11.11 |
| Socket | /run/mysqld/mysqld10.sock |
| Root 密码 | iLoveJava5! |
| 连接 | /usr/local/mariadb10/bin/mysql -S /run/mysqld/mysqld10.sock -u root -p |
4.3 Oracle Cloud(反代层,129.146.203.203)
| 项目 | 值 |
|---|---|
| 公网 IP | 129.146.203.203 |
| SSH 用户 | ubuntu(密钥 ~/.ssh/oracle_sentinel) |
| 系统 | aarch64 (Ampere A1 2C12G), Ubuntu 20.04 LTS |
| Tailscale | 100.74.137.126(已安装在线,与 NAS 端口不通) |
| 登录命令 | ssh -i ~/.ssh/oracle_sentinel ubuntu@129.146.203.203 |
| 反代职责 | frp/nginx 反代公网端口 → NAS AI 网关入口 |
4.4 Gitea 代码仓库
| 项目 | 值 |
|---|---|
| URL | http://192.168.50.64:3000/ericwyuan/NexusAI |
| 账号 / 密码 | ericwyuan / iLoveJava5 |
5. AI 渠道 API Key(服务器 .env 管理,不入库)
⚠️ Gemini 与 NVIDIA 的 Key 将申请新值,此处不写入旧 Key,配置于 NAS
.env的占位变量。
| 渠道 | 模型 / 端点 | Key 环境变量(NAS .env) |
|---|---|---|
| Gemini | generativelanguage.googleapis.com/v1beta,模型 gemini-flash-latest |
${GEMINI_API_KEY}(新 Key 待申请) |
| NVIDIA NIM | integrate.api.nvidia.com/v1,默认 meta/llama-3.2-11b-vision-instruct |
${NVIDIA_API_KEY}(新 Key 待申请) |
| Groq | api.groq.com/openai/v1 |
${GROQ_API_KEY} |
| OpenRouter | openrouter.ai/api/v1 |
${OPENROUTER_API_KEY} |
| Mistral | api.mistral.ai/v1 |
${MISTRAL_API_KEY} |
各渠道 Key 申请后填入 NAS
.env,One-API 后台添加渠道时引用,切勿提交版本库。
6. 提交规范(AI Agent 必读)
工作流程(强制):
- 开工前:仓库根目录
git pull --rebase - 每完成一项验收子任务:立即
git add+git commit+git push,一任务一 commit,不批量合并 - 遇到阻塞:先 commit 可工作部分,message 加
[WIP]前缀 - 修 Bug:单独 commit,格式
fix(模块): 问题简述
commit message 格式:
- 任务:
[阶段X.Y子任务号] 子任务名称 - 完成内容简述 - Bug:
fix(模块): 问题简述 - 性能/文档:
perf(...)/docs(...)
禁止:不 commit 直接继续;一次 commit 多个子任务;git push --force;跳过 hooks(--no-verify)。
自检清单:任务对应哪一行验收标准?是否已 commit + push?message 是否合规?开工前是否 pull --rebase?
7. 当前进度与待办
已完成(截至 2026-08-23)
- 软件需求文档(SRS V1.0)与软件设计文档(SDD V1.0)入库(
docs/) - 项目仓库初始化(Gitea: ericwyuan/NexusAI)
- NAS→各渠道直连稳定性验证、frp 隧道打通、四容器(mysql/one-api/lobe-chat/nginx)部署上线,端到端
https://129.146.203.203:3210/已可访问(详见 PROGRESS.md)
待办(见 SDD 第 8 章待确认事项)
| # | 任务 | 优先级 | 说明 |
|---|---|---|---|
| 1 | 高 | 五渠道均直连正常 | |
| 2 | 申请新 Gemini / NVIDIA API Key | 高 | 需用户在对应厂商控制台自行申请 |
| 3 | 高 | 复用既有 frps,新增 nexusai 代理 | |
| 4 | 高 | ||
| 5 | NAS 实际部署 + One-API 渠道配置 + Failover 验证 | 高 | 容器已部署上线;管理员账号/渠道/Token 需用户在 One-API Web UI 手动完成(涉及密钥输入,AI 不代做) |
| 6 | 确认 HTTPS 域名与证书方案 | 中 | 当前为临时自签名证书跑通链路;是否购买正式域名待紫川决定,届时替换证书即可 |
| 7 | 中 | 详见 SDD 第 8 章:保持 Client-Side、管理端口走 SSH 隧道、暂不接入 Prometheus/Grafana |
文档结束