如果你的模型一夜之间「行为变了」,但模型本身没换,那大概率是 tokenization drift。同一个 tokenizer、语义相同的输入,产生的 token 序列却完全不同。MarkTechPost 给的 GPT-2 例子:`" classify"` 前面带一个空格,tokenize 出来是一个 ID — `[36509]`;`"classify"` 不带空格,tokenize 出来是两个 ID — `[4871, 1958]`。对模型来说,这俩不是同一个词,而是落在 token 空间不同区域里的两个不同输入。「我的 prompt 跟 fine-tune 时看到的差不多」跟「一模一样」,完全是两回事。
文章把这个失败模式量化了。用 GPT-2 的 tokenizer 加一个 fine-tune 过的分类器:SFT 对齐的 prompt 格式拿到约 83% 的 accuracy;把换行去掉,掉到 40-50%;改写一下指令措辞,token 集合和训练格式的 Jaccard overlap 掉到 ~50%,输入直接走出分布,准确率随之崩。文章给的修法叫 APO(Automated Prompt Optimization):测试五个以上的 prompt 模板变体,每一个都用「token 集合与原始 SFT 模板的 Jaccard 重叠度」打分,然后给 validation accuracy 乘上一个跟 overlap 成正比的 OOD 惩罚因子,最后选在合成指标上赢的模板。落地代码也就是几行 HuggingFace `AutoTokenizer.encode()` 加集合比较。
更宏观的规律是:线上 prompt 出现「回归」,通常不是模型回归,而是 tokenizer 层面的对不上 —— 模型在 instruction tuning 阶段学到了什么样的格式,你的应用现在送进去的是什么样的格式,两者出现了偏差。这种 bug 会绕过你所有现有的测试,除非你显式地把 token 序列拿去跟参考分布比对。这也解释了一件每个人都干过的事:把一段「同样的 prompt」从 Notion 粘到代码编辑器里,然后悄无声息地变差 —— 编辑器把空白做了 normalization,或者把末尾换行删了,你与训练数据的距离一下子变了。RLHF 和 instruction-tuned 的模型对这事尤其敏感,因为格式本身已经成了任务表征的一部分,不是任务文本周围的装饰。
周一可以动手的事:对线上的 prompt,把原始的 token 序列也 log 下来 —— 不要比字符串,要比 token ID,然后跟你部署时验证过的那条原始 prompt diff。把 Jaccard-overlap 做成 prompt CI 流水线里的一条回归测试。如果你跑 fine-tune,把训练格式确切的 tokenization 存档,推理时拿进来的输入要去验证一遍。如果你做的是闭源权重的模型、没法重训,文章那套 APO 循环就是务实选择:经验性地搜「能让 token 与已知模型见过的格式最大重叠」的 prompt 变体。「我的 prompt 现在有用吗」这个问题,从此变成 tokenizer 层面的问题,而不是「我措辞够不够好」的问题。
