中文 English

别把 API Key 到处塞了:我用 NewAPI 搭了一个自用 AI 中转站

发布时间: 2026-07-10
NewAPI New API AI Gateway Docker Docker Compose Self-Hosted LLM 中转站

先说结论

NewAPI 不是一个“白嫖模型”的工具,也不是一个神秘代理。它更像我家里的“AI 前台”:所有 App 只认一个入口,前台负责转发到合法授权的上游模型,顺手做令牌、额度、分组、日志、模型限制和用量统计。对自用场景来说,最舒服的地方不是“多了一个网页”,而是终于不用把每个上游 Key 分散塞进十几个客户端。

本文会按我自己搭自用中转站的思路写:为什么需要它、容易误解在哪里、最小可用 Docker Compose 怎么跑、为什么我建议加持久化目录、Windows 11 / Ubuntu 26.04 / macOS 26 三套一键脚本怎么写,以及让 Agent 自动配置时应该给它什么边界。全文不放真实内网地址、完整主机名、私有域名和任何密钥。

AI 生成封面:New API 位于多个模型提供方和应用之间,像一个安全网关。

图 1:AI 生成封面。NewAPI 的价值不是“替你造模型”,而是把模型入口、权限和账本收在一个地方。

一、问题背景:为什么我需要一个自用 AI 中转站

如果你只在一个聊天网页里用一个模型,确实不需要 NewAPI。问题是我现在的用法早就不是这样了:桌面客户端、命令行 Agent、代码编辑器、脚本任务、临时测试工具,每个地方都想调用模型;上游也不止一家,有的便宜,有的快,有的上下文长,有的适合写代码,有的适合轻量任务。

最开始的做法很直接:每个工具里各填一遍 API 地址和 Key。能用,但很快失控。

第一,密钥散落。某个配置文件、某个环境变量、某个旧脚本里到底还有没有 Key,时间一长自己都说不清。

第二,模型名不统一。A 工具叫 gpt-xxx,B 工具配置成另一个别名,后来上游模型下线,排查时要翻每个客户端。

第三,用量看不清。到底是哪个工具消耗多、哪个模型突然变贵、哪个 token 该限额,如果没有统一账本,只能靠猜。

第四,权限不好收。把上游 Key 直接给应用,就像把整串钥匙交给临时工。它可能只需要打开厨房门,但你给了它整栋楼的钥匙。

NewAPI 解决的正是这类“入口太散”的问题。官方 README 把它定位为“新一代大模型网关与 AI 资产管理系统”,支持多语言 UI、数据看板、令牌分组、模型限制、用量和成本管理,也能兼容 OpenAI、Claude、Gemini 等不同接口格式。我的理解更朴素:它就是一个可自建的模型总前台。

真实截图:New API 官方安装页列出了 Docker Compose、Docker 单容器、面板部署、集群部署等方式。

图 2:真实截图,来源于 New API 官方文档安装页。官方把 Docker Compose 放在部署方式里,适合长期运行的自建服务。

二、问题表现:你以为只是“转发请求”,实际是“管钥匙和账本”

很多人第一次听“中转站”,会以为它只是一个反向代理:客户端发请求,它照搬到上游,再把结果照搬回来。如果只做到这一步,Nginx 都能凑合,没必要专门上 NewAPI。

NewAPI 真正有价值的地方在中间层:

打个小学生也能懂的比方:上游 Key 是“食堂后厨钥匙”,NewAPI 是“饭卡系统”。学生不该直接拿后厨钥匙去开冰箱,而是刷自己的饭卡。饭卡里有余额、能买哪些窗口、今天刷了几次,系统都知道。某张饭卡丢了,挂失这一张就行,不用把整个食堂的锁都换掉。

架构解释图:应用、Token、管理员都进入 NewAPI,再由 NewAPI 转到合法授权的上游提供方。

图 3:NewAPI 更像饭卡系统。客户端只拿自己的 token,上游 Key 留在网关侧集中管理。

三、问题分析:自用部署最容易踩的 5 个坑

我看 NewAPI 部署时,最容易犯的错不是命令写错,而是把“能打开网页”误认为“部署完成”。

坑 1:不挂载数据目录。 用户给出的最小 Compose 可以启动服务,但如果没有 ./data:/data,后续容器重建时就容易把 SQLite 数据、上传文件或本地状态弄丢。自用也要持久化,因为配置 channel 和 token 是有成本的。

坑 2:上来就用 latest 官方示例为了通用,常用 calciumion/new-api:latest。我自己更偏向固定一个明确 tag,比如本文使用用户给出的 calciumion/new-api:v1.0.0-rc.19-i18nfix.2。固定版本的好处是:今天能跑,明天重建还是同一套行为;等你想升级,再主动换 tag。

坑 3:把上游 Key 写进博客、脚本或聊天记录。 文章里只能写占位符,真实 Key 应该通过 Web UI、密钥管理器或本机安全输入进入系统。尤其是让 Agent 帮你配置时,不要让它读取 .env 里的真实值,也不要让它把 sk- 内容打印出来。

坑 4:没有初始化后的验证。 容器状态 Up 只是说明进程还活着,不代表你能登录、不代表 channel 可用、不代表 token 能调用模型。至少要看容器日志、访问状态接口、登录后台、添加一个授权 channel、创建一个低额度 token,再用客户端发一次最小请求。

坑 5:把自用网关误当公开服务。 如果你要对公众提供生成式 AI 服务或 API 转售服务,就不是“家里跑个容器”这么简单。官方 README 也明确提醒,需要自行处理备案、许可、内容安全、实名、日志留存、税务和上游授权等合规要求。本文只讨论合法授权下的自用和小范围内部场景。

真实截图:官方 Docker Compose 部署文档说明了 clone、编辑 docker-compose.yml、docker compose up、查看日志和访问系统。

图 4:真实截图,来源于官方 Docker Compose 部署页。部署不是只复制 YAML,还要会看日志和状态。

四、如何解决:先跑最小可用,再补上持久化和验证

用户给出的 Compose 是最小可用形态:

services:
  new-api:
    image: calciumion/new-api:v1.0.0-rc.19-i18nfix.2
    container_name: new-api
    restart: always
    ports:
      - "3000:3000"
    environment:
      TZ: "Asia/Shanghai"

这段可以启动,但我建议自用时至少加一个数据卷:

services:
  new-api:
    image: calciumion/new-api:v1.0.0-rc.19-i18nfix.2
    container_name: new-api
    restart: always
    ports:
      - "3000:3000"
    environment:
      TZ: "Asia/Shanghai"
      SESSION_SECRET: "${SESSION_SECRET}"
    volumes:
      - ./data:/data

SESSION_SECRET 不要手写固定示例值,脚本里生成。./data:/data 是自用部署的底线。它就像把笔记本放进书包:容器可以重建,书包里的数据不能跟着丢。

如果后续要更稳,可以再引入 PostgreSQL/MySQL 和 Redis。官方 Docker Compose 配置指南里给了包含数据库和 Redis 的生产示例,也说明了 SQL_DSNREDIS_CONN_STRINGSESSION_SECRETNODE_TYPESYNC_FREQUENCY 等环境变量的用途。我的建议是:第一天先用最小版跑通;当你已经有多个用户、多个渠道、明确备份方案,再升级到数据库版。

真实截图:官方 Docker Compose 配置指南给出了包含数据库、Redis、日志、健康检查的标准配置。

图 5:真实截图,来源于官方配置指南。长期运行时,要关注数据卷、数据库、Redis、健康检查和默认密码。

五、一键脚本:Windows 11 / Ubuntu 26.04 / macOS 26

下面三套脚本的目标一致:

  1. 检查 Docker 和 Docker Compose 是否可用;
  2. 创建部署目录;
  3. 生成随机 SESSION_SECRET
  4. 写入 docker-compose.yml.env
  5. 启动 NewAPI;
  6. 做本机状态检查;
  7. 不安装第三方工具,不写入真实上游 Key,不暴露内网地址。

注意:脚本本身不调用任何额外 SaaS;但 Docker 拉取镜像仍需要你当前机器能访问配置好的镜像源。脚本里的访问地址只使用 localhost 和占位域名。

Windows 11:PowerShell

保存为 install-newapi-windows.ps1,右键用 PowerShell 运行:

$ErrorActionPreference = "Stop"

$Root = Join-Path $HOME "newapi"
$Image = "calciumion/new-api:v1.0.0-rc.19-i18nfix.2"
$Port = "3000"

function Need-Cmd($Name) {
  if (-not (Get-Command $Name -ErrorAction SilentlyContinue)) {
    throw "Missing command: $Name. Please install Docker Desktop first."
  }
}

Need-Cmd docker
docker compose version | Out-Null

New-Item -ItemType Directory -Force -Path $Root | Out-Null
New-Item -ItemType Directory -Force -Path (Join-Path $Root "data") | Out-Null

$SecretBytes = New-Object byte[] 32
[Security.Cryptography.RandomNumberGenerator]::Fill($SecretBytes)
$SessionSecret = [Convert]::ToBase64String($SecretBytes)

@"
SESSION_SECRET=$SessionSecret
"@ | Set-Content -Encoding UTF8 (Join-Path $Root ".env")

@"
services:
  new-api:
    image: $Image
    container_name: new-api
    restart: always
    ports:
      - "$Port`:3000"
    environment:
      TZ: "Asia/Shanghai"
      SESSION_SECRET: "`${SESSION_SECRET}"
    volumes:
      - ./data:/data
"@ | Set-Content -Encoding UTF8 (Join-Path $Root "docker-compose.yml")

Push-Location $Root
docker compose up -d
Start-Sleep -Seconds 8
docker compose ps
try {
  $r = Invoke-WebRequest -UseBasicParsing "http://localhost:$Port/api/status" -TimeoutSec 8
  Write-Host "Status endpoint HTTP:" $r.StatusCode
} catch {
  Write-Host "Container started. If status check failed, open http://localhost:$Port and check docker compose logs."
}
Pop-Location

Write-Host "NewAPI files are in $Root"
Write-Host "Open http://localhost:$Port for first-time initialization."

Ubuntu 26.04:Bash

保存为 install-newapi-ubuntu.sh,执行 bash install-newapi-ubuntu.sh

#!/usr/bin/env bash
set -euo pipefail

ROOT="${HOME}/newapi"
IMAGE="calciumion/new-api:v1.0.0-rc.19-i18nfix.2"
PORT="${NEWAPI_PORT:-3000}"

need_cmd() {
  command -v "$1" >/dev/null 2>&1 || {
    echo "Missing command: $1. Please install Docker Engine and Docker Compose plugin first." >&2
    exit 1
  }
}

need_cmd docker
docker compose version >/dev/null

mkdir -p "${ROOT}/data"
SESSION_SECRET="$(openssl rand -base64 32 2>/dev/null || head -c 32 /dev/urandom | base64)"

cat > "${ROOT}/.env" <<EOF
SESSION_SECRET=${SESSION_SECRET}
EOF
chmod 600 "${ROOT}/.env"

cat > "${ROOT}/docker-compose.yml" <<EOF
services:
  new-api:
    image: ${IMAGE}
    container_name: new-api
    restart: always
    ports:
      - "${PORT}:3000"
    environment:
      TZ: "Asia/Shanghai"
      SESSION_SECRET: "\${SESSION_SECRET}"
    volumes:
      - ./data:/data
EOF

cd "${ROOT}"
docker compose up -d
sleep 8
docker compose ps

if command -v curl >/dev/null 2>&1; then
  curl -fsS "http://localhost:${PORT}/api/status" || true
  echo
fi

echo "NewAPI files are in ${ROOT}"
echo "Open http://localhost:${PORT} for first-time initialization."

macOS 26:Zsh

保存为 install-newapi-macos.zsh,执行 zsh install-newapi-macos.zsh

#!/usr/bin/env zsh
set -euo pipefail

ROOT="${HOME}/newapi"
IMAGE="calciumion/new-api:v1.0.0-rc.19-i18nfix.2"
PORT="${NEWAPI_PORT:-3000}"

need_cmd() {
  if ! command -v "$1" >/dev/null 2>&1; then
    echo "Missing command: $1. Please install Docker Desktop or a compatible Docker runtime first." >&2
    exit 1
  fi
}

need_cmd docker
docker compose version >/dev/null

mkdir -p "${ROOT}/data"
SESSION_SECRET="$(openssl rand -base64 32)"

cat > "${ROOT}/.env" <<EOF
SESSION_SECRET=${SESSION_SECRET}
EOF
chmod 600 "${ROOT}/.env"

cat > "${ROOT}/docker-compose.yml" <<EOF
services:
  new-api:
    image: ${IMAGE}
    container_name: new-api
    restart: always
    ports:
      - "${PORT}:3000"
    environment:
      TZ: "Asia/Shanghai"
      SESSION_SECRET: "\${SESSION_SECRET}"
    volumes:
      - ./data:/data
EOF

cd "${ROOT}"
docker compose up -d
sleep 8
docker compose ps
curl -fsS "http://localhost:${PORT}/api/status" || true
echo
echo "NewAPI files are in ${ROOT}"
echo "Open http://localhost:${PORT} for first-time initialization."

六、人工自动执行:跑完脚本之后还要做什么

脚本只负责把服务跑起来。初始化和上游配置建议人工做,因为这里会接触真实账号、真实上游 Key 和合规边界。

我的顺序是:

  1. 打开 http://localhost:3000,按页面提示创建管理员账号;
  2. 进入后台后先改默认配置,开启必要的登录保护;
  3. 新增一个合法授权的上游 channel,只填你自己拥有权限的上游;
  4. 用 channel 测试功能确认上游可用;
  5. 创建一个低额度 token,限制可调用模型;
  6. 在一个客户端里只配置 NewAPI 的 base URL 和这个低额度 token;
  7. 发一次最小请求,确认日志里能看到调用记录;
  8. 备份 ~/newapi/data,并至少做一次恢复演练。

这一套流程看起来比“复制一段 compose”麻烦,但它能避免很多后患。尤其是第 5 步,低额度 token 是安全阀。某个客户端配置错、脚本死循环、模型名误写,最多烧掉这个 token 的额度,而不是拖着上游主 Key 一起出事。

部署检查清单:Compose、Secrets、Start、Init、Channel、Token、Verify、Backup。

图 6:容器启动只是第 3 步。真正可靠的自用中转站,要闭环到 token、验证和备份。

七、Agent 自动配置:给它边界,不要给它裸 Key

如果你想让 Codex、Claude、OpenClaw、HermesAgent 这类 Agent 帮你自动配置,可以直接给下面这段提示词。重点是让 Agent 做“文件和服务编排”,不要让它读取或打印真实密钥。

请在当前机器部署一个自用 NewAPI 服务,要求:
1. 只使用 Docker Compose,不安装额外第三方服务。
2. 使用镜像 calciumion/new-api:v1.0.0-rc.19-i18nfix.2。
3. 部署目录为用户主目录下的 newapi,必须挂载 ./data:/data。
4. 自动生成 SESSION_SECRET,写入 .env,权限尽量收紧。
5. 只暴露本机 3000 端口,不写入任何真实内网地址、主机名、私有域名或 API Key。
6. 启动后执行 docker compose ps、docker compose logs --tail=80 new-api,并检查 http://localhost:3000/api/status。
7. 如果需要上游模型 Key,只给我明确的人工输入位置,不要读取、显示或记录真实 Key。
8. 完成后输出部署目录、验证结果、下一步人工初始化清单。

让 Agent 做这类事时,我会额外加三条硬约束:

Agent 很像执行力很强的实习生。你让它“把店开起来”,它可能连门牌、收银台、仓库都一起动;你让它“只把货架摆好,钥匙我自己拿”,风险就小得多。

八、验证:不要相信“应该能用”

官方 API 文档里可以看到 NewAPI 提供 OpenAI Chat Completions 兼容格式,路径是 /v1/chat/completions。这意味着很多客户端可以把 NewAPI 当成 OpenAI 风格入口来接。但真正验证时,不要第一步就拿复杂 Agent 流程测试,先做最小闭环。

验证清单如下:

cd ~/newapi
docker compose ps
docker compose logs --tail=80 new-api
curl -fsS http://localhost:3000/api/status

后台初始化后,再做应用层验证:

  1. channel 测试成功;
  2. token 限额和模型范围正确;
  3. 客户端 base URL 指向你的 NewAPI 入口;
  4. 客户端只拿 NewAPI token,不拿上游 Key;
  5. 日志里能看到本次请求;
  6. 禁用 token 后,客户端请求会失败;
  7. 备份 data 目录后,能在测试目录恢复。

验证这件事像收快递。短信说“已签收”不算,你要打开门看包裹真的在门口,还要拆开确认里面不是空盒子。

真实截图:New API 官方 API 文档入口展示了管理接口和模型接口分类。

图 7:真实截图,来源于官方 API 文档。接入客户端前,先确认你要用的是哪种兼容格式。

九、问题根因:中转站真正解决的是“控制面分散”

回头看,NewAPI 解决的根因不是“我需要一个代理地址”,而是“AI 使用的控制面太分散”。

没有 NewAPI 时,控制面散在每个客户端里:哪个模型能用、额度多少、Key 在哪里、请求日志在哪里、失败该看谁。每个客户端都像一个小账本,小账本多了就会乱。

有 NewAPI 后,控制面集中到网关:客户端只负责调用,管理员负责 channel、token、额度、日志、模型范围和备份。这种结构不一定更简单,但更可控。

所以我建议的自用原则是:

十、Q&A

Q1:NewAPI 是不是越权调用模型?

不是。它只是网关。你必须合法取得上游模型、账号、Key 和接口权限,并遵守上游条款。没有授权的上游,它不能凭空变出模型。

Q2:我能不能把它公开给别人用?

技术上可以,责任上很重。公开服务涉及内容安全、实名、日志留存、支付、税务、备案、上游授权等问题。本文只建议自用或小范围内部授权使用。

Q3:SQLite 够不够?

个人自用、少量 token、少量 channel,SQLite 很方便。多人团队、重负载、集群或更严格备份恢复要求,建议看官方生产 Compose,使用数据库和 Redis。

Q4:为什么不用 latest

latest 适合快速体验,不适合“今天部署、半年后重建仍要一样”的服务。固定 tag 能降低不可预期变化。升级时再主动改 tag、备份、验证。

Q5:脚本为什么不自动配置上游 Key?

因为 Key 是最高风险信息。脚本可以部署框架,真实上游 Key 应该由你在 Web UI 或安全输入流程里填,不应该出现在博客、聊天记录、终端历史或 Agent 输出里。

Q6:我应该怎么备份?

最小部署至少备份 ~/newapi/datadocker-compose.yml.env。注意 .env 可能包含 session secret,要加密保存。备份不是压缩完就结束,至少要在测试目录恢复一次。

参考资料