一个你早晚要搞懂的概念
如果你在用大模型——不管是在线聊天还是调用 API——你一定会反复遇到一个词:Token。
「这个模型支持 128K token 的上下文」
「API 按 token 数量计费」
「你的对话已经超出了 token 上限」
但 token 到底是什么?它和「字」有什么区别?为什么不直接用字数来衡量?
这篇文章就把这件事给你讲透。不需要任何技术背景,只要你认识字就行。
Token 不是字,也不是词
先说结论:Token 是大语言模型处理文本时使用的最小单位,它既不等于一个字,也不等于一个词,而是介于两者之间的某种东西。
举个例子,英文句子 “Hello, world!” 在不同的 tokenizer 下可能被切成:
1 | ["Hello", ",", " world", "!"] → 4 个 token |
而中文句子「你好世界」可能被切成:
1 | ["你", "好", "世界"] → 3 个 token |
或者:
1 | ["你好", "世界"] → 2 个 token |
具体怎么切,取决于使用的 tokenizer(分词器)。不同的模型可能使用不同的 tokenizer,所以同一段文本在不同模型下的 token 数量可能不同。
Tokenizer 是怎么工作的
为什么不能直接用字符
你可能会想:为什么不直接把每个字符当作一个处理单位呢?
原因很简单:效率太低。
英文有26个字母。如果每个字母是一个处理单位,模型在处理一个长单词(比如 “understanding”)时需要逐个字母去理解。这就像让你一个字母一个字母地阅读英文,效率很差。
反过来,如果每个完整的单词是一个处理单位呢?问题是英语中有超过几十万个不同的单词(加上各种变形、专有名词),词表太大了。而且你没见过的新词怎么办?
Token 是一种折中方案:把文本切成比字符大、比完整单词小(或等于完整单词)的片段。常用的词保留为完整的 token,不常用的词被切成更小的子片段。
BPE:最主流的分词方法
目前大语言模型最常用的分词方法叫 BPE(Byte Pair Encoding,字节对编码)。
BPE 的训练过程是这样的:
- 从单个字符(或字节)开始
- 统计所有相邻片段对的出现频率
- 把出现频率最高的一对合并成新的 token
- 重复步骤2和3,直到词表达到预设大小
举个简化的例子,假设训练数据中 “th” 这个组合出现了1万次,那 BPE 就会把 “t” 和 “h” 合并成一个新 token “th”。然后可能 “the” 出现了8千次,就把 “th” 和 “e” 合并成 “the”。
这样做的结果是:
- 常用的词(如 “the”, “is”, “are”)会被保留为完整的 token
- 不常用的词会被切成更小的子片段
- 全新的词(模型没见过的)也能被表示——虽然可能需要更多的 token
中文的 tokenization
中文的处理和英文不太一样。中文没有空格分隔,一个汉字本身就是一个意义单位。
在早期的 tokenizer 中(比如 GPT-2 的),中文字符经常被切得很碎——一个汉字可能需要 2-3 个 token 来表示。这导致中文在 token 消耗上非常吃亏:同样的内容,中文需要的 token 数量远多于英文。
后来的 tokenizer(比如 GPT-4、Llama 3、Qwen 使用的)对中文做了专门的优化,很多常用汉字和词组被加入了词表,一个汉字通常只需要 1 个 token,常用词组甚至可以被整体编码为 1 个 token。
一个粗略的估算:在现代 tokenizer 下,1个中文字大约等于 1-1.5 个 token,1个英文单词大约等于 1-2 个 token。
上下文窗口是什么
搞清楚了 token 的概念,接下来说一个和它直接相关的概念:上下文窗口(Context Window)。
定义
上下文窗口就是模型一次能处理的最大 token 数量。你可以把它理解为模型的「工作记忆容量」。
如果一个模型的上下文窗口是 4K(4096 个 token),那它一次最多能看到大约 4096 个 token 的内容。这包括了你的所有输入(system prompt + 对话历史 + 当前问题)和模型的输出。
常见的上下文窗口大小
| 大小 | 大约相当于 | 典型代表 |
|---|---|---|
| 2K (2048) | 约1500中文字 | 早期模型(GPT-2) |
| 4K (4096) | 约3000中文字 | GPT-3.5 初版、Llama 1 |
| 8K (8192) | 约6000中文字 | GPT-4 初版 |
| 32K (32768) | 约24000中文字 | 一些中期模型 |
| 128K (131072) | 约10万中文字 | Llama 3.1、Hermes 3、GPT-4 Turbo |
| 200K+ | 约15万+中文字 | Claude 3、Gemini 1.5 |
10万中文字是什么概念?大概是一本中等篇幅的小说。128K 上下文意味着你可以把整本小说喂给模型,让它理解内容后回答问题。
上下文窗口的实际影响
上下文太短的时候:
- 长对话聊到后面,模型会「忘记」前面的内容(因为早期的对话已经被截断了)
- 无法处理长文档
- 复杂的多轮任务容易中途断线
上下文够长的时候:
- 可以维持很长的连贯对话
- 可以分析整篇论文、整个代码文件
- 复杂任务的完成率更高
但注意:上下文窗口越大不一定越好。更大的上下文窗口意味着更多的计算量和更高的延迟。如果你的任务只需要几百个 token 的上下文,128K 的窗口就是浪费。
Hermes 各版本的上下文长度
把 Hermes 各版本的上下文窗口整理一下:
| 版本 | 上下文窗口 | 说明 |
|---|---|---|
| Hermes 1 (Llama 1/2) | 2K-4K | 早期限制,现在已经不够用了 |
| Hermes 2 (Mistral 7B) | 8K-32K | 有了明显提升 |
| Hermes 2 Pro | 8K-32K | 和 Hermes 2 相当 |
| Hermes 3 (Llama 3.1) | 128K | 大幅跃升,基本够用了 |
| Hermes 4/4.3 | 128K+ | 保持高水位 |
从 Hermes 3 开始,128K 的上下文窗口已经能满足绝大多数使用场景。如果你想了解 Hermes 各版本的更多差异,可以参考 Hermes 模型版本怎么看 这篇。
Token 和 API 计费
如果你通过 API 调用大模型(不管是 OpenAI、Anthropic 还是 OpenRouter 上的 Hermes),费用通常是按 token 数量来计算的。
输入 token 和输出 token
API 计费一般区分两种 token:
- 输入 token(Input / Prompt tokens):你发送给模型的内容。包括 system prompt、对话历史、当前问题等。
- 输出 token(Output / Completion tokens):模型生成的回复内容。
通常输出 token 比输入 token 贵。原因是生成输出需要更多的计算量(每个 output token 都需要一次完整的模型前向传播)。
典型的价格量级
不同模型和平台的定价差异很大,但给你一个大致的量级感知:
| 模型类型 | 输入价格 (每百万token) | 输出价格 (每百万token) |
|---|---|---|
| 小模型 (7B-8B) | $0.03 - $0.10 | $0.05 - $0.15 |
| 中等模型 (70B) | $0.50 - $1.00 | $0.75 - $1.50 |
| 大模型 (405B+) | $2.00 - $5.00 | $3.00 - $10.00 |
Hermes 通过 OpenRouter 调用的话,因为是开源模型,通常价格会比闭源商业模型便宜不少。
怎么估算你的 token 消耗
几个实用的估算方法:
方法1:粗略估算
- 英文:1 个 token ≈ 4 个字符 ≈ 0.75 个单词
- 中文:1 个 token ≈ 1-1.5 个汉字
方法2:在线工具
很多平台提供 token 计算工具,比如 OpenAI 的 Tokenizer 页面,可以粘贴文本直接查看 token 数量。
方法3:代码计算
1 | from transformers import AutoTokenizer |
省 token 的技巧
如果你在意 API 费用,可以注意以下几点:
1. 精简 system prompt
system prompt 在每次 API 调用中都会被发送,所以一个冗长的 system prompt 会在每次请求中都消耗 token。保持 system prompt 简洁精准。
2. 控制对话历史长度
多轮对话中,整个对话历史都会作为输入发送。如果对话很长,可以考虑只保留最近的 N 轮对话,或者做摘要压缩。
3. 设置 max_tokens 限制
在 API 调用中设置输出的最大 token 数量,防止模型生成过长的回复。
4. 选择合适大小的模型
不是每个任务都需要用最大最贵的模型。简单的问答用 8B 模型就够了,没必要上 405B。
关于 token 的几个常见误解
误解1:token 数量 = 字数
前面已经说了,token 和字数不是一回事。特别是中文,一个 token 的对应关系比较复杂。不要简单地用字数来估算 token。
误解2:上下文窗口越大越好
更大的上下文窗口确实提供了更多的灵活性,但也有代价:
- 计算量增加:处理 128K token 比处理 4K token 需要多得多的计算资源
- 延迟增大:上下文越长,模型生成第一个 token 的时间越长
- 注意力稀释:一些研究表明,当上下文太长时,模型对中间部分内容的注意力会下降(所谓的 “Lost in the Middle” 问题)
实际使用中,根据任务需要选择合适的上下文长度就好。
误解3:token 数量相同 = 内容相同
两段不同的文本可能碰巧有相同数量的 token,但内容完全不同。token 数量只是一个长度度量,不包含语义信息。
误解4:所有模型的 tokenizer 都一样
不同模型家族使用不同的 tokenizer。Llama 系列的 tokenizer、Qwen 的 tokenizer、GPT 系列的 tokenizer 各不相同。同一段文本在不同 tokenizer 下的 token 数量可能有不小的差异。
这在实际中意味着:你不能用一个模型的 tokenizer 来估算另一个模型的 token 消耗。
特殊 token
除了表示文本内容的普通 token 之外,还有一些特殊 token,用来标记特殊的含义。
常见的特殊 token:
<|im_start|>和<|im_end|>:ChatML 格式中的消息起止标记。关于 ChatML 的详细说明,可以看 ChatML 是什么 那篇。<s>和</s>:序列的开始和结束标记<|endoftext|>:文本结束标记<pad>:填充标记,用于将不等长的序列对齐
这些特殊 token 也占用上下文窗口的空间,虽然数量很少,但在你精确计算 token 消耗时需要考虑在内。
和 Hermes 使用相关的实际建议
本地使用时
如果你用 Ollama 之类的工具在本地跑 Hermes,token 本身不花钱(你已经在用自己的硬件了)。但上下文窗口的大小仍然有影响:
- 上下文越长,推理越慢
- 上下文越长,需要的显存越多
- 8B 模型在 4K 上下文下很流畅,128K 上下文下会明显变慢
所以本地使用时,根据实际需要调整上下文窗口的大小是有意义的。大多数工具都允许你设置最大上下文长度。
API 调用时
通过 OpenRouter 或其他平台调用 Hermes API 时,费用直接和 token 挂钩。按照上面说的省 token 技巧来优化就好。
在 cocoloop 社区有开发者分享过他们的 API 成本优化经验——有人通过精简 system prompt 和合理截断对话历史,把每月的 API 费用降低了 40% 以上。这些实战经验很有参考价值。
写在最后
Token 的概念其实并不复杂,但它在大模型的使用中无处不在。搞懂它,你就:
- 理解了上下文窗口的含义和限制
- 能估算 API 调用的费用
- 知道怎么优化 token 消耗
- 不会在看到「128K context」之类的说法时一头雾水
这些都是日常使用大模型的基本功。不用一次记住所有细节,但核心概念——token 是模型处理文本的最小单位,上下文窗口是模型的工作记忆容量——这两点记住就够应付大多数场景了。