一次大模型请求到底发生了什么?从 API 入口到最后一个 Token 全流程讲清楚(GPT-5.4-high 生成)
关键词: LLM 请求生命周期、Prefill、Decode、KV Cache、TTFT、Output Speed、Throughput、Dense、MoE、llama.cpp、vLLM、SGLang、推理系统
如果你现在对这些现象很熟:
TTFT很高,但Output Speed看起来不差- 同一个模型在
llama.cpp和vLLM上表现完全不一样 - 单用户很顺,一上并发就开始变得很奇怪
MoE明明总参数更大,结果实际吞吐反而比Dense更好
那大概率不是你不会看 benchmark,而是你脑子里还缺一个更底层的模型:
一次大模型请求在系统里,到底经历了什么。
这篇文章就想补上这块。
它不打算只解释几个术语,而是想回答 3 个更根本的问题:
- 一次请求从 API 入口到最后一个 token 返回,中间到底经过了哪些阶段?
Dense、MoE、长上下文、reasoning、多模态,会在哪些地方让这条链路发生分叉?- 为什么你最后看到的
TTFT、Output Speed、Throughput,很多时候并不只是“模型能力”的结果?
如果你把这条链路真正看懂,后面再看任何推理 benchmark,都会比以前顺很多。
一、为什么有必要单独理解“一次请求到底发生了什么”
很多人第一次看大模型推理,脑子里的画面是这样的:
用户发一句话 -> 模型算一下 -> 返回一句话
这个理解当然不能说错,但它太粗了。
一旦你开始做下面这些事情:
- 本地部署
- 长上下文压测
- 比较
llama.cpp和vLLM - 分析
TTFT为什么很高 - 研究
Dense和MoE的性能差异 - 做并发、吞吐、买卡决策
你就会发现,如果脑子里没有“一次请求在系统里实际经历了哪些阶段”的模型,很多现象都会变得很难解释。
比如:
- 为什么有时首 token 很慢,但后面输出速度很快?
- 为什么同一个模型单用户很顺,一上并发就掉得厉害?
- 为什么有的模型参数更多,实际吞吐却更高?
- 为什么长上下文不只是让
TTFT变慢,还会拖慢 decode? - 为什么同一个模型在
llama.cpp和vLLM上表现会差很多?
这些问题的共同根源是:
推理指标只是结果,请求生命周期才是原因。
所以这篇文章不先讲结论,而是先讲一次请求里到底发生了什么。
二、先给一条总流程:一次请求的完整生命周期
如果从真实推理系统角度看,一次请求大致会经过下面这些阶段:
1. 请求进入 API / Gateway
2. 消息拼装与 prompt template 渲染
3. tokenizer 分词
4. 请求进入调度器 / batch 队列
5. Prefill:一次性处理整段输入
6. 建立或扩展 KV Cache
7. Decode 循环:逐 token 生成
8. 采样 / 终止条件判断
9. detokenize
10. 流式返回或一次性返回
11. 记录 usage、latency、日志、监控指标
如果再按“谁在干活”来分,可以粗略拆成三层:
- 应用层:接请求、做模板、拼上下文、处理工具调用
- 推理引擎层:排队、batch、KV cache、调度、采样、返回
- 模型层:真正的
Prefill + Decode
这三层混在一起,就是为什么很多人以为自己在比较模型,其实比的是整条链路。
三、请求进入系统后,第一步并不是“立刻进模型”
1. API / Gateway 入口
不管你走的是:
- OpenAI 兼容接口
- 自己封装的 Gateway
llama.cpp自带 servervLLM的 API serverSGLang的 serve 接口
请求真正进入模型之前,通常都要先过一层 API。
这一层会做一些很基础但很关键的事情:
- 接收 HTTP 请求
- 解析 JSON
- 校验参数
- 决定模型路由
- 统一鉴权、限流、日志、错误处理
如果你已经接了多模型路由、fallback、灰度流量,这一层甚至还会先决定:
- 请求到底发给哪个模型
- 用哪个端口
- 走哪个 backend
也就是说,“一次请求到底发生了什么”,第一步其实是系统逻辑,而不是模型逻辑。
2. 消息拼装与 prompt template 渲染
这是很多人最容易忽略的一步。
用户传进来的往往不是最终送给模型的那串 token,而是类似:
- system prompt
- user message
- assistant 历史
- tool definitions
- function schema
- chat template kwargs
- RAG 检索结果
系统会先把这些内容拼成最终 prompt。
这一阶段会影响很多事情:
- token 数到底有多少
- 是不是触发了 thinking 模式
- 是不是附带了工具定义
- 上下文长度会不会突然暴涨
- 为什么“明明用户只说了一句话,模型却吃了上万 token”
很多“模型突然变慢”的问题,根本不是模型本身出了问题,而是模板和上下文拼装层把输入变得太重了。
3. tokenizer 分词
接下来系统才会把字符串变成 token。
这一层看起来很朴素,但它直接影响:
prompt_tokens- 上下文上限是否会超
- KV cache 规模
- 成本估算
- 并发能力
同样 10 万中文字符,不同模型的 token 数可能明显不同。
所以很多时候你真正要问的不是:
这个请求有多少字符?
而是:
这个请求在当前 tokenizer 下到底有多少 token?
这也是为什么做容量规划时,字符数只能做粗估,真正有效的是 token 数。
四、真正进模型前,还有一个非常关键的环节:调度器
请求 token 化之后,不一定会马上进模型。
在真实推理引擎里,通常还会经过:
- 请求排队
- 调度
- continuous batching
- prefix sharing
- slot / sequence 分配
这一步主要发生在推理引擎层,比如:
vLLMSGLang- 各种自定义 serving stack
它在做的事情不是“理解内容”,而是“决定这批请求怎么更高效地塞进 GPU”。
这一步为什么重要?
因为很多你最后看到的现象,其实和调度器强相关:
- 为什么单用户速度很好,一上并发就不均匀
- 为什么有的请求 TTFT 很低,有的很高
- 为什么系统总吞吐升高了,但单用户体感变差
换句话说:
用户感觉慢,不一定是模型慢,也可能是调度慢。
五、真正进入模型以后,核心只有两大阶段:Prefill 和 Decode
这是整条链路里最关键的一层。
1. Prefill:先把整段输入“读完”
Prefill 阶段处理的是整段输入 token。
特点是:
- 整段输入并行计算
- 计算量大
- GPU 利用率高
- 更接近算力密集型
这一阶段结束后,模型才有能力生成第一个 token。
所以:
- prompt 越长
- prefill 越慢
TTFT往往越高
很多长上下文场景里,真正拖慢体验的第一元凶,就是 prefill。
2. KV Cache:把历史中间状态存下来
在 prefill 过程中,模型不会每次都从头重新理解所有历史,而是会把中间状态存进 KV Cache。
KV Cache 的作用可以理解成:
- 把已经算过的历史结果存起来
- 让后续 decode 不用整段重算
它非常关键,因为 decode 阶段的效率,很大程度上取决于:
- KV cache 有多大
- 每一步访问它有多贵
- 多并发时它占了多少显存
这也是为什么:
- 上下文越长
- 并发越高
- decode 往往也会越来越慢
不是因为模型“智商下降了”,而是因为它每次生成新 token 时,要带着更大的历史状态往前走。
3. Decode:一个 token 一个 token 地往外吐
进入 decode 后,请求就变成了一个循环:
读当前上下文状态
-> 算下一个 token 的概率
-> 采样选出一个 token
-> 把新 token 拼回去
-> 更新 KV cache
-> 再生成下一个 token
这一阶段的特点是:
- 每步计算量相对小
- 但每步都要访问权重和 KV cache
- 更容易受显存带宽限制
- 更容易在高并发下暴露瓶颈
所以:
Output SpeedTBT- 系统级吞吐
这些指标,大多都和 decode 阶段强相关。
六、Dense 和 MoE,到底会让请求生命周期哪里发生分叉
这个问题非常关键。
因为“请求生命周期”不是完全架构无关的。
主干流程都差不多,但在模型内部每一步到底怎么走,和架构关系很大。
1. Dense:每一层都走全量参数
Dense 模型的特点是:
- 每个 token 经过每一层时,都走完整的 FFN / attention 路径
- 参数利用方式稳定
- 计算路径比较固定
它的好处是:
- 行为更直接
- 路径更均匀
- 引擎适配往往更成熟
它的代价是:
- 每个 token 都要动用全量参数
- decode 访存压力通常更直接
- 大模型时显存和带宽压力会更重
2. MoE:每个 token 先路由,再只走部分专家
MoE 模型在生命周期里多了一步:
routing
也就是每个 token 进入某层时,不是直接走完整 FFN,而是先:
- 计算 gating / routing 分数
- 选出 top-k experts
- 只让这个 token 经过部分专家
所以在模型内部,MoE 的单 token 路径更像:
attention
-> routing
-> top-k experts
-> combine
-> 下一层
这会带来几个非常直接的差异:
- active params 远小于 total params
- 单 token 实际参与计算的参数更少
- 某些场景下 decode 吞吐更高
- 但调度和实现复杂度更高
也就是说:
Dense 和 MoE 的区别,不是“一个更先进”这么简单,而是:
每个 token 在模型内部实际走的路不同。
3. 为什么 MoE 有时参数更多,实际反而更快
这正是因为:
- Dense:每 token 走全量参数
- MoE:每 token 只走少量被选中的 experts
所以你会看到一种很反直觉的现象:
- 总参数更大的 MoE
- 实际 active params 更小
- decode 吞吐反而可能比 Dense 更高
但这不代表 MoE 一定全面更优。
因为它还会带来:
- routing 开销
- 实现复杂度
- 引擎兼容性问题
- 部分场景下更复杂的调度行为
所以在真实系统里,MoE 的优势不是“永远更快”,而是“在某些服务形态下更有机会把大模型做成可部署系统”。
七、除了 Dense 和 MoE,还有哪些架构差异会影响请求生命周期
1. GQA / MQA / KV heads 数量
这部分直接影响:
- KV cache 大小
- decode 访存压力
- 长上下文和并发能力
同样的上下文长度下,KV heads 更少的模型,往往更容易在长上下文下保持可部署性。
2. Long-context 机制
比如:
- sliding window
- hybrid attention
- 各种 rope / sparse / chunk tricks
这些机制会影响:
- prefill 怎么算
- decode 时历史范围怎么访问
- 长上下文下的算力和带宽压力
所以“支持 128K”从来不只是一个 marketing 数字,它背后意味着模型内部访问历史状态的方式已经变了。
3. Reasoning 模型
Reasoning 模型最重要的变化不一定是结构本身,而是输出形态:
- 它可能先输出 thinking token
- 再输出真正可见答案
这会让请求生命周期里多出一个非常关键的体验分叉:
TTFT很快- 但
TTFAT很慢
所以评估 reasoning 模型时,如果你不把“第一个 token”与“第一个答案 token”区分开,很多结论都会错。
4. 多模态模型
多模态请求就更不是“文本 token 进,文本 token 出”这么简单了。
它通常还会多出:
- 图像编码器
- 投影层
- 跨模态对齐
也就是说,请求生命周期会变成:
图像 / 文本输入
-> 视觉编码
-> 投影到语言空间
-> 与文本 token 拼接
-> 再进入语言模型主干
这时如果你还用“纯文本模型”的心智去理解 TTFT、吞吐、显存,你就很容易误判。
八、为什么同一个模型在不同引擎里表现会差很多
这是另一条经常被忽略的分叉线。
很多人以为自己在比模型,其实比的是:
llama.cppvLLMSGLang- 某个自定义 gateway
不同引擎在请求生命周期里介入的地方主要包括:
- 调度器
- continuous batching
- prefix cache / prefix sharing
- KV cache 管理
- streaming 实现
- sampling 实现
- 内存布局和 kernel 优化
所以同一个模型:
- 在
llama.cpp上也许更适合本地单机、GGUF、固定服务 - 在
vLLM上也许更适合多并发 serverless / API 服务 - 在
SGLang上也许更适合 Agent、多轮、共享长前缀场景
这也是为什么你最后看到的:
TTFTOutput SpeedThroughputConcurrent Users @ SLO
很多时候不仅是模型决定的,也是引擎决定的。
九、把整条链路重新映射回那些你熟悉的指标
到这里,很多指标应该已经可以重新“挂回去”了。
和输入阶段更相关的
TTFTPrefill Speed- 长 prompt 下的 E2E
和输出阶段更相关的
TBTOutput SpeedDecode Speed
和系统容量更相关的
ThroughputQPSConcurrent Users @ SLO
和成本决策更相关的
每美元 ThroughputUsers / GPU @ SLOGPU / User @ SLO
这时你会发现,很多指标其实不是孤立的定义,而是这条生命周期上不同位置的投影。
十、最容易犯的错误,其实是把“模型问题”和“系统问题”混在一起
当你开始真正理解一次请求的生命周期以后,一个最大的收获就是:
你会更容易区分:
- 到底是模型本身慢
- 还是 prompt 拼装太重
- 还是 tokenizer 让 token 数暴涨
- 还是调度器排队太久
- 还是 KV cache 太大把 decode 拖慢了
- 还是 serving engine 本身没有把这类模型服务好
也就是说:
同样一句“这个模型很慢”,背后其实可能是五六种完全不同的原因。
而只有当你把整条请求链拆开,才知道问题该归因到哪里。
十一、最后一句总结
如果只留一句最重要的话,那就是:
一次大模型请求,不是“输入一句话、模型算一下、输出一句话”这么简单。
它是一条完整的系统链路:
- API
- prompt 拼装
- tokenizer
- 调度器
- prefill
- KV cache
- decode
- sampling
- detokenize
- streaming 返回
而 Dense、MoE、长上下文、reasoning、多模态,只是在这条主链路上,让某些环节的代价和形态发生了变化。
理解这一点之后,你再回头看:
TTFTOutput SpeedThroughputConcurrent Users @ SLO
这些指标才不会只是名词,而会变成一套真正能指导部署、压测和买卡决策的判断框架。
换句话说,这篇文章真正想解决的,不是“再解释几个术语”,而是帮你把下面这件事想清楚:
当你说“这个模型快”或者“这个系统慢”的时候,你到底在描述哪一段链路,哪一个瓶颈,哪一种成本。
只要这个问题想清楚了,很多原来混在一起的判断就会自动分层:
- 是模型问题,还是系统问题
- 是
Prefill慢,还是Decode慢 - 是单用户体验问题,还是系统容量问题
- 是 benchmark 好看,还是服务真的可上线
参考资料
更多推荐

所有评论(0)