AI 网关统一计费与对账:多上游费用归集实践
如果你团队里同时挂了 OpenAI、Anthropic、DeepSeek 三家的 key,月底对账大概率会遇到这个场面:财务拿着三张不同币种、不同结算周期的账单来问你”这个月 AI 到底花了多少钱、哪个团队花的”,你翻遍三个控制台也拼不出一张完整的表——这就是没做统一计费的代价。账单分散在不同控制台,计费单位不统一,内部成本分摊缺乏依据。AI 网关的统一计费层把所有上游费用归集到一处,对内支持团队/项目分摊,对外支持多租户独立出账。
计费单位怎么选:token、次数还是时长
网关记账前先得想清楚按什么维度计量,这个选错了后面全部推倒重来:
| 计费维度 | 适用场景 | 坑点 |
|---|---|---|
| 按 token(输入+输出分开算) | 主流文本/对话模型,OpenAI、Anthropic、DeepSeek 都是这个口径 | 输入输出单价通常差 2–5 倍,混算会严重失真 |
| 按请求次数 | 部分图像生成、embedding 批量接口 | 长 prompt 和短 prompt 同价,成本大户容易被平均掉 |
| 按时长(秒) | 语音合成、部分实时流式接口 | 网络抖动导致的重试时长不该算进用户成本 |
力达云内部的取舍是:只要上游返回了 usage 字段就优先按 token 记账,没有 usage 字段(比如老版本的图像接口)才退化为按次计费,两种模式在同一张报表里用 billing_unit 字段区分,不要试图强行统一成一个数字,那样对账时反而看不出问题在哪。
多上游计费的核心挑战
| 挑战 | 具体表现 |
|---|---|
| 计费单位不统一 | OpenAI 按 token、某些厂商按字符或次数 |
| 价格表频繁变动 | 服务商随时调价,需同步更新本地价格表 |
| Prompt Cache 折扣 | 部分服务商对缓存 token 打折,需区分计量 |
| 汇率波动 | 境外服务商以美元计费,汇率影响实际成本 |
| 账单滞后 | 服务商月结,月中难以准确预估当月总费用 |
网关侧计费的实现架构
请求完成
│
├─ 读取响应中的 usage(prompt_tokens + completion_tokens)
├─ 查询本地价格表(模型 + 上游 → 单价)
├─ 计算本次请求费用:
│ cost = input_tokens × input_price + output_tokens × output_price
├─ 写入计费记录(关联 key_id、upstream、model、timestamp)
└─ 扣减对应 key 的额度(如配置了预付费额度)
这套流程里最容易被忽略的是”扣减对应 key 的额度”这一步——如果你的额度扣减和费用计算是两条并发执行的代码路径,高并发场景下会出现读到的余额是扣减前的旧值,两个请求同时判断”余额够用”然后都放行,结果实际扣成负数,也就是俗称的”超卖”。解决办法不是加锁这么简单(加锁会拖慢整个网关的吞吐),实际做法是把”读余额、判断是否足够、扣减”这三步压缩成数据库层面的一条原子更新语句,类似 UPDATE keys SET balance = balance - ? WHERE id = ? AND balance >= ?,靠 affected_rows 是否为 0 来判断这次扣减是否成功,而不是先查询再判断再更新。这个坑我们踩过一次:大促期间某个客户的 key 被并发刷了 3 倍额度还没被拦下来,root cause 就是查询和扣减分成了两次数据库往返。
价格表管理是核心运维工作——需要持续跟踪各服务商的定价变化,尤其是:
- 新模型上线(价格可能与旧版不同)
- 旧模型降价(如 OpenAI 历次大幅降价)
- Prompt Caching 推出(命中缓存的 token 折扣 25%–75%)
价格表本身也要做版本管理,不能简单地”改一个数字”了事。比如某个模型 7 月 1 日零点降价,但你的计费任务是每小时批量跑一次,6 月 30 日晚上 23 点到 7 月 1 日凌晨 1 点这段时间产生的请求,到底按哪个价格算?正确做法是价格表按”生效时间区间”存储(effective_from、effective_to),计费时按请求的实际发生时间去匹配对应区间的价格,而不是只存一个”当前价格”字段直接覆盖。我们见过有团队图省事直接覆盖旧价格,结果月底对账时发现跨越降价窗口的那批请求全部算错了价,返工重新算了一整天的历史数据。
内部成本分摊
对企业内部使用场景,网关计费主要解决”谁用了多少、花了多少”的问题:
月度 AI 成本分摊报表(示意)
团队 模型 Token 用量 折算成本(参考)
──────────────────────────────────────────────────────────
产品团队 gpt-4o 2.1M tokens ¥1,260
技术团队 claude-3-5-sonnet 1.5M tokens ¥1,350
数据团队 deepseek-chat 8.0M tokens ¥240
客服 Bot gpt-4o-mini 12.0M tokens ¥360
──────────────────────────────────────────────────────────
合计 23.6M tokens ¥3,210
注意:网关侧的”折算成本”是参考值(基于本地价格表),以服务商实际账单为准,两者可能有 1%–5% 的误差。
要让这张报表真正落地到”谁该为超支负责”,光靠模型名和团队名对不上号——现实中经常是一个 key 被多个小组共用,或者一个团队申请了好几个 key 分给不同项目组用。可行的做法是给每个 key 挂两个业务标签:team_id 和 project_id,计费记录里原样带上这两个字段,报表按标签聚合而不是按 key 本身聚合。这样即使某个团队后来把 key 轮换了、重新申请了一个新 key,历史成本数据依然能正确归到同一个团队名下,不会因为 key 变了就断档。
预算告警也建议挂在这一层:给每个 team_id 设置月度预算阈值,网关在计费写入的同时做一次累加判断,超过 80% 阈值时推一条企业微信/飞书机器人消息给团队负责人,而不是等财务月底翻账单才发现某个团队把整月预算跑爆了。这个告警逻辑要注意去抖动——不要每次超阈值请求都推送一条消息,正确做法是按团队维度记一个”本月是否已告警”的标记位,告警一次之后当月不再重复推送,避免消息刷屏被大家直接忽略。
对账流程
定期(建议每月)将网关统计与服务商账单做对账:
第一步:导出网关用量数据
按上游(OpenAI、Anthropic 等)汇总月度 token 用量,格式如:
upstream,model,prompt_tokens,completion_tokens,cost_usd_estimated
openai,gpt-4o,45234567,8123456,456.78
anthropic,claude-3-5-sonnet,12345678,2345678,345.67
第二步:对比服务商账单
从各服务商控制台下载月度账单,与网关数据按模型维度比对:
| 对比项 | 允许误差 | 若超出则排查 |
|---|---|---|
| Token 总量 | ±3% | 是否有请求漏记(非 200 响应的 token 未计入) |
| 费用总额 | ±5% | 是否有折扣/优惠未在本地价格表中体现 |
第三步:差异分析
常见差异来源:
- 流式响应 usage 未返回:部分上游在流式模式下不返回 usage,网关只能估算
- Prompt Caching 折扣:本地价格表未及时更新缓存折扣规则
- 模型版本混淆:
gpt-4o和gpt-4o-2024-11-20是同一模型,但价格表配置时需统一 - 退款/调整:服务商对部分故障请求退款,账单金额低于预期
说一个真实碰到过的对账案例:某月网关统计 OpenAI 侧总费用是 456.78 美元,服务商账单却是 512.30 美元,差了 12%,远超 5% 的允许误差。排查顺序是这样走的:先看 token 总量是否对得上——对得上,说明不是漏记请求;再看是不是模型版本配置错了价格——查了一遍价格表,gpt-4o 和 gpt-4o-2024-11-20 都配的同一个单价,也没问题;最后把账单明细按天拆开跟网关日报表逐日比对,才发现某一天服务商侧有一批请求命中了官方的 batch API 折扣(异步批处理接口价格是实时接口的一半),但这批请求是运维同事手动调用批处理脚本跑的,压根没经过网关,所以网关这边根本没有记录,自然对不上。这类”绕过网关直连服务商”的调用,是对账差异里最容易被忽略、但也最常见的一类——建议给所有能接触到服务商原始 API Key 的人立一条规矩:所有调用必须走网关,原始 key 只留给网关自己用,其他人一律用网关签发的子 key,从源头上避免统计口径被绕过。
流式响应 usage 缺失这个坑值得展开说一下,因为它几乎每个自建网关团队都会踩到。部分上游(尤其是走 SSE 协议的老版本接口)在 stream=true 时,最后一个 chunk 里不带 usage 字段,网关拿不到官方口径的 token 数,只能退化成用本地分词器(如 tiktoken)对拼接后的完整回复文本做估算。这个估算值和上游真实计费值通常有 2%–8% 的偏差,因为分词器版本、特殊 token 处理方式都可能不完全一致。如果你发现某个上游的流式请求成本估算长期偏低,先去查一下这家服务商是否已经支持在流式模式下加一个 stream_options: {"include_usage": true} 之类的参数把 usage 带出来——较新版本的 OpenAI 兼容接口大多已经支持,只是很多网关的适配代码没跟上升级,这个参数很容易被漏配。
多租户对外出账
对外提供 AI API 服务时,需在网关成本基础上叠加利润率,生成面向客户的账单:
客户用量:100,000 tokens
上游成本:¥3.00(参考价格表)
出账价格:¥5.00(叠加 67% 利润率)
NewAPI 内置了这套逻辑——通过”倍率”配置(相对 OpenAI 定价的倍数)控制出账价格,同时在用户界面展示用量和余额。
这里有个金额精度的坑必须提一嘴:出账价格算到小数点后几位,直接用浮点数存储和计算是会出问题的。比如 100000 tokens × 0.00003 元/token 这种计算,用 JavaScript 的 Number 类型直接算,浮点数精度误差会让结果出现类似 2.9999999999999996 这种尾巴,累积到几十万条计费记录求和之后,总金额和预期值能差出几毛钱到几块钱,财务对账时这种”对不上的零头”最让人头疼。正确做法是把所有金额换算成”分”或者更小的整数单位(比如以 0.0001 元为最小单位)来存储和计算,只在最终展示给用户时才转换成带小数点的金额字符串,中间过程一律用整数运算,杜绝浮点数误差。
自建计费 vs 直接用聚合平台内置计费,这个选择取决于你的团队规模和诉求:
| 场景 | 建议方案 | 理由 |
|---|---|---|
| 团队内部几个人用,不对外收费 | 直接用 NewAPI/One-API 内置报表 | 够用,不用额外开发 |
| 需要对接自己的财务系统、按客户维度出正式发票 | 网关计费数据只做参考,接一层自建的计费中台 | 财务入账需要更严格的审计轨迹 |
| 纯粹想省事、请求量不大 | 干脆不做统一计费,月底手工核对各家账单 | 请求量小的话自动化的投入产出比不划算 |
上线前自查清单
把计费系统真正推上线之前,建议按这几条自己过一遍,比事后被财务追问强:
- 价格表能不能查历史版本:随便挑一个上个月已经降过价的模型,看报表里那批跨越降价时间点的请求,价格算对了没有,而不是只看当前价格对不对。
- 额度扣减是不是原子操作:拿压测工具对同一个 key 并发打 50 个请求,看扣完之后余额是不是精确对得上(不多不少),如果出现负数或者扣多了,说明并发扣减那步有问题。
- 流式请求有没有 usage 缺失的兜底:找一个支持
stream=true的接口测一下,确认拿不到官方 usage 时是不是有本地估算兜底,而不是直接算成 0 费用。 - 金额计算是不是整数运算:翻一下计费模块的源码,看金额是用浮点数还是整数最小单位在算,浮点数直接用于金额计算这条要重点排查。
- 有没有绕过网关的直连调用:查一下团队里还有谁手里攥着服务商的原始 API Key,能收回的尽量收回,全部改用网关签发的子 key。
这五条里,第 2 条和第 5 条是实际踩坑率最高的两个,建议优先自查。
常见问题
网关的计费数据能替代服务商账单用于财务入账吗? 不建议。网关侧计费存在估算误差,应以服务商官方账单为财务入账依据。网关数据的价值在于实时监控和内部分摊,提供服务商月结账单无法给出的团队/项目粒度。
如何处理包月套餐(非按量计费)的服务商? 对固定月费的服务商(如某些企业协议),将月费平摊为”每 token 等效成本”录入价格表,或在报表中单独列出固定成本项,不与按量计费部分混合。
汇率波动对成本计算影响大吗? 境外服务商以美元计费,汇率每月都有变化。建议价格表中以美元存储,报表生成时按当月平均汇率换算人民币,同时保留美元原始数据,避免汇率误差积累。
延伸阅读:
- 多模型聚合 API 完整指南
- 用量统计与配额管理
- 统一鉴权与多租户 Key 管理
- 更多聚合 API 内容见 聚合API 专题
需要透明的统一计费?申请力达云聚合 API 内测,人民币统一结算,多模型用量一张账单。