从一次请求看懂大模型推理平台:网关、推理服务、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 GatewayLLM 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 CacheTTFT
decode每次生成一个新 tokenTPOT、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 不一样:

对比项RedisKV 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每秒请求数网关入口压力
TTFTTime To First Token,首 token 延迟排队、路由、prefill、长 prompt
TPOTTime Per Output Token,输出 token 间隔decode、GPU 利用率、batch 调度
tokens/stoken 吞吐推理服务整体吞吐
P95 / P99尾延迟生产稳定性
KV Cache usageKV 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 GatewayvLLM网关管请求,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,就会更容易知道每个概念在工程里解决什么问题。

参考资料

posted @ 2026-07-02 13:14  牛马chen  阅读(114)  评论(0)    收藏  举报