资讯动态

大模型Token计费全解析:从分词原理到成本优化实战

发布时间:2026/10/20 2:37:42 来源:尧图企业网站定制
记得我刚接触大模型开发那会儿最让我头疼的还真不是提示词怎么写而是后台账单上的那一串token数字。明明感觉没聊几句钱就悄悄没了看计费详情时又一头雾水。后来自己跑接口做应用才彻底搞明白Token到底是什么以及为什么所有大模型厂商都乐意用这个单位来计费。这篇文章就围绕Token这个核心展开从它是什么、怎么算的到计费规则背后的逻辑再到常用的省钱技巧把整套概念梳理清楚。适合刚开始接触大模型API调用、被token计费搞晕过的朋友也适合已经在写代码但没系统研究过计费机制的开发者。1. 内容整体设计与思路拆解1.1 为什么厂商都拿Token而不是字数来计费先回想一下我们平时的使用习惯。不管是用网页版聊天还是调API接口消息发出去之后返回结果的同时往往还会附带一串类似使用的token数tokens之类的统计信息。一开始我以为是厂商故意整得玄乎后来才意识到Token才是模型处理文本的真正基本单位。如果用字数计费问题在于一个英文单词算一个字还是算几个字一个标点算不算字中文一句话和一个英文长句又怎么对比这些换算规则本身就是模糊的。Token——本质上可以理解为模型能够识别的最小文本单元可能是半个词、一个词也可能是一个汉字或者一个标点——很好地解决了这个问题。它既贴近模型的内部处理逻辑又方便厂商在不同语言、不同模型之间做统一计费。我之前做过一个小实验拿同一段中文文字分别发给几个不同模型的API让它们只回“收到”两个字结果返回的token统计并不完全一样。这说明Token切分不但和语言有关还和每个模型自己的分词器Tokenizer有关系。对于小白来说先不用管那么细只要记住“Token不等于字也不等于词”这一条就已经能避开很多误解。1.2 理解Token的一张生活类比图如果觉得Token这个概念仍然有点抽象可以用快递包裹来理解。假设我们要寄一份很长的物品快递公司不会把它当做一个整体搬运而是会拆分成多个标准大小的箱子好让运输和处理更高效。这里的“标准箱子”就对应Token。同样一段文本不同快递公司对应不同模型拆箱的方式可能不太一样有的公司习惯按重量拆有的公司习惯按体积拆。于是就可能出现同样的货物文本一家公司拆出的箱子多、计费贵另一家拆出的箱子少、计费便宜。这个类比能解释一个常见的困惑为什么我在模型A里面问同样的问题显示消耗了100 token在模型B里却变成130 token就是因为两家分词器的切分策略不同。实际开发的时候直接用模型自带的分词工具去估算就行不用自己瞎猜。1.3 这篇文章能帮你解决什么问题梳理清楚Token的本质之后这篇文章重点解决三个实际痛点。第一个痛点是“看不懂账单”。很多新手第一次跑到后台看调用明细发现余额少了一大截但又不知道具体是哪些请求烧掉的。看完这篇你会知道一个请求的计费范围包括输入和输出两部分长对话的成本会不断累积缓存命中与未命中的区别也会反映在你的账单里。第二个痛点是“拿到了token数据但不会用”。API返回里的total_tokens并不是一个摆设它可以帮助你在代码里做成本监控可以在发送请求之前就把预算卡死。我会给出一个简单的用量统计思路方便你把它整合进自己的项目。第三个痛点是“不知道怎么省钱”。Token计费机制弄懂之后很多开销其实是可以通过策略优化的。比如缩短system prompt、控制历史消息条数、合理利用缓存这些技巧对个人开发者和中小团队都特别实用。2. Token的核心概念与切分逻辑2.1 分词器Tokenizer到底做了什么要理解Token必须先理解Tokenizer。它做的事情看起来简单把外面传入的原始文本切成模型能理解的一个个小片段。但这一步直接决定了后续模型能看到什么、理解成什么以及你要为这次调用花多少钱。我举个更具体的例子。英文文本里传统的切分可能按空格来做但这会带来一些问题。比如“dont”这个词如果按空格切它会被当成一个整体但模型的词表里如果没有“dont”这个完整词条模型就没法直接处理它。分词器通常会把它拆成“don”和“t”之类的组合保证组成它的子单元都在模型词汇表里。中文的情况更值得聊。中文没有天然空格分词器通常会把一句话切成“字”“词”或者“子词”级别的片段。不同分词器对中文的支持力度差别很大——有些中文优化做得好的模型一两个常见词能压成一个Token有些模型则基本是一个汉字一个Token甚至一个汉字可能拆成两个Token。这就是为什么同样是300个汉字有的模型计费贵有的计费便宜。2.2 从字符到Token的换算公式实操估算实际开发时最常遇到的困惑就是我有一段文本大概会消耗多少个Token虽然最终以API返回为准但事前估算能让心里有底。我总结了一个粗略的经验公式适合大多数通用大模型对于纯英文文本1个Token大致等于4个字符也就是说100个英文单词大约等于75到100个Token。对于纯中文文本1个汉字通常约等于1到2个Token。如果句子里的常见词比较多可能趋近于1个Token一字如果包含生僻字和专业术语可能接近2个Token一字。对于中英混合文本先分别按上述规则估算再加总末尾补一点余量。举个例子我之前做问答系统时固定系统提示词大概这么写“你是一个资深的编程助手。请用简洁准确的中文回答问题。当用户提供代码报错信息时请先定位问题原因再给出修复建议最后附上测试用例。”这段大约60个字按一个汉字约1.5到2个Token估算系统提示词大约会消耗90到120个Token。如果用户的提问是200个汉字那就是约200到400个Token。一次请求如果输入总共300个Token输出回复了500个Token那么这一次API调用的总消耗就是800个Token左右。这里要注意估算只是辅助真正计费以模型分词器实际切分为准。有些模型会在文档里放一个交互式的Token计算器我通常会把有疑问的文本粘贴进去切一下出来多少Token一眼就知道。2.3 为什么模型不一样Token数量也不一样有不少读者问过我我把同一篇文档发给不同的对话助手问“这篇文档讲了什么”为什么它们显示的消耗Token数不一样一个原因就是各个模型使用不同的分词算法和词表。有的模型语料里中文占比高中文压缩能力就强相同文字切出来的Token就更少。有的模型偏向英文场景处理中文时可能一个汉字要拆成两个或多个Token自然更费钱。另一个原因在API层面不同平台对历史消息的处理策略不同。有的平台默认带上所有历史消息一起计费有的平台会自动截断多余内容还有的平台对系统提示词有特殊折扣或配额。真实消耗数的差异不一定是模型本身的原因也可能是因为两端统计口径不同。建议的做法不要试图在多个平台之间手动对比Token数字来找“最省钱”的模型先把单个模型的规律摸清楚然后固定在一个平台上做成本估算这样才比较有意义。3. 计费逻辑与幕后规则3.1 输入Token和输出Token的价格差异收费规则几乎是所有主流大模型API按两个方向分开算的用户传给模型的全部Token输入Token和模型生成出来的全部Token输出Token。通常输出Token的单价高于输入Token。看到这个差异时新手容易产生一种误会以为只要让模型“少说点”就能大幅省钱。这话对但只对了一半。输出单价之所以高一个原因是生成过程本质上是逐Token预测每生成一个Token都要走一次完整的模型前向计算。也就是说模型说100个字它不是一下子出来的而是像打字一样一个字一个字地“想”出来。生成Token越多承载这串推理的算力消耗就越大。相比之下输入部分可以做并行化处理而且很多输入在实际请求里是重复的于是厂商还能用缓存技术进一步压成本。从产品设计角度来说把输出定得更贵也是在引导开发者珍惜模型生成的内容。如果输出和输入一样便宜有些开发者就可能贪图省事让模型一次性生成海量文本反正便宜。分价计费之后大家在设计提示词时会更倾向于要求模型“精准回答”而不是“啰嗦铺陈”。我自己的一个体验是同样让模型解释“什么是闭包”如果不加约束它可能会写出一篇小作文输出Token轻松破千如果我在提示词里明确“用三句话解释每句话不超过20字”模型通常能控制在100个Token以内。同样的理解需求成本差了好几倍。这不只是省钱更是倒逼你思考怎么提问。3.2 历史消息与上下文窗口的“叠加陷阱”这是我最想提醒小白注意的地方。很多刚接触API的人以为一次调用就是“我发一句模型回一句”实际上在支持多轮对话的API里情况完全不是这样。比如你要让模型记住之前的对话内容就必须在每次请求里把历史消息全部带上。假设第一轮你发了10个Token的用户输入模型回了50个Token第二轮你又发了10个Token的新输入这时候请求实际包含的不只是10个Token而是第一轮的1050加上第二轮的10还要加上系统提示词。第三轮再问前面所有轮次又都累积进去。整个请求的输入Token就是指数级叠加的。这种设计让模型可以做到跨轮对话但代价就是上下文越长单次请求费用越贵。很多项目一跑起来用户聊了几十轮后后台平均单请求成本已经翻了好几倍原因就在这里。实际操作时我会用一个片段来帮助理解假设系统提示词固定占100 Token。第一轮用户输入50 Token模型回复200 Token。第二轮请求的输入 100 50 200 用户第二轮输入。第三轮请求的输入会把前面所有轮的输入输出全部再加一遍。要控制这种叠加常见的解决办法有三个。一是限制历史轮数只保留最近的几轮消息二是做“记忆摘要”让模型把前面的对话浓缩成一小段摘要再跟当前问题一起发出去三是有些API平台提供上下文缓存如果历史内容没变就能按较低价格计算。这三个方案在后面的省钱章节还会详细展开。3.3 缓存命中与未命中的区别大模型API平台普遍引入了提示词缓存Prompt Caching机制。简单说如果你的输入Token和之前的请求完全一样或者只是前缀匹配上了缓存那么在计算输入成本时这部分Token就能以更便宜的价格结算有的平台甚至按约十分之一的价格算。比如你的系统提示词特别长是一条几千Token的说明文档而每个用户请求都会带上这段相同内容。如果没有缓存每次请求这段都要按正常输入价重新计费开启缓存后第一次请求还是要走完整计算但后续相同前缀的重复内容就能命中缓存钱包压力会显著下降。但缓存不是自动的——有的平台需要在请求参数里显式开启有的平台默认启用但要求前缀满足一定的最小长度还有的平台缓存命中也有有效期限制过期了就要重建。开发的时候我建议养成一个习惯在代码里通过返回的缓存字段来判断这次请求是命中还是未命中。如果发现自己的场景大量要求每次都重新计算就要想想是不是把动态内容比如时间戳、随机数写进了前缀破环了前缀复用。有一个需要注意的坑不是所有场景都适合开缓存。如果你的每条请求内容前缀都不一样那开启缓存不仅省不到钱还可能让平台额外产生缓存相关的用量记录账单看起来更复杂。缓存适合的是“大部分内容稳定只有尾部动态变化”的场景。3.4 总Token数的构成拆解API返回的usage字段一般会包含几个子项prompt_tokens或者叫input_tokens、completion_tokens或者叫output_tokens、total_tokens。有些平台还会额外区分缓存Token和未命中Token。total_tokens输入Token输出Token这行公式看着简单但有几个隐含信息值得分析。第一输入Token里已经包含了系统提示词、历史消息、上下文文档、用户当前提问以及可能附加的工具调用说明。如果让模型调用外部搜索引擎或执行代码相关的指令和返回结果也会被折算成输入Token。第二输出Token里包含模型生成的主回复也包含它在思考过程中可能调用的函数名、参数JSON以及其他辅助输出。有些平台把“思维链”也作为一个特殊Token输出项展示这部分同样会计费。第三统计回来的total_tokens如果明显比预期大排查重点往往不在模型“多嘴”而在输入部分塞进了超出预期的内容。最常见的情况是代码里不小心把第一次请求的响应体原封不动塞进了第二次请求的消息列表。4. 实操过程与核心环节实现4.1 用一段代码读取Token消耗量讲完规则直接上代码。下面用一个常见的调用流程作为示例展示怎么从API返回值里拿到Token统计并打印成容易读的日志。import json import time from openai import OpenAI client OpenAI( api_key你的API密钥, base_urlhttps://你的模型服务地址 ) def chat_with_stats(messages, model你的模型名称): start_time time.time() resp client.chat.completions.create( modelmodel, messagesmessages ) elapsed time.time() - start_time usage resp.usage stats { input_tokens: usage.prompt_tokens, output_tokens: usage.completion_tokens, total_tokens: usage.total_tokens, } # 一些平台会返回缓存相关字段没有时用0兜底 if hasattr(usage, prompt_cache_hit_tokens): stats[cache_hit_tokens] usage.prompt_cache_hit_tokens else: stats[cache_hit_tokens] 0 if hasattr(usage, prompt_cache_miss_tokens): stats[cache_miss_tokens] usage.prompt_cache_miss_tokens else: stats[cache_miss_tokens] stats[input_tokens] stats[latency_seconds] round(elapsed, 2) return resp.choices[0].message.content, stats if __name__ __main__: messages [ {role: system, content: 你是一个简短回答问题的助手每次回复不超过三句话。}, {role: user, content: 请解释一下内存泄漏是什么意思}, ] answer, usage chat_with_stats(messages) print(answer) print(json.dumps(usage, indent2, ensure_asciiFalse))这段代码的工作流程不复杂把消息列表传给模型拿到回复后立刻读取usage对象把输入、输出、总Token以及缓存相关字段记录下来连同响应时间一起打日志。对刚开始做应用的朋友我建议一定把它封装成公共函数以后每次请求的地方都走这一个入口成本统计就会统一很多。如果使用的不是Python SDK而是直接调HTTP接口逻辑也是一样的。从返回JSON里的usage字段取值即可字段名可能会稍有不同比如有的接口是prompt_tokens有的是input_tokens以API文档为准就好。4.2 把Token消耗写入日志做成本监控拿到Token数据之后只print到控制台没多大用更合理的是把它落到日志或者数据库里。我会用标准日志模块来实现一个简化版方便你在自己的脚本里扩展。import logging logging.basicConfig(levellogging.INFO, filenameapi_cost.log, filemodea, format%(asctime)s %(levelname)s %(message)s) def log_usage(model, scenario, prompt_tokens, completion_tokens, total_tokens): logging.info(fmodel{model}, scenario{scenario}, prompt{prompt_tokens}, fcompletion{completion_tokens}, total{total_tokens})这样每天跑完一批数据之后用grep或者Excel打开日志就能看到每个场景的Token消耗分布。出现异常时也很好排查——比如某一天的total_tokens突然暴涨先看请求次数再看是不是某条提示词或历史消息策略改了。对于更正规一点的项目可以把它写成一个计数器存进Redis每次请求把“今日总Token”“今日请求次数”原子加一做一个简单的实时看板。这个工程量不大但对掌握预算状况非常有用。4.3 发送请求前用Tokenizer估算消耗很多模型SDK自带离线Token计算能力可以直接用它来估算文本消耗不必真正向模型发起计费请求。以某个通用模型为例可以这样做from openai import OpenAI client OpenAI() def estimate_tokens(text): return len(client.tokenize(text)) system_prompt 你是一个擅长项目总结的助手请给出结构清晰的回答。 user_input 请帮我总结这个季度的研发进展。 print(系统提示词估算:, estimate_tokens(system_prompt)) print(用户输入估算:, estimate_tokens(user_input))如果你用的平台没有提供在线分词接口也有本地词表方案从模型仓库里找到tokenizer.json或者词表文件用对应分词库离线切分。不过对绝大多数应用来说直接用官方估算工具就够了没必要自己造轮子。现实中要注意的是预估值和实际消费之间通常会有10%以内的浮动。所以做预算时我会在估算结果上再乘1.2的安全系数避免月底对不上账。4.4 一个完整请求的成本估算示例我拿一个虚构的“企业制度问答机器人”作为示例演示一下单次请求的成本估算过程。假设系统提示词约1500 Token包含角色定义、回答规范、敏感词处理策略。候选参考文档约3000 Token每次从知识库里检索出3段相关文本拼进提示词。用户当前提问约100 Token。历史对话保留最近2轮合计约800 Token。模型回复设定为最多600 Token。那么一次典型请求的输入Token大约是1500加3000加100加800等于5400 Token输出Token按上限600算。总Token约6000。如果输入单价是百万Token收费若干元输出单价按输入的三倍左右算那么单次请求的价格可以这样估算输入部分5400除以一百万再乘以输入单价输出部分600除以一百万再乘以输出单价然后把结果相加。你会发现按这个配置跑一万次请求费用会是一个不能忽视的数字。而如果能把系统提示词压缩到500 Token、参考文档只保留最关键的两段、历史轮次降到1轮单次请求的总Token可能就能降到4200左右成本直接就省下来了。这种估算方法特别适合在项目开发前做成本预演。先拿预估并发量、日均请求数、平均输入输出长度套一遍公式基本就能算出月度账单的大致区间也好判断当前方案能不能上线。5. 常见问题与排查技巧实录5.1 明明没聊几句为什么Token消耗那么大这是新手最容易碰到的疑惑。我的排查顺序一般是从以下几个方面来定位问题。第一检查消息列表的历史长度。如果代码里把每轮对话的结果都追加到messages里而且从不清理消耗量就会越来越大。你可以在日志里打一条消息列表的Token估算值看看一次请求是不是八轮前的对话都还完整带着。第二检查系统提示词或者注入的上下文。有些框架会自动附加很长的工具说明、函数调用定义或者一组few-shot示例这些都会计入输入Token。表面上你没说什么但背后已经背着上千Token的隐性输入。第三检查是否开了循环调用或重试。有的代码出错之后会无意识重试好几次每次重试虽然没成功生成结果但请求和Token消耗可能已经发生了。建议在错误处理分支里明确记录“重试请求也计费”能让排查时少走弯路。下面整理成速查表症状可能原因快速判断方法单次消耗远超实际字数消息列表太长打印messages的Token估算值什么都没回复但扣了钱系统提示词过长或模型解析失败重试查看日志中每次请求的usage字段同一问题的消耗不稳定前缀内容里包含时间戳/随机数缓存失效检查输入的头部字段是否动态变化多次调用后账单暴涨历史消息无上限累积查看消息列表长度是否随轮数线性增长5.2 为什么返回的Token数和本地估算对不上再聊聊估算偏差问题。常见原因有三类。第一类平台统计口径包含“可见Token之外的部分”。比如消息里可能有隐藏的系统字段、安全检测文本、工具调用定义它们在对话里不可见但照样会被计入输入Token。第二类模型分词器更新了。底层词表调整之后同一段文本的切分结果可能变化。你本地用的是旧版词表在线估算或实际计数用的是新版自然对不上。这种差异通常不大但会稳定存在。第三类自动截断。当输入超过上下文窗口时有些平台的网关会截断历史内容但截断机制未必透明。你看到返回里的prompt_tokens比自己发过去的messages估算值少可能就是平台自动丢掉了最靠前的部分。针对性解法一是不要依赖本地离线词表长期估算要定期和线上实测值校准一次二是如果发现明显差异先查官方文档里关于usage字段的定义三是实在拿不准的时候用同一个固定文本分别跑三次请求看统计是否稳定。稳定说明规则一致只是估算方式有差异不稳定说明系统侧可能存在动态因素。5.3 上下文缓存的生效条件不好判断怎么办上下文缓存是一个能省钱的好功能但也是新手最容易困惑的。明明开了为什么我看账单还是原价经验上有几个判断维度。前缀一致性。缓存要求的是连续相同前缀。如果你的messages越往前越稳定缓存命中率越高如果每轮对话都把用户的随机问题放在靠前位置或者把时间戳拼进第一段内容那命中率就会很差。理想的前缀结构是系统提示词、固定文档片段、动态轮次内容、用户当前问题让前面两大段保持稳定不动。最小缓存长度。不少平台规定只有输入Token达到一定规模例如大于1024 Token时才启用缓存计算太短的请求就没有缓存意义。生命周期。缓存内容不是永久存在的可能几分钟或几小时后失效。实际开发中如果两次请求间隔很长第二次大概率会变成缓存未命中重新全价计算。实操经验是先关注返回统计里缓存命中和未命中的数值如果命中值长期为0优先检查前缀里是否有变化数据再检查请求头参数是否漏传了缓存开关。条件允许时可以设计一个接口专门传固定前缀内容做测试验证缓存是否生效。5.4 如何从源码层面杜绝Token浪费最后分享一个我在工程上常用的习惯给发送消息的组件统一加一道Token预算检查。MAX_INPUT_TOKENS 8000 # 根据自己的上下文窗口设定 def trim_message_list(messages, limitMAX_INPUT_TOKENS): total 0 keep [] # 从最后一条消息往前计算累积Token数超出上限则丢弃更早消息 for msg in reversed(messages): estimated estimate_tokens(msg[content]) if total estimated limit: break keep.append(msg) total estimated return list(reversed(keep))这个函数从最近的用户消息往前倒推把超出预算的早期消息裁掉保证每次请求不会无限制往历史里填内容。这样做当然可能会牺牲一部分早期记忆但避免的是成本的不可控增长。如果项目对早期记忆真的很在意那就走“历史消息摘要”方案每轮之后让模型把当前对话内容压缩成100字摘要放进系统提示词代替逐字逐句保留历史。多轮摘要的触发条件也建议设成自动当历史消息估测超过多少Token时才触发一次摘要操作。这样既不影响前期对话的完整性也不会让每一轮都多花一次摘要调用的计算成本。6. 实操心得与进阶建议6.1 控制成本的三个常规策略第一收紧输出长度。在提示词里直接告诉模型“控制在50字以内”“只列出三点”“禁止展开背景说明”对降低输出Token非常有效。虽然模型不一定100%守规矩但绝大多数时候会依此收敛篇幅。第二使用小模型。很多任务用参数规模小一些的模型就够用了价格往往是主力模型的一半甚至更低。对于分类、抽取、改写这类场景小模型完全能hold住。我的经验是先从大模型调通流程验证效果之后再切成小模型对比质量通常能省一大块成本。第三做批量处理。如果业务是非实时的可以把多达几十条输入打包进一次请求让模型逐条回答摊薄固定开销。不过批量场景需要注意输出上限一次塞太多可能导致模型生成不完整反而诱发额外重试。6.2 排查计费异常时要注意的三个细节细节一注意SDK版本与平台规则的版本差异。曾经遇到过SDK旧版不会展示新的usage字段的情况结果就是日志里看不到缓存命中数据误以为平台没给这个能力。细节二注意“请求失败也可能产生费用”。某些请求在中间阶段出错比如生成了100个Token然后网络中断虽然客户端没拿到完整回复后端仍然可能记录了已生成的Token。排查时不能按“成功响应数”去反推成本。细节三注意工具调用中的隐性Token。函数调用、外部接口返回、代码执行器的执行结果这些都会作为额外消息重新进入模型上下文。每执行一次工具调用就相当于多了一轮消息输入这个成本和频繁工具调用的场景直接挂钩。6.3 后续可以这样扩展Token这套知识学完之后可以继续向几个方向深入。一是向量检索与Token预算的关系。做知识库问答时召回多少文本片段、每段多少字都直接决定输入Token消耗。这里可以和向量库的相似度阈值联动起来通过调参找到“回答准确率”和“Token成本”的平衡点。二是缓存策略产品化。如果你的服务有多用户共享相同系统提示词那缓存机制带来的成本优势就很可观。可以考虑把所有静态内容统一编译成固定前缀段让更多请求命中缓存。三是做一套自动化账单分析工具。把Token日志收集起来定期生成报表按“用户维度”“时间维度”“消息长度维度”分析成本构成能在项目早期就发现异常增长。我个人的体会是Token不只是账单上的数字它实际上是理解大模型工作方式的一面镜子。看懂了Token就明白为什么对话会越聊越贵为什么模型的“记忆”那么金贵为什么提示词越精准越省钱。这些小道理背后都是一整套系统的设计取舍。刚开始做应用的朋友不用一开始就追求极致的Token压缩先跑通功能再把成本意识带进工程习惯里慢慢就能形成自己对“贵不贵”的体感。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑