One API 管理面板详解:渠道、令牌、分组与额度怎么算
已经装好 One API,却分不清渠道、令牌、用户分组和倍率?这篇从面板讲起:一次请求怎么被路由和计费,倍率在哪个页面看,root/123456 必须先改,面板如何藏在反向代理后面加 TLS,无可用渠道怎么一步步查,SQLite 什么时候该换成 MySQL,多机部署又要加什么。
One API 面板管的是四样东西
One API 的管理面板字段很多,真正需要搞懂的只有四样:渠道、令牌、用户分组、倍率。渠道是一个上游 API key 加上它愿意提供的模型清单;令牌是你的客户端拿在手里的那把 key;用户分组决定这把 key 能落到哪些渠道上;倍率决定这次请求从账上扣掉多少额度。日志页是把这四样串起来的账本,出问题先看它。
下面出现的每个字段名、按钮文字和报错字符串,都对着 One API v0.6.10 核过。v0.6.10 是 GitHub Releases 上最后一个正式版本,发布于 2025-02-02。这件事有实际后果:截至 2026-09-27,Docker Hub 上的 latest 和 v0.6.11-preview.7 指向同一个 digest,也就是一个 preview 构建。所以不要用 latest,自己钉一个 tag。
先把 root / 123456 改掉,再做别的
One API 的 README 写得很直白:初始账号用户名是 root,密码是 123456,并且要求「务必修改默认密码」。这一段在 2026-09-27 对着上游 README 确认过。
为什么这条要放在最前面。README 给的 docker run 示例用的是 -p 3000:3000,Docker 会把端口绑到所有网卡上。默认口令加上一个对公网开放的 3000 端口,等于把一个能改倍率、能发令牌、能拿你的上游 key 去调上游模型的后台挂在互联网上。花的是你自己的钱,而且在面板里显示为一笔正常消费,不会报任何错。
登录后点右上角进入个人设置页改密码。如果面板还没有域名和 TLS(transport layer security,传输层安全),先用 SSH 隧道进去改,不要为了改密码先把端口开出去:
ssh -L 3000:127.0.0.1:3000 you@your-vps然后在本机浏览器打开 http://127.0.0.1:3000。
一份不暴露到公网的 compose
下面这段在你自己的 VPS 上跑。它是上游 docker-compose.yml 的删减版,改了四处:镜像钉到 tag 加 digest、端口只绑 127.0.0.1、去掉 MySQL 和 Redis(单机用 SQLite 就够)、SESSION_SECRET 换成真随机值。
services:
one-api:
image: justsong/one-api:v0.6.10@sha256:e667221a2e197f005bdf3aba300885ac165fde37a0de9773e478678a9e04b24a
container_name: one-api
restart: always
command: --log-dir /app/logs
ports:
- "127.0.0.1:3000:3000"
volumes:
- ./data/oneapi:/data
- ./logs:/app/logs
environment:
- SESSION_SECRET=${SESSION_SECRET}
- TZ=Asia/Shanghaiprintf 'SESSION_SECRET=%s\n' "$(openssl rand -hex 32)" > .env
docker compose up -d
curl -s http://127.0.0.1:3000/api/status最后一条应该返回一段 JSON,里面能看到 "success":true。上游 compose 的 healthcheck 抓的就是这个字段,所以它是判断「进程活着」的官方口径。没有输出或者连接被拒,先看 docker compose logs one-api。
上面那个 digest 是 2026-09-27 从 Docker Hub 读到的。自己核一遍再用:
docker buildx imagetools inspect justsong/one-api:v0.6.10对不上就说明上游动过这个 tag,那就别照抄,用你自己核到的值。
SESSION_SECRET 不是可选项。源码 common/config/config.go 里它的默认值是 uuid.New().String(),每次启动重新生成,所以不设置的话每次重启都会把所有登录踢掉。
渠道:一个上游 key,加上模型清单、模型重定向和分组
渠道编辑页上的字段是:类型、名称、分组(提示语「请选择可以使用该渠道的分组」)、模型、模型重定向、密钥、代理。一条渠道就是一把上游 key,加上围绕这把 key 的路由规则。
最关键的机制:渠道能不能被选中,看的是请求 body 里的 model 名字在不在这条渠道的「模型」列表里。v0.6.10 的 middleware/distributor.go 调用的是 CacheGetRandomSatisfiedChannel(userGroup, requestModel),两个入参就是用户分组和请求里的模型名,命中多条时随机取一条。名字不在列表里,客户端拿到的就是 当前分组 default 下对于模型 xxx 无可用渠道。
模型重定向是一段 JSON,面板上的说明是「此项可选,用于修改请求体中的模型名称,为一个 JSON 字符串」,键是请求里的名称,值是要替换成的名称:
{
"gpt-4o": "deepseek-chat",
"gpt-3.5-turbo": "deepseek-chat"
}这里有两个顺序问题,都会咬人。第一,重定向发生在渠道选定之后,所以左边那个名字,也就是客户端写的名字,必须出现在这条渠道的「模型」列表里,否则这条渠道根本轮不上,重定向也就无从执行。第二,计费用的是重定向之后的名字:relay/controller/text.go 里 getMappedModelName 在 GetModelRatio(textRequest.Model, meta.ChannelType) 之前执行,所以模型倍率查的是上游那个名字。把 gpt-4o 映射到更便宜的上游模型,账单会跟着变便宜,反过来也一样。
分组下拉框下面还有一行提示:「请在系统设置页面编辑分组倍率以添加新的分组:」。这句话描述的是真实机制,不是客套:分组不能在渠道页新建,你得先去倍率设置里的分组倍率 JSON 里加一行,这个分组名才会出现在渠道和用户的下拉框里。
令牌:客户端手里的 key,额度和过期都在它身上
令牌编辑页的字段是:名称、模型范围、IP 限制、过期时间、额度。
- 过期时间的说明是「请输入过期时间,格式为 yyyy-MM-dd HH:mm:ss,-1 表示无限制」,旁边有「永不过期」按钮,按下去就是写
-1。过期之后客户端收到的是该令牌已过期。 - 额度旁边有「设为无限额度」和「取消无限额度」。额度用完,客户端收到
该令牌额度已用尽。 - IP 限制填的是网段,说明是「请输入允许访问的网段,例如:192.168.0.0/24,请使用英文逗号分隔多个网段」。
- 模型范围留空表示不限制,填了就是这把令牌允许的模型白名单。
- 令牌被手工禁用后是
该令牌状态不可用,key 本身写错是无效的令牌。
面板上这一行字值得抄下来:「注意,令牌的额度仅用于限制令牌本身的最大额度使用量,实际的使用受到账户的剩余额度限制。」意思是令牌额度是一个上限,不是一个钱包。给某个工具发一把额度 5 美元的令牌,它花的仍然是账户的剩余额度,令牌额度只保证它最多花掉 5 美元。账户额度不够时错误换成另一条:user quota is not enough,错误码 insufficient_user_quota。这两条错误指向的是两个不同的地方,别混着查。
客户端侧就是普通的 OpenAI 兼容调用:
curl http://127.0.0.1:3000/v1/chat/completions \
-H "Authorization: Bearer sk-YOUR-ONE-API-TOKEN" \
-H "Content-Type: application/json" \
-d '{"model":"gpt-4o","messages":[{"role":"user","content":"ping"}]}'健康的结果是一段正常的 chat completion JSON。失败时 One API 把上面那些中文错误串直接放进 error.message,所以报错原文本身就告诉你卡在哪一层:令牌层、分组与渠道层,还是账户额度层。
用户分组决定一个令牌能碰到哪些渠道
令牌上没有分组字段。分组挂在用户身上:管理员在用户页编辑某个用户,里面有「分组」和「剩余额度」两项。这个用户名下创建的全部令牌,走的都是这个用户的分组。
所以整条链是:令牌属于某个用户,用户带一个分组,分组筛出所有「分组」里包含它的渠道,再从中筛出「模型」列表包含请求模型名的那些,最后随机挑一条。中间任何一环断掉,客户端看到的都是同一句 当前分组 %s 下对于模型 %s 无可用渠道,所以报错里印出来的分组名和模型名是你唯一需要的两个线索。
这也是给团队分权的正确做法:给每个人或者每个项目建一个用户,用分组区分他们能用到哪些上游 key,而不是把同一把令牌复制给所有人。令牌一旦散出去,日志里你只有「令牌」这一列,分不清是谁在花钱。
一次请求的额度是怎么扣出来的
README 给的公式是这一句:
分组倍率 模型倍率 (提示 token 数 + 补全 token 数 * 补全倍率)
源码对得上。v0.6.10 的 relay/controller/helper.go 里先算 ratio := modelRatio * groupRatio,最终扣除的额度是 ceil((promptTokens + completionTokens * completionRatio) * ratio)。补全倍率只作用在输出 token 上,「输出比输入贵」就是这么表达的。
请注意一件事:具体某个模型的倍率是多少、某个分组的倍率是多少,不要背,也不要相信任何一篇文章里写死的数字,包括这一篇。这些值是你自己面板里的配置,位置在 设置 → 运营设置 → 倍率设置 的三个 JSON 文本框:
- 模型倍率:「为一个 JSON 文本,键为模型名称,值为倍率」。
- 补全倍率:「为一个 JSON 文本,键为模型名称,值为倍率,此处的倍率设置是模型补全倍率相较于提示倍率的比例,使用该设置可强制覆盖 One API 的内部比例」。留空就用内置比例。
- 分组倍率:「为一个 JSON 文本,键为分组名称,值为倍率」。
想知道某一次请求实际用了什么倍率,去日志页看那一行的「详情」列。One API 写进详情的字符串模板是 模型倍率 %.2f,分组倍率 %.2f,补全倍率 %.2f,三个值都是这次请求真正生效的值。看它比自己推算可靠。
额度的显示单位也是配置。v0.6.10 的 common/config/config.go 里 QuotaPerUnit 默认是 500 * 1000,DisplayInCurrencyEnabled 默认开启,所以面板默认把 50 万额度显示成 1 美元。运营设置的通用设置里那个「以货币形式显示额度」就是这个开关,关掉之后你看到的是原始额度数字。
还有一个容易让人以为面板算错了的地方:预扣费。请求进来时 One API 先按「请求预扣费额度 + 提示 token 数」(请求里带了 max_tokens 的话再加上它)乘以倍率,从账上先扣一笔,响应结束后再用真实用量算差额回补。所以一个长的流式回答进行中,你在面板上看到的余额是偏低的,结束后才回正。这个预扣值在运营设置的额度设置里,字段名是「请求预扣费额度」,源码默认 500。
把面板挡在反向代理后面
面板和 API 共用同一个端口,所以「只开 API 不开面板」在 One API 上做不到。绑好 127.0.0.1 之后,用 Nginx 做 TLS 终止,把域名指进来:
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffering off;
proxy_read_timeout 600s;
}其中两行是给 LLM 网关专门加的。proxy_buffering off 关掉缓冲,否则 Nginx 会把流式响应攒到结束再一次性吐给客户端,客户端的表现是长时间没有任何输出、然后整段文字突然出现,看起来像模型很慢。proxy_read_timeout 600s 是因为默认值是 60 秒,一个长回答会在 60 秒处变成 504,而这时 One API 的日志里往往已经记了一笔消费。其余几行的作用可以对着 Nginx 反向代理配置逐行讲解 过一遍,尤其是 X-Forwarded-For:不传它,One API 看到的来源地址永远是代理自己,令牌上的 IP 限制就形同虚设。
SQLite、MySQL 和 Redis 各管什么
单机就用 SQLite,不设 SQL_DSN 时 One API 默认用它。数据在容器的 /data 里,上面的 compose 把它映射到 ./data/oneapi,备份就是停掉容器、复制这个目录。一个人或者一个小团队的私有网关,这个组合可以一直用下去。
要上第二台机器,或者并发高到 SQLite 的写锁开始排队,README 的多机部署要求是四条:所有节点设成同一个 SESSION_SECRET;必须改用 MySQL,所有节点用 SQL_DSN 连同一个库;从节点设 NODE_TYPE=slave;需要的话用 SYNC_FREQUENCY 控制节点从数据库同步配置的间隔,源码里的默认值是 600 秒。上游仓库的 docker-compose.yml 里有一份带 MySQL 和 Redis 的完整例子,照它抄的时候记得把 MySQL、Redis 的镜像也钉到你自己核过的 tag 上,那个文件里写的是 latest。
SESSION_SECRET 在多机场景下是硬要求,原因还是前面那句:它默认是随机 UUID。两个节点各自生成一个,就互相不认对方发的 session cookie,负载均衡把你转到另一个节点时表现为「刷新一下就退出登录」。
Redis 是缓存层,不是存储层。配上 REDIS_CONN_STRING 之后 One API 把令牌、用户额度这类热数据放进去,减少数据库压力;Redis 挂了数据不会丢,性能掉回去而已。和它相关的还有 MEMORY_CACHE_ENABLED,README 自己就提醒开启内存缓存会让额度更新出现延迟。所以在追查「额度对不上」之前先想到这一条:有可能只是缓存还没刷新,而不是算错了。
客户端报「无可用渠道」时的排查回路
这是这套面板最常见的故障,而且面板里刚好有一对现成的工具:日志和渠道测试。按顺序走,不要跳步。
- 先读报错原文。
当前分组 X 下对于模型 Y 无可用渠道的含义是:分组 X 里没有任何一条启用状态的渠道声明支持模型 Y。X 和 Y 都印在错误里,照着查比猜快得多。 - 去渠道页看这条渠道的状态。运营设置的监控设置里有「失败时自动禁用渠道」和「成功时自动启用渠道」两个开关。开着的时候,上游连续报错会让渠道被自动禁用,于是之后所有请求一律变成「无可用渠道」,即使你什么都没改过。
- 点这条渠道的「测试」按钮。成功时的提示是「渠道 xxx 测试成功,模型 xxx,耗时 x.xx 秒。」;失败时提示里带的是上游返回的原文,这是区分「我配错了」和「上游坏了」最快的一步。批量按钮是「测试所有渠道」和「测试禁用渠道」,点下去只会提示「已成功开始测试渠道,请刷新页面查看结果。」,结果要刷新页面才看得到。
- 核对两份清单:渠道的「模型」列表里有没有客户端请求的那个名字,渠道的「分组」里有没有这个用户所在的分组。绝大多数「无可用渠道」死在这两份清单上。
- 去日志页。列是 时间、渠道、用户、令牌、类型、模型、提示、补全、额度、详情,可以按 令牌名称、模型名称、渠道 ID、用户名称 和起止时间筛选,类型可选 全部、充值、消费、管理、系统。日志里完全没有这次请求,说明它在渠道选择之前就被拒了,回头查令牌那一层;有记录但金额不对,看「详情」列里的三个倍率。
- 日志空得可疑时,检查运营设置的日志设置里「启用额度消费日志记录」是不是被关掉了。关掉之后消费不留痕,排查就只能靠猜。
另外几条同样出自 middleware/distributor.go 的错误串,遇到了不用愣:指定的渠道 ID 不存在是 无效的渠道 Id;用指定渠道的方式调用一条已禁用的渠道是 该渠道已被禁用;数据库一致性已被破坏,请联系管理员 表示缓存里有这条渠道、库里查不到,通常出现在手工动过数据库、或者多个节点连的库不一致之后。
这个面板适合谁,不适合谁
诚实的定位:One API 适合在你自己拥有的上游 key 前面架一层私有网关。你的团队,你手上的 Claude、OpenAI、DeepSeek 等等 key,对外收成一个 OpenAI 兼容端点,喂给那些只会说这一种方言的工具。它给你的是别家网关不太给的三件事之外的东西:一个中文后台,按令牌的额度和有效期,以及一份能按模型和用户翻的消费流水。
不适合的用法要说清楚。One API 的 README 自己就引了《生成式人工智能服务管理暂行办法》,写着「请勿对中国地区公众提供一切未经备案的生成式人工智能服务」。打算拿它开一个对外卖 key 的站,那你读错文章了,上游各家的服务条款通常也不允许转售。
和我们另外两篇怎么分工,一句话各自说清。如果你希望网关配置跟代码一起进 git、能做 CI、能按文件评审,那 以配置文件为中心的 LiteLLM 网关 更合适,因为 One API 的全部状态在数据库里,没有一份可审查的配置文件;如果你的问题出在编码 agent 那一侧,比如把多个 harness 接到同一个端点、按任务难度切模型,那看 专为 agent harness 做的 HarnessRouter 和 给编码 agent 做多模型路由的取舍。反过来说,One API 赢在按人发 key、按 key 设额度和有效期、以及一个非技术同事也能看懂的中文后台,配置文件式网关在这几点上要你自己写工具。如果你的目标是把每月账单压下来,网关只是手段之一,在自己的 VPS 上给 AI agent 做成本控制 那篇讲的是整套做法。
FAQ
One API 的默认账号密码是什么?
用户名 root,密码 123456,这是上游 README 写明的初始账号,README 同时要求「务必修改默认密码」。登录后立刻去个人设置改掉。同样重要的是别把面板暴露在公网:README 的示例命令用 -p 3000:3000,会绑到所有网卡,改成 -p 127.0.0.1:3000:3000 再用反向代理加 TLS 对外。面板和 API 共用一个端口,所以保护面板等于保护 API。
为什么客户端报「当前分组 default 下对于模型 xxx 无可用渠道」?
这句话的意思是:这个用户所在的分组里,没有任何一条启用状态的渠道声明支持请求里的那个模型名。v0.6.10 的 middleware/distributor.go 用 CacheGetRandomSatisfiedChannel(userGroup, requestModel) 选渠道,所以只有两个变量要查。先看渠道是不是被「失败时自动禁用渠道」自动禁掉了,再点渠道的「测试」按钮看上游原文报错,然后核对渠道的「模型」列表里有请求用的那个名字、「分组」里有这个用户的分组。注意模型重定向在渠道选中之后才生效,所以映射的左边那个名字也必须出现在「模型」列表里。
一次请求的额度按什么扣,倍率在哪里看?
README 给的公式是「分组倍率 模型倍率 (提示 token 数 + 补全 token 数 * 补全倍率)」,源码里对应 ratio := modelRatio * groupRatio 与 ceil((promptTokens + completionTokens * completionRatio) * ratio)。具体数值不要凭记忆写:模型倍率、补全倍率、分组倍率三个 JSON 文本框在 设置 → 运营设置 → 倍率设置 里,那里显示的就是当前生效的配置。某一次请求实际用了哪三个值,看日志页那一行的「详情」列,One API 会按 模型倍率 %.2f,分组倍率 %.2f,补全倍率 %.2f 写进去。
什么时候必须从 SQLite 换成 MySQL?
一台机器跑一个实例,SQLite 就够,数据在 /data 目录里,备份等于复制目录。要加第二个节点就必须换 MySQL,因为 README 的多机部署要求所有节点连同一个库,并且所有节点设成相同的 SESSION_SECRET、从节点设 NODE_TYPE=slave,必要时用 SYNC_FREQUENCY 调同步间隔。Redis 是缓存层而不是存储层:配 REDIS_CONN_STRING 是为了减轻数据库压力,它挂掉不会丢数据。如果额度更新看起来有延迟,先检查 MEMORY_CACHE_ENABLED,README 明确提到开启内存缓存会带来额度更新延迟。
One API 和 LiteLLM 该选哪个?
看你想把网关的状态放在哪里。要按令牌发额度和有效期、要一个非技术同事也能自己看消费流水的中文后台,选 One API,这些都是面板里现成的。要网关配置进 git、要 CI 校验、要评审每一次改动,选 LiteLLM,因为 One API 的渠道、令牌、倍率全在数据库里,你没有一份可以评审的配置文件,迁移和回滚都得靠导数据库。两者都不解决模型选择本身的问题,那是路由策略的事。