我用 Python 做了一个 Token 计算器:一段文字到底消耗多少 Token?

前面我们介绍过:
大语言模型并不是直接处理我们看到的文字,而是先把文本切分成 Token。
理解这个概念之后,很快就会遇到一个实际问题:
我写了这么多内容,到底会消耗多少 Token?
比如这段 Prompt:
text
帮我写一篇关于 Java 并发编程的文章......
它究竟是 100 Token、500 Token,还是 1000 Token?
开发 AI 应用时,我们还会关心:
- 一次请求使用了多少 Token?
- 一篇 PDF 大约有多少 Token?
- Prompt 是否超过了模型限制?
- 为什么相同长度的中文和英文,Token 数量不同?
- RAG 为什么容易消耗大量 Token?
- Context Window 到底能容纳多少内容?
- API 返回的 Token 使用量应该怎么看?
这次我们不用复杂框架,只用 Python 做一个简单的 Token 计算器,实际看看文本是如何被切分的,并了解如何在调用大模型 API 时统计真实的 Token 使用量。
先看最终效果

我们希望程序能够接收一段文本,并输出字符数、Token 数量以及两者的比例:
text
====== Token Calculator ======
请输入文本:你好,我正在学习大语言模型。
字符数:16
Token 数:...
Token / 字符:...
你可以分别输入中文、英文和代码:
text
Hello, how are you?
你好,今天过得怎么样?
def hello():
print("Hello World")
通过对比可以直观看到:
不同文本的字符数和 Token 数量,并不是简单的一一对应关系。
Token 数量取决于 Tokenizer
开始写代码之前,需要先明确一点:
Token 没有一种适用于所有模型的统一计算方式。
不能简单地认为:
text
一个汉字 = 一个 Token
一个英文单词 = 一个 Token
实际情况是,不同模型可能使用不同的 Tokenizer 和词表。同一段文本交给不同的 Tokenizer,得到的 Token 数量可能并不相同。
例如:
text
你好,世界
使用不同的 Tokenizer,结果可能是:
text
Tokenizer A → 结果 A
Tokenizer B → 结果 B
Tokenizer C → 结果 C
因此,计算 Token 时,最好使用与目标模型匹配的 Tokenizer。
这次使用的是 tiktoken。它可以把文本编码成 Token ID 序列,我们再通过序列长度得到 Token 数量:
text
文本
↓
Tokenizer
↓
Token IDs
↓
统计数量
需要注意的是,tiktoken 主要用于 OpenAI 系列模型常见编码方式的本地 Token 统计。如果你调用的是其他服务商或其他模型,应该优先查看对应服务商提供的 Tokenizer 或 Token 计算工具。
安装 tiktoken
打开终端,执行:
bash
pip install tiktoken
如果电脑上安装了多个 Python,也可以使用:
bash
python3 -m pip install tiktoken
安装完成后,进入 Python 环境测试:
python
import tiktoken
print("tiktoken 安装成功")
如果没有报错,说明环境已经准备好了。
先写一个最小版本
新建文件:
text
token_calculator.py
写入以下代码:
python
import tiktoken
text = "你好,世界!"
encoding = tiktoken.get_encoding("cl100k_base")
tokens = encoding.encode(text)
print("原始文本:", text)
print("Token 数量:", len(tokens))
print("Token IDs:", tokens)
运行:
bash
python token_calculator.py
你会看到类似这样的输出:
text
原始文本: 你好,世界!
Token 数量: ...
Token IDs: [...]
核心代码只有两行:
python
tokens = encoding.encode(text)
这一步把文本转换成 Token ID 序列。
python
len(tokens)
这一步统计序列长度,也就是 Token 数量。
Token ID 是什么?
如果打印:
python
print(tokens)
可能会得到:
text
[57668, 53901, 3922, ...]
这些数字就是 Token ID。模型内部处理的并不是我们看到的汉字和单词,而是经过编码后的数字序列。
整个过程可以简单表示为:
text
你好,世界!
↓
Tokenizer
↓
[Token ID, Token ID, Token ID, ...]
↓
Embedding
↓
向量
↓
Transformer
这里的 Token ID 只是文本进入模型前的表示。后续模型还会把这些 ID 映射成向量,再交给 Transformer 处理。
看看文本是如何被切分的
我们可以把每个 Token 单独解码出来:
python
import tiktoken
encoding = tiktoken.get_encoding("cl100k_base")
text = "Hello, 世界!"
tokens = encoding.encode(text)
print("Token IDs:")
print(tokens)
print("\n逐个 Token 查看:")
for token in tokens:
print(token, repr(encoding.decode([token])))
输出结果会因版本和编码方式而不同,但你通常会看到:
- 某些 Token 对应完整单词;
- 某些 Token 只是单词的一部分;
- 标点、空格和特殊字符也可能单独占用 Token;
- 中文字符可能按照不同的片段进行切分。
所以:
Token 不是单词,也不是汉字,更不是字符。
更准确地说,Token 是 Tokenizer 根据模型词表和编码规则得到的离散文本单元。
比较中文、英文和代码
把文本分别替换成下面几组内容:
python
text = "你好,今天过得怎么样?"
text = "Hello, how are you today?"
text = """
def hello():
print("Hello World")
"""
然后分别运行:
python
tokens = encoding.encode(text)
print("字符数:", len(text))
print("Token 数:", len(tokens))
你会发现,字符数和 Token 数量之间没有固定换算关系。
这是因为 Tokenizer 会根据词表和编码规则切分文本。中文、英文、数字、标点、代码、URL 等内容,都可能产生不同的切分结果。
因此,下面这种估算:
text
Token 数量 = 字符数 ÷ 4
最多只能用于粗略判断,不能替代实际计算。
把程序改成可交互版本
刚才的代码只能计算固定文本。接下来让用户自己输入内容:
python
import tiktoken
encoding = tiktoken.get_encoding("cl100k_base")
print("====== Token Calculator ======")
text = input("请输入文本:")
tokens = encoding.encode(text)
print()
print("文本:", text)
print("字符数:", len(text))
print("Token 数:", len(tokens))
运行:
bash
python token_calculator.py
输入:
text
你好,我正在学习大语言模型。
程序就会输出对应的字符数和 Token 数量。
再增加一个比例指标:
python
import tiktoken
encoding = tiktoken.get_encoding("cl100k_base")
print("====== Token Calculator ======")
text = input("请输入文本:")
tokens = encoding.encode(text)
char_count = len(text)
token_count = len(tokens)
print()
print("字符数:", char_count)
print("Token 数:", token_count)
if char_count > 0:
ratio = token_count / char_count
print("Token / 字符:", round(ratio, 2))
例如:
text
字符数:100
Token 数:80
Token / 字符:0.8
这个比例只适用于当前文本和当前 Tokenizer,不能当成所有模型通用的换算公式。
使用 API 时,用户应该关注哪些 Token?
本地计算可以帮助我们提前估算,但真正调用大模型 API 时,还需要关注 API 返回的使用量。
一次聊天请求通常包含:
text
输入内容
+
系统提示词
+
历史消息
+
工具定义
+
模型输出
因此,API 中常见的 Token 指标包括:
text
Input Tokens
Output Tokens
Total Tokens
它们可以理解为:
text
Input Tokens:发送给模型的内容消耗的 Token
Output Tokens:模型生成内容消耗的 Token
Total Tokens:输入 Token + 输出 Token
例如:
text
Input Tokens = 2,000
Output Tokens = 1,000
Total Tokens = 3,000
这次请求总共使用了 3,000 Token。
不过,不同服务商返回的字段名称可能不同。有些 API 会返回:
json
{
"usage": {
"prompt_tokens": 2000,
"completion_tokens": 1000,
"total_tokens": 3000
}
}
也有一些 API 会使用:
json
{
"usage": {
"input_tokens": 2000,
"output_tokens": 1000,
"total_tokens": 3000
}
}
所以,实际开发时应该以当前 API 文档中的字段定义为准。
使用 OpenAI API 查看 Token 使用量
下面以 OpenAI Python SDK 为例,演示如何调用 API 并读取 usage 信息。
先安装 SDK:
bash
pip install openai
设置 API Key:
bash
export OPENAI_API_KEY="你的 API Key"
Windows PowerShell 可以使用:
powershell
$env:OPENAI_API_KEY="你的 API Key"
然后编写代码:
python
from openai import OpenAI
client = OpenAI()
response = client.responses.create(
model="gpt-4.1-mini",
input="请用一句话解释什么是 Token。"
)
print(response.output_text)
print("\nToken 使用情况:")
print(response.usage)
如果当前 SDK 和接口返回了使用量信息,通常可以看到类似:
text
Token 使用情况:
input_tokens=...
output_tokens=...
total_tokens=...
也可以分别读取:
python
usage = response.usage
print("输入 Token:", usage.input_tokens)
print("输出 Token:", usage.output_tokens)
print("总 Token:", usage.total_tokens)
不同版本的 SDK、不同接口以及不同服务商的字段可能存在差异。如果某个字段不存在,应以实际返回结果和官方文档为准。
使用 Chat Completions API 查看 Token 使用量
一些项目仍然使用 Chat Completions 接口,代码大致如下:
python
from openai import OpenAI
client = OpenAI()
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{
"role": "user",
"content": "请用一句话解释什么是 Token。"
}
]
)
print(response.choices[0].message.content)
usage = response.usage
print("\nToken 使用情况:")
print("输入 Token:", usage.prompt_tokens)
print("输出 Token:", usage.completion_tokens)
print("总 Token:", usage.total_tokens)
这里的字段通常是:
text
prompt_tokens
completion_tokens
total_tokens
可以简单对应为:
text
prompt_tokens → 输入 Token
completion_tokens → 输出 Token
total_tokens → 总 Token
需要注意:
不要只统计用户输入的文本,然后把它当成 API 的完整输入 Token。
实际请求中还可能包含:
- system message;
- 多轮历史消息;
- 工具定义;
- JSON Schema;
- 图片或其他多模态内容;
- SDK 或服务商额外处理的消息结构。
因此,本地使用 tiktoken 计算用户文本,通常只是一个估算值;最终使用量应以 API 返回的 usage 为准。
用 API 返回的 Token 估算成本
一次请求的成本通常可以按照输入和输出分别计算:
text
输入成本 = Input Tokens ÷ 1,000,000 × 输入单价
输出成本 = Output Tokens ÷ 1,000,000 × 输出单价
总成本 = 输入成本 + 输出成本
例如,假设某个模型的价格是:
text
输入价格:每 1,000,000 Token 收费 1 美元
输出价格:每 1,000,000 Token 收费 2 美元
一次请求使用:
text
Input Tokens = 2,000
Output Tokens = 1,000
那么:
text
输入成本 = 2,000 ÷ 1,000,000 × 1
输出成本 = 1,000 ÷ 1,000,000 × 2
总成本 = 输入成本 + 输出成本
实际价格会根据模型、区域、缓存策略、批处理方式和服务商规则变化,因此不要把示例价格当成当前真实价格。
可以写一个简单的成本计算函数:
python
def calculate_cost(
input_tokens,
output_tokens,
input_price_per_million,
output_price_per_million
):
input_cost = input_tokens / 1_000_000 * input_price_per_million
output_cost = output_tokens / 1_000_000 * output_price_per_million
return input_cost + output_cost
cost = calculate_cost(
input_tokens=2000,
output_tokens=1000,
input_price_per_million=1,
output_price_per_million=2
)
print(f"预计成本:${cost:.6f}")
这个函数适合用于开发阶段的粗略估算。生产环境中,还应该记录每次请求的模型名称、输入 Token、输出 Token、请求时间和实际计费信息。
Token 计算在开发中有什么用?
Token 计算看起来只是统计数字,但它会直接影响 AI 应用中的几个关键问题:
text
上下文长度
推理成本
响应速度
RAG 数据量
Prompt 设计
API 使用量
控制 Prompt 长度
假设一个 Prompt 包含大量背景说明、格式要求和示例:
text
你是一名专业的 Java 技术专家......
......
......
如果每次请求都发送几千甚至上万 Token,就可能增加请求成本,也可能挤占模型的上下文空间。
发送前可以先做一次检查:
text
Prompt
↓
本地计算 Token
↓
判断是否超过限制
↓
压缩、截断或重新组织
↓
调用 API
↓
读取 usage 进行校验
分析 RAG 的上下文
RAG 通常会把检索到的文本放入 Prompt:
text
用户问题
+
检索结果
+
系统提示
+
历史对话
这些内容都会占用上下文空间。
如果检索结果过多,可能出现两个问题:
- 请求超过模型的上下文限制;
- Prompt 变长后,成本和响应时间增加。
因此,RAG 不只是"搜到内容就全部塞给模型",还需要控制 Chunk 数量、Chunk 长度和最终上下文大小。
估算 API 使用成本
一次请求通常可以拆成:
text
Input Tokens
+
Output Tokens
例如:
text
Input = 2,000 Tokens
Output = 1,000 Tokens
这次请求总共使用:
text
3,000 Tokens
实际费用还要根据具体模型、输入价格、输出价格以及服务商当前的计费规则计算。不同模型的价格并不相同,不能用一个固定数字套用所有服务。
分析 Agent 的上下文增长
Agent 往往需要多次调用模型和工具:
text
用户任务
↓
模型规划
↓
调用工具
↓
工具返回结果
↓
继续判断
↓
调用另一个工具
↓
再次返回结果
如果每一步的结果都加入上下文,后续请求可能越来越长:
text
第一次:2,000 Tokens
第二次:5,000 Tokens
第三次:8,000 Tokens
所以在 Agent 应用中,历史消息、工具结果和中间过程都需要合理管理。必要时可以采用摘要、截断、压缩或只保留关键结果等方式。
完整版本
下面是一个可以循环使用的版本:
python
import tiktoken
def main():
print("=" * 40)
print(" Token Calculator")
print("=" * 40)
encoding = tiktoken.get_encoding("cl100k_base")
while True:
text = input("\n请输入文本(输入 q 退出):")
if text.lower() == "q":
break
tokens = encoding.encode(text)
char_count = len(text)
token_count = len(tokens)
print("\n------ 计算结果 ------")
print(f"字符数:{char_count}")
print(f"Token 数:{token_count}")
if char_count > 0:
ratio = token_count / char_count
print(f"Token / 字符:{ratio:.2f}")
print("----------------------")
if __name__ == "__main__":
main()
运行:
bash
python token_calculator.py
一个最基础的 Token 计算器就完成了。
增加 API 调用和 usage 统计
如果希望把本地 Token 计算和 API 实际使用量放在一起,可以写一个简单示例:
python
import tiktoken
from openai import OpenAI
def main():
encoding = tiktoken.get_encoding("cl100k_base")
client = OpenAI()
text = input("请输入问题:")
estimated_tokens = len(encoding.encode(text))
print(f"\n本地估算输入 Token:{estimated_tokens}")
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{
"role": "user",
"content": text
}
]
)
print("\n模型回答:")
print(response.choices[0].message.content)
usage = response.usage
print("\nAPI 实际使用量:")
print("输入 Token:", usage.prompt_tokens)
print("输出 Token:", usage.completion_tokens)
print("总 Token:", usage.total_tokens)
if __name__ == "__main__":
main()
这个例子可以帮助我们对比:
text
本地 Token 估算
↓
调用 API
↓
API 返回实际 usage
↓
比较两者差异
两者不一定完全相同,因为 API 的输入可能包含系统消息、消息格式、工具定义或其他额外内容。
如果使用的是其他模型服务商,也可以采用相同思路:
text
构造请求
↓
调用 API
↓
读取响应中的 usage 字段
↓
记录 input / output / total tokens
但具体的 SDK、字段名称和返回结构,需要以对应服务商的官方文档为准。
这个工具还能怎么扩展?
当前版本只能处理手动输入的文本,后续可以继续增加功能:
text
V1:统计输入文本的 Token 数量
V2:展示每个 Token 的切分结果
V3:支持多种 Tokenizer
V4:读取 TXT、Markdown 和代码文件
V5:解析 PDF 并统计 Token
V6:分析 RAG 检索结果的上下文长度
V7:根据模型价格估算调用成本
V8:调用 API 并记录真实 usage
V9:统计不同用户、不同模型的 Token 消耗
V10:生成 Token 使用报表
如果做成网页,还可以让用户直接粘贴文本或上传文件,不需要安装 Python。
一个简单的网页版本可以包含:
text
文本输入框
字符数
Token 数量
Token / 字符比例
Token 详细拆分
模型选择
输入价格
输出价格
预计成本
进一步还可以做成一个小型工具箱:
text
AI Token Toolkit
├── Token Calculator
├── Prompt Analyzer
├── Context Analyzer
├── RAG Token Analyzer
├── Cost Calculator
├── API Usage Monitor
└── Token Visualizer
不过需要注意,网页端如果想准确使用某个模型的 Tokenizer,通常需要考虑运行环境和对应库的支持,不能只在页面上用字符数做简单估算。
一个容易忽略的问题
如果准备把这个工具用于实际项目,不要默认 cl100k_base 能准确代表所有模型。
不同模型可能使用不同的 Tokenizer:
text
模型 A → Tokenizer A
模型 B → Tokenizer B
模型 C → Tokenizer C
即使输入完全相同:
text
你好,世界
得到的 Token 数量也可能不同。
另外,API 实际统计的 Token 数量还可能受到以下因素影响:
- system message;
- 多轮历史消息;
- 工具定义;
- 函数参数 Schema;
- 多模态输入;
- 服务商对消息格式的内部处理;
- 缓存或批处理相关规则。
因此:
本地 Tokenizer 的结果适合用于估算、分析和开发辅助;最终使用量应以 API 返回的 usage 数据为准。
还要注意,tiktoken 只能帮助你计算文本 Token,不能替代 API 的完整计费统计。真正做成本监控时,应该保存每次请求的:
text
用户 ID
请求时间
模型名称
输入 Token
输出 Token
总 Token
请求状态
错误信息
这样才能进一步分析:
text
哪个用户消耗最多?
哪个接口成本最高?
哪个 Prompt 最浪费 Token?
哪个 Agent 步骤上下文增长最快?
写在最后
做完这个小工具之后,Token 不再只是一个抽象概念,而是变成了一组可以实际观察的数据:
text
一段文字
↓
Tokenizer
↓
Token ID
↓
Token 数量
调用 API 时,还可以继续观察:
text
请求内容
↓
API
↓
Input Tokens
↓
模型生成
↓
Output Tokens
↓
Total Tokens
它还会继续影响后面的 Embedding、Transformer、Context、RAG 和 Agent。
学习 AI 时,很多概念只看解释很容易忘记。真正写一次代码,看到文本被编码成 Token ID,再调用 API 查看真实的 usage 数据,理解会更扎实。
这也是这个小项目的价值:
不只是知道 Token 是什么,而是亲手把它计算出来,并知道一次 API 请求到底消耗了多少 Token。
下一步可以继续做一个 PDF Token 分析器,看看一份 PDF 经过文本解析后到底包含多少 Token,再进一步进入 RAG:大模型如何使用外部知识库。