上个月,某个创业团队把后台账单截图发给我,三行字印象深刻:一个月API调用费用两万多,其中六成消耗在一个只有两百行的文本分段任务上。他们没有换模型,也没有砍需求,只是压根没意识到——用户每打一次招呼,系统都在把同样的历史记录重新算一遍Token费。这不是个案。我接触过不少“成本敏感型开发”的项目,绝大多数团队在选模型的时候都在纠结“哪个更强”,却很少有人把Token计数当成成本台账来精算。等到账单出来才开始算账,基本已经错过了省钱最好的时机。
这篇文章想聊的,就是成本敏感型开发里一直被忽视的一环:Token计数与模型选型之间的关系。我会用实际项目里的算法、公式、踩坑记录,把“一次请求到底花了多少钱”“换小模型到底能省多少”“为什么省下来的钱会被准确率吃掉”这些问题讲透。适合正在做API接入、LLM应用落地、AI功能开发,并且对成本有硬约束的团队和个人开发者参考。
1. Token计费拆解:钱到底花在了哪里
1.1 看得见的单价和看不见的隐形消耗
所有接触过大模型API的人都知道计费单位是Token,但“1K Token多少钱”这个单价,只是账本上最表层的一行。实际的消耗路径比这个复杂得多:每一轮请求消耗的Token,不仅包含当前用户输入的内容,还包含系统提示词、历史会话、检索到的上下文片段,以及模型生成的全部输出。
举一个典型的客服工单分类场景。用户提交一条工单,系统把工单内容、用户历史消息、企业知识库检索结果拼进Prompt,再让模型输出一个分类标签。表面上看起来只是一个“简单分类”,但真实发送给模型的文本可能已经有3000到4000字。很多团队在对Token计数无感知的情况下,把这个Prompt做成“每轮都完整拼接”,于是每次请求都在重复计费——同样的几条检索片段,同一个会话里被反复计入成本。
这里有个经验:Prompt的静态部分和动态部分必须拆开算账。静态部分是系统提示词、固定指令、工具定义,动态部分是用户输入、检索结果、历史摘要。静态部分如果每次请求都原封不动带着走,它的Token消耗会随着调用量线性增长,这笔钱属于“看不见的隐形消耗”。我在项目里一般会把系统提示词精简到能完成指令的最短长度,凡是可以在服务端做的判断,绝不写进提示词里让模型去“理解”。
注意:很多平台的计费是按照模型输入和输出分别计价的,输入的单价通常只有输出的四分之一到五分之一。也就是说,模型生成得越啰嗦,成本涨得越快。后面讲到参数配置的时候,这个点还会展开。
1.2 多轮对话与上下文膨胀的成本陷阱
多轮对话是Token消耗的重灾区。表面上,用户每轮对话的平均输入Token数可能只有几百,但累积起来,下一轮的请求会把前面所有轮次的内容全部带上。一个聊了20轮的会话,第21轮的输入Token可能是第1轮的20倍以上,而且这个数字还在持续增长。
我在某个陪练类应用里见过一个典型情况:产品团队为了保留“记忆”,把每轮对话的完整原文都塞在请求里。用户聊了50轮之后,单次请求的Token数已经超过2万。如果按一天1000个活跃会话、每个会话平均30轮计算,仅历史上下文消耗的Token就是天文数字。问题的根源在于,团队默认“大模型需要上下文才能理解”,但没有考虑上下文可以由产品逻辑来管理。
解决方案有三个层次。最粗暴的是滑动窗口,只保留最近N轮对话;稍微精细一点的是摘要压缩,每隔几轮让模型把前面的核心信息浓缩成一小段摘要;再进阶一点的是关键信息抽取,只保留用户身份、偏好、待办事项这类结构化数据,对话原文用完即弃。我在实际项目中倾向第三种思路,虽然前期需要多做一些解析和存储设计,但长期来看Token成本最可控,而且响应速度也会更快。
1.3 一次请求的真实成本公式
把成本算明白,必须回到公式层面。单次请求的真实成本可以拆成四个部分:
- 静态指令Token:系统提示词、工具定义、输出格式约束
- 动态上下文Token:用户输入、外部检索结果、历史摘要
- 生成输出Token:模型回答、JSON结果、任何附带文本
- 重试与失败成本:超时重试、解析失败后的二次调用、熔断后的补偿请求
用公式表达就是:
单次成本 =(静态Token + 动态Token)× 输入单价 + 输出Token × 输出单价
如果调用失败或超时重试,成本至少翻倍。这个公式写出来很简单,但大多数团队根本没有做过这个统计。他们会看总账单,却不知道提升Word Embedding类任务占比是否合理、哪个功能模块烧钱最多、哪类用户行为推高了成本。
建议在项目初期就从日志里记录每次请求的三个关键数据:输入Token数、输出Token数、单次请求的计费金额。不需要额外开发专门的系统,在调用层包装一层日志中间件就能做到。一周之后你就能拿到一张功能模块维度的成本分布表,这才是成本优化的起点。
2. 模型选型的ROI计算框架:从单价思维到总账思维
2.1 大模型和小模型的成本代偿逻辑
成本敏感型开发里最常见的一个误区,是“用最便宜的模型处理所有任务”。我见过不少团队在选型阶段就把所有请求切到小模型,结果发现准确率达不到业务要求,大量输出需要人工审核返工,最终算下来人力成本反而比直接用大模型更高。
这里面的逻辑叫成本代偿:模型能力的缺口,不会凭空消失,只会转移。要么转移给人工审核,要么转移给二次调用和提示词工程优化。所以真正的ROI计算,不是比较“大模型单价贵几倍”,而是比较“大模型 + 少量人工抽检”和“小模型 + 大量人工兜底”哪个总成本更低。
我一般会建议团队做一个简单的四步评估。第一步,用同样的测试集分别跑大模型和小模型,记录准确率差异;第二步,估算不准确的输出会对业务造成什么影响,需要多少人工介入成本;第三步,把模型调用成本和人工处理成本加起来计算总成本;第四步,对比大模型小模型的总成本,而不是单价。这套方法看起来朴素,但实用性远高于凭感觉选型。
2.2 分层路由:让每个任务匹配最便宜的合格模型
模型选型的另一个方向是分段调度。同一个产品里,不同接口的难度差异可能很大:有的任务是复杂的意图理解和多步推理,非得上大模型不可;有的任务只是结构化抽取、格式转换、简单分类,小模型完全能胜任。把所有流量统一走同一个模型,等于让高成本的运力去跑低价值的活儿。
我在一个内容审核项目里用过一套分层路由策略:请求进来后先过一个轻量规则的判定逻辑,把明显合规或明显违规的内容直接分流,只有边界模糊的内容才调用大模型做语义判断。这个策略上线后,大模型调用量下降了近七成,而审核精度几乎没有变化。类似的做法还有:先用小模型给出候选标签,如果置信度低于阈值再升级到大模型;或者对长文档先做分段处理,只有关键段落才进入大模型分析。
2.3 一个客服分类场景的ROI计算示例
举个我实际帮某个小团队算过的例子。业务是做在线客服工单的自动分类,每天5000条工单需要打成“退费、物流、技术、投诉”四类,每条约750个Token,模型输出约50个Token。
选型A是某主流大模型,输入单价按每百万Token 15元、输出单价按每百万Token 60元算。单条工单的成本就是:(750 / 1000000) × 15 + (50 / 1000000) × 60 = 0.01125 + 0.003 = 0.01425元。5000条就是71.25元/天。选型B是某个小模型,输入单价每百万Token 1.2元、输出每百万Token 6元。单条工单成本是(750 / 1000000) × 1.2 + (50 / 1000000) × 6 = 0.0009 + 0.0003 = 0.0012元。5000条是6元/天。
光看这组数,小模型一个月能省近2000元,看上去优势巨大。但把准确率放进来算:大模型准确率95%,小模型88%。每一条分错的工单需要人工处理,人工处理一条按0.5元算。大模型每天的人工成本是5000 × 5% × 0.5 = 125元,小模型是5000 × 12% × 0.5 = 300元。总成本一加:大模型每天196.25元,小模型每天306元。反而是大模型划算。
这就是ROI计算和单价计算的区别。单价低不等于总账省,准确率差异会充分抵消模型单价优势。如果你的业务场景中小模型准确率能达到92%以上,情况又会反过来。所以每次有人说“小模型省钱”,我的回应都是:先算清你的人工兜底成本,再下结论。
3. Token计数的工程化落地:如何把账算得准
3.1 用tokenizer算法而不是字符数估算
很多团队在做Token统计的时候,直接用字符长度除以一个经验常数来估算。这个做法在业务量小的时候能凑合,但一旦涉及成本核算和预充值,误差会大到无法接受。中英文混合文本、代码片段、JSON格式,不同内容类型的Token密度差异非常大,一个汉字可能对应0.6到2个Token,一个英文字符可能对应0.2到0.3个Token,字符数估算法完全不可靠。
正确的方式是直接使用目标模型配套的tokenizer工具包。你把文本喂进去,它返回真实的Token序列,长度就是准的。这里要特别注意:不同模型的tokenizer算法可能不同,用A模型的tokenizer去统计B模型的Token数是有偏差的。项目里如果同时接入了多个模型,就必须给每个模型分别建Token统计逻辑。
一个工程上很容易做到的封装是这样的:在代码里定义一个统一的计数函数,根据当前路由的模型ID选择对应的tokenizer,然后返回准确的Token数量。这样不管是日志统计、成本预估算还是用量展示,用的都是同一套口径。
def count_tokens(text, model_id): # 每个模型对应独立的tokenizer,避免混用 tokenizer = get_tokenizer_for_model(model_id) tokens = tokenizer.encode(text, add_special_tokens=False) return len(tokens)注意:有些模型接口允许你在调用时设置“返回usage详情”的参数。开启之后,响应体里会直接带上输入Token数和输出Token数,比客户端估算准得多,建议在生产环境默认打开,并把返回值落库。
3.2 预计算、缓存与用量上报
Token计数不只是在日志里记录,还可以前置到请求发送之前。如果一个功能模块的Prompt结构比较固定,你可以在离线阶段就把静态指令部分的Token数算好,存入配置中心。到了运行时,只需要计算动态部分的Token数,两者相加就是预估消耗。这样可以提前判断一次请求是否会超过预算阈值,或者识别出恶意用户构造的超长输入。
缓存是另一个省钱利器。多轮对话里,用户的历史消息在一段时间内是固定的,同一个会话内重复请求没有必要反复把所有上下文重新编码。有些平台本身提供了上下文缓存机制,命中缓存的输入Token收费大幅降低。工程上要求你把请求结构设计成“静态前缀 + 动态后缀”的形态,让缓存命中率尽可能高。更进一步的方案是在应用层做结果缓存:对相同输入直接返回之前的结果,完全不调用模型。
用量上报方面,我习惯在每个请求结束后把四个字段写入日志:模型ID、输入Token、输出Token、缓存命中Token。按天聚合后能看到每个模型、每个功能、每个用户维度的消耗曲线。有了这些数据,优化就变得很具体,而不是拍脑袋。
3.3 降低有效token的三条实用路径
路径一:压缩Prompt模板。不要用一大堆固定话术去“让模型更听话”,把指令写成简洁明确的短句,同时用严格的输出格式约束替代自然语言描述。实测中,同样的任务,Prompt从400Token压到150Token,效果几乎不变,成本直接降一半。
路径二:减少输出长度。在参数层面设置max_tokens上限,防止模型在一个短分类任务里输出一大段解释。很多模型默认会“答完题还附送解题步骤”,你不限制它,它就给你生成几百Token的废话。这里有个技巧:用Few-shot示例里的输出格式做严格约束,让模型跟着示例的格式走,比单纯调温度参数有效得多。
路径三:先摘要后分析。针对长文本场景,不要直接让模型读全文。先把全文切块,让模型逐块提取关键信息,最后再基于这些关键信息做判断。比如一篇5000字的文章,直接分析可能消耗几千Token,先摘要成500字再分析,成本能降一个数量级。这类处理会增加一次模型调用,但总Token数远低于直接处理原文本,综合下来还是划算的。
4. 成本敏感型开发的常见坑与排查实录
4.1 计费口径不一致,账单比预期多两倍
有次帮一个团队排查账单异常,他们用的模型和统计工具来自不同生态,结果发现统计出的Token数和平台账单差了近一倍。排查了半天,根因是统计工具用错了tokenizer版本,旧版本的编码方式对新模型的多字节字符支持不全,导致中文文本在统计端被严重低估。从那天起,我们对所有项目的Token统计都执行一条铁律:必须用当前接模型官方配套的tokenizer版本,且每次模型升级后都要重新验证统计模块的准确性。
还有一次是系统提示词的隐性膨胀。某个团队在功能迭代时给Prompt加了一长串业务规则说明,表面看起来每个规则都有用,但积少成多,提示词从200Token涨到了800Token。调用量一上来,这部分成本被无限放大。排查方法很简单:把每个功能模块的静态Prompt Token数做成一个看板,任何变动都走代码审查,防止不知不觉的膨胀。
4.2 流式输出与重试机制造成的隐性开销
流式输出和普通请求在计费上容易产生误解。很多团队以为流式输出更省钱,因为用户看到的响应更快。实际上,流式输出只是改变了数据返回的节奏,Token总数并不会减少。真正要注意的是:一些SDK在流式模式下不会实时返回累计Token数,如果统计逻辑只在接收过程中做累加,中途断流会导致统计值少于真实值。解决方式是以请求结束的最终响应体为准做统计。
重试机制是另一个容易被忽视的烧钱点。我在一个系统里见过,网关层设置了“请求失败自动重试3次”的策略,但下游模型接口因为限流频繁超时,导致大量请求实际上被重复调用了2到3次。账单翻了一倍还不止,而且用户看到的响应反而更慢。后来我们把重试策略调整为“仅对超时且未开始生成的情况重试”,一旦模型已经开始输出,宁可接受降级结果也不重试,成本立刻恢复正常。
4.3 参数策略对成本的蝴蝶效应
模型参数不是配置完就不用管的。温度参数调高会让输出更随机,某些模型在高温下更容易生成多余的解释文本,Token数比低温时多出10%到30%。max_tokens设置得过小会导致输出被截断,触发解析失败,进而引发重试。频率惩罚和存在惩罚参数也会改变Token分布,惩罚值设置不当可能让模型绕圈子说话。
经验是:把模型参数也纳入成本审查的范畴。每次调整参数,都跑一遍固定测试集,对比输出Token数的变化。不要以为这是一个纯功能性的调整,参数和钱是直接挂钩的。还有一个细节:把输出格式约束为最紧凑的JSON或纯标签列表,能明显减少生成Token数,配合正则解析反而比让模型输出自然语言更稳定。
最后再分享一个我自己的体会——做成本敏感型开发,目标不是把单次推理做到最便宜,而是让账算得清、花得对。我在实际项目中最受益的一条经验:先懂账,再选型。任何模型能力上的优势,都要放到你的业务量、失败率、人工兜底成本里去换算,才有真正的意义。省钱的本质,是把每一分Token都花在能产生业务价值的地方。