解释人类文字如何变成模型能计算的 token id,以及这个转换为什么会影响上下文窗口、成本、延迟、RAG、工具调用和开源部署兼容性
| 环节 | 它做什么 | 工程上会影响什么 | 最容易忽略的点 |
|---|---|---|---|
| Normalize | 处理 Unicode、大小写、空格、换行或字节表示 | 同一段文本是否得到稳定 token 序列 | 肉眼一样的字符,底层编码可能不同 |
| Pre-tokenize | 先按空格、标点、字节或脚本做粗切 | 中文、英文、代码、表格的切分差异 | tokenizer 不是按“自然词”工作 |
| Subword Merge | 把常见片段合并成更短 token 序列 | token 数、上下文预算、计费和延迟 | 高频片段便宜,罕见片段会被切碎 |
| Vocabulary Lookup | 把片段映射为整数 id | 词表大小、兼容性、输出候选集合 | 换 tokenizer 就等于换输入语言 |
| Special Tokens | 标记角色、边界、停止、工具调用和模板结构 | Chat、工具、结构化输出和安全边界 | 模板错了,模型看到的对话就错了 |
你写的是文本,模型读的是整数序列。tokenizer 把人类可见的字符世界翻译成模型训练时学过的 token id 世界;这一步不只是前处理,而是模型输入协议的一部分。
| 机制 | 核心思路 | 适合抓住的直觉 | 工程注意 |
|---|---|---|---|
| BPE | 从小片段开始,把训练语料里高频相邻片段逐步合并 | 常见组合变短,罕见组合保持碎片 | 词表和 merge rules 必须和模型匹配 |
| WordPiece | 选择能提升语料似然的子词片段,常见于 BERT 路线 | 用子词覆盖未知词,减少 OOV | 不同实现有不同前缀和未知词处理规则 |
| SentencePiece | 把文本当原始字符流处理,可不依赖空格分词 | 对中日文、多语言和混合文本更自然 | 空格常被编码成可见的特殊片段 |
| Byte fallback | 词表没有的字符退回到字节级表示 | 几乎任何输入都能编码,但罕见字符会变长 | emoji、乱码、罕见符号会显著增加 token 数 |
按字会让英文和长文本序列太长,按词又会遇到新词、拼写、代码和多语言覆盖问题。子词算法是在词表容量、序列长度、泛化能力和多语言覆盖之间做折中。
| 对象 | 如果配置错了 | 常见症状 | 排查方向 |
|---|---|---|---|
| EOS / Stop | 模型不知道该在哪里停 | 提前截断、重复输出、停不下来 | 检查 tokenizer special tokens 与生成参数 |
| Role Tokens | 对话角色边界混乱 | 模型模仿用户、泄露模板、忽略 system | 检查 chat template 与训练格式是否一致 |
| Tool Tokens | 工具调用结构不可识别 | JSON 损坏、函数名混乱、参数漂移 | 检查工具 schema 渲染、停止条件和解析器 |
| PAD / Batch | 批处理位置和 mask 出错 | 输出异常、性能波动、训练不稳定 | 检查 padding side、attention mask 和 position ids |
| 输入类型 | 为什么成本不同 | 工程后果 | 治理动作 |
|---|---|---|---|
| 中文长文 | 取决于词表覆盖和训练语料比例 | 同样信息量下 token 数可能高于预期 | 用目标 tokenizer 做 chunk 预算 |
| 英文技术文档 | 常见词和空格模式通常更省 | 长段落可能比想象中更能塞进窗口 | 保留标题结构,减少重复模板 |
| 代码 / JSON | 符号、缩进、换行和重复键名都计入 token | 工具调用、日志分析、代码审查成本高 | 压缩无关字段,限制输出 schema 大小 |
| emoji / 罕见符号 | 可能走字节 fallback,被拆得很碎 | 用户昵称、社媒文本、乱码会放大成本 | 清洗不可见字符,必要时做输入规范化 |
| 空格 / 换行 | 很多 tokenizer 会把空白当成片段的一部分 | 模板格式、Markdown 和表格会消耗预算 | 保留必要结构,删除纯装饰排版 |
| 变量 | 由 token 数怎样放大 | 你会在哪里感知 | 对应专题 |
|---|---|---|---|
| Context Window | 窗口上限按 token 计,不按字符计 | 长文档、历史对话、RAG 片段塞不下 | 长上下文 / 记忆 |
| Billing | 输入和输出 token 通常分别计费 | 同一功能不同语言、不同模板成本差异大 | 成本 / 性能工程 |
| Prefill | 输入越长,首 token 前计算越重 | 用户等很久才看到流式输出 | 推理计算 / KV Cache |
| KV Cache | 每个历史 token 都会留下可复用状态 | 长上下文并发下降,尾延迟上升 | 模型服务 / 网关 |
| Output Length | 生成是一 token 一 token 追加 | 回答越长,流式越久,总耗时越高 | Logits / Softmax、解码 / 采样 |
Tokenizer 负责把文本和协议符号变成 token;解码器负责在词表上选下一个 token;应用层解析器负责把输出解释成 JSON、函数调用或引用结构。任何一层含混,都会表现为“模型不听话”。
| 兼容对象 | 为什么重要 | 不兼容症状 | 检查项 |
|---|---|---|---|
| tokenizer.json / merges / vocab | 决定文本到 id 的映射 | 输出乱码、能力骤降、特殊符号异常 | 与模型权重同源下载,校验版本 |
| chat_template | 决定消息如何渲染为训练格式 | 多轮对话错位、system 不生效、工具调用失败 | 核对 model card、推理框架默认模板 |
| special_tokens_map | 决定 BOS、EOS、PAD、UNK、role 等 id | 停不下来、提前结束、batch 异常 | 核对 special token id 和 generation config |
| 推理框架 | 不同框架可能内置不同模板和 padding 逻辑 | 本地与线上结果不一致 | 固定版本,记录 tokenizer 与引擎配置 |
| 量化 / 转格式 | 转换模型时可能丢配置或改默认项 | GGUF、ONNX、TensorRT 部署行为偏移 | 转换后跑 tokenizer、模板和停词回归用例 |
| 证据节点 | 必须记录的字段 | 用来排查什么 |
|---|---|---|
| 文本入口 | input_source、language_hint、normalization_rule、raw_text_hash | 乱码、空格差异、Unicode 归一化和多语言 token 成本异常 |
| 模板符号 | chat_template_version、role_tokens、bos / eos / pad / stop_tokens | system 不生效、停不下来、提前结束或角色边界错位 |
| 编码结果 | tokenizer_version、vocab_hash、token_count、special_token_count、token_ids_hash | 同一文本在不同环境下为什么 token 数、费用或输出不同 |
| 预算归因 | control_tokens、user_tokens、rag_tokens、tool_tokens、output_reserved_tokens | 窗口被谁吃掉,为什么关键证据被截断或首 token 变慢 |
| 兼容检查 | model_id、engine_version、tokenizer_config_hash、generation_config_hash | 开源模型迁移、量化或推理框架升级后行为漂移 |
如果说 Transformer 解释 token 进入模型后如何互相读取信息,那么这一页解释 token 出现之前的翻译层:文本、词表、特殊符号和模板如何共同决定模型真正看见的输入。