引言:Token——AI 世界的通用货币
在与 ChatGPT、Claude 或任何大语言模型(LLM)交互时,你可能已经注意到一个无处不在的概念:Token。API 调用按 Token 计费,模型有上下文长度限制(如 128K tokens),甚至你的输入和输出都会被拆分成一串 Token ID。
但 Token 究竟是什么?它仅仅是“单词”吗?为什么一个简单的标点符号也要收费?当你的敏感数据被转换成 Token 发送给云端模型时,隐私如何保障?
本文将摒弃浅显的比喻,深入到 Hugging Face Transformers 库、OpenAI API 规范、以及前沿的隐私计算技术中,为你揭示 Token 背后的完整技术图景。

Token 并非天然存在,而是由一个名为 Tokenizer(分词器) 的组件创造出来的。它是连接人类语言与机器语言的桥梁。
大模型的神经网络无法直接处理原始字符串。它们的输入是一个数字序列(ID)。因此,我们需要一个映射:
"Hello, world!" → [15496, 11, 995, 0]
这个映射过程就是分词。早期的分词方法(如 Word-level)存在严重问题:
为了解决这些问题,现代 LLM 普遍采用 子词(Subword)分词算法。
BPE 是目前最主流的分词算法,被 GPT 系列、Llama 系列等广泛采用。其核心思想是:从字符开始,逐步合并最常见的相邻字符对,形成新的“词元”。
让我们通过 Hugging Face tokenizers 库的源码来理解 BPE 的工作流程。
# Hugging Face tokenizers: tokenizers/src/models/bpe/model.py
class BPE:
def __init__(self, vocab: Dict[str, int], merges: List[Tuple[str, str]]):
self.vocab = vocab # 词汇表:{"h": 0, "e": 1, ..., "hello": 100}
self.merges = merges # 合并规则:[("h", "e"), ("he", "l"), ...]
def _encode_word(self, word: str) -> List[str]:
"""对单个单词进行 BPE 编码"""
# 1. 初始化:将单词拆分为字符列表
# "hello" -> ['h', 'e', 'l', 'l', 'o']
tokens = list(word)
# 2. 循环应用合并规则
while len(tokens) > 1:
# 找到 tokens 中第一个存在于 merges 列表中的相邻对
pair_to_merge = self._find_pair_to_merge(tokens)
if pair_to_merge is None:
break
# 3. 执行合并
# 例如,合并 ('h', 'e') -> 'he'
tokens = self._merge_tokens(tokens, pair_to_merge)
return tokens # 返回子词列表,如 ['hel', 'lo']
def encode(self, text: str) -> List[int]:
"""对整个文本进行编码"""
words = text.split() # 简化的按空格分词
all_subwords = []
for word in words:
subwords = self._encode_of_word(word)
all_subwords.extend(subwords)
# 4. 将子词映射为 ID
return [self.vocab[subword] for subword in all_subwords]BPE 的关键优势:
["un", "happi", "ness"]。让我们用 OpenAI 的 tiktoken 库(GPT-4 使用的分词器)来直观感受:
import tiktoken
enc = tiktoken.get_encoding("cl100k_base") # GPT-4 的编码
# 英文:单词通常是一个 Token
print(enc.encode("Hello")) # [15496]
print(enc.encode("Hello, world!")) # [15496, 11, 995, 0]
# 中文:每个汉字通常是独立的 Token
print(enc.encode("你好")) # [92543, 92728]
print(enc.encode("人工智能")) # [88917, 87760, 91985, 93285]
# 数字和特殊符号
print(enc.encode("2023年")) # [2023, 269] # 数字 "2023" 是一个 Token!
print(enc.encode("$100")) # [28, 100] # "$" 和 "100" 分开结论:
理解了 Token 是什么,我们就能看透各大厂商的计费策略。
OpenAI 的定价清晰地分为两部分:
为什么分开计费?
源码视角(伪代码):
def calculate_cost(model, prompt, completion):
input_tokens = tokenizer.count(prompt)
output_tokens = tokenizer.count(completion)
# 不同模型有不同的单价
input_price = PRICING[model]["input"]
output_price = PRICING[model]["output"]
total_cost = (input_tokens * input_price) + (output_tokens * output_price)
return total_cost模型的上下文长度(如 GPT-4 Turbo 的 128K tokens)是其能“记住”的最大信息量。这不仅仅是技术限制,更是经济模型的一部分。
开发者启示:
当你调用云端大模型 API 时,你的数据(无论是邮件、代码还是病历)首先被 Tokenizer 转换成一串数字 ID,然后发送到服务器。这是否意味着你的数据是安全的?
答案是:否。Tokenization 不等于 Encryption(加密)。
BPE 分词器是完全可逆的。只要攻击者拥有相同的词汇表(vocab.json)和合并规则(merges.txt),就能轻松将 Token ID 序列还原成原始文本。
# Hugging Face tokenizers: 解码过程
def decode(self, ids: List[int]) -> str:
# 1. 将 ID 映射回子词
tokens = [self.decoder[id] for id in ids]
# 2. 将子词拼接成原始字符串
text = "".join(tokens)
return text这意味着,Token ID 序列本身就包含了完整的原始信息。如果你的数据包含 PII(个人身份信息),那么这些信息同样暴露给了 API 提供商。
为了解决这个问题,业界正在探索多种 隐私计算(Privacy-Preserving Computation) 技术。
Token 作为当前 LLM 的基石,也面临着挑战和变革。
未来的 AI 系统将是多模态的。图像、音频、视频都需要被转换成 Token,与文本 Token 一起输入模型。例如,CLIP 模型就将图像和文本都映射到同一个向量空间。
挑战:如何设计一个统一的、高效的多模态 Tokenizer?
一些研究开始探索绕过离散的 Token,直接使用连续的 Embedding 作为输入。这可能会打破现有基于 Token 的计费和上下文限制模型,但也会带来新的工程挑战。
终极目标或许是构建完全不需要显式分词的模型,它们能像人一样直接从原始字节流中学习。虽然目前仍是研究前沿,但这代表了摆脱 Token 限制的一种可能方向。
Token 远不止是一个计费单位。它是:
深入理解 Token 的本质,是每一位 AI 时代开发者和使用者的必修课。只有这样,我们才能在享受大模型强大能力的同时,做出更明智、更安全、更具成本效益的技术决策。