2026-10-17 · 林一舟
你做了一个AI应用(agent、MCP服务、套壳product都算),第一个真实的商业问题不是模型能力,是:每一次调用你都在花钱,用户不付钱你就亏钱。
订阅制订不通——重度用户一次烧掉你一个月月费;免费额度拍脑袋——给多了被薅,给少了被骂。这篇文章把"用量计费"从架构到落地讲一遍,全部来自我自己给MCP服务接计费的真实过程,代码骨架可以直接抄。
AI产品的计量单位,从粗到细三档:
| 档位 | 单位 | 适合 | 坑 |
|---|---|---|---|
| 按次 | 一次调用/一次任务 | 工具型、结果可预期 | 用户觉得"我这条才几个token你收我一次的钱" |
| 按token | 原样转嫁模型账单 | 透明、开发者向 | 用户看不懂,客服成本高;价格随模型价格浮动 |
| 按credit | 平台虚拟币,1 credit=约定价值 | 最主流,兼顾心理账户与成本 | 汇率要一次定对,改汇率=公关事故 |
结论:用credit制。原理是把"模型的价格波动"和"用户的付费心智"解耦——你内部按token核算成本,对外只说"这个操作扣10 credit"。改成本时你调内部汇率,不动用户价签。
定价的一个可用锚点:单次典型操作的成本 × 5~8 = credit售价。留出的倍数覆盖:失败重试、免费额度、支付通道费、以及你自己的毛利。
不要指望事后从模型厂商的账单API反推每个用户的用量——账单是T+1的,而且粒度不够。在网关层/服务层,把每次调用的输入输出token当场记下来:
// 伪代码:每次LLM调用后的记账
await db.execute(
`INSERT INTO usage_events (user_id, action, tokens_in, tokens_out, cost_credit, created_at)
VALUES (?, ?, ?, ?, ?, ?)`,
[userId, "chat.generate", usage.input_tokens, usage.output_tokens, price, Date.now()]
);
三个必须刻进肌肉记忆的点:
usage,自己估算必错。新手做法是每次扣费前SELECT SUM(cost_credit) FROM usage_events WHERE user_id=?——用量一大这就是慢查询+并发灾难。正确做法:用户表上放一个balance字段,扣费用原子UPDATE:
-- 原子扣费:余额不足直接失败,不存在读到旧值
UPDATE users SET balance = balance - :cost
WHERE id = :uid AND balance >= :cost;
-- 受影响行数=0 → 余额不足,拒绝服务
usage_events聚合出来的消耗总量,和balance的减少量,每天对一次。差值不为0说明有漏记或重复记——计费系统最怕的不是错,是错了自己不知道。
并发扣减:用户开三个窗口同时发请求,三个请求同时读到balance=100,各扣60,全都"成功"——你亏了80。解法就是上面的原子UPDATE(WHERE balance >= :cost),把判断和扣减合成一条语句,让数据库的行锁替你串行化。
幂等:网络超时后客户端重试,同一个操作扣两次费,用户投诉到怀疑人生。给每次操作发一个request_id,扣费表对其做唯一索引:
CREATE TABLE billing_ledger (
request_id TEXT PRIMARY KEY, -- 幂等键:重试同请求不会插第二条
user_id TEXT NOT NULL,
cost_credit INTEGER NOT NULL,
created_at INTEGER NOT NULL
);
-- 扣费=INSERT这条ledger+UPDATE余额,同一事务
INSERT冲突→说明是重试→直接返回上次结果。这两招之后,你的计费就比市面上大量AI套壳产品扎实了——真心话,很多产品连这个都没做。
免费额度上线第一天就会被脚本盯上。最小防线四条,按性价比排序:
INCR+过期时间就够。一个真实数字供参考:我见过免费额度上线72小时内,0.3%的账号消耗了41%的token,全是脚本。防线第2条就能拦住它们。
Stripe有漂亮的metered billing,但国内个人开发者用不了。现实路径就两条:
预充值的坑只有一个:credit过期策略要想清楚。建议"永不过期但价格随采购成本调整"或者"12个月有效期+到期前三次提醒",别玩"7天过期"——那是给自己上黑名单。
把上面全部揉在一起,个人开发者一天能写完的最小计费系统:
表结构(3张):
users(id, balance, created_at)
billing_ledger(request_id PK, user_id, delta, reason, created_at)
usage_events(id, user_id, action, tokens_in, tokens_out, created_at)
规则(4条):
1. 扣费 = 事务{ INSERT ledger(幂等) + UPDATE users SET balance=balance-? WHERE balance>=? }
2. 记用量 = usage_events INSERT,与扣费同事务
3. 充值 = ledger INSERT(delta>0, reason='topup:订单号') + balance UPDATE
4. 每日对账 = SUM(delta of ledger) == balance 当前值,逐用户
三张表四条规则,撑到日活几千没问题。等你需要多币种、订阅、团队席位了,再上专业计费(那是一个新文章的话题)。
用量计费的本质是把你的成本结构翻译成用户能理解的价格语言。翻译错了,要么亏死要么没人买;翻译对了,用户花钱花得心甘情愿。
我在做的「夜航船」系列里,MCP服务端模板(Kit版)内置了这套计费骨架的完整可运行版:幂等扣费、credit余额、卡密兑换,TypeScript写完带测试。想省事的可以去淘宝搜"夜航船",或者到 nightship.cc 用免费工具先玩一玩。
下一篇写《MCP服务上线后的监控:一眼看出谁在薅你》。