Token 是什么?为什么 AI 按它收费?
定义
Token(词元)是 tokenizer 把文本编码后交给模型的编号单位。一个 Token 可能是完整单词、汉字组合、词的一部分、带前导空格的片段、标点,甚至只是某个字符的一段 UTF-8 字节;因此 Token 数与字数、字符数都不是一一对应关系。
先搭一个心智模型
可以把 tokenizer 想成模型随身携带的一盒“拼字积木”。盒子里既有常见大块,也有字母、标点和字节这样的小块。遇到熟悉片段时,它直接拿一块;遇到少见词或混合格式时,只能拆成更多小块。不同模型携带的积木盒不同,所以同一句话不一定拆出相同数量。
说明
编码通常经历四步:程序拿到 Unicode 文本,将它表示为 UTF-8 字节;tokenizer 再按已训练好的词表和合并规则寻找片段;每个片段映射成一个整数 ID;模型最后读取这些 ID 对应的向量。模型并不是直接看见人类眼中的“词”和“字”。
BPE(Byte Pair Encoding)及其变体会在训练语料中统计经常相邻的小单元,并把常见组合逐步加入词表。编码时,常见片段可以合成较大的 Token;少见人名、代码、连续数字、表情或中英文混排更容易退回到较小片段。这里的“大块”来自既定词表,并不是按中文语法或英文单词边界现场分词。
空格也参与编码。例如 hello 与 hello 在一些词表里都可能各占 1 个 Token,但会对应不同的 Token ID,因为后者把前导空格包含在片段中。另一些切分结果会出现无法单独显示的字节片段;只有把相邻 Token 的字节重新拼起来,才会还原成完整字符。
切分结果取决于具体 encoding。下面的实例是本项目编辑时使用官方 tiktoken 0.14.0 得到的本地结果:o200k_base 把 你好,世界! 切成 4 个 Token,而 cl100k_base 对同一句话得到 7 个 Token。这个差异说明“一个汉字等于一个 Token”不是可靠规则,也不能把某个示例外推到所有模型。
聊天请求里还有用户看不见的结构,例如消息角色、系统提示、工具定义和特殊分隔符,它们同样可能占用上下文。因此本地切分适合预估正文,最终用量应以目标模型的官方 tokenizer、计数接口或 API 返回的 usage 为准。
上下文窗口、输入输出限制和多数 API 计费都以 Token 为单位。账单还可能区分输入、输出、缓存读写或推理相关用量;分类和价格由具体 Provider 与模型决定,不能把所有内部计算统一叫作“隐藏 Token”。
跟着一个例子走
同一句话怎样被拆成 Token
以下是本项目编辑时用官方 tiktoken 0.14.0 得到的本地结果,只用来观察指定 encoding 的切分。可用 tiktoken.get_encoding(...) 与 encode(...) 重算数量,再用 decode_single_token_bytes(...) 查看每个 Token 的原始字节。方括号里的每一项代表 1 个 Token。
- 英文与前导空格
cl100k_base将tiktoken is great!切为["t", "ik", "token", " is", " great", "!"],共 6 个 Token。可以看到空格被并入后面的is和great,而不是单独成块。 - 较新的中文词表
o200k_base将你好,世界!切为["你好", ",", "世界", "!"],共 4 个 Token。高频的你好与世界各自进入词表后,可以作为较大的块。 - 换一个 encoding
同一句
你好,世界!换成cl100k_base会得到 7 个 Token,其中部分 Token 只是 UTF-8 字节片段,不能单独显示成完整汉字。文字没有变,词表与合并规则变了,数量就变了。
边界与误区
Token 不是词: 它是模型词表中的编号单位,边界不必符合中文分词或英文单词边界。
Token 数不是固定常数: 模型、版本、encoding、语言、空白和字符组合都会改变切分结果。
可见正文不是完整请求: 系统提示、角色标记、工具定义和输出预留也会占用上下文。
估算不能代替实测: 做限长、截断或成本控制时,应在真实请求链路上统计,并以 Provider 返回的 usage 核对。
来源与核验
- [openai] OpenAI Cookbook:使用 tiktoken 统计 Token
- [tiktoken] OpenAI:tiktoken 官方仓库
- [anthropic] Anthropic:Token counting
核验日期:2026-08-31