外观
LLM 推理与服务优化
目录
- 1. 学习目标
- 2. 面试结论
- 3. 面试官为什么问
- 4. 概念与边界
- 5. 原理剖析
- 6. 实现与代码
- 7. 实际项目案例
- 8. 方案权衡与常见误区
- 9. 面试题与参考答案
- 10. 递进追问
- 11. 实践任务
- 12. 相关知识与参考资料
- 13. 简明总结
1. 学习目标
- 理解自回归生成、Prefill、Decode 和流式返回的完整数据流;
- 能推导 Temperature、Top-k、Top-p 的概率变化和适用边界;
- 能从张量维度和字节数计算 KV Cache 的显存,并解释 MHA/GQA/MQA 的差异;
- 能区分单请求 KV Cache、跨请求 Prompt/Prefix Cache、应用侧 Prompt Bundle Cache 与答案缓存;
- 能分析 Batching、Paged KV、量化和并行策略对 TTFT、逐 Token 延迟、吞吐与质量的影响;
- 能设计带准入、限流、取消、超时、幂等、降级、监控和回滚的生产服务。
2. 面试结论
2.1 30 秒回答
LLM 推理分为 Prefill 和 Decode:Prefill 并行处理完整 Prompt 并建立各层 KV Cache,Decode 每次只生成一个 Token,用新 Query 读取历史 K/V。KV Cache 避免重复计算历史 K/V,但显存随层数、序列长度、KV 头数和并发线性增长。服务优化不能只看单请求速度,要同时管理 TTFT、逐 Token 延迟、吞吐、P95/P99、显存和质量;常用手段包括连续批处理、分页式 KV 管理、量化、长度感知调度、前缀缓存和并行,但每项都必须在固定模型、硬件、数据集与质量门槛下验证。
2.2 一分钟复述版
用户 Prompt 先 Tokenize,Prefill 一次前向得到首 Token Logits 和每层 K/V;之后每个 Decode 步把新 Token 作为输入,把新 K/V 追加到缓存,并对当前词表 Logits 做 Greedy 或 Temperature、Top-k、Top-p 采样。第一个可见 Token 的延迟是 TTFT,后续 Token 间延迟常用 TPOT/ITL 描述。标准 Attention 的 Prefill 随上下文存在平方项,Decode 单步读取长度为
3. 面试官为什么问
- 算法理解:是否知道生成不是一次性输出整段文本,而是自回归循环;
- 硬件意识:能否区分 Prefill 与 Decode 的计算/内存访问特征;
- 容量规划:是否会计算权重、激活和 KV Cache,而不只报“显存不够”;
- 系统设计:能否协调吞吐、交互延迟、公平性、质量和成本;
- 故障处理:是否考虑断连、排队、OOM、重复计费、量化回归和版本不一致。
4. 概念与边界
小白先这样理解:爆满寿司店怎样一片片出餐
晚高峰时,后厨先一次读完整张订单,弄清口味和已点食材,并把之后会复用的备料放进带标签的货架。接着厨师每次只做下一片寿司,端出后再决定下一片。管理员还会把多张订单动态编成一组,尽量不让炉台空等。
生活角色 → 技术概念: 整张订单的首次处理是 Prefill,一片片出餐是逐 Token Decode,带标签的备料货架是 KV Cache,动态合并订单对应连续批处理,第一片等待时间类似 TTFT。
类比边界: KV Cache 保存的是各层历史 Key/Value 张量,不是预先写好的答案;它不能免掉 Prefill,且会持续占用显存。批处理提高吞吐也可能增加排队和单请求延迟,必须通过实测权衡。
4.1 推理阶段
| 阶段 | 输入 | 主要工作 | 输出/状态 |
|---|---|---|---|
| Tokenization | 文本 | 编码、模板、截断 | input_ids、Mask |
| Prefill | 全部 Prompt Token | 并行前向、建立 K/V | 首步 Logits、KV Cache |
| Decode | 最新 Token + 历史 Cache | 单 Token 前向与采样 | 新 Token、扩展 Cache |
| Detokenize/Stream | Token ID | 增量解码与协议封装 | 文本片段、结束原因 |
4.2 延迟与吞吐指标
- TTFT(Time To First Token):请求到首个可见 Token,包含排队、Tokenize、Prefill、首步采样和网络;
- ITL(Inter-Token Latency)/TPOT(Time Per Output Token):首 Token 后相邻输出 Token 的时间,具体口径应在团队内固定;
- 端到端延迟:请求到完成,受输入长度、输出长度、停止条件与排队共同影响;
- 吞吐:单位时间完成的请求或处理/生成的 Token,必须说明是输入、输出还是总 Token;
- 并发:同时在队列、Prefill 或 Decode 的请求数,不等于批大小。
平均值会掩盖长尾,生产容量至少查看 P50/P95/P99,并按输入长度、输出长度、模型版本、租户和完成原因切片。
4.3 解码策略边界
- Greedy 每步选最大概率,确定性强但不是全局最优;
- Beam Search 维护多条高概率序列,常见于强调序列似然的任务,但开放式对话中可能增加重复和计算;
- Temperature 调整分布尖锐程度;Top-k 限制候选数量;Top-p 选择累计概率质量最小集合;
- 采样只能改变已存在的模型概率分布,不能补充模型不知道的事实或修复错误上下文;
- 固定随机种子也未必跨硬件、内核、批处理与版本完全复现,应把“可复现”限定在明确环境内。
4.4 优化边界
- KV Cache 省历史 K/V 重算,不省 Prefill 的完整上下文计算,也会消耗显存;
- Batching 提升吞吐,不保证单请求延迟下降;
- 量化降低数值位宽,不保证所有硬件上更快,也不保证质量不变;
- 前缀缓存只在模型、适配器、Prompt Token、位置/Cache 语义一致时复用;
- speculative decoding、并行和融合内核的收益依具体实现与接受率/通信开销,必须实测。
5. 原理剖析
5.1 自回归生成与采样公式
模型在第
放大 Logit 差异,分布更尖; 缩小差异,分布更平; - 工程 API 常把“零温度”实现为 Greedy 特例,不能直接代入公式除以 0。
Top-k 只保留概率最高的
5.2 Prefill、Decode 与 KV Cache 张量
对 Decoder-only Transformer 第
- 新 Query:
; - 缓存 Key/Value:各为
; - Attention 分数在 MHA 情况下为
;GQA/MQA 需要按组共享/广播 K/V 头; - 新一步只追加
的 K 和 V。
对该层某个 Token 的隐藏状态
- Query(Q) 表示当前 Token 想从历史中查什么;
- Key(K) 表示每个历史 Token 可以用什么特征被匹配;
- Value(V) 表示匹配后真正参与加权汇总的信息。
生成下一个 Token 时会产生新的 Query,但历史 Token 的 K/V 在模型权重和位置语义不变时不会改变。因此系统缓存各层历史 K/V,让新 Query 直接读取;过去的 Query 已经完成了当时那一步查询,未来不会再次使用,所以通常不把历史 Q 作为自回归推理缓存的主体。这就是 KV Cache:它是模型中间张量缓存,不是原始 Prompt、数据库记录、长期记忆或最终答案。
忽略分配器碎片、元数据、对齐和其他状态,KV Cache 理论字节数约为:
其中 2 表示 K 与 V,
5.3 为什么 Cache 能加速,为什么仍不免费
设初始 Prompt 长度为
这还不包含 Prefill 的
同时,每个 Decode 步仍要读取大部分模型权重、执行新 Token 的线性层和 FFN,因此 Decode 常受显存带宽和小批次设备利用率限制。连续批处理把不同请求的 Decode 步组合起来,提高权重读取的复用和设备利用率,但会引入调度、公平性和排队权衡。
教学插图:KV Cache 的增量复用与持续读取

替代文本: Prefill 为提示词各 Token 计算并保存 K/V;Decode 每一步只计算并追加新 Token 的 K/V,新 Query 仍与全部历史 K 交互,并按权重聚合历史 V,缓存长度随生成过程持续增加。
读图结论: KV Cache 省掉的是历史 K/V 投影的重复计算,不是历史读取和 Attention 本身;它用持续增长的显存占用换取更快的增量解码。
图片只画出一层的逻辑关系,实际模型通常每层都有独立 Cache;Q 不作为历史 Cache 的核心内容,图中的连续货架也不代表物理显存一定连续,分页式实现可以通过块表映射非连续内存。
5.4 Prompt/Prefix Cache 如何跨请求复用前缀
这里的 Prompt Cache 通常指模型服务侧的 Prefix Cache(前缀缓存):服务第一次处理一段稳定前缀时,完成 Tokenize 和 Prefill,并缓存这段前缀在各层产生的 K/V 状态;后续请求若拥有完全兼容的相同前缀,就直接复用缓存,只计算新增后缀的 Prefill,再进入 Decode。
它缓存的不是自然语言答案,也不是让模型“永久记住 Prompt”。更准确地说,它把重复的前缀计算结果变成可复用的张量状态:
text
稳定前缀 = System Prompt + Tool Schema + 公共 Few-shot
动态后缀 = 用户问题 + RAG Context + 本轮工具结果
首次请求:稳定前缀 Prefill + 动态后缀 A Prefill + Decode
后续命中:复用稳定前缀 KV + 动态后缀 B Prefill + Decode5.4.1 系统怎样识别“相同前缀”
系统一般不是拿两段自然语言做语义相似度比较,而是沿下面的精确匹配链路寻找 最长公共 Token 前缀(Longest Common Token Prefix):
- 按 Chat Template 序列化 System、Tools 和 Messages,再由固定 Tokenizer 转成
token_ids; - 构造隔离命名空间,至少包含模型/权重、Adapter、Tokenizer、模板、Cache 语义和租户或权限域指纹;
- 从第一个 Token 开始,使用 Token 前缀树/Radix Tree 逐段查找,或者把 Token 切成固定块并计算链式哈希;
- 连续命中的前缀块直接绑定到已缓存的 KV Page;遇到第一个未命中块就停止复用,从该位置继续 Prefill;
- 新计算出的完整块可以写回缓存,供后续兼容请求复用;显存压力、TTL 或淘汰策略可能让本来相同的前缀仍然 Miss。
固定块哈希可以抽象为:
其中 namespace 约束模型和安全边界,token_ids_i 是当前 Token 块。因此系统命中的是“从开头连续一致的前缀”,不是在 Prompt 中间偶然找到一段相同文字。
下面是概念级伪代码,不代表某个具体引擎 API:
python
token_ids = tokenizer(apply_chat_template(messages))
parent_hash = namespace_fingerprint(model, adapter, tokenizer, tenant)
matched = 0
for block in split_into_blocks(token_ids):
block_hash = hash(parent_hash, block)
if block_hash not in prefix_cache:
break
reuse_kv_pages(prefix_cache[block_hash])
matched += len(block)
parent_hash = block_hash
prefill(token_ids[matched:])例如,块大小假设为 4 Token:
text
请求 A:[S1 S2 S3 S4] [T1 T2 T3 T4] [用户问题 A ...]
请求 B:[S1 S2 S3 S4] [T1 T2 T3 T4] [用户问题 B ...]
命中第 1、2 块 ─────┘ 从第 3 块开始计算如果请求 B 在 T2 就发生变化,按块缓存的实现通常只能安全复用变化点之前的完整块;具体是否支持更细粒度匹配取决于引擎的数据结构和块策略。肉眼相同的文本也不保证命中,因为角色标记、空白、工具顺序、模板或 Tokenizer 版本可能让最终 Token 序列不同;反过来,缓存命中只代表计算前缀兼容,不代表两个用户问题语义相同。
多轮提问还要区分两种情况:独立请求 System + Tools + 问题 A/B 通常只能共享稳定的 System + Tools;同一会话的第二轮输入一般是 System + 问题 A + 回答 A + 问题 B,它可能继续命中更长的历史前缀。但能否复用第一轮 Decode 期间生成的“回答 A”对应 K/V,取决于引擎是否把生成 Token 的 KV Page 持久化并纳入跨请求索引;不支持时仍需为这部分重新 Prefill。缓存只是一项可选性能优化,因此无论 Hit 还是 Miss,答案正确性都应相同。
因此它主要降低重复长前缀的 Prefill 计算、TTFT 和输入侧资源消耗;动态后缀和输出 Decode 仍需正常计算。某些托管 API 会根据缓存输入 Token 单独计费,但计费、最小可缓存长度、有效期和显式/自动缓存方式属于产品策略,不能从“支持 Prompt Cache”直接推导,接入时应核对目标模型与版本。
图:机制|Prompt/Prefix Cache 的跨请求复用边界
替代文本: 首次请求为稳定公共前缀执行 Prefill 并写入前缀 KV Cache;后续请求在模型、Tokenizer、模板、Token 序列和隔离域兼容时命中,只计算动态后缀;任一关键指纹变化都回退到完整 Prefill。
图表加载中…
读图结论: Prompt/Prefix Cache 跨请求复用的是稳定前缀的模型中间状态;它以严格的版本、Token 与安全隔离一致性换取 Prefill 加速,命中失败只是性能下降,错误命中则可能造成静默答案污染或越权风险。
一次可靠命中至少要约束以下维度:
- 计算身份:模型、权重/量化制品、Adapter、Tokenizer、Chat Template 和推理 Cache 语义兼容;
- 前缀身份:比较实际 Token ID 前缀及位置语义,不能只比较肉眼看起来相同的字符串;
- 安全身份:租户、权限、数据域和敏感级别不得越界共享;公共前缀与私有上下文要有明确边界;
- 生命周期:Prompt、工具 Schema 或模型升级后,通过版本指纹主动失效,而不是等待旧缓存自然过期;
- 内容布局:稳定且可共享的内容尽量放在前面,时间戳、Request ID、用户数据、RAG 片段和工具结果放在后面。前部任一 Token 改动都可能使其后的长前缀无法命中。
四种常被混称为“Prompt 缓存”的机制应分开管理:
| 机制 | 缓存内容 | 复用范围 | 主要收益 | 主要风险 |
|---|---|---|---|---|
| 单请求 KV Cache | 当前请求已处理 Token 的各层 K/V | 同一次生成的 Decode 步 | 避免每步重算历史 K/V | 显存随长度和并发增长 |
| Prompt/Prefix Cache | 稳定前缀 Prefill 后的 K/V 状态 | 多个兼容请求之间 | 降低重复前缀 Prefill、TTFT 和输入计算 | 错误命中、跨租户泄漏、版本污染 |
| Prompt Bundle Cache | 应用从 Registry 解析出的 Prompt 文本、Schema、工具与模型策略配置 | 应用实例或配置分发层 | 减少配置读取并稳定运行版本 | 缓存旧配置、运行中版本漂移 |
| Response Cache | 完整请求对应的最终答案 | 相同且允许复用的业务输入 | 跳过整次模型调用 | 时效错误、权限泄漏、非确定输出被误复用 |
Agent 中最常见的布局是把稳定的 System Prompt、工具定义和公共示例作为前缀,把会话消息、RAG 上下文和工具结果追加在后面。但不能为了提高命中率而削弱指令优先级、ACL 或工具权限;工具集合按请求动态装载时,还要接受前缀变化或按稳定工具集合分桶。
验证时不要只看总延迟,应同时记录 cache_hit_rate、缓存输入 Token 数、命中/未命中 TTFT、Prefill 时延、端到端成本和错误命中审计;回归测试至少覆盖合法命中的 Logits/答案一致性,以及跨模型、跨 Prompt 版本、跨工具版本、跨租户必须不命中的负例。
5.5 分页式 KV 与连续批处理
传统按最大长度预留连续 Cache 容易产生内部浪费,也难以处理动态增长。PagedAttention 的核心思想是把逻辑连续的 KV 序列映射到非连续固定大小块,由块表寻址,类似虚拟内存分页;这有利于减少碎片并支持共享/复制管理。代价是块表、调度器和内核复杂度,实际效果依工作负载。
连续批处理不等待整批请求全部结束:调度器在迭代边界移除完成/取消请求,加入新请求。它通常提升总体吞吐,但若 Prefill 和 Decode 不做隔离或预算,大 Prompt 可能阻塞交互式 Decode,造成 ITL 尾延迟。
5.6 量化原理与边界
线性量化把浮点权重近似为:
其中
- Weight-only:权重量化,激活保留较高精度;适合权重带宽主导场景;
- Weight + Activation:同时量化,可能进一步提高特定硬件吞吐,但校准和异常值处理更难;
- KV Cache 量化:降低长上下文和并发的 Cache 占用/带宽,但需专门质量回归;
- 量化是否加速取决于硬件是否有高效内核、反量化开销、批大小和瓶颈位置。
5.7 服务调用链可视化
图 1:生产 LLM 请求从准入到流式完成的调用链替代文本: 客户端请求先经过鉴权、配额与 Token/KV 预算,再由调度器执行 Prefill 和循环 Decode,流式网关处理断连与取消,指标链路记录版本、延迟、显存和结束原因。
图表加载中…
读图结论: 推理优化不是模型 Worker 单点问题;准入预算、调度、流式取消和可观测性共同决定容量、尾延迟和是否会浪费 GPU。
5.8 复杂度因果链
- 输入长度增加 → Prefill Attention 平方项和 TTFT 上升 → 单个大请求更容易阻塞批次;
- 输出长度增加 → Decode 步数与 KV Cache 线性增加 → 端到端延迟、占用时间和成本上升;
- 并发增加 → KV 总量与调度队列增加 → 先出现显存或尾延迟瓶颈,而非必然提高吞吐;
- 批次增大 → 权重读取摊薄、吞吐可能提高 → 单请求排队和 ITL 可能恶化;
- 量化位宽降低 → 权重/KV 容量和带宽下降 → 质量、校准和内核兼容风险增加。
6. 实现与代码
6.1 最小可运行 Python 采样器
运行环境:Python 3.10+,仅使用标准库。示例展示 Greedy、Temperature、Top-k 与 Top-p 的候选过滤;它不包含真实模型。
python
import math
import random
def softmax(logits):
largest = max(logits)
exps = [math.exp(value - largest) for value in logits]
total = sum(exps)
return [value / total for value in exps]
def sample_token(logits, temperature=1.0, top_k=None, top_p=1.0, rng=None):
if not logits:
raise ValueError("logits 不能为空")
if temperature < 0:
raise ValueError("temperature 不能为负数")
if temperature == 0:
return max(range(len(logits)), key=logits.__getitem__)
if not 0 < top_p <= 1:
raise ValueError("top_p 必须位于 (0, 1]")
candidate_ids = list(range(len(logits)))
if top_k is not None:
if top_k <= 0:
raise ValueError("top_k 必须为正数")
candidate_ids = sorted(candidate_ids, key=logits.__getitem__, reverse=True)[:top_k]
scaled_logits = [logits[index] / temperature for index in candidate_ids]
probabilities = softmax(scaled_logits)
ranked = sorted(
zip(candidate_ids, probabilities), key=lambda item: item[1], reverse=True
)
nucleus = []
cumulative = 0.0
for token_id, probability in ranked:
nucleus.append((token_id, probability)) # 至少保留概率最高的一个
cumulative += probability
if cumulative >= top_p:
break
ids, weights = zip(*nucleus)
rng = rng or random.Random()
return rng.choices(ids, weights=weights, k=1)[0]
logits = [2.0, 1.8, 1.6, -0.5]
assert sample_token(logits, temperature=0) == 0
assert sample_token(logits, top_k=1, rng=random.Random(7)) == 0
experiment_rng = random.Random(7)
print("greedy:", sample_token(logits, temperature=0))
print(
"repeatable sample sequence:",
[
sample_token(
logits,
temperature=0.8,
top_k=3,
top_p=0.9,
rng=experiment_rng,
)
for _ in range(8)
],
)6.2 关键实现说明
- Softmax 先减最大 Logit,防止指数溢出;
temperature=0作为 Greedy 分支处理,而不是执行除零;- Top-k 后重新计算候选概率,再按累计概率构造 nucleus;
- 至少保留一个候选,避免极小
top_p得到空集合; - 重复采样必须复用同一个 RNG;如果函数内部每次用相同 Seed 新建 RNG,每次都会回到同一状态,经验分布实验就是假的;
- 真实框架还会处理重复惩罚、最小/最大长度、停止序列、无效值、随机数生成器和批处理,参数组合语义必须以目标版本文档与测试为准。
6.3 边界条件与验证
- 对全相等 Logits、极大/极小 Logits、
top_k>V、非法 Temperature/Top-p 写测试; - 固定 Logits 重复采样足够次数,比较不同 Temperature 下的经验分布,而不是只看单个样本;
- 服务评测同时固定 Prompt、模型、Tokenizer、采样配置和随机环境;
- 质量比较使用任务指标/人工评审与失败切片,不能用“输出更随机”替代质量证据;
- KV Cache 优化要比较 Cache 与无 Cache 的逐步 Logits 容差,并覆盖长序列、批次和取消释放。
6.4 技术栈与横向选型
LLM 服务选型必须先固定模型、硬件、序列长度分布、并发和 SLO。下表是参考实现而非通用胜负结论;不同引擎版本的能力和 API 需在部署时重新核对。
| 技术点 ID | 技术点/环节 | 类型 | 采用方案 | 链路职责 | 版本/证据边界 |
|---|---|---|---|---|---|
| TP-IS-01 | 连续批处理与 KV Cache 调度 | 推理服务 | vLLM 作通用 GPU 服务参考;TGI 作同层候选 | 执行请求排队、Prefill/Decode、KV Cache 管理与流式输出 | 吞吐与延迟受模型、硬件和流量形状影响;不以一次基准宣称普遍更快 |
| TP-IS-02 | 权重量化 | 算法/库 | AWQ 作离线量化参考;GPTQ 作候选 | 降低权重显存和带宽需求,生成带量化指纹的模型产物 | 实际加速依赖 Kernel 与硬件;每个模型和质量切片都要回归 |
| TP-IS-03 | 客户端调用与流式协议 | 协议 | 对外 HTTP + SSE;受控内部链路可评估 gRPC | 承载请求、取消、流式 Token、错误码和版本指纹 | 协议只解决传输,不替代准入、幂等、限流和模型路由 |
| TP-IS-04 | 跨请求前缀复用 | 推理优化 | 版本化 Prompt/Prefix Cache;无跨请求缓存作基线 | 复用兼容公共前缀的 Prefill K/V,减少重复输入计算 | 具体能力依模型服务版本;必须验证命中收益、隔离、失效与输出一致性 |
| 技术点 ID | 候选方案 | 优点 | 缺点/代价 | 适用场景 | 不适用场景 | 选择结论与依据 |
|---|---|---|---|---|---|---|
| TP-IS-01 | vLLM | 连续批处理和 KV Cache 管理面向高并发生成负载 | 引擎、模型与量化支持随版本变化,需独立运维 | GPU 在线生成、长短请求混合、需 OpenAI 兼容服务的参考场景 | 单请求离线实验或目标模型未经支持验证 | 作默认基准,用真实长度分布测 TTFT、TPOT、吞吐和错误率 |
| TP-IS-01 | Hugging Face TGI | 容器化服务链路完整,与 Hugging Face 模型生态结合 | 同样存在模型、Kernel 与版本支持矩阵,切换有协议与运维成本 | 团队已标准化 Hugging Face 部署与容器运维 | 只因生态偏好即期待在任意硬件上更快 | 与 vLLM 在相同模型、硬件和压测轨迹上对比后决策 |
| TP-IS-02 | AWQ | 针对权重量化保留显著通道信息,常有现成推理集成 | 需校准数据与兼容 Kernel,不同任务可能质量回归 | GPU 权重带宽或显存受限,引擎已验证支持 | 高精度敏感任务或引擎缺少高效 Kernel | 作第一个离线候选,以分层质量和端到端延迟门禁决策 |
| TP-IS-02 | GPTQ | 成熟的后训练量化路线,有多种工具与模型产物 | 量化耗时、校准敏感,实际推理效率取决于引擎实现 | 现有模型产物与部署引擎已完成兼容验证 | 只比较文件大小、不做任务质量和 Kernel 基准 | 作同层对照,不以量化格式名称替代实测 |
| TP-IS-03 | HTTP + SSE | 浏览器和通用 SDK 友好,通过标准 HTTP 设施易集成 | 双向控制和严格类型契约较弱,断连恢复需应用层设计 | 对外文本生成 API、浏览器和多语言客户端 | 高频内部二进制交互或复杂双向流 | 对外默认参考,必须定义取消、重连、错误与版本语义 |
| TP-IS-03 | gRPC Streaming | Protobuf 契约严格,支持双向流与内部服务治理 | 浏览器直连不便,网关、调试和客户端生成有额外成本 | 受控内部服务、多语言强契约通信 | 公网浏览器优先且团队无 gRPC 运维栈 | 仅在内部类型契约与流控收益高于复杂度时采用 |
| TP-IS-04 | 不做跨请求前缀缓存 | 实现简单,请求天然隔离,Prompt 更新不需额外失效 | 重复长 System/Tools 前缀每次完整 Prefill | 前缀短、变化频繁、低复用或隔离风险高 | 大量请求共享很长且稳定的公共前缀 | 作为正确性和性能基线,先证明重复 Prefill 是实际瓶颈 |
| TP-IS-04 | 版本化 Prompt/Prefix Cache | 可降低高复用长前缀的 TTFT、输入计算和容量压力 | 增加 Cache Key、显存/存储、隔离、逐出、失效和观测复杂度 | Agent 工具定义、公共规则和 Few-shot 稳定且重复率高 | 前缀前部含时间戳/随机值,或不同租户私有内容无法安全共享 | 仅在固定流量回放中同时通过收益、一致性和安全负例后启用 |
6.5 架构与技术调用流程
图:架构|LLM 在线推理服务组件边界
替代文本: 客户端经 HTTP/SSE 或内部 gRPC 进入网关,准入控制执行鉴权、配额和 Token/KV 预算,路由器选模型版本,调度器组批后调用推理引擎、权重与 KV Cache,流式网关返回结果,观测系统记录每阶段指标。
图表加载中…
读图结论: 推理引擎只是核心计算层,可用的在线服务还需要准入、版本路由、取消传播和分阶段观测。
架构图将协议、调度、权重优化与运行治理分层。如果只有平均端到端延迟,就无法判断瓶颈是排队、Prefill、Decode、网络还是客户端断连。
图:技术调用流程|生成请求的准入、流式与降级
替代文本: 请求先经鉴权和预算准入,超限时立即拒绝;通过后进入调度与推理,资源不足时只能在策略允许下路由到受验降级模型,否则返回可重试过载错误;成功时持续流式返回并传播取消。
图表加载中…
读图结论: 超额在准入层拒绝,过载只在有明确质量和合规边界时降级,客户端取消必须穿透到引擎释放计算与 KV Cache。
时序图表达的是用户可见语义,不承诺任意引擎的内部 API。压测必须同时验证正常、过载、断连和降级路径。
7. 实际项目案例
示例项目,非真实仓库实现;容量、延迟和成本数值必须在目标硬件实测。
7.1 背景、目标与约束
建设多租户企业 LLM Gateway,为聊天、文档摘要和结构化抽取提供流式推理。交互请求重视 TTFT/ITL,离线摘要重视吞吐;租户有不同配额,Prompt 长度差异大,GPU 显存固定,模型和 Adapter 需要版本化切换。
7.2 架构与调用链
- 网关鉴权、幂等键、租户限流和请求大小检查;
- Tokenizer 服务计算实际输入 Token,结合最大输出预算估算 KV;
- 准入器按模型池、优先级、显存和队列时限决定接收、排队或拒绝;
- 调度器分离/协调 Prefill 与 Decode,使用连续批处理并处理取消;
- Worker 加载固定模型、量化、Adapter 和 Tokenizer 制品,执行 Prefill/Decode;
- 流式层返回增量文本和明确
finish_reason; - 追踪记录模型/配置版本、队列、TTFT、ITL、Token、KV、批次、错误和计费事件。
7.3 方案选择与实现难点
真正困难的不是单独启用某个推理优化,而是让资源、调度、协议、质量和版本语义在同一条链路中保持一致:
| 技术难点 | 难在哪里 | 关键设计与权衡 | 必须取得的验证证据 |
|---|---|---|---|
| 混合负载调度 | 长 Prompt 的 Prefill、短对话的 Decode 和离线生成竞争同一计算资源;吞吐最优不等于交互体验最优 | 分队列或做优先级/长度感知调度;必要时分块 Prefill,但要评估切换开销与公平性 | 按输入/输出长度和请求类型切片的排队、TTFT、ITL、吞吐与饥饿时间 |
| KV 容量与生命周期 | 单请求 Cache 会随长度增长,取消、抢占、异常退出和块碎片都会让理论公式低估真实占用 | 用 Token/KV 块而非仅用请求数做准入;为请求建立分配、追加、抢占、释放和对账状态 | 请求级 KV 账本、块池高水位、断连释放时延、长短请求混合压测结果 |
| 流式协议与幂等 | 首个片段发出后,请求已经产生外部可见结果;普通 HTTP 重试语义不再成立 | 区分“未开始生成、已生成未发送、已流式发送、已完成记账”,只对明确可恢复阶段自动重试 | 故障注入下的事件序列、唯一计费记录、客户端无重复拼接、明确 finish_reason |
| 量化制品治理 | 性能收益依赖硬件和内核,质量损失又可能只出现在长上下文、格式或安全切片 | 把权重、激活、KV 量化作为独立制品;性能门槛与质量门槛同时发布和回滚 | 固定硬件上的基准报告、分任务质量集、长上下文/结构化输出/安全回归及制品指纹 |
| 前缀缓存一致性 | 文本相同不代表 Token、位置语义或 Adapter 相同,错误命中可能静默污染答案 | Cache Key 纳入模型、Adapter、Tokenizer、模板、Token ID、位置与 Cache 配置;版本切换主动失效 | 跨租户、跨版本不命中测试;合法命中的 Logits 容差对比;命中率与错误命中审计 |
7.4 可验证的工程亮点与证据边界
下表是候选项目亮点,不是已经取得的成果。只有完成对应验证后,才能在项目复盘或面试中使用“实现了”“降低了”“避免了”等结果性表达。
| 候选亮点 | 技术价值 | 最小证据包 | 表达边界 |
|---|---|---|---|
| Token/KV 双预算准入 | 在真正分配显存前识别长上下文与高并发风险 | 预算公式与误差说明、请求级 KV 账本、极端长度压测、拒绝原因分布 | 只能说明在已测模型、硬件、长度和并发范围内稳定,不能外推到任意负载 |
| 端到端取消与资源回收 | 客户端断连后尽快停止无效 Decode 并释放 Cache | 网关到 Worker 的取消 Trace、活跃序列/KV 回落曲线、异常退出对账 | 未覆盖进程崩溃和网络分区时,不能声称“资源绝不泄漏” |
| 工作负载感知调度 | 同时治理交互式尾延迟、离线吞吐和租户公平性 | 固定到达分布的 A/B 压测、长度切片分位数、饥饿/抢占记录 | GPU 利用率上升不能单独证明用户体验改善 |
| 制品与 Cache 全链路指纹 | 避免模型、Adapter、Tokenizer、模板和 Cache 语义混用 | 每个 Trace 的版本字段、跨版本隔离测试、灰度与回滚记录 | 缺少任一影响输出的版本字段时,只能称“部分可追踪” |
| 性能—质量联合发布门禁 | 防止吞吐优化以格式、长上下文或安全质量为代价 | 同一制品的性能报告、固定回归集、失败切片、门禁与回滚演练 | 没有代表性业务集和人工复核时,不能宣称“质量无损” |
7.5 生产问题闭环演练
以下均为基于机制设计的故障演练,不代表本仓库或某个真实服务发生过这些事故。表中的“根因”是对应演练场景的设定,线上仍需以 Trace、指标、日志和制品版本确认。
| 问题 | 现象与影响 | 定位证据 | 场景根因 | 应急止损 | 长期修复 | 验证 | 防复发 |
|---|---|---|---|---|---|---|---|
| 高并发 OOM | Worker OOM/重启,在途请求失败,模型池容量进一步下降 | 输入与最大输出 Token、活跃序列、KV 块/分配器水位、取消记录、OOM 前 Trace | 准入只看权重或请求数,未预算峰值 KV、碎片和延迟释放 | 暂停长请求准入,降低并发/最大输出,摘除并排空异常 Worker,切换低容量模型 | 请求级 KV 预算、水位限流、块对账、取消释放和 Worker 隔离 | 长短请求混合阶梯压测下不崩溃;拒绝发生在预算边界且资源可回落 | 容量矩阵纳入发布门禁;KV 水位与释放时延告警;定期对账 |
| P99/ITL 突增 | 平均延迟正常,但交互请求卡顿或中途停顿 | 按长度/类型切片的排队、Prefill/Decode 时间、批次组成和抢占记录 | 大 Prefill 阻塞 Decode,或等待组批/公平策略不合理 | 限制超长同步输入,隔离离线队列,提高交互请求优先级 | 分块 Prefill、长度感知与带老化的公平调度,分别设 TTFT/ITL 预算 | 回放相同到达与长度分布,对比 P50/P95/P99、吞吐和饥饿时间 | 固定混合负载基准;同时告警队列时间和计算时间,禁止只看平均值 |
| 断连后仍占 GPU | 客户端已离开,活跃序列与 KV 水位不下降,吞吐被无效任务占用 | 网关断连时间、取消 Span、调度器序列状态、Worker 完成/释放事件 | 取消只停在网关,或异常分支未触发 Cache 释放 | 主动终止对应 Run,超时回收孤儿序列,必要时排空 Worker | 端到端取消令牌、幂等清理钩子、租约/心跳与请求—块对账 | 注入断连、超时和 Worker 异常后,序列在预算时间内结束且 KV 回落 | 取消延迟 SLI、孤儿序列告警、周期性资源对账与故障演练 |
| 重试后重复文本或计费 | 客户端收到重复片段,或同一业务请求产生多条计费/完成记录 | 幂等键、流式事件序号、Run 状态迁移、供应商调用 ID、计费唯一键 | 流式开始后仍按无副作用请求自动重放,状态与记账未原子化 | 关闭该错误类自动重试,按 Run ID 查询既有结果,冻结重复记账 | 明确阶段状态机、事件序号/续传点、计费唯一约束和错误分类 | 在首 Token 前后分别注入故障,确认只产生一个最终结果和一次记账 | 重试策略契约测试;审计重复键;新增错误码必须声明可重试语义 |
| 量化后格式/能力回归 | 吞吐可能改善,但 JSON 解析、长上下文、事实或安全切片失败增加 | 制品指纹、任务/长度切片指标、解析错误、逐层或 Logits 对比 | 校准集不代表业务,异常值或敏感层不适合当前量化粒度 | 回滚全精度或已验证制品;关键任务临时路由高精度模型 | 扩充校准与回归集,调整 Group/敏感层精度,对权重与 KV 量化分别评估 | 同一数据、采样与硬件复测质量门槛和性能门槛,人工检查关键失败样本 | 量化制品独立版本;质量—性能双门禁;新业务切片先影子评测 |
| 前缀 Cache 错命中 | Cache 命中率看似上升,但答案在版本/租户切换后静默异常 | Cache Key 展开值、模型/Adapter/Tokenizer/模板指纹、命中 Trace、Logits 对比 | Key 漏掉影响输出的版本、Token 或位置语义,或跨租户共享错误 | 关闭可疑前缀缓存并清空相关命名空间,回退到正常 Prefill | 完整版本化 Key、租户隔离、发布失效协议和命中审计 | 跨版本/租户必须不命中;合法命中与无 Cache 输出在约定容差内一致 | Cache Key 契约测试;版本字段变更评审;错误命中率和命名空间监控 |
| 流式输出乱码 | 中英混合、Emoji 或组合字符出现替换符/残缺,客户端无法可靠拼接 | 原始 Token ID、字节片段、增量解码器状态、客户端事件边界 | 在任意字节或子词边界直接解码/截断,未保留增量状态 | 暂停有问题的字节级拼接,改为安全缓冲或返回完整片段 | 使用 Tokenizer 的状态化增量解码,协议明确事件编码与拼接规则 | 覆盖中文、Emoji、组合字符、停止序列和任意分块的端到端回归 | 流式字符集用例进入契约测试;协议版本化;解码异常指标与样本留存 |
7.6 监控、容量与验收
监控分三层:
- 入口:请求率、Token 长度、拒绝/限流、队列等待、租户配额;
- 调度/Cache:运行序列、批次 Token、KV 块使用率、碎片、抢占、取消释放时延;
- Worker/结果:TTFT、ITL、吞吐、显存、内核错误、完成原因、模型版本、质量与安全回归。
验收必须在目标硬件、固定模型/精度、代表性长度分布和并发曲线下完成。报告原始配置、预热、测量窗口、分位数、质量门槛和失败样本;没有这些证据,不宣称“提升若干倍”。
8. 方案权衡与常见误区
8.1 优化手段与代价
| 手段 | 主要收益 | 主要代价/失效边界 |
|---|---|---|
| KV Cache | 避免历史 K/V 重算 | 显存随长度和并发增长 |
| Continuous Batching | 提高设备利用率和吞吐 | 调度复杂,可能增加等待与尾延迟 |
| Paged KV | 降低连续预留和碎片问题 | 块管理与内核复杂度 |
| Prefix Cache | 复用公共前缀 Prefill | 命中率、隔离、安全和失效复杂 |
| Weight Quantization | 减少权重容量/带宽 | 质量与硬件内核兼容性 |
| KV Quantization | 降低长上下文 Cache 成本 | Attention 质量和实现复杂度 |
| Tensor Parallel | 单卡放不下或需更高计算并行 | 每层通信与拓扑敏感 |
| Speculative Decoding | 目标模型一次验证多个草稿 Token | 接受率、草稿成本和实现复杂度 |
8.2 常见错误回答
- “KV Cache 让 Attention 变成
”:新 Query 仍读取长度 的历史 K/V; - “批越大延迟越低”:吞吐可能提高,但排队和单请求 ITL 可能恶化;
- “INT4 一定比 FP16 快四倍”:位宽不是端到端速度的线性保证;
- “Temperature 越低越准确”:它只改变采样分布,事实性还依模型和上下文;
- “平均延迟合格即可”:交互体验和容量事故常由 P95/P99 与长度切片暴露;
- “流断了重试就行”:若已生成/记账,重试需要幂等和续传语义。
8.3 生产风险与防线
- 过载雪崩:有界队列、Token/KV 准入、租户限流、快速失败和降级模型;
- 资源泄漏:取消传播、超时清理、请求状态机和 KV 块对账;
- 质量回归:量化/内核/模型每个制品独立走 Golden、任务、安全和长上下文评测;
- 版本混用:模型、Adapter、Tokenizer、采样默认值和 Cache Key 绑定发布;
- 观测污染:统一 TTFT/ITL/Token 口径,区分排队、计算和网络时间;
- 隐私泄漏:跨租户 Cache 严格隔离,Prompt/输出日志按数据分类脱敏与限时保存。
8.4 可复用的优化与排障经验
- 先固定实验清单,再谈优化收益:至少固定模型、制品哈希、Tokenizer、Prompt 模板、采样参数、硬件、驱动/内核、预热方式、长度分布、并发曲线和质量集;否则前后结果无法归因。
- 先拆排队、Prefill、Decode、网络,再定位“模型慢”:TTFT 变差不等于 Prefill 内核变慢,ITL 变差也可能来自调度抢占;Trace 必须能还原阶段时间。
- 容量单位优先使用 Token、KV 块和执行步,而非只用请求数:同样十个请求,长 Prompt、长输出和不同 KV 头数可能产生完全不同的资源压力。
- 先定义副作用与完成边界,再配置重试:流式片段、计费和业务回调一旦外部可见,就必须依赖幂等键、事件序号和状态机,而不是“失败就重试”。
- 性能优化与质量、安全共同发布:量化、Cache、调度、内核和模型升级都可能改变输出;每次只改变可追踪变量,并保留灰度、回滚和失败样本。
- 把经验固化为资产:长期保留基准测试清单、混合负载矩阵、容量预算表、故障注入用例、Cache Key 契约、发布门禁和故障 Runbook,避免经验只停留在个人判断中。
面试中应把亮点表达为“难点—决策—权衡—证据—边界”。例如可以说“我设计了基于 Token/KV 的准入,并用长短请求混合压测验证预算误差和资源回落”;没有压测记录时,只能说“设计了验证方案”,不能把预期收益说成已实现结果。
9. 面试题与参考答案
问题 1:Prefill 和 Decode 有什么区别?
- 难度:基础;
- 考察点:自回归推理阶段;
- 合格答案要点:Prefill 并行处理完整 Prompt 并建立 Cache;Decode 每步处理新 Token、读取历史 Cache;
- 优秀答案加分项:连接 TTFT、ITL、计算密集/带宽特征和调度;
- 常见错误:认为首 Token 也只处理一个输入 Token;
- 可继续追问:长 Prompt 和长输出分别压垮什么资源?
问题 2:KV Cache 显存如何估算?
- 难度:中级;
- 考察点:张量维度和容量规划;
- 合格答案要点:
,说明 K/V、层、批、头、长度、头维和字节; - 优秀答案加分项:补充 GQA/MQA、块分配碎片、并发和其他内存;
- 常见错误:只按参数量估算服务显存;
- 可继续追问:为什么不能在已有 MHA 权重上直接把 KV 头改成 1?
问题 3:连续批处理为什么能提高吞吐,又可能伤害延迟?
- 难度:中级;
- 考察点:硬件利用率与排队论直觉;
- 合格答案要点:动态加入/移除序列,摊薄权重读取;等待组批、大 Prefill 和公平调度会增加尾延迟;
- 优秀答案加分项:提出 Prefill/Decode 分离、长度感知、公平性和 P99 切片;
- 常见错误:只看 GPU 利用率判断用户体验;
- 可继续追问:如何设计压测矩阵证明调度策略更好?
问题 4:量化后吞吐提高但业务质量下降,如何处理?
- 难度:高级;
- 考察点:性能—质量联合决策与回滚;
- 合格答案要点:确认制品/配置,按任务和长度切片复现,对比 Logits/输出,调整粒度或敏感层精度并保留回滚;
- 优秀答案加分项:区分权重、激活、KV 量化,说明校准集代表性和灰度门禁;
- 常见错误:用平均自动指标掩盖安全/格式失败;
- 可继续追问:什么证据可以支持只对部分层保留高精度?
问题 5:Prompt/Prefix Cache 与 KV Cache、答案缓存有什么区别?
可直接口述的参考回答: 我会先澄清 Prompt Cache 的口径。模型服务里的 Prompt 或 Prefix Cache,是跨请求复用相同稳定前缀 Prefill 后的 K/V 状态,主要减少重复输入计算和 TTFT;普通 KV Cache 则服务于同一个请求的逐 Token Decode。它们都不等于答案缓存,答案缓存会直接复用最终输出。Prefix Cache 命中必须同时满足模型、Tokenizer、模板、Token 前缀、位置语义和租户隔离一致,项目中我会用命中率、缓存 Token、命中/未命中 TTFT、输出一致性和跨版本/跨租户不命中测试验证,而不是只看平均延迟。
- 难度:中级;
- 考察点:推理阶段、缓存层次、版本治理与多租户安全;
- 合格答案要点:区分单请求 Decode KV、跨请求 Prefix KV 和最终答案缓存,说明 Prefix Cache 省 Prefill 而不省动态后缀与 Decode;
- 优秀答案加分项:说明实际 Token/位置匹配、版本指纹、租户隔离、稳定内容前置及命中/未命中对照指标;
- 常见错误:认为缓存了 Prompt 文本或最终答案,或者只比较字符串就跨租户共享;
- 可继续追问:为什么把当前时间写在 System Prompt 最前面会显著降低缓存收益?
10. 递进追问
- 基础概念:Greedy、Temperature、Top-k、Top-p 分别如何改变下一 Token 选择?
- 张量推导:Decode 第
步 Q、K、V 和 Attention 分数形状是什么? - 容量计算:给定层数、KV 头、头维、dtype、长度和并发,计算理论 KV 字节并说明为什么实测更高;
- 复杂度边界:为什么 KV Cache 避免重复计算,却没有让长输出成本变成常数?
- 调度权衡:交互短请求和离线长摘要共享 GPU,如何兼顾吞吐、公平与 P99?
- 事故复盘:发布量化模型后出现 Cache 命中错误、重复计费和尾延迟上升,你如何止损、定位、修复并建立防回归门禁?
11. 实践任务
- [ ] 采样实验:运行本文代码,用固定 Logits 重复采样,绘制不同 Temperature/Top-p 的经验频率;
- [ ] KV 计算:为 MHA、GQA、MQA 分别计算相同长度和并发下的理论 Cache;
- [ ] 基准设计:定义输入长度 × 输出长度 × 并发 × 精度的压测矩阵,记录 TTFT、ITL、吞吐、显存和质量;
- [ ] 故障注入:模拟客户端断连、队列超时、Worker OOM 和流式后重试,验证取消、幂等和资源释放;
- [ ] 量化回归:建立事实、格式、安全、长上下文和代码五类固定样本,对比全精度与量化制品;
- [ ] 容量计划:根据业务长度分布而非单一最大长度,设计准入水位与降级策略;
- [ ] 前缀缓存实验:固定模型与动态后缀,比较合法命中、Prompt 版本变化和跨租户负例的缓存 Token、TTFT、输出一致性与审计结果;
- [ ] 面试口述:用一分钟讲清 Prefill → KV Cache → Decode → Continuous Batching 的因果链。
12. 相关知识与参考资料
12.1 相关知识
- 输入链路:Token 与 Embedding;
- 核心算子:Attention 与 Transformer;
- 权重来源:LLM 预训练、微调与对齐。
- Prompt 资产治理:Prompt 与结构化输出。
12.2 一手参考资料
以下资料均于 2026-07-10 访问。服务框架支持的后端、量化格式、Cache 类型和参数会变化,部署时必须重新核对锁定版本的官方文档与源码。
- Hugging Face Transformers, Generation strategies 官方文档;
- Hugging Face Transformers, Cache strategies 官方文档;
- Kwon et al., Efficient Memory Management for Large Language Model Serving with PagedAttention,vLLM/PagedAttention 论文;
- vLLM Project, 官方文档与官方源码;
- Frantar et al., GPTQ: Accurate Post-Training Quantization for Generative Pre-trained Transformers;
- Lin et al., AWQ: Activation-aware Weight Quantization for LLM Compression and Acceleration;
- Leviathan et al., Fast Inference from Transformers via Speculative Decoding;
- Dao, FlashAttention-2,高效 Attention 内核论文。
13. 简明总结
一句话记忆: 单请求 KV Cache 加速 Decode,跨请求 Prompt/Prefix Cache 复用稳定前缀的 Prefill;服务优化是在正确性和隔离门槛下联合管理 TTFT、ITL、吞吐、显存和尾延迟。
- 单请求 KV Cache 的理论字节约为
,它省重算但随长度和并发增长;Prompt/Prefix Cache 不缓存答案,只在模型、Token 前缀、位置和安全隔离兼容时跨请求复用前缀 K/V; - Temperature、Top-k、Top-p 只改变采样分布,不能修复知识和上下文错误;
- 连续批处理、分页式 KV、量化和并行都有收益边界,必须在目标硬件与固定评测集上实测;
- 项目最易踩坑的是 KV 超卖、长请求阻塞、断连资源泄漏、流式重试不幂等和跨版本 Cache 污染;
- 面试应从张量与复杂度讲到准入、调度、监控、故障恢复和质量回滚,并用实验清单、故障注入和回滚记录界定亮点。