AI 系统成本核算:Token、GPU、存储与人工成本
AI Demo 最容易让人产生一种错觉:一次调用只花几分钱,所以产品规模起来以后也不会太贵。真正上线以后,账单通常不是被某一次调用击穿的,而是被上下文越来越长、失败反复重试、缓存没有命中、后台索引任务和人工兜底一点点推高的。
我第一次认真算 AI 成本时,发现团队之前只记录了“模型调用次数”,却不知道每次请求带了多少上下文,也不知道同一条请求因为超时重试了几遍。表面上调用量没有增加,Token 消耗却已经翻了几倍。
成本核算的目的不是把每个工程师都变成财务,而是让技术决策有事实:换模型到底省不省钱,缓存值不值得做,端侧部署是否划算,某个高质量流程是否值得它的人工审核成本。
一、先算一次“成功任务”的成本
不要只看单次 API 价格。一个用户任务可能包含多次检索、重排、模型调用、工具调用、失败重试和人工审核。更有意义的指标是:
单位成功任务成本
= 总运行成本 / 成功完成的任务数
例如 1000 次请求里只有 800 次真正完成任务,那么分母应该是 800,而不是 1000。一个看似便宜、但失败率很高的系统,单位成功任务成本可能比贵一点但稳定的系统更高。
可以先定义成本事件:
type CostEvent = {
traceId: string
tenantId: string
taskId?: string
resource: 'llm' | 'embedding' | 'gpu' | 'storage' | 'bandwidth' | 'human'
quantity: number
unit: string
unitPrice: number
status: 'succeeded' | 'failed' | 'refunded'
timestamp: string
}
所有服务用同一种事件格式上报,月末才能按租户、功能、模型和任务类型归因。否则 API 账单在供应商后台,GPU 在云平台,人工审核在表格里,最后谁也说不清钱花在哪里。
二、Token 成本不只取决于用户说了多少话
LLM 成本通常分输入和输出。输入里除了用户问题,还有系统 Prompt、历史对话、检索上下文、工具结果和格式约束。很多成本增长,来自开发者为了“保险”不断把更多内容放进上下文。
输入 Token
= 系统指令
+ 对话历史
+ RAG 上下文
+ 工具结果
+ 当前问题
建议把这些部分分别统计:
type TokenBreakdown = {
system: number
history: number
retrieval: number
tools: number
user: number
output: number
}
这样才能知道问题是历史太长、召回太多,还是工具返回了一大段无关 JSON。优化时可以做上下文摘要、结果压缩、字段裁剪和缓存,而不是盲目换更便宜的模型。
重试也要计费。一次超时后重新发送完整 Prompt,可能产生两次输入和两次输出成本;如果请求已经在供应商侧成功,只是响应丢失,重试甚至会造成重复副作用。错误分类、幂等和指数退避不仅是可靠性设计,也直接影响账单。
三、Embedding 和索引是容易被漏算的长期成本
RAG 的一次查询成本可能不高,但知识库首次构建和持续更新会产生大量 Embedding。文档切分过细,切片数量会迅速膨胀;切分策略改变后全库重建,又会产生一轮额外成本。
应记录:文档数、切片数、Embedding 调用数、失败重试数、向量维度、存储容量和索引更新频率。对没有变化的内容使用内容哈希,避免重复向量化;对修改文档做增量更新,不要每次清空全库。
向量存储还要加上备份、副本、索引和网络流量费用。高维向量数量大时,单个向量看起来很小,总体容量却不容忽视。可以评估降维、量化、冷热分层和过期清理,但质量变化必须进入离线评测。
四、自建 GPU 不能只除以运行小时
自建模型时,很多人用“GPU 每小时价格 ÷ 每秒 Token”估算成本,却忘了 GPU 可能大部分时间在等待请求。真实成本还包括:
- GPU 实例和预留容量;
- CPU、内存、磁盘和网络;
- 模型加载和空闲保活;
- 监控、发布、备份和运维人员;
- 多版本模型并存带来的额外资源。
可以用利用率修正:
有效推理成本
≈ GPU 及配套资源成本 / 实际完成的 Token 数
如果为了 99.9% 可用性预留两台 GPU,但流量只够一台,另一台的空闲也属于产品成本。动态扩缩容、请求合并、Continuous Batching 和模型分层路由,都可能比单纯购买更大机器更有效。
不过,GPU 利用率高也不一定是好事。排队时间过长会增加超时和用户流失,最终提高单位成功任务成本。成本和 SLO 必须一起看。
五、缓存是成本优化,也是数据边界
问题缓存、Embedding 缓存和工具结果缓存都能减少调用,但缓存键必须包含租户、权限、模型版本和知识库版本。否则为了省几分钱,把错误答案给了不该看到的人,代价会非常大。
缓存命中率要和质量一起记录:
缓存收益
= 减少的模型成本
- 缓存失效维护成本
- 旧答案造成的质量损失
对于完全确定的结构化任务,缓存通常很划算;对于依赖实时库存、权限和最新知识的回答,缓存时间要更短,甚至只缓存中间 Embedding。别为了让命中率好看,保留无法解释的旧答案。
六、把人工成本放进模型里
高风险 AI 应用常常需要人工审核、纠错和客服介入。一个自动化流程节省了 80% 的处理时间,但剩下 20% 的复杂样本可能恰好最耗人。只计算模型账单,会严重低估真实成本。
单位任务总成本
= 模型与基础设施成本
+ 人工审核时间 × 人工时成本
+ 失败重做成本
+ 客诉与补偿成本
同时,人工反馈有长期价值:高质量纠错可以进入评测集和训练集,降低未来失败率。它不是纯粹的消耗,也是一种数据资产,但必须有授权、脱敏和用途边界。
七、成本归因要能定位到功能和租户
不要只在月末看总账。每条请求都应关联功能名、租户、模型、版本、Token、延迟和结果状态:
type UsageDimension = {
feature: 'chat' | 'rag' | 'agent' | 'indexing'
tenantId: string
model: string
promptVersion: string
success: boolean
inputTokens: number
outputTokens: number
}
这样可以发现:某个功能只占 10% 的请求,却占了 60% 的 Token;某个租户频繁重试,或者某个 Prompt 版本让上下文突然变长。成本数据必须能下钻到请求样本,否则优化建议只能停留在“大家省一点”。
配额也要给用户看得懂。显示“本月已使用 72% 输入 Token,预计还能完成约 2400 次标准问答”,比只显示一个模糊的“额度不足”更有帮助。超过配额时,可以降级到小模型、关闭昂贵工具或请求升级,而不是突然让服务完全不可用。
八、优化顺序:先消除浪费,再换模型
我通常按这个顺序排查:
- 是否重复发送相同内容?先做缓存和去重。
- 上下文是否过长?做摘要、裁剪和检索压缩。
- 失败是否重复计费?完善超时分类、幂等和重试。
- 是否把简单任务交给了大模型?增加小模型和规则路由。
- GPU 是否长期空闲?优化调度、批处理和扩缩容。
- 最后再比较供应商和模型价格。
换模型当然可能省钱,但如果系统每次都把 50 页文档塞进 Prompt,换便宜模型只是把浪费放大得慢一点。先把数据流和调用链看清楚,优化才不会凭感觉。
总结:便宜不是目标,单位价值才是
AI 系统成本核算让我最深的感受是,账单只是结果,系统设计才是原因。Token 多了,背后可能是上下文管理差;GPU 贵了,背后可能是调度和利用率低;人工成本高了,背后可能是模型没有学会拒答或路由;失败重试多了,背后可能是可靠性设计不完整。
把成本事件统一起来,按功能、租户和任务归因,区分输入输出与重试,计算单位成功任务成本,再把质量、延迟和隐私一起放进决策,团队才真正知道哪里值得优化。
一个成熟的 AI 产品不追求每次调用都最便宜,而是让每一分钱都对应用户得到的价值:简单问题快速处理,复杂问题值得调用更强模型,高风险问题保留人工判断,失败任务不会无止境烧钱。成本意识不是给创新踩刹车,而是让好想法有机会长期运行下去。