前两天有个朋友问我,他们在做的一个对话机器人,客户那边反馈“也没聊几句,怎么账单就快爆了”。我让他把请求日志翻出来,一算才发现问题不在并发量,也不在模型选型,而是他们压根没弄明白大模型 API 是按什么计费的。这个“什么”,就是 Token。
Token 计费机制,直接决定你调用一次大模型接口要花多少钱、一个上下文窗口到底能塞多少字、以及你的业务模型在规模化之后成本曲线是陡还是平。不管是做 AI 应用的开发者、运营同学,还是帮客户落地大模型项目的技术负责人,只要你在碰大模型 API,就必须把这套机制吃得透透的。否则,测试阶段风平浪静,一旦上了生产、接了真实流量,月底账单能让你怀疑人生。
这篇文章我从 Token 怎么来、官方按什么规则收费、到一个真实请求如何手动算账、再到生产环境里怎么省钱和避坑,一条线给你理顺。全程是实操视角,不堆概念,看完你就能拿出自己的账单逐条对着看。
1. 为什么大模型计费偏偏盯着 Token 这个单位
1.1 先搞清楚 Token 是什么,别再把“字数”当“Token”
很多人有一个误区:以为大模型按字符数或者按字数计费。实际上,主流大模型 API 全部按 Token 计费,Token 是模型处理文本的最小单位。所谓 Tokenization(分词),就是把一段自然语言切成模型能理解的最小片段。这个片段有时候是一个英文单词,有时候是一截单词,甚至可能是一个标点。
举一个最直观的例子。英文里 "AI" 通常是一个 Token,但 "tokenization" 这个词会被拆成 "token" 和 "ization" 两个 Token。中文没有天然空格,分词规则各家略有差异,但大体上常见的一个汉字在多数模型里约等于 0.6~1 个 Token,三个汉字大概占 1~2 个 Token。这意味着,一段 1000 个汉字的 Prompt,实际 Token 数往往在 800~1200 之间浮动,绝不是你肉眼看到的“1000 字”。
为什么要这么计费?因为大模型内部处理时,注意力机制是按 Token 做矩阵运算的,输入的 Token 数越多,计算量就越大。你每次请求,模型都要把全部输入 Token 读一遍、算一遍;生成结果时,每一个新 Token 也都是一次前向计算。成本和你交出去的字数、拿回来的字数直接挂钩,所以供应商按 Token 收费最公平也最透明。
1.2 上下文窗口和 Token 的关系:为什么上下文越长越烧钱
上下文窗口(Context Window)是大模型一次能接受的最大 Token 数量,包括输入和输出。注意,不是只看你这一轮问了什么,而是整个对话历史累积的 Token 数。一个经典场景:你做一个客服机器人,用户聊天记录越滚越长,你每次都把全部历史拼进 Prompt 发给模型,很快就会发现两个问题:一是请求报错“超出上下文长度”,二是账单金额直线上升。
这里面的计算逻辑是:每一次请求的计费 Token 数 = 全部输入 Token 数 + 模型生成的输出 Token 数。即使模型一句话都没输出,只返回了一个“好的”,你每轮发送的输入中仍包含所有历史对话。假设用户聊了 20 轮,每轮平均 300 Token,那第 20 轮你发的输入就是 20×300=6000 Token,这个数字会随着对话轮数线性增长。
我见过不少团队在生产环境里犯了“无限拼接”的错。有人甚至把知识库全文不分青红皂白往上下文里塞,一个 200KB 的文档直接转成几万 Token 塞进去,单次请求成本比模型生成文本的贵一个数量级。所以理解上下文窗口的真正含义,是整个计费机制里最重要的一环:窗口越大,单次可处理的内容越多,但单次成本也越高,两者是直接绑定的。
2. 一套请求的计费规则拆解:输入、输出、缓存、超额
2.1 输入和输出单价不相等,这是最容易被忽视的规则
几乎所有主流大模型 API 的定价表里,输入单价都明显低于输出单价。原因很实在:输入 Token 只需要并行计算一次注意力,而输出 Token 是逐个生成的,每生成一个都在做一次自回归采样,计算开销大得多。我记得某个主流模型的定价是输入百万 Token 收费十几块人民币,输出百万 Token 收费几十块,输出价格大约是输入的 3~4 倍。
这个差异直接影响你的成本结构。如果是一个问答产品,用户问题短、回答长,那钱主要花在输出 Token 上;如果是一个文档总结工具,每次塞进去一大段文本、输出只要一行结论,那钱主要花在输入 Token 上。做成本预算时,必须分别估算输入和输出的量级,再按各自单价去算,不能笼统按“平均 Token 单价”估。
很多小伙伴会盯着“百万 Token 多少钱”这个指标,但千万要看清是“输入百万 Token”还是“输出百万 Token”。同一家厂商,输入和输出价格差出三倍以上是常态,不看单位直接对比,很容易在选型时做出错误判断。还有一个细节:某些服务商对系统提示词(System Prompt)单独计费,或对特殊内容加收处理费,这些都要在计价文档里逐字看清。
2.2 缓存命中:你以为没花钱,其实计费规则里还有一条隐藏车道
为了让你把同一份知识库、同一套系统提示词反复发送时不再全额计费,服务商设计了提示词缓存(Prompt Caching)。核心机制是:如果请求中的前缀 Token 和之前某次请求完全一致,这部分命中的 Token 按一个很低的折扣价计费,通常只有原价的 10% 左右,甚至更低。比如系统提示词 5000 Token,每次请求都带着,没缓存时每次都是全价;有缓存后,第二次开始这 5000 Token 很可能只需付零头。
但要注意几个触发条件。第一,缓存前缀必须完全一致,哪怕中间多了一个空格、改了一个字,整段前缀就失效了。第二,不同服务商对缓存时效有要求,有的要求两次请求间隔不超过几分钟,超过时间缓存自动过期。第三,缓存的 Token 数和未命中的 Token 数在账单里是分开列出的,需要辨认识别。
这里有一个实际收益案例:我之前优化过某智能客服项目,系统提示词加历史对话模板占了总输入 Token 的 70%,开启缓存后整体 API 成本直接降了近四成。实现方式也不复杂,就是把固定不变的内容(系统提示词、固定的工具说明)放在最前面,让变动内容排在后部,保证前缀稳定可缓存。相反,如果你把用户名、时间戳这些动态内容塞在 Prompt 的最前面,缓存就永远无法命中,这个隐性损失很多人根本不会注意到。
2.3 上下文超出上限怎么办:截断策略直接决定你多花多少钱
当你的 Prompt 总 Token 超过模型上下文窗口时,请求会直接失败,这是最直接的结果。但生产中还有另一种情况:服务商允许你“超额”请求,后台自动截断最前面的内容,只把后半段送入模型。这种策略很危险,因为你完全没有感知,还以为模型在正常回答,实际上最早的对话历史已经被丢弃,而且这部分的输入 Token 仍然可能被计费(具体看服务商规则)。
我遇到过一个案例:某团队做长文档问答,文档 4 万 Token,模型窗口 8K,他们想着“超了就超了,模型会自己处理”,结果文档后半部分进了上下文,前半部分被静默丢弃。用户问“第一章的核心结论是什么”,模型一本正经答非所问。排查了半天才发现是截断策略惹的祸,更尴尬的是,被截掉的那部分 Token 照常计费。
所以做长上下文应用时,绝不能依赖服务商的默认截断行为,一定要在代码里主动控制发送给模型的 Token 数。可以在发送前调用 Tokenizer 计算长度,超过阈值就触发摘要、滑动窗口、或者分段处理。控制逻辑掌握在自己手里,才不会既丢了内容又丢了钱。
3. 动手算账:从一个真实请求看 Token 费用的完整计算过程
3.1 如何精确获取一段文本的 Token 数,而不是靠猜
每个模型都有对应的 Tokenizer 工具,绝大多数服务商提供在线调试页面,或者提供开源的分词器库,支持你用 Python 或 JS 本地调用。你在本地算出来的 Token 数,和线上计费系统基本一致。这个是做成本管理的基础设施,每个团队都必须封装成一个公共方法。
我自己常用的做法是维护一个 Python 工具函数,输入任意文本,输出 Token 数量和计数明细。平时做需求的时候,顺手把用户可能输入的最长 Prompt 拿进去跑一遍,立刻就能评估单次请求成本。下面是基于常见的开源分词库的示例(这里用 OpenAICli100k_base 类似的分词器做演示,你在实际项目中换成你用的模型对应的库即可):
import tiktoken # 按你使用的模型选择对应的编码器 encoder = tiktoken.get_encoding("cl100k_base") def count_tokens(text: str) -> int: return len(encoder.encode(text)) prompt = "请总结以下会议纪要,并提炼出三个待办事项:..." input_tokens = count_tokens(prompt) print(f"输入 Token 数: {input_tokens}")不同模型可能使用不同的分词器,有的甚至官方不提供本地库,只提供在线 API。这种情况下,你在估算阶段可以用近似比例:中文按“1 汉字 ≈ 1 Token”做上限预估,英文按“1 单词 ≈ 1.3 Token”做粗略算。注意这些只是估算,真正上生产前,一定要用目标模型对应的标准 Tokenizer 验证过再批量上线。
3.2 手把手算一笔完整账单:输入、输出、缓存分开算
假设你选的模型定价如下(仅为举例,非真实厂商价格):
| 计费项目 | 单价(人民币 / 百万 Token) |
|---|---|
| 输入(未命中缓存) | 12 元 |
| 输入(缓存命中) | 1.2 元 |
| 输出 | 36 元 |
现在用户连续发起两次请求,每次请求的系统提示词固定为 2000 Token,用户问题平均 500 Token,模型回答平均 800 Token。
第一次请求:输入 2500 Token(2000 固定 + 500 问题),全部未命中缓存,输出 800 Token。费用为 2500×12/1000000 = 0.03 元,800×36/1000000 = 0.0288 元,合计约 0.0588 元。
第二次请求:系统提示词命中缓存,2000 Token 按缓存价计费,用户问题 500 Token 未命中缓存。费率为 2000×1.2/1000000 = 0.0024 元,500×12/1000000 = 0.006 元,输出 800 Token 还是 0.0288 元,合计约 0.0372 元。
你发现问题没有?第二次请求因为有缓存,总成本比第一次便宜了约 37%。如果这个系统每天有 100 万次请求,缓存开没开,一个月下来就是几万块的差额。把这个计算逻辑固化到代码里,每次上线新功能前先跑一遍,就能在开发阶段预判成本,而不是等月底账单出来再追悔莫及。
3.3 并发量、限流、Token 耗尽之后会发生什么
计费 Word 之外,还要掌握 API 返回状态码的含义。常见的是 429 限流和 400 上下文超长,但有些服务商在账户余额不足时也会返回特殊错误。这里有一个很容易踩的坑:某些平台允许你设置“最大生成 Token 数”,一旦回答长度触顶,API 返回正常但内容被截断。此时系统会计费完整的已生成 Token,而你拿到的是不完整结果,用户只会觉得模型“答到一半就停了”。
生产环境中,必须对这类截断状态做响应识别。很多模型的返回结构里会包含 finish_reason 字段,如果它的值是 “length”,说明是触达限制而非正常结束。此时你应该主动做后续处理,比如把现有内容作为中间结果再做一轮续写,而不是直接当成最终答案返回给用户。忽略了这一步,你不但白花了钱,用户体验还会被打折扣。这个细节,我建议你在联调阶段就写进测试用例。
4. 生产环境里的省钱实操:三种策略帮你把 Token 账单打下来
4.1 Prompt 压缩:花最少的钱保留最多的语义
Prompt 里非必要的内容越少,单次输入成本就越低。这听起来像废话,但真正执行的人不多。我看过太多项目的 Prompt 里残留着大量冗余注释、重复指令、无效示例。比如一个系统提示词里写了三个功能说明,其中两个已经很久没用了,却仍然跟随每次请求一起发送,白白占用几百上千 Token。
我的习惯是每隔一两周做一次 Prompt 体检:把当前线上 Prompt 导出,逐行审视每句话是否依然有效;把输出格式说明用最精简的方式重写,把示例从三段合并成一段;如果某些工具函数说明只在特定场景才需要,就改成动态拼接,而不是全局常驻。经过几次压缩,同样的功能,输入 Token 能减少 30% 以上。
还有个经验是:能用 10 个 Token 说清的指令,绝不用 50 个 Token 写一大段限定语。模型的指令遵循能力并不完全和 Prompt 字数成正比,写得再长该不听还是不听,但钱是按字数实打实收的。把 Prompt 当作代码一样去精简,是每个 AI 应用负责人应有的基本素养。
4.2 模型分级与路由:让便宜模型先干 80% 的活
生产环境里没必要所有流量都上最强模型。我常用的策略是分级路由:先花几毫秒和极少的 Token 成本,用一个小分类模型判断用户请求的复杂度,简单请求(比如查天气、查时间、固定格式问答)直接路由到便宜的轻量模型;复杂请求(需要推理、需要长文生成)才转发给高性能大模型。这样可以显著拉低整体成本,毕竟轻量模型的价格往往只有旗舰模型的十分之一甚至更低。
这个策略在落地时要注意两个点:一是分类模型的误判率,如果 30% 的简单请求被误判为复杂请求,成本优势直接消失;二是轻量模型在某些任务上的质量可能不足,你需要提前设定一个降级标准,比如用户追问“你确定吗”就自动升级重答。我身边有团队靠这套做法把月成本压低了五成以上,质量和成本之间的平衡是可以被精密调出来的。
4.3 批量处理和异步合并:把多次请求合并成一次
很多业务场景根本不需要实时返回。比如夜间批量总结用户反馈、周期性的文档处理,这类任务可以攒一批、合并成一个大请求,让模型一次处理多条内容。这样做有两个好处:第一,大请求中固定开销(系统提示词等)被摊薄到更多业务内容上,单位成本下降;第二,可以利用服务商可能提供的批量 API 折扣价,部分厂商对 Batch 请求额外打折。
举个例子:你要逐个总结 100 条用户评论,每条评论 200 Token,如果分开请求,每个请求都要附带一份 1000 Token 的说明性指令,总输入就是 100×(1000+200)=120000 Token;如果合并成一个数组发送,指令只带一次,输入大约 1000+100×200=21000 Token,输入成本直接降到原来的六分之一。这种思路在数据清洗、批处理场景里非常普遍,收益也非常可观。
5. 常见误区与排查技巧实录:那些账单爆掉时的自救指南
5.1 为什么代码没报错,Token 却消耗极快
一个典型场景:应用在后台循环调用模型,但每次请求都带上了一个巨大的历史记录对象,而你并没有意识到这个对象一直在累加。我有一个学员的项目就是这样,用户在聊天页面停留一个小时后,前端会不断把新的用户输入追加进数组,后端未做裁剪就拼接进 Prompt。结果单次请求 Token 从最初的 200 涨到 20000,他还在奇怪“用户没问几个问题,API 费用怎么这么高”。
排查方法是把每次请求的 token 消耗日志单独拉出来,按时间排序观察增长曲线。如果 Token 数随着请求次数近乎线性上涨,且不是因为你主动实现了记忆功能,那大概率是历史消息忘清了。解决办法很简单:在代码里设置一个 MAX_HISTORY_TOKENS 常量,超过就启动滑动窗口或摘要压缩。
5.2 缓存命中率低的排查方向:动态位置、隐藏字符、时间过期
如果你的服务商支持缓存,但账单显示命中率等于零,按从常见到不常见的顺序排查这几个点:
- 动态内容是否放在了 Prompt 前部:比如时间戳、用户名、随机 ID 放在最前面,前缀每次不同,缓存必然失效。
- 是否引入了隐藏字符:从数据库读出的文本可能自带换行符、空格、BOM,肉眼看不出来,却足以让缓存前缀失配。建议打印每个字符的 Unicode 编码对比。
- 是否超过了缓存的 TTL(有效时间):间隔太久的请求,缓存已服务商侧过期,命中率自然低。
以上三个原因,基本覆盖 90% 的缓存未命中场景。实际工作中我把排查步骤写成自检清单,每接入一个新服务商就照着走一遍。
5.3 快速定位成本异常的账单分析方法
我建议所有使用大模型 API 的团队,从上线第一天就把 Token 使用日志结构化落地,至少记录:请求时间、模型名、输入 Token 数、输出 Token 数、缓存命中 Token 数、请求所属功能模块。这些字段可以按天聚合,做成一张简单的日报表。一旦账单出现问题,你可以快速定位是哪个模块、哪个模型、哪个时段消耗异常。
有一次客户找到我,说某天的账单突增两倍。我拉出日志一看,发现某个模块的输入 Token 平均值没有变化,但请求量翻了倍,再一查是营销团队给该模块加了新入口,导流力度远超预估。这不是技术故障,而是业务调整带来的成本变化,但因为日志结构清晰,五分钟就定位了原因。反过来,如果当时没有日志,面对一脸黑的账单数据,只能靠猜,那就非常被动了。
5.4 一个小教训:别把“Token 上限设大”当成“性能调优”
有些开发者认为把 max_tokens 调大就能让模型回答得更多更好,于是直接按上下文上限去设,动不动就是几千。表面上确实是“模型能输出更多内容”,实际上绝大多数交互用不到这么长的输出,但这部分预留的 max_tokens 虽然多数平台不会提前扣费,却会影响调度和用户体验。更危险的是,在部分按“最大生成量”预留资源的服务商那里,这一项会直接抬高你每次请求的计费基数,和实际生成量无关。
正确做法是先统计你业务场景的真实输出长度分布。比如大多数回答在 200~500 Token 之间,那就把 max_tokens 设置为 600 左右留点余量,而不是无脑怼到 4096。一个简单的统计脚本就能解决的事,太多人懒得做,最后被账单教育。
结尾:一点个人经验分享
大模型 API 的 Token 计费机制,说白了就是两件事:搞懂 Token 怎么被计算的,搞懂计费规则里哪些地方能优化。我在这些年做过的项目里发现,真正把成本控制得好的团队,往往不是技术最炫的,而是把“每一次请求花多少钱”这件事做成常态化观察的人。他们会在每次联调时看一眼 Token 消耗,在每个版本上线的同时更新成本预算表,而不是等到月末收到账单才如梦初醒。
如果你刚开始接触这一块,我建议你先把今天这篇文章里提到的计算公式自己手动跑一遍,找一个真实请求,拆出输入、输出、缓存各项,算出单次成本,再乘上你预估的日活和调用频次。做过这一步,你对 Token 计费的理解就会从“知道”变成“掌握”。后续再优化 Prompt、接入缓存、做模型路由,你都能清楚地看到每一分钱省在哪里。要是你手边正好有被账单困扰的项目,就拿文中的排查清单逐项核对,我敢说,至少有一半的问题当场就能找到答案。