AI 电子伴侣
创建时间: 2026-08-05最后更新: 2026-08-05

1. 计费系统不能只测正常请求

输入 1,000 Token、输出 500 Token,然后断言金额正确,只覆盖了最容易的一条路径。重复结算、并发预占、价格切换、Redis 故障和流式中断,才是容易造成真实损失的地方。

上线门槛也不能只是“接口返回 200”。系统要证明金额可复算、事务不会产生半成品、旧请求不能再次扣费,并且新旧计费结果的差异已经被解释。

2. 纯计算使用表格测试和属性测试

费用计算引擎适合使用表格测试,把普通输入、缓存、推理、长上下文、倍率和按次计费组合成清楚的案例。每个案例同时断言费用明细与总额。

属性测试不固定某个数字,而是验证始终成立的规律:Token 增加时基础费用不能下降;倍率为 1 时售价等于基础费用;所有 Token 为零时 Token 模式费用为零;费用桶之和等于基础费用;相同输入重复计算结果完全相同。

billing-properties.spec.ts
01
it('never charges less when output tokens increase', () => {
02
fc.assert(fc.property(
03
fc.nat(),
04
fc.nat(),
05
(small, delta) => {
06
const a = calculate({ outputTokens: small })
07
const b = calculate({ outputTokens: small + delta })
08
expect(b.chargedMicroUsd).toBeGreaterThanOrEqual(a.chargedMicroUsd)
09
},
10
))
11
})

金额断言使用整数或十进制字符串,不使用 toBeCloseTo 掩盖精度问题。

3. 事务与并发怎样测试

集成测试使用真实 PostgreSQL,不能只 mock repository。启动一百个并发预占,账户可用余额必须始终大于等于零,成功预占金额之和不能超过初始余额。

同一个结算命令并发提交多次,只允许一个返回 applied: true,最终只有一条消费账本。相同 request ID 携带不同指纹时,所有后续请求都应得到冲突错误,余额保持第一次结算后的结果。

还要在事务每一步注入失败:幂等声明后失败、用量写入后失败、账本写入后失败、账户更新后失败。事务回滚后,幂等键、账本、余额和 hold 都不能留下半成品。

4. 流式与依赖故障测试

准备可控的假上游,模拟完整 SSE、事件被拆成任意 chunk、客户端中途断开、上游返回半个事件、最终 usage 缺失、响应结束后连接重置和同一事件重复发送。

Redis 测试覆盖超时、读失败、写失败、Lua 执行失败和窗口切换。余额缓存故障应回源或 fail-closed,RPM 故障则按照配置验证 fail-open。Queue 测试要重复投递、乱序投递、延迟投递和进入死信。

熔断器需要使用可控时钟测试 closed、open、half-open 状态,避免测试等待真实 30 秒。恢复探测只允许配置数量的请求通过,探测失败重新打开,成功后清空失败计数。

5. 先做影子计费

旧系统已经有流量时,不要直接切换扣费。新引擎先运行在 shadow 模式:读取同一份 usage 和价格,只计算并记录,不修改余额。后台按请求比较旧金额和新金额。

差异要分类,而不是只看平均值。缓存字段口径不同、舍入规则变化、模型映射不一致、价格版本不同和旧系统漏记 usage,都会产生差异。只有每种差异都有解释并达到阈值,才进入小流量真实结算。

灰度可以按内部账号、测试用户、特定 API Key 和低风险模型逐步扩大。每个阶段都保留一键停止新请求的计费熔断开关,但已经完成的请求仍然要结算,不能在回滚时丢失在途账单。

6. 上线验收清单

上线前至少确认以下项目:

  1. 所有常用模型都能在转发前解析价格,未知模型默认拒绝。
  2. 用量适配器使用当前厂商字段,并保存适配器版本。
  3. 余额预占、结算和释放均可幂等恢复。
  4. 账本、余额、用量和配额在同一事务中更新。
  5. Redis、Queue 和通知失败不会破坏核心账务。
  6. 每个请求能够关联价格版本、上游账号和厂商响应 ID。
  7. 对账任务、差异告警和补偿审批已经运行。
  8. 管理员调账不允许直接覆盖余额,并且具有审计日志。
  9. 日志、导出和 usage 证据没有泄露 API Key 或 Prompt。
  10. 影子计费差异率、漏 usage 比例和结算延迟达到发布门槛。

这些检查项应进入持续集成和发布流程,不能只保存在文档里。价格表或适配器变更同样要触发计费回归测试。

7. 面试时怎样说明这套设计

面试中介绍 Token 计费,不要只说“根据 input 和 output 乘单价”。可以从请求开始讲:网关鉴权后冻结价格版本和余额,代理层从上游提取真实 usage,适配器统一厂商口径,纯计算引擎生成费用明细,数据库事务通过幂等键同时更新账本、余额和配额,最后由异步任务刷新缓存、聚合统计并定期对账。

继续追问时,再解释三个取舍:为什么账本不能用缓存代替,为什么流式断开需要待对账,以及为什么路由失败的上游成本和用户应付金额要分开。能够把这些边界讲清楚,比堆砌“高并发”“分布式”更能说明真正处理过生产问题。

8. 总结

一套可靠的 Token 计费系统,需要把计量、定价、预占、结算、幂等、缓存、对账和运营连接起来。每个模块都不算神秘,难点在于故障和并发发生时,资金事实仍然只有一份,并且能够解释。

到这里,大章已经从一笔 Token 费用走到了完整中转站。后续把方案接入 AI 伴侣时,可以先实现价格快照、统一 usage、账本和余额预占,再逐步加入 Redis、支付和自动对账,不需要一开始就部署所有基础设施。