输入如何被规范化、分词并送入运行时#
这章解决什么问题#
前几章已经把请求怎样进入系统、怎样回包、怎样处理中止与 session 边界讲清楚了,但还缺一层非常靠近“输入如何真正变成 runtime 工作单元”的细节:调用方给出来的是文本、消息、工具参数、多模态输入或 API 请求体,这些东西到底怎样被整理成 TokenizerManager 和 Scheduler 真正能消费的对象?
如果没有这一层,请求生命周期仍然会像一条大路线,而不是一条可执行的输入管线。
为什么输入规范化不是小细节#
很多系统问题都会伪装成“模型表现不对”,但根源其实是输入对象在更早阶段就已经被变形了。例如请求体的字段是否完整、priority 是否被设置、routed_dp_rank 是否合法、多模态输入是否已按预期准备、LoRA 信息是否正确附着,这些都发生在真正 forward 之前。
这也解释了为什么输入规范化值得单独成章。它不是“在真正逻辑之前做点前处理”,而是在决定一条请求会以什么形状进入 runtime。
TokenizerManager.generate_request(...) 前后到底在发生什么#
从前面章节的锚点可以继续往前走一步:TokenizerManager.generate_request(...) 之所以重要,不只是因为它接住了请求,还因为它要把外部表面上的输入形状折叠成统一的内部对象。也就是说,这里真正发生的是一次抽象转换:从“调用方语言”到“runtime 语言”。
这层转换一旦想清楚,很多后面的行为都会更好理解。因为一条请求如果在这里已经被规范化成了另一种形状,那么后面的 scheduler、batch、cache、execution model 就都只会看到这个被整理过的形状,而不会再回头关心原始调用表面。
tokenization 为什么属于生命周期而不属于纯 tokenizer 细节#
如果只从名字看,tokenization 好像属于 tokenizer 内部实现。但在 request lifecycle 里,它实际上承担的是“把输入送进可调度形态”的职责。这就是为什么在这里看 tokenization,比在模型内部看 tokenizer 词表更有价值。
你要抓住的不是“它怎么编码每个 token”,而是“它怎么把输入变成可被 send_to_scheduler 推进的对象”。这是两个层次的问题。
一个更像输入管线的图#
flowchart TD
A["调用方输入\nmessages / prompt / multimodal / API body"] --> B["GenerateReqInput / request object"]
B --> C["TokenizerManager.generate_request(...)"]
C --> D["normalize / validate / attach priority / LoRA / routing"]
D --> E["tokenize / prepare multimodal inputs"]
E --> F["send_to_scheduler"]这张图补的是“从表面输入到 runtime 对象”的中间层。它比前面更大的 request lifecycle 时序图多解释了一层:进入 scheduler 之前,系统已经做了一次很重要的形状转换。
调试时这一层意味着什么#
如果一个请求从一开始就表现异常,例如字段缺失、某些参数不生效、路由结果不对、LoRA 或多模态输入行为异常,更稳的入口通常不是 scheduler,而是输入规范化这一层。因为如果输入在这里已经被变形,后面所有执行和缓存逻辑都只是在“错误前提上正确运行”。
本章对应哪些代码路径#
这一章最重要的锚点包括 python/sglang/srt/managers/tokenizer_manager.py、python/sglang/srt/managers/io_struct.py、python/sglang/srt/managers/mm_utils.py 以及与 GenerateReqInput 和多模态输入准备相关的路径。
小结#
请求生命周期只有把“输入怎样真正变成 runtime 对象”讲出来,才算完整。否则你知道请求怎么走,却不知道它到底是以什么形状开始走的。
叶王 © 2013-2026 版权所有。如果本文档对你有所帮助,可以请作者喝饮料。