非生成请求、health_generate 与控制请求分叉#
这章解决什么问题#
前面的 request lifecycle 基本都围绕生成请求在讲:请求进入、分词、排队、forward、detokenize、回包。但真实服务里并不是所有请求都长成这条路径。还有一类非常重要的分叉:
- embedding / classify / score / rerank 这类非生成请求
health_generateopen_session/close_sessionabort_requestpause_generation
如果不把这些分叉讲出来,request lifecycle 就仍然会默认“所有请求都要穿过完整生成闭环”,这对一本文字上已经开始变厚的技术书来说是不够的。
为什么这些分叉值得单独成章#
因为它们不是“边缘接口”,而是在运行时里承担不同控制职责:
- embedding / classify / rerank 会把请求送进不同的内部对象或输出路径。
health_generate用来探测运行时是否还可推进。- session / abort / pause 更直接进入控制面,而不是普通生成流。
也就是说,这里讨论的不是“更多接口名”,而是“请求生命周期并不只有一种形状”。
一张图:主生成链之外还有哪些常见分叉#
这张图解决的理解障碍是:很多读者脑中只有一条 GenerateReqInput -> Scheduler -> Detokenizer 主线,容易忽略非生成与控制请求的岔路。
flowchart TD
In["HTTP / OpenAI / native request"] --> Gen["GenerateReqInput"]
In --> Emb["EmbeddingReqInput / classify / score / rerank"]
In --> Ctrl["session / abort / pause / flush / health"]
Gen --> Main["generate path"]
Emb --> Pool["embedding / classify / rerank path"]
Ctrl --> Gate["control-plane path"]图比纯文字多解释的一点是:这些分叉不是“额外功能”,而是共享同一入口之后沿不同运行时语义散开的主线分支。
embedding / classify / score / rerank 为什么不该按生成主线理解#
从 serving_embedding.py、serving_classify.py、serving_score.py、serving_rerank.py 看,这些 surface 往往会把请求转换成 EmbeddingReqInput,而不是 GenerateReqInput。这说明它们虽然共享 OpenAIServingBase.handle_request(...) 的入口骨架,但进入 runtime 时已经换成了不同对象模型。
这类分叉的阅读价值在于:同样是“从 OpenAI-compatible 表面进入系统”,有些请求追求的是文本生成,有些请求追求的是 embedding、分类分数、cross-encoder rerank 或 decoder-only rerank 结果。生命周期虽然共享入口,但中段和尾段语义并不相同。
rerank 路径为什么特别值得提#
serving_rerank.py 里显式写了两条可能:
- cross-encoder reranker:适配成
EmbeddingReqInputpairs - 某些 decoder-only reranker:走不同解释路径
这说明 even within “非生成请求” 本身,也存在不同形状。把这层补进书里,能防止读者把所有非生成请求都简单等同于 embedding。
health_generate 为什么不是普通生成请求#
http_server.py 和 utils/common.py 里都有 /health_generate。这说明系统对健康检查的理解不是“随便发个普通请求看看能不能回”,而是提供了一条更接近运行时活性探测的专门路径。
结合前面写过的 watchdog / liveness 章节,这一点很重要:health_generate 的职责不是产出高价值业务结果,而是确认整个请求推进链是否仍然可达、可推进、可回包。
也正因为这样,它在 request lifecycle 里应被视为控制性探测请求,而不是普通业务生成请求。
open_session / close_session 怎么改变 request lifecycle 的边界#
http_server.py 里这两个路由直接落到 TokenizerManager.open_session(...) / close_session(...)。这说明 session 控制请求本身并不走完整 token generation loop,而是在 request lifecycle 的更外层修改“后续请求将以什么会话状态进入系统”。
换句话说:
- 普通生成请求消耗当前 session 状态。
open_session/close_session改写 session 边界本身。
这两种请求虽然都属于同一服务,但生命周期职责明显不同。
abort_request 与 pause_generation 为什么属于控制请求而不是错误处理补丁#
abort_request 和 pause_generation 在 http_server.py 中都有专门入口。这说明系统把“主动中止请求”“暂停新生成流量”视为正式控制面,而不是异常情况下才临时触发的补丁逻辑。
这类设计对一本系统书尤其重要,因为它告诉读者:
- abort 不是单纯客户端断开后的副作用。
- pause 不是内部 debug hack。
它们都是 runtime 对自身调度与流量控制公开暴露出的能力。
这一层最容易出现的误判#
1. 把 embedding / classify / rerank 当成“生成的简化版”#
实际上它们进入 runtime 时就已经换了对象模型和输出语义。
2. 把 health_generate 当成普通业务请求#
它更接近 liveness 探测请求,不应与真实业务流量同口径理解。
3. 把 open_session / pause_generation 当成边缘管理接口#
它们直接修改的是后续请求进入主链的边界条件。
如果你在排查“为什么某类请求看起来不像生成主线”,先怎么查#
建议按这个顺序:
- 先确认当前是生成请求、embedding 类请求,还是控制请求。
- 看 serving handler 最终构造的是
GenerateReqInput还是EmbeddingReqInput。 - 如果是控制请求,看它是否根本不会进入完整 scheduler/detokenizer 生成闭环。
- 再决定要回 request lifecycle 主线、运行时控制面,还是 observability/liveness 章节。
小结#
这一章真正想补齐的,是 request lifecycle 里经常被默认掉的事实:
- 系统不只有一种请求形状。
- 生成、非生成、探测、会话控制、暂停中止,共享入口但不共享同一条中段语义。
- 只有把这些分叉看清,request lifecycle 才真正像一本书里的“请求全景图”,而不是单一路径说明。
叶王 © 2013-2026 版权所有。如果本文档对你有所帮助,可以请作者喝饮料。