quota:内部计量单位
NewAPI 的用户余额和消费记录并不是直接以美元或人民币存储,而是使用整数 quota 作为内部计量单位。
例如,后台显示余额为 ¥73.00,数据库中对应的 users.quota 可能是 5,000,000。两者之间通过 QuotaPerUnit 和展示汇率进行转换。
// common/constants.go
var QuotaPerUnit = 500 * 1000.0 // $0.002 / 1K tokens
默认情况下,50,0000 quota 对应一个系统标准金额单位,即 1 USD。quota 本身不包含币种,它只是系统内部使用的计量单位。
采用整数 quota 有几个好处:避免浮点数直接参与余额累计,方便进行额度扣减、退款和账户余额管理,同时可以将内部计费与前端货币展示分离。
不过,这并不意味着 NewAPI 完全实现了币种无关的计费体系。模型定价和部分计费逻辑仍然以美元价格为基准。
QuotaPerUnit 的来历
代码里没有解释为什么 QuotaPerUnit 定义成 50 万,但提交历史能定位:2023 年 6 月 20 日的 a6cc0226a,标题是 feat: able to display quota in dollar。
2023 年 GPT-3.5 Turbo 的价格为每 1000 Token 0.002 美元,换算下来,每个 Token 的价格是 0.000002 美元。500000 quota 对应 1 美元,恰好与这一价格形成对应关系。
PS:这只是我的推测,没有官方说明作为支撑。
随着模型定价体系不断变化,QuotaPerUnit 逐渐成为内部额度换算刻度。今天它更多承担统一计量单位的作用,而不是代表某个模型的实际 Token 价格。
从 Token 到 quota
对于常见的 Token 计费模型,可以将计算过程简化为:
quota = (
prompt_tokens
+ completion_tokens × 补全倍率
) × 模型倍率 × 分组倍率
其中,模型倍率决定模型相对于系统标准价格的成本,分组倍率则用于不同用户分组的差异化定价。
例如,倍率为 1 表示采用标准价格,0.5 表示半价,2 表示两倍价格。
按次计费的模型则根据配置的单次价格和相应倍率计算 quota。图片、音频、缓存等模型能力还可能采用不同的计费规则,不能简单套用上述公式。
-
传统倍率模式不存“每百万 Token 多少钱”,只存无量纲倍率:
ModelRatio = 0.5表示每 token 扣 0.5 个 quota,按QuotaPerUnit = 50 万换算即$1/M输入;CompletionRatio = 4表示输出按输入的 4 倍计价,即 $4/M。
倍率以 Go 的
float64存放,计费时才转定点数,叠加模型/补全/缓存/音频/分组倍率算出 quota,取整后从整数余额扣减——配置侧是浮点倍率,账本侧是整数 quota。 -
后加的表达式模式(
tiered_expr)反过来,系数直接写每百万 Token 的真实价格(p * 2.5= $2.50/M),余额仍然是整数 quota。
两者最终都在 quota 上汇合;而倍率/单价的货币含义由 QuotaPerUnit 和站点币种口径决定,格式化之后才变成钱。
三个独立的货币参数
NewAPI 中最容易混淆的是三个参数:
| 参数 | 作用 | 示例 |
|---|---|---|
QuotaPerUnit |
标准金额与 quota 的换算 | 500000 (代码写死) |
USDExchangeRate |
标准金额转换为展示货币 | 7.3 (默认) |
Price |
充值一个标准金额单位所需支付的本地货币 | 7.3 (默认) |
PS:这两个 7.3(人民币汇率)应该和上面的 QuotaPerUnit 一样,来自系统开发时的真实数据。
假设 QuotaPerUnit=500000、USDExchangeRate=7.3、Price=7.3,那么:
- 500000 quota 对应 1 个标准金额单位。
- 用户界面将其显示为 ¥7.30。
- 用户充值 1 个标准金额单位,需要支付 ¥7.30。
展示汇率与充值价格虽然默认可能相同,但两者并不等价。
例如,将 Price 设置为 5,而展示汇率保持 7.3,用户充值一个标准金额单位只需要支付 ¥5。这相当于提供了充值折扣。
货币展示与实际支付
NewAPI 将内部额度、货币展示和实际支付分成不同环节。
用户消费时,系统根据模型价格和倍率计算 quota;展示余额时,再将 quota 转换为相应货币;用户充值时,则根据 Price 计算实际支付金额。
因此,人民币展示不意味着必须采用人民币作为内部计价基准。
如果只是希望用户看到人民币余额,通常只需要选择 CNY 展示类型,并配置正确的 USD/CNY 汇率。模型定价和内部 quota 无须因此改变。
充值价格也可以独立配置,不必与展示汇率相等。
设计上的局限
NewAPI 采用的是一套简单的技术方案,所有金额在底层都统一换算成锚定美元的计量单位,然后在交互层根据货币单位进行换算显示。
NewAPI 底层采用美元作为标准计价单位,通过 QuotaPerUnit 将美元转换为整数 quota(数据库设计上就不需要考虑精度问题了),再通过 USDExchangeRate 转换为本地货币展示。这种设计在美元市场比较自然,在人民币等非美元市场存在的问题我做一个列举:
- 增加一次换算,少不了又来四舍五入,存在一点精度损失。
- USDExchangeRate/Price 只能是固定值,无法根据实际汇率动态调整的。
如果 USDExchangeRate 调整,现有客户的余额就会变,无法解释。
如果 Price 调整,比如改成当前实际汇率 6.68,用户充值 668 元,底层算 100 单位(即 50,000,000 quota),再显示页面上变成 730 元。
-
模型成本口径不统一。 国内模型供应商通常直接采用人民币报价,海外模型则可能采用美元报价。统一使用美元作为内部基准,需要额外进行货币转换,容易造成成本核算与实际采购金额不一致。
-
充值价格与展示汇率容易混淆。
USDExchangeRate决定展示金额,Price决定充值实际售价。两者虽然独立,但配置人员容易将其混为一谈,导致充值折扣、实际支付金额与余额显示不一致。 -
API 兼容性存在隐患。 部分兼容接口沿用
soft_limit_usd等美元字段名称,但返回值可能已经按照站点展示币种转换。第三方客户端如果仍按美元解释,就会产生金额错误。 -
财务核算不够直接。 quota 记录的是内部消费额度,不等于人民币实际收入。充值赠送、支付手续费、退款、汇率差异和上游采购成本都需要额外核算,不能直接根据 quota 统计经营收入和利润。
总体而言,美元作为内部计价基准本身没有问题,问题在于 NewAPI 对非美元市场的适配主要集中在展示和充值环节,缺少完整的多币种成本管理、定价和财务核算机制。 对普通中转站影响有限,但对于面向国内用户的商业化 API 平台,需要额外建设这些能力。