从一次请求看懂大模型推理平台:网关、推理服务、KV Cache 与 SSE
从一次请求看懂大模型推理平台:网关、推理服务、KV Cache 与 SSE
很多后端工程师刚接触大模型平台时,容易把几个概念混在一起:开源大模型、vLLM、模型网关、KV Cache、SSE、TTFT、TPOT。它们都和「让模型对外提供服务」有关,但处在不同层次,解决的问题也不一样。
本文不从 Transformer 公式讲起,而是从一次 chat completion 请求出发,用后端工程的视角拆开大模型推理平台。
先看整体链路:
用户 / Agent
-> LLM Gateway
-> Inference Service
-> GPU Runtime
-> Streaming Response
-> Metrics
一句话概括:
模型网关管请求治理,推理服务管生成 token,GPU Runtime 管实际计算,Metrics 管可观测。
1. 一次大模型请求不是简单函数调用
从用户视角看,请求很简单:
我输入一个 prompt,模型开始回答。
但从系统视角看,它是一条服务链路:
用户输入
-> Agent 或业务系统构造请求
-> LLM Gateway 做鉴权、限流、路由和日志
-> Inference Service 执行 tokenize、prefill、decode
-> GPU Runtime 做矩阵计算和 KV Cache 读写
-> Streaming Response 持续返回 token
-> Metrics 记录延迟、吞吐和错误
对应到普通后端系统,可以这样类比:
| 普通后端系统 | 大模型推理平台 |
|---|---|
| API Gateway | LLM Gateway |
| 业务服务 | Inference Service |
| 数据库 / 缓存 | 模型权重 / KV Cache |
| 一次性 JSON 响应 | 流式 token 响应 |
| QPS、P95、错误率 | QPS、TTFT、TPOT、tokens/s、KV Cache usage |
| 日志和 trace | 请求日志、token 统计、推理指标 |
普通业务服务通常计算完一次性返回 JSON。大模型服务经常是边生成边返回,因为模型本身就是逐 token 生成文本。
2. User / Agent:请求从哪里来
大模型请求通常接近 OpenAI-compatible API。一个简化请求长这样:
{
"model": "qwen2.5-7b-instruct",
"stream": true,
"messages": [
{
"role": "system",
"content": "You are a concise assistant."
},
{
"role": "user",
"content": "Explain where KV Cache sits in an LLM serving stack."
}
]
}
这一层的重点不是推理,而是构造任务、上下文和模型调用参数。
如果是 Agent 应用,它可能还会在请求前做任务规划、工具选择、上下文拼接。但最终进入模型服务平台时,通常还是一次结构化的模型请求。
3. LLM Gateway:管请求,不生成 token
LLM Gateway 类似普通后端里的 API Gateway,只是治理对象从「业务接口」变成了「模型请求」。
它通常负责:
- 鉴权:这个 API Key 或租户能不能调用模型。
- 限流:这个租户的 QPS、并发、token quota 是否超限。
- 路由:请求应该去哪个模型、哪个后端实例。
- 日志:记录请求 ID、租户、模型、耗时和错误。
- 计量:统计 prompt tokens、completion tokens 和成本。
- 流式代理:把推理服务返回的 token chunk 继续转发给客户端。
它不负责:
- 加载模型权重。
- 执行矩阵计算。
- 生成下一个 token。
- 管理 GPU 显存里的 KV Cache。
Go 伪代码:
func ChatCompletionsHandler(w http.ResponseWriter, r *http.Request) {
tenant := authenticate(r)
checkRateLimit(tenant)
req := decodeChatCompletionRequest(r)
backend := routeModel(req.Model, tenant)
start := time.Now()
stream := proxyToInferenceBackend(backend, req)
flushSSE(w, stream)
recordGatewayMetrics(tenant, req.Model, time.Since(start))
}
所以,模型网关不是「大模型本身」,也不是 vLLM。它是模型服务平台的入口治理层。
4. Inference Service:真正执行推理流程
Inference Service 才是 vLLM、SGLang、TensorRT-LLM 这类系统所在的位置。
它通常负责:
- tokenizer:把文本转成 token id。
- prefill:处理完整 prompt,建立初始 KV Cache。
- decode:逐 token 生成。
- batch 调度:把多个请求组织到一起,提高 GPU 利用率。
- prefix caching:复用相同前缀的缓存,降低重复计算。
- metrics:暴露 TTFT、TPOT、tokens/s、KV Cache usage 等推理指标。
推理服务的简化流程:
def handle_request(prompt):
token_ids = tokenizer.encode(prompt)
# prefill:处理输入上下文,通常影响 TTFT
kv_cache = model.prefill(token_ids)
# decode:每次生成一个新 token,通常影响 TPOT
while not stop:
next_token = model.decode_one(kv_cache)
kv_cache.append(next_token)
yield tokenizer.decode(next_token)
这里有两个关键阶段:
| 阶段 | 做什么 | 常见指标 |
|---|---|---|
| prefill | 处理输入 prompt,建立初始 KV Cache | TTFT |
| decode | 每次生成一个新 token | TPOT、tokens/s |
这也是为什么大模型服务不能只看 QPS。一个请求的 prompt 很长,或者输出很长,计算成本都会明显不同。
5. GPU Runtime 与 KV Cache
GPU Runtime 是实际计算发生的地方。这里会加载模型权重,执行矩阵乘法和 Attention,并读写 KV Cache。
第一个需要建立的位置感是:
Inference Service 管 KV Cache 的生命周期和调度策略。
GPU 显存承载 KV Cache 的主要数据。
KV Cache 是什么?
在自回归生成中,模型每生成一个新 token,都需要参考前面的历史 token。如果每次都重新计算全部历史上下文,效率会很差。KV Cache 会缓存历史 token 在 Attention 层产生的 Key / Value,后续生成新 token 时直接复用。
简化理解:
没有 KV Cache:
每生成一个 token,都重新计算所有历史 token。
有 KV Cache:
历史 token 的 Key / Value 已经缓存,只计算新 token,并复用历史缓存。
它像普通后端里的热点缓存,因为都是用空间换速度。但它和 Redis 不一样:
| 对比项 | Redis | KV Cache |
|---|---|---|
| 类型 | 业务缓存 | 推理时 Attention 缓存 |
| 存储内容 | 业务数据、会话、查询结果 | Key / Value 张量 |
| 主要资源 | 内存 | GPU 显存 |
| 生命周期 | 业务 TTL 或主动删除 | 请求生命周期、调度策略、缓存复用策略 |
| 影响 | 业务查询性能 | TTFT、TPOT、并发、显存压力 |
KV Cache 是推理平台岗位里非常重要的概念,因为它直接影响并发能力、显存使用和生成速度。
6. 开源大模型到底开源什么
网上说「开源大模型」时,通常不是说「推理服务」或「模型网关」开源,而是说模型相关资产开放出来。
按平台层次拆开看:
| 部分 | 是什么 | 常见文件或系统 | 是否通常属于「开源大模型」 |
|---|---|---|---|
| 模型权重 | 训练后的参数 | safetensors、.bin | 是,最核心 |
| 模型结构配置 | 层数、隐藏维度、注意力头数等 | config.json | 通常是 |
| tokenizer | 文本和 token id 的转换规则 | tokenizer.json、vocab 文件 | 通常是 |
| 推理运行时 | 加载模型并生成 token 的服务 | vLLM、SGLang、TensorRT-LLM、Transformers | 另一个项目,不等于模型本身 |
| 模型网关 | 鉴权、路由、限流、日志、计量 | LLM Gateway、Envoy AI Gateway、Higress | 平台代码,不等于模型本身 |
| 训练数据和训练代码 | 训练模型用的数据与脚本 | 数据集、训练框架、pipeline | 不一定开放 |
后端类比:
模型权重 + config + tokenizer = 可被加载的业务资产
推理服务 = 运行这份资产的服务进程
模型网关 = 统一入口和治理层
GPU = 实际执行计算的硬件资源
一个典型模型目录可能长这样:
Qwen2.5-7B-Instruct/
config.json
tokenizer.json
tokenizer_config.json
model-00001-of-00004.safetensors
model-00002-of-00004.safetensors
model-00003-of-00004.safetensors
model-00004-of-00004.safetensors
它们的协作流程:
1. 推理服务读取 config.json,知道模型有多少层、隐藏维度多大、注意力头怎么组织。
2. 推理服务加载 safetensors 权重,把参数放到合适的内存或显存里。
3. 请求进来后,tokenizer 把文本 prompt 转成 token ids。
4. 模型用 token ids 和权重做前向计算,逐步生成新的 token ids。
5. tokenizer 再把生成的 token ids 转回文本,通过流式响应返回给用户。
最小伪代码:
config = load_json("config.json")
tokenizer = load_tokenizer("tokenizer.json")
weights = load_weights("model.safetensors")
model = build_model(config)
model.load_weights(weights)
input_ids = tokenizer.encode("你好,解释 KV Cache")
output_ids = model.generate(input_ids)
text = tokenizer.decode(output_ids)
这里的分工很清楚:
config 决定模型结构。
weights 决定模型学到了什么。
tokenizer 决定文本如何变成模型能处理的 token id。
如果缺了 tokenizer,服务可能有模型权重也不知道如何把中文 prompt 变成 token id。
如果缺了 config,推理框架不知道应该按什么结构装配这些权重。
如果缺了权重,模型结构只是空壳,没有训练后的能力。
7. SSE:大模型为什么可以边生成边返回
SSE 全称是 Server-Sent Events,可以理解成「服务端通过一个 HTTP 连接持续往客户端推数据」。
普通 HTTP 响应像这样:
请求 -> 服务端计算完整结果 -> 一次性返回完整 JSON
大模型流式响应更像这样:
请求 -> 先返回第一个 token -> 继续返回后续 token -> 最后发送结束标记
典型响应头:
HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
典型数据格式:
event: message
data: {"token":"KV"}
event: message
data: {"token":" Cache"}
event: done
data: [DONE]
SSE 本质上还是 HTTP,不是一个新的传输协议。区别是服务端不马上结束响应,而是保持连接打开,持续写入一段段文本事件。
后端视角的核心动作是 Flush():
func streamTokens(w http.ResponseWriter, tokens <-chan string) {
w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Cache-Control", "no-cache")
flusher := w.(http.Flusher)
for token := range tokens {
fmt.Fprintf(w, "data: {\"token\":%q}\n\n", token)
flusher.Flush()
}
fmt.Fprint(w, "data: [DONE]\n\n")
flusher.Flush()
}
如果没有 Flush(),数据可能会被缓冲,客户端就不能及时看到第一个 token。这样会直接拉高用户感知的 TTFT。
模型网关做 SSE 代理时,不能等推理服务完整生成完再返回。正确流转是:
客户端请求网关
-> 网关请求推理服务
-> 推理服务返回第一个 token chunk
-> 网关立刻转发给客户端
-> 后续 chunk 边读边转发
-> 收到 [DONE] 后结束响应
工程上要注意三件事:
边读边写,保留首 token 体验。
客户端断开时,及时取消上游推理请求。
网关要记录 TTFT、E2E latency、token 数和错误。
8. 为什么大模型服务不能只看 QPS
在普通 Web 服务里,QPS 是一个很重要的吞吐指标。但在大模型服务里,只看 QPS 会误判。
两个请求的 QPS 都是 1:
请求 A:prompt 20 token,输出 10 token。
请求 B:prompt 8000 token,输出 2000 token。
它们对 GPU、KV Cache、网络连接和用户等待时间的压力完全不同。
大模型服务至少要同时关注:
| 指标 | 含义 | 常见定位 |
|---|---|---|
| QPS | 每秒请求数 | 网关入口压力 |
| TTFT | Time To First Token,首 token 延迟 | 排队、路由、prefill、长 prompt |
| TPOT | Time Per Output Token,输出 token 间隔 | decode、GPU 利用率、batch 调度 |
| tokens/s | token 吞吐 | 推理服务整体吞吐 |
| P95 / P99 | 尾延迟 | 生产稳定性 |
| KV Cache usage | KV Cache 使用率 | 显存压力、并发能力、缓存复用 |
| queue length | 排队长度 | 限流、扩容、调度瓶颈 |
用户说「模型很慢」时,可以按链路拆:
1. 网关慢?
看鉴权、限流、路由、队列、网关延迟。
2. 首 token 慢?
看 TTFT。可能是排队、长 prompt、prefill 慢、prefix cache 没命中。
3. 后续 token 慢?
看 TPOT 和 tokens/s。可能是 decode 慢、batch 调度差、GPU 利用率不足。
4. 显存紧张?
看 KV Cache usage。可能是上下文太长、并发太高、缓存碎片或抢占重算。
5. 尾延迟差?
看 P95 / P99。可能是少数长请求拖慢、冷热启动、资源争抢。
这样排查比一句「QPS 高不高」更接近真实问题。
9. 常见概念边界
| 概念 | 不要混成 | 正确位置 |
|---|---|---|
| LLM Gateway | vLLM | 网关管请求,vLLM 管推理 |
| Agent | 模型本体 | Agent 调模型和工具,模型生成 token |
| KV Cache | 普通 Redis 缓存 | KV Cache 是推理时的 Attention 缓存,主要在 GPU 显存 |
| QPS | 大模型吞吐全部 | 大模型还要看 tokens/s、TTFT、TPOT |
| streaming | 模型更聪明 | streaming 只是返回方式,不改变模型能力 |
| GPU 利用率 | 用户体验 | GPU 利用率高不等于 TTFT 一定低 |
10. 小结
从后端工程视角看,大模型推理平台并不神秘。它仍然是一条服务链路,只是服务对象从普通业务请求变成了模型请求。
可以先记住这几句话:
开源大模型通常开放的是模型资产,不是整套平台。
vLLM / SGLang 是运行模型资产的推理服务。
LLM Gateway 是治理模型请求的平台入口。
KV Cache 是推理时的 Attention 缓存,主要消耗 GPU 显存。
SSE 是流式响应机制,用来尽快把生成中的 token 推给客户端。
大模型服务不能只看 QPS,还要看 TTFT、TPOT、tokens/s 和 KV Cache usage。
理解这条链路后,再深入 Transformer 的 Q/K/V、KV Cache 细节、PagedAttention 和 continuous batching,就会更容易知道每个概念在工程里解决什么问题。

浙公网安备 33010602011771号