☰
大模型API token成本怎么算?精确统计与省钱实践
2026/10/21 2:49:26 网站建设 项目流程

每天打开 API 控制台,看着一个请求动辄几千 token,几个需求迭代下来账户余额就像被开了个小口子。群里也经常有人问:“今天又烧了一百多万 token,换算成钱到底是多少?”“同一个问题多聊了几句,费用直接翻倍,正常吗?”

其实 token 消耗并不是玄学,它有一套清晰的换算逻辑。只要理解了 token 的拆解方式、模型的计费结构,再加上简单的脚本统计,你就能把“烧了多少 token”精确换算成“花了多少钱”,还能在项目里提前设置水位,避免账单失控。

这篇文章就围绕“大模型 API 的 token 成本”展开,适合正在接入大模型 API、做应用开发、或者只是好奇费用构成的朋友。读完你会掌握:token 与字符的关系、常见模型的计费模式、如何用开源库精确统计 token、如何写一个费用估算器,以及生产环境里的省钱与监控手段。

1. 背景:为什么“烧 token”成了开发者的日常焦虑

以前做 NLP 项目,成本焦虑来自 GPU 租用时长;现在接入大语言模型 API,压力变成了 token 的消耗速度。只要你的应用里有一段 prompt,每一次用户提问,底层都会把文本拆成 token 放进模型,输出时再按 token 生成结果。这个过程本质上是被精确计费的。

“每天烧 token”不是抽象概念,而是实实在在的账单。特别是做以下事情时,消耗会非常明显:

  • 聊天机器人:每轮都要携带历史上下文,用户聊得越久,单次请求的 token 越大。
  • 文档总结:把长文档整段塞进 prompt,一次可能吃掉几万 token。
  • 代码生成与重构:代码文本中空格、缩进、符号都会被拆分,token 密度和自然语言不同。
  • 数据抽取与批量处理:一次任务循环十几次、上百次 API 调用,token 总数快速累积。

如果不懂计费规则,很容易出现“功能做好了,账单也爆了”的情况。反过来,只要掌握 token 的统计方法,成本是可以提前算出来的。

2. token 是什么?按 token 计费背后的逻辑

2.1 token 与字符的区别

大多数中文开发者第一次接触 token 时,容易把它理解为“字数”或“字符数”。其实 token 是模型进行文本处理的最小单位,可以理解为“词的碎片”。一个 token 可能是一个完整的英文单词,也可能是半个词、一个标点,甚至是一个空格。

以 OpenAI 官方文档中常用的例子为例:

英文字符串Hello world, how are you?大约会被拆分为 6 个 token。

中文文本则更特殊。由于中文字符的信息密度高,通常一个字可能对应 1 到 2 个 token,甚至更多。下面是一个粗略参考:

文本类型1 个 token 大约对应
英文单词0.7 到 1 个单词
中文汉字0.5 到 1 个汉字
英文标点/空格可能单独成 1 个 token
多语言混合文本不稳定,需要实测

所以在描述 token 数时,不要简单说“这篇文章有多少字”,要说“这段文本会被 tokenizer 拆成多少个 token”。tokenizer 是决定拆分结果的关键组件。

2.2 为什么按 token 计费

大语言模型底层是通过预测 token 的概率来生成内容的。模型推理时,计算量基本和输入 token 数 + 输出 token 数成正比。也就是说,你发送多少个 token、模型回复多少个 token,直接决定了 GPU 的计算开销。

因此 API 厂商按 token 计费是合理的:

  • 输入 token 多,意味着模型需要“阅读”更多内容。
  • 输出 token 多,意味着模型需要“思考并生成”更多内容。

这也是为什么上下文长度、输出长度限制(max tokens)都和 token 紧密相关。token 既是计算单位,也是计费单位。

2.3 不同模型的 tokenizer 差异

不同模型系列使用的 tokenizer 并不完全一致。比如 OpenAI 的 GPT-4 系列和 GPT-3.5 系列使用 tiktoken 库,不同模型对应不同的编码器;Claude 系列有自己的 tokenizer;国内各大模型厂商也各自实现分词逻辑。

这意味着同样一段中文,在不同模型下统计出的 token 数量可能有差异。在做成本预估时,最好优先使用模型官方提供的 tokenizer 或计数接口,不要跨模型套用经验值。

3. 大模型 API 计费模式与价格结构解析

各家 API 的计费结构大同小异,核心是“输入价格 + 输出价格分开计算”。以下是一个演示用价格表,仅用于理解结构,真实价格请以官方控制台为准。

计费项示例价格说明
输入 token0.005 元 / 1K tokensprompt、历史记录、工具返回结果都算输入
输出 token0.015 元 / 1K tokens模型生成的回复内容
缓存命中输入0.001 元 / 1K tokens未变更的上下文缓存命中时更便宜
特殊服务按次或按资源计费比如函数调用、图片输入、批量任务

从这个结构里可以得出几个关键结论:

  • 输出 token 单价通常高于输入 token。避免模型废话、限制 max tokens,是省钱的重点。
  • 输入 token 也不是免费的。系统提示词、历史消息、工具返回结果,都会循环计费。
  • 同一模型在处理长上下文时可能存在“缓存计费”。如果的对话上下文中大部分内容没变,命中缓存的部分可能便宜一些。

3.1 输入与输出分开计费的影响

假设你设计了一个 prompt,固定系统提示词 1000 token,每次用户问题约 200 token,模型回复约 500 token。那么一次请求的计费构成是:

输入部分:1000 + 200 = 1200 token 输出部分:500 token

如果用户连续对话 10 轮,每轮都把历史消息带上去,第二轮输入就会变成:

历史第1轮 (1200 + 500) + 第2轮用户问题 200 = 1900 token

这还只是第二轮。到了第 10 轮,输入 token 会迅速膨胀到上万。这就是“越聊越贵”的根本原因。

3.2 上下文缓存的省钱逻辑

部分模型支持上下文缓存。也就是说,前面已经计算过的 prompt 如果原样出现,可以按缓存价计费。例如系统提示词很长,每次请求都带相同内容,这部分如果命中缓存,费用可以降低不少。

但缓存并不是自动对所有文本生效的,一般有前缀匹配、缓存有效期等限制。使用时需要查看对应平台的文档。

4. 如何准确估算 token 消耗

4.1 使用 tiktoken 精确统计 token

如果你想精确知道一段文本会消耗多少 token,可以使用 OpenAI 开源的tiktoken库。它是一个 Python 库,用于将文本编码为 token,官方模型 GPT 系列基本都支持。

安装方式:

pip install tiktoken

下面是一个最基础的统计示例:

# 文件路径:token_count_demo.py import tiktoken # 指定模型对应的编码器 # 新模型一般对应 "cl100k_base" encoding = tiktoken.get_encoding("cl100k_base") text = "今天 token 消耗是多少?Hello, how much will this cost?" tokens = encoding.encode(text) print("文本内容:", text) print("token 数量:", len(tokens)) print("token 列表:", tokens)

运行结果大致为:

文本内容: 今天 token 消耗是多少?Hello, how much will this cost? token 数量: 26 token 列表: [32844, 30210, ...]

如果用的是按模型名获取编码器的方式,可以这样写:

import tiktoken encoding = tiktoken.encoding_for_model("gpt-4") tokens = encoding.encode("你好,请帮我总结这段文本。") print(len(tokens))

encoding_for_model会自动根据模型名称选择合适的编码器。模型名称需要和 tiktoken 库支持列表一致,实际使用时要确认库版本和模型是否匹配。

4.2 常用文本的 token 数量参考

为了帮助你建立直觉,下面是一些常见场景的 token 估算,数值会因模型和 tokenizer 有少量浮动:

场景文本长度参考token 范围
一句话20 个汉字左右15 - 30
一个标准网页正文500 汉字300 - 600
一份会议纪要3000 汉字2000 - 4000
一份技术文档1 万汉字7000 - 15000
一个完整代码文件200 行1000 - 3000

可以看出,中文文本的 token 数并不等于汉字数,有时 1 个汉字约等于 1 个 token,但遇到英文、数字、代码符号混合时,波动范围很大。生产环境建议直接调用 tokenizer 统计,不要凭感觉预估。

4.3 估算多轮对话的总成本

如果要做成本预估,不能只看单次 prompt 的 token 数,还要把“请求次数”和“对话轮次”算进去。

多轮对话成本估算公式可以简化成:

单轮成本 = 输入token数 * 输入单价 + 输出token数 * 输出单价 总成本 = 所有轮次成本之和

其中第 N 轮输入 token 数并不是固定的,而是等于“历史消息 token + 新增用户消息 token”。下面会给出自动计算的示例。

5. 完整实战:写一个 token 费用估算器

这一节我们实现一个小工具,读取对话消息列表,统计每一轮的 token 消耗,并按照配置的单价换算成费用。

5.1 项目结构

token-cost-estimator/ ├── estimator.py # 核心估算逻辑 ├── token_counter.py # token 统计封装 ├── example_usage.py # 示例调用 └── requirements.txt # 依赖列表

5.2 安装依赖

创建requirements.txt:

tiktoken>=0.5.0

安装:

pip install -r requirements.txt

5.3 实现 token 统计封装

先建立一个统一的 token 统计模块,方便切换不同模型的编码器。

# 文件路径:token_counter.py import tiktoken class TokenCounter: def __init__(self, model="gpt-4"): # 根据模型名获取编码器,如果模型不支持,可以回退到 cl100k_base try: self.encoding = tiktoken.encoding_for_model(model) except KeyError: self.encoding = tiktoken.get_encoding("cl100k_base") def count(self, text: str) -> int: if not text: return 0 return len(self.encoding.encode(text)) def count_messages(self, messages: list) -> int: """ 计算一组对话消息的 token 总数。 messages 格式:[{"role": "user", "content": "你好"}, ...] """ total = 0 for msg in messages: # 每条消息包含角色和内容两部分 total += self.count(msg.get("role", "")) total += self.count(msg.get("content", "")) return total

这里把每条消息的role也统计进去,是因为 API 实际计费时,角色标识同样会参与 token 拆分。虽然每个角色词只占几个 token,但在多轮对话中,所有历史角色叠加后也不容忽视。

5.4 实现费用估算逻辑

接下来实现核心估算类。它负责维护“历史消息”,并在每次新增用户消息时计算本轮费用。

# 文件路径:estimator.py from token_counter import TokenCounter class CostEstimator: def __init__(self, input_price=0.005, output_price=0.015, model="gpt-4"): """ input_price: 每 1K 输入 token 的价格(演示用) output_price: 每 1K 输出 token 的价格(演示用) """ self.counter = TokenCounter(model) self.history = [] self.input_price = input_price self.output_price = output_price def add_user_message(self, content: str) -> dict: self.history.append({"role": "user", "content": content}) input_tokens = self.counter.count_messages(self.history) # 模拟模型输出,实际项目中这里应该替换成真实 API 返回结果 reply_content = f"收到,当前问题已经理解。你的问题是:{content[:20]}" self.history.append({"role": "assistant", "content": reply_content}) output_tokens = self.counter.count(reply_content) input_cost = input_tokens / 1000 * self.input_price output_cost = output_tokens / 1000 * self.output_price total_cost = input_cost + output_cost return { "input_tokens": input_tokens, "output_tokens": output_tokens, "total_tokens": input_tokens + output_tokens, "input_cost": round(input_cost, 6), "output_cost": round(output_cost, 6), "total_cost": round(total_cost, 6), } def reset(self): self.history = []

为了让示例完整,代码里用了一段模拟回复。在实际项目中,你只需要把add_user_message里的模拟回复替换成真实 API 调用,再把返回的完成文本传回即可。

5.5 运行示例与结果说明

创建一个测试入口:

# 文件路径:example_usage.py from estimator import CostEstimator # 以演示价格为例:输入 0.005 元/1K,输出 0.015 元/1K estimator = CostEstimator(input_price=0.005, output_price=0.015) questions = [ "帮我解释一下什么是大模型 token?", "那 API 是怎么按 token 收费的?", "如果我想降低费用,有什么建议?", ] for q in questions: result = estimator.add_user_message(q) print(f"用户问题:{q}") print(f" -> 输入 token:{result['input_tokens']}") print(f" -> 输出 token:{result['output_tokens']}") print(f" -> 本轮费用:{result['total_cost']} 元") print() print(f"总历史消息数:{len(estimator.history)} 条")

运行:

python example_usage.py

你会在输出中看到,随着问题数量增加,每一轮的输入 token 都在累积。比如第一轮的输入 token 只有几百,第三次可能已经超过一千。这说明多轮对话的历史上下文是成本增长的主要来源。

这个脚本本身不产生真实 API 费用,所以可以放心运行。当你接入真正 API 后,可以进一步把返回的usage.prompt_tokens和usage.completion_tokens取出来,替换掉本地的统计值,准确度会更高。

6. 成本监控与告警方案

光靠本地估算还不够,生产环境里必须有线上监控,否则哪天缓存命中率下降、上下文长度变大,账单就会悄悄上涨。

6.1 记录每次调用的 token 用量

不论使用哪个平台的 SDK,响应结果里一般都会包含用量信息。以常见 OpenAI 风格接口为例,响应中可能有usage字段:

{ "usage": { "prompt_tokens": 1200, "completion_tokens": 500, "total_tokens": 1700 } }

封装一个简单的日志记录函数:

# 文件路径:usage_logger.py import sqlite3 import datetime def init_db(db_path="usage.db"): conn = sqlite3.connect(db_path) conn.execute( """ CREATE TABLE IF NOT EXISTS api_usage ( id INTEGER PRIMARY KEY AUTOINCREMENT, request_time TEXT, model TEXT, prompt_tokens INTEGER, completion_tokens INTEGER, total_tokens INTEGER, estimated_cost REAL ) """ ) conn.commit() conn.close() def save_usage(model, prompt_tokens, completion_tokens, estimated_cost): conn = sqlite3.connect("usage.db") conn.execute( "INSERT INTO api_usage (request_time, model, prompt_tokens, completion_tokens, total_tokens, estimated_cost) VALUES (?, ?, ?, ?, ?, ?)", (datetime.datetime.now().isoformat(), model, prompt_tokens, completion_tokens, prompt_tokens + completion_tokens, estimated_cost), ) conn.commit() conn.close()

这样做的好处是,后续可以按天、按模型统计消耗趋势。

6.2 设置每日消耗告警

你可以写一个定时任务,统计当天累计 token 和费用,超过阈值就发送告警。下面是简洁版:

# 文件路径:daily_report.py import sqlite3 import datetime def daily_cost_report(): today = datetime.date.today().isoformat() conn = sqlite3.connect("usage.db") row = conn.execute( """ SELECT COUNT(*), SUM(total_tokens), SUM(estimated_cost) FROM api_usage WHERE request_time LIKE ? """, (today + "%",), ).fetchone() conn.close() count, total_tokens, total_cost = row print(f"日期:{today}") print(f"调用次数:{count}") print(f"总 token:{total_tokens or 0}") print(f"估算费用:{total_cost or 0} 元") limit = 5.0 if (total_cost or 0) > limit: print("告警:今日费用超过设定阈值!") if __name__ == "__main__": daily_cost_report()

注意:告警只是辅助手段,更重要的是在业务层限制单次请求的max_tokens,以及限制多轮对话的最大轮数。否则即使有告警,垃圾请求也可能在告警触发前就已经打完了。

6.3 可观测性数据落库

除了 token 用量,还建议记录:

  • 请求来源业务线。
  • 用户 ID 或会话 ID。
  • 系统提示词的版本号。
  • prompt 是否命中缓存。
  • 请求耗时。

这些数据能帮助你快速定位是哪个功能、哪个用户、哪个 prompt 在消耗成本。比如某天费用突然上涨,先看模型版本是不是变了,再看是不是某个会话的历史消息没有清理。

7. 常见问题与排查思路

我在实际排查成本问题时,发现下面几个问题出现频率最高。

问题现象常见原因解决思路
明明只问了一句,账单却不低请求里带了大量 system prompt 和工具结果精简系统提示词,删除无用历史工具输出
对话多轮后费用快速增长历史消息全部追加到上下文做滑动窗口,只保留最近 N 轮
同一段 prompt 重复计费很多次没有开启上下文缓存或缓存未命中调整 prompt 结构,前缀完全一致,确认缓存有效期
实际费用比本地预估多本地使用的 tokenizer 与模型不一致使用模型官方 tokenizer,或以 API 返回的 usage 为准
输出 token 超预期max_tokens 设置过大设定合理的 max_tokens,必要时做回复长度约束
并行请求过多,消耗像坐火箭没有做批量控制和并发限流增加信号量限制并发,必要时排队处理

下面展开两个典型问题的排查方法。

7.1 历史消息无限增长导致费用翻倍

现象:用户会话超过 10 轮后,请求的 prompt_tokens 已经超过 5000,费用明显上升。

排查:查看请求体里的 messages 数组长度和内容。如果每轮都原样带上全部历史,就会线性增长。

解决:对历史消息做裁剪。例如只保留最近 5 轮,或者把超过 3000 字符的历史消息先做一轮摘要再放回上下文。

def trim_history(messages, max_tokens=2000): from token_counter import TokenCounter counter = TokenCounter() kept = [] for msg in reversed(messages): msg_tokens = counter.count_messages([msg]) if sum(counter.count_messages([m]) for m in kept) + msg_tokens > max_tokens: break kept.insert(0, msg) return kept

7.2 系统提示词过长导致单次请求成本高

现象:每次请求都固定消耗 2000 token,几乎不会带来额外的回答收益。

排查:把系统提示词单独拿出来统计 token,对比用户问题 token。

解决:压缩系统提示词,把必要说明用简洁的短句表达;如果提示词必须很长,尽量保证内容稳定,以触发缓存计费。

8. 最佳实践:降低 token 消耗的工程手段

成本优化并不是把模型换成最便宜的就完事了。以下几个手段结合使用,效果更明显。

8.1 精简 prompt,去掉“寒暄”和重复说明

模型不会因为你写“你好呀,请帮我写一个项目方案,谢谢你啦”就更认真,这些字也会被计入 token。系统提示词里只保留必要规则,去掉语气词和重复强调。

8.2 控制上下文长度,优先使用滑动窗口

对聊天类应用,没必要把三个月前的对话都塞给模型。推荐策略:

  • 固定最近 N 轮完整消息。
  • 更早的消息用摘要替换。
  • 超长文档先检索相关片段,而不是全文塞入。

8.3 合理设置 max_tokens

max_tokens控制的是输出 token 上限,而不是“一定会输出这么多”。但设置过大会在异常生成时产生多余成本。常见做法是设置为期望回复长度的 1.2 到 1.5 倍。

8.4 使用上下文缓存

如果系统提示词很长且很少变化,先确认 API 平台是否支持缓存。使用缓存时,prompt 的前缀必须保持稳定。不要在每个请求前面拼接动态时间戳、随机内容,这会破坏缓存命中。

8.5 批处理与并发控制

批量任务不要一次性全部并发请求。一方面容易触发速率限制,另一方面一旦中间有问题,无法及时熔断。推荐用生产者消费者模式,控制同时进行的请求数量。

8.6 对模型输出做结构化约束

让模型输出 JSON 时,使用函数调用或结构化输出格式,比让模型自由发挥更容易控制长度。这一点对降低输出 token 成本很有效。

8.7 按场景选择模型

不是所有任务都需要最强模型。简单分类任务、关键词抽取,使用轻量模型足以完成;复杂推理、长文档深度分析,才使用更强的模型。把请求按难度分发到不同模型,整体成本会下降很多。

8.8 记录成本和质量的对应关系

成本优化不能只盯着数字。推荐建立一个评估集,每隔一段时间对比模型输出质量。如果换了一个便宜模型后,业务指标明显下滑,那么省钱是得不偿失的。

9. 总结与后续建议

回到开头的问题:“每天烧 token,我需要花多少钱?”其实它不再是模糊的疑问,而是可以被回答的工程问题。

这个回答由三部分组成:

  1. 把业务文本交给官方 tokenizer 统计 token。
  2. 根据 API 的输入/输出单价算出每次调用的费用。
  3. 记录线上用量,观察每天、每周的趋势,提前设置告警。

在此基础上,通过精简 prompt、限制历史上下文、合理使用缓存、按场景分流模型,可以在不牺牲效果的前提下把费用降下来。

我建议你下周就做一次“成本体检”:打开你的 API 日志或数据库,按模型、按功能统计 token 排名前 10 的请求,看看是哪些场景在消耗大头。很多项目的优化点不是模型选错了,而是某个循环里重复携带了整段长文档。

下一步可以继续研究:多轮对话的摘要压缩算法、RAG 场景下的检索片段截取策略、以及大模型 API 网关层面的统一限流。成本控制是一项长期工程,养成每周查看用量明细的习惯,比等账单出来再吃惊要划算得多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询