01计价单位
全链路只用一个整数:quota。浮点只出现在展示层换算,落库与判定永远是 i64。
| 常量 | 值 | 含义 |
|---|---|---|
| QUOTA_PER_UNIT | 500000.0 | 1 USD 对应的 quota 数 |
| quota = -1 | −1 | 不限量(用户余额 / 令牌子额度 / 房间池通用此约定) |
| 展示换算 | q / 500000 | quota_usd、used_usd、today_usd 等视图字段 |
之所以不存浮点:并发扣减需要原子 CAS,浮点没有可靠的比较交换语义;而且 $0.000002 级的单次费用用浮点累加会漂。
02公式
五个倍率,两个夹取,一次向上取整。
quota = ceil( ( (prompt − cached − cache_write) // 未命中输入,原价 + cached × cdr // 缓存命中,打折 + cache_write × cwr // 缓存写入,可能溢价 + completion × cr // 补全 ) × mr × gr // 模型总价倍率 × 分组倍率 )
- 子集夹取:
cached先夹到[0, prompt],cache_write再夹到[0, prompt − cached]。上游把三者报重了也不会算出负数或重复计费。 - 口径归一:Anthropic 的
input_tokens不含缓存读写,OpenAI 的prompt_tokens含命中缓存。内核统一按「prompt 为全集」记账,与 Anthropic 的 inclusive 语义对齐。 - 按渠道协议计费:跨协议时,用量取自上游(渠道)协议的 usage,倍率也用该模型的倍率——不是按客户端协议猜。
- 向上取整:
ceil意味着宁可多收 1,不放过零头。
怎么自证
本页公式与网关扣费同口径:同一笔用量在任何协议入口得到的 quota 一致。可先用下方计算器算一遍,再与控制台「使用日志」的逐笔金额核对。
03计算器
与内核同一份公式,输入用量与倍率,输出整数 quota 与美元。
输入
tokens · ratios结果
同公式以上四项加权求和后,再乘模型倍率与分组倍率并向上取整。
预扣按估算的 completion 计算,真实用量返回后多退少补。
04预扣 · 结算 · 退回
额度在请求生命周期里走三步,任何一步失败都有明确的账务结果。
预扣 pre_consume
用估算的 prompt token 与配置的 prealloc_completion_tokens 算出 pre_quota,对内存原子量做 CAS 扣减。扣不动直接 402,请求不会打到上游,因此不会产生任何真实费用。
结算 settle
拿到真实 usage 后按公式重算实际额度,把 pre_quota 释放、按实际值正式扣除,并写一条消费日志(含四种 token 分项、延迟、状态、渠道)。
退回 refund
上游失败、重试耗尽或请求在转发前被拒时,把预扣整笔退回。日志仍会写,状态标为失败,便于排查上游抖动。
| 场景 | 账务结果 | 响应 |
|---|---|---|
| 正常完成 | 按真实 usage 扣 ceil(...),多退少不补(少则追加扣减) | 协议原生响应 |
| 预扣时余额不足 | 不扣 | 402 insufficient_quota |
| 房间池不足 | 不扣个人,房间池也退 | 402 |
| 房间已冻结 | 不扣 | 403 room_forbidden |
| 全部渠道失败 | 整笔退回 | 502 upstream_error |
| 429 后换渠道成功 | 只按最终成功的 usage 扣一次 | 协议原生响应 |
05三种账务形态
同一套公式,三种池子。区别只在「扣谁」。
Personal
个人账户
用户余额 −1 = 不限量
最普通的形态:一个用户一个余额,令牌跟随用户,扣减按「用户 + 令牌」双层原子检查。
- 令牌
remain_quota = -1时跟随用户余额 - 用户禁用即全部令牌失效
- 分组倍率
gr挂在用户上
Token
令牌子额度
从用户余额里切一块
给某个项目或某把外发的 Key 单独设上限与过期时间,超了就 402,不影响同账号其他令牌。
model_limits逗号白名单expired_at = -1永不过期- 一次性明文展示,面板只回显一次
Room
房间共享池
≤5 人 · 独立池
房间 Key 的消费只扣房间池,个人账户完全不参与;房间可用独享渠道,个人 Key 调度不到。
- 入房名额由 SQL 原子抢占
- 池尽 402,冻结 403
- 日志带房间归属
06倍率从哪来
倍率是数据,不是代码。控制面用 pricing 端点改,内核侧在内存里读,改完即刻生效。
curl -X PUT https://<你的部署地址>/admin/pricing/models/deepseek-flash \ -H "Authorization: Bearer $ADMIN_TOKEN" \ -d '{ "model_ratio": 0.14, # mr:把 token 折算成美元尺度的总倍率 "completion_ratio": 4, # cr:补全相对输入的倍数 "cached_discount_ratio": 0.1, # cdr:命中缓存打折 "cache_write_ratio": 1.25 # cwr:写入缓存溢价 }' # 分组倍率挂在用户所属分组上,用于给不同人群整体打折或加价 curl -X PUT https://<你的部署地址>/admin/pricing/groups/vip \ -H "Authorization: Bearer $ADMIN_TOKEN" \ -d '{ "ratio": 0.8 }'
缺省即 1.0
模型没有配置倍率时,四个倍率与分组倍率全部按 1.0 处理——也就是「按 token 原样扣」。新接上游记得先写 pricing,否则计费尺度和你想的不一样。
07自部署账
内核自身不收钱:它按你自己的倍率给下游记账,上游账单来自你配的渠道密钥。
| 项 | 需要什么 | 说明 |
|---|---|---|
| 运行时 | Rust 工具链 · 单个二进制 | Axum + Tokio + sqlx,无外部服务依赖 |
| 存储 | 一个 SQLite 文件 | WAL 模式,busy_timeout 5 秒,连接池 32 |
| 配置 | 一份 TOML | admin_token 必填,转发参数全有默认值 |
| 上游 | 你自己的渠道密钥 | 密钥只进不出:写进去,任何响应字段都不返回它 |
| 验证 | 三个脚本 | schemaguard 校契约 · roomtest 校房间 · realchannel 打真实上游 |
cargo build --release ./target/release/opennext-kernel -c config.toml # → 监听 0.0.0.0:3000 · 首次启动自动建表并写入 root 用户与令牌 curl http://127.0.0.1:3000/health # ok
node scripts/schemaguard.mjs # 管理面字段与契约快照逐一对齐 node scripts/roomtest.mjs <admin_token> # 建房 · 入房 ≤5 · 房间池计费 · 并发 409 node scripts/realchannel.mjs <admin_token> <ds_key> # 真实上游 19 条断言