Appearance
分词与词表 — 概念
分词算法
llama.cpp 支持以下分词算法:
BPE (Byte Pair Encoding)
GPT 系列模型使用的算法:
- 从字符级开始
- 反复合并 rank 最小的相邻 token 对(由优先队列
llm_bigram_bpe::queue调度,rank 越小越优先) - 编码时对剩余相邻 bigram 重复上述合并,直到无可合并项
"hello world" → ["he", "llo", " world"]SPM (SentencePiece)
LLaMA 系列使用,基于 byte-level BPE + byte fallback:
- 预留 256 个 byte token 作为 fallback
- 空格替换为
▁(U+2581) - 支持添加 BOS token
WPM (WordPiece)
BERT 系列使用:
- 类似 BPE 但按词汇表匹配,未知词用
##前缀标记子词 - 归一化选项已重构为独立结构体
normalizer_options,含lowercase与新增的strip_accents,用于 Jina Embeddings 等需要大小写/重音不敏感的场景
c
struct normalizer_options {
bool lowercase = true;
bool strip_accents = true;
// TODO: clean_text, handle_chinese_chars
};关于 BOS 的实际来源:BOS token 从 GGUF 元数据(
tokenizer.ggml.bos_token_id等)读取,并不存在"采纳 leading TemplateProcessing 特殊 token 作为 BOS"这一机制。
Unigram
T5 系列使用:
- 从大词表中逐步删减
- 编码时使用概率最大的路径
近期修复了 UGM(Unigram)tokenizer 在
precompiled_charsmap处理上的越界读(4a7ee3126,#18750)。
HybridDNA
专为 DNA 序列设计的混合分词器:
- 文本部分使用标准 BPE
- DNA 序列使用固定长度 k-mer(6 碱基)分词
- DNA 段由
<dna>和</dna>标签界定 - 非 ACGT 字符回退到
<oov>token - 内部通过(普通)继承并重写虚方法扩展 BPE tokenizer(
llm_tokenizer_bpe_session的子类,非 C++ virtual-base 虚拟继承)
词表结构
c
enum llama_vocab_type {
LLAMA_VOCAB_TYPE_NONE = 0,
LLAMA_VOCAB_TYPE_SPM = 1, // SentencePiece
LLAMA_VOCAB_TYPE_BPE = 2, // Byte Pair Encoding
LLAMA_VOCAB_TYPE_WPM = 3, // WordPiece
LLAMA_VOCAB_TYPE_UGM = 4, // Unigram
LLAMA_VOCAB_TYPE_RWKV = 5, // RWKV greedy
LLAMA_VOCAB_TYPE_PLAMO2 = 6, // PLaMo-2 (Aho-Corasick + dynamic programming)
};
// 注意:HybridDNA 是 BPE 的变体(类型仍为 LLAMA_VOCAB_TYPE_BPE),通过虚拟继承扩展每个 token 包含:
- 文本表示(text)
- 分数(score)— 用于 BPE 合并优先级(GGUF 里除 F32 外现也接受 INT32 整数分数数组)
- 类型(normal, control, unknown, byte 等)
- 特殊标记(BOS, EOS, PAD, EOT 等)
预分词器(pre-tokenizer)
分词行为由 llama_vocab_pre_type(src/llama-vocab.h)选择,现共 59 种:本窗口新增 LLAMA_VOCAB_PRE_TYPE_HY_V4(Tencent Hy 4,复用 DeepSeek3-LLM 的正则集)与 LLAMA_VOCAB_PRE_TYPE_SPARK2_5(llama3 风格但单独切分 [\r\n]、增加 \p{N} 第四条表达式)。
suppress_tokens(模型级屏蔽 token)
GGUF 键 tokenizer.ggml.suppress_tokens(INT32 数组)记录模型要求屏蔽的 token。它不再是 gemma4 专属的图级 logits-bias hack(原 llm_graph_input_logits_bias 已删除):词表加载时过滤出有效 id,经新公共 API llama_vocab_get_suppress_tokens() 暴露,common/sampling.cpp 在 common_sampler_init 里把它们并入 logit-bias 采样器(置 -INFINITY)。
分词流程
相关概念
- tokenization — 分词算法详解
- gguf — 词表在 GGUF 中的存储
新增 tokenizer 支持
近期上游新增了以下 tokenizer:
- Cohere2 MoE (TINY_AYA) — 新增
cohere2moevocab 与专用 chat template parser(North Code) - jina-embeddings-v2-base-zh — 中文嵌入模型,使用 whitespace 预分词
- LFM2.5-8B-A1B — 新增 tokenizer 和 chat template(含 reasoning round-trip 修复)
- MiniCPM5 — 转换工具新增 tokenizer 支持;并新增 MiniCPM5 工具调用解析器(PEG parser,处理 XML 风格 tool call),修复 Jinja
min/maxAPI 以对齐 Jinja2 - MiniMax-M3 — 新增专用 chat parser(
common/chat);其架构使用 MSA 稀疏注意力 - Granite 4.1 — 新增 chat template
- Hy 4(hy_v4)与 Spark 2.5(spark2_5)(本窗口)— 两个新预分词类型,见上文「预分词器」
- GGUF 读取加固 — vocab 加载时校验各 KV 的数组类型(merges 必须字符串数组、scores 接受 INT32/F32、suppress_tokens 为 INT32 数组等),类型不符抛异常而非未定义行为;plamo2 的 byte-token hex 先验证再
stoi;超范围的特殊 token id 改为禁用并告警(原先会越界)
多模态分词(mtmd)新 API
本窗口 mtmd 的输入分词接口有明显扩展:
mtmd_tokenize_from_parts()— 新公共 API:接受mtmd_input_part(文本或位图)数组,不再要求 prompt 里有媒体标记(<__media__>等),可逐 part 控制parse_special;add_special提升为调用级参数。原mtmd_tokenize重构为mtmd_tokenizer类- 位图合并改为显式 opt-in — 连续视频帧的时间维合并现在要求每帧调用
mtmd_bitmap_set_mergeable(true),不再自动合并(95c409c13) - chunk 序列化 —
mtmd_input_chunk_save/_load/_get_placeholder:server 保存会话 KV 时媒体 chunk 只存元数据,加载后为占位符 - TTS(音频生成)子系统 — 实验性
mtmd_gen_*API(mtmd_gen_audio_type:QWEN3TTS / POCKETTTS;mtmd_gen_audio_process等)与 clip 侧CLIP_MODALITY_GEN_AUDIO/clip_encode();helper 拆出mtmd-helper-gen.cpp(1000+ 行)的 step-prompt / step-gen 循环。模型图:qwen3tts-gen.cpp、pockettts-gen.cpp(+ spkenc / seanet)。llama-tts二进制有破坏性变更 - Pillow 级精确缩放 — 图像预处理的 resize 算法(BILINEAR / BICUBIC / 新增 LANCZOS)与 pad 样式(NONE / CEIL / NEAREST)声明为与 PIL 逐字节一致,overview 与 refined 图可分别配置算法;预处理器方法全部
const化,新增 deepseek4v / minicpmv / muse-glimmer 预处理器与 dots3-note 音频预处理器 - webp 解码(经 ffmpeg)、视频 moov-atom-at-EOF 修复、
--mmproj-device参数(mtmd_context_params新增device字段)、位图 id 改用 SHA-256(原 FNV)
Chat Template 变化
llama.cpp 使用 Jinja 模板处理 chat 格式,近期更新:
- Cohere2 MoE (North Code) — 专用 chat template parser
- Jinja 过滤器 — 新增
count/d/e过滤器别名;修复负步长 slice(带 start/stop)与 split/replace 空 first arg 的解析 - Jinja
call语句 — 模板内现支持{% call %}调用语句,把宏(macro)回传的 caller 上下文移入函数处理,适配更复杂的 chat 模板 - LFM2 / LFM2.5 — 修复 tool-call 双重转义、json_schema 忽略、reasoning round-trip、空白处理及 peg-native grammar 生成器 bug
- MiniCPM5 工具调用解析器 — 新增 PEG parser 处理 MiniCPM5 的 XML 风格 tool call(autoparser + 严格 JSON 参数解析),配套
models/templates/openbmb-MiniCPM5-1B.jinja模板 --reasoning-preserve— jinja/chat caps 新增保留推理(reasoning)内容的开关,控制输出是否保留 thinking 内容- reasoning-budget sampler — 推理预算采样支持多个 end-sequence 强制停止;
common_params新增reasoning_budget_tokens/_start/_end(vector) /_forced/_message/reasoning_control等字段。配套新增common/trie.{cpp,h}用于多序列匹配(注意:是 reasoning-budget 基础设施,不是推测解码) --dump-prog— 测试专用(仅tests/test-chat-template.cpp)选项,导出模板编译后的程序(AST),便于排查 chat 模板渲染问题;底层走common/jinja/runtime.h的debug_dump_program辅助函数,不是 llama-server 的运行时参数- Granite 4.1 template (
models/templates/ibm-granite-granite-4.1.jinja) - LFM2/LFM2.5 tool parser 统一重构
语法生成 (Grammar)
约束生成(grammar-constrained generation)依赖从结构化输入(JSON schema、GBNF)生成解析器:
- PEG ac parser — 新增 ac parser 用于更严格的语法生成;
until子句的 GBNF 语法生成重构 - json-schema-to-grammar — 间距规则(spacing)与各 parser 实现对齐,减少生成语法的歧义
- regex-partial 移除 — 删除未使用的
common/regex-partial.cpp/.h及其测试(-551 行),精简公共解析代码