大模型推理部署思考与答疑
大模型推理部署思考与答疑
本文用于连续记录近期关于大模型推理部署的思考和答疑。后续相关问题都追加到本文,不再按每个主题拆成新的文档。
记录格式:
## 问题 N:问题标题
### 我的思考 / 问题
用户提出的问题或阶段性理解。
### 答疑结论
直接结论。
### 扩展知识
相关背景、架构、示例和实践建议。目录
| 序号 | 问题 | 主题 |
|---|---|---|
| 1 | 后端大模型推理是否保存记忆? | 推理架构基础 |
| 2 | 每次请求体应该如何携带上下文? | 上下文管理 |
| 3 | 大模型推理部署的逻辑是什么? | 部署原理(含扩展 3.1 硬件与精度) |
| 4 | 主流大模型高并发推理架构是什么? | 并行架构(含扩展 4.1~4.3 TP/PP/DP/EP) |
| 5 | 对外 API 如何做内容审查? | 内容安全 |
| 6 | Open WebUI 联网搜索的逻辑是什么? | RAG 与搜索 |
| 7 | 大模型推理调优涉及哪些层面? | 推理调优 |
| 8 | 大模型微调是什么? | 微调(LoRA、数据、部署) |
| 9 | KV Cache 与最大上下文长度是什么关系? | KV Cache 原理 |
| 10 | 昇腾部署推理必须量化吗? | 量化 |
| 11 | 推理后端如何管理不同用户的上下文? | 多用户隔离 |
问题 1:后端大模型推理是否保存记忆?
我的思考 / 问题
后端模型推理不存记忆,每次推理请求都需要依赖请求带上的上下文,大模型才有“记忆”。也就是说,大模型推理并没有多用户的概念,不同用户的不同请求对于后端大模型来说都是一次独立无关联的推理任务。记忆系统和上下文管理是由应用层面处理的,这么理解对吗?
答疑结论
这个理解是正确的。
vLLM、vLLM-Ascend 这类推理服务通常是无状态服务。对后端推理模型来说,每次请求本质上都是:
本次请求输入 tokens
↓
模型推理
↓
本次请求输出 tokens它不会天然知道:
- 当前请求来自哪个用户;
- 这是该用户的第几轮对话;
- 之前聊过什么;
- 用户有哪些长期偏好;
- 哪些历史信息应该继续保留。
这些能力都应该由应用层、数据库、向量库或网关外的业务系统维护。
扩展知识
推理部署中常见的职责边界:
| 层级 | 职责 |
|---|---|
| vLLM / vLLM-Ascend | 执行单次推理,不负责长期记忆和用户会话 |
| LiteLLM Proxy | 多模型路由、统一鉴权、限流、审计,通常不负责业务记忆 |
| 应用服务 | 管理用户、会话、历史消息、长期记忆、RAG 和上下文拼装 |
| 数据库 / 向量库 | 存储聊天记录、用户画像、摘要记忆、知识库片段 |
需要特别区分 KV Cache / Prefix Cache 和业务记忆:
| 概念 | 是否等于用户记忆 | 说明 |
|---|---|---|
| KV Cache | 否 | 用于加速当前序列或批次的注意力计算 |
| Prefix Cache | 否 | 用于复用相同前缀的计算结果 |
| 应用数据库中的聊天历史 | 是 | 可按用户和会话持久保存 |
| 向量库中的长期记忆 | 是 | 可检索相关记忆后重新注入 prompt |
因此,不能依赖推理服务本身保存“用户记得什么”。真正的记忆应由应用层保存,并在下一次请求时重新拼进上下文。
问题 2:每次请求体应该如何携带上下文?
我的思考 / 问题
如果问题 1 的理解正确,那么每次请求的请求体应该是怎样的?是在 role=assistant 写上下文吗?
答疑结论
不应该把所有上下文都写成 role=assistant。
OpenAI 兼容 Chat API 的核心是 messages 数组。不同类型的信息应放在不同 role 中:
| role | 用途 |
|---|---|
system | 全局规则、助手身份、安全约束、应用注入的背景信息 |
user | 用户当前或历史输入 |
assistant | 模型之前真实回复过的内容 |
tool | 工具调用结果,取决于模型和框架是否支持 |
assistant 应该表示“模型之前真实说过的话”,不是应用层想塞给模型的任意上下文。
扩展知识
多轮对话的请求体应按真实历史轮次重新拼装:
{
"model": "glm-5.1",
"messages": [
{
"role": "system",
"content": "你是一个专业、严谨的中文技术助手。"
},
{
"role": "user",
"content": "我正在部署 vLLM-Ascend。"
},
{
"role": "assistant",
"content": "好的,我会结合昇腾 NPU 和 vLLM-Ascend 部署场景回答。"
},
{
"role": "user",
"content": "LiteLLM 的作用是什么?"
}
],
"max_tokens": 512,
"temperature": 0.7
}不同上下文的推荐位置:
| 上下文类型 | 推荐位置 | 示例 |
|---|---|---|
| 系统规则 | system | “你是一个严谨的技术助手” |
| 用户画像 | system 或应用注入的背景段 | “用户偏好简体中文,正在部署 GLM-5.1” |
| 最近聊天历史 | 按原始轮次放 user / assistant | 多轮对话完整保留 |
| 历史摘要 | system 或专门的背景段 | “以下是本会话摘要...” |
| RAG 检索资料 | 当前 user 前置资料,或 system 中的参考资料 | “参考资料如下...” |
| 工具结果 | tool,或普通文本上下文 | 数据库查询、搜索结果、代码执行结果 |
一个多用户聊天应用通常按以下流程处理:
用户请求
↓
鉴权,得到 user_id
↓
确定 session_id
↓
读取最近聊天历史
↓
读取用户画像 / 长期记忆
↓
按当前问题检索知识库
↓
裁剪、摘要、排序上下文
↓
组装 messages
↓
请求 LiteLLM / vLLM
↓
保存本轮 user 消息和 assistant 回复上下文管理会直接影响:
| 能力 | 依赖项 | 说明 |
|---|---|---|
| 历史连续性 | 最近历史是否完整 | 少带历史会导致模型“不记得之前说过什么” |
| 回答准确性 | 相关资料是否被注入 | RAG 资料缺失时,模型只能依赖自身参数知识 |
| 用户个性化 | 用户画像是否准确 | 偏好、身份、项目背景需要应用层提供 |
| 多用户隔离 | user_id / session_id 是否隔离 | 混用历史会造成串话或数据泄露 |
| 成本与延迟 | 上下文长度 | 带太多历史会增加 token 成本和响应时间 |
| 稳定性 | 上下文裁剪策略 | 裁错关键信息会导致回答偏离 |
实践上,推荐保留最近几轮原始对话,把更早历史压缩成摘要,把长期事实抽取到用户画像或长期记忆库。
问题 3:大模型推理部署的逻辑是什么?
我的思考 / 问题
大模型推理部署的逻辑是怎样的?大模型的参数量、上下文长度等和计算卡的算力、显存之间的关系是怎样的?
答疑结论
大模型推理部署的核心逻辑是:把模型权重加载到计算卡上,通过推理引擎接收请求、调度 batch、管理 KV Cache,并执行 Prefill 和 Decode 两个主要计算阶段。
启动推理服务
↓
加载模型权重到 NPU / GPU 显存
↓
根据 TP / DP / EP 等并行策略初始化通信
↓
等待客户端请求
↓
收到 messages / prompt,tokenizer 转成 tokens
↓
调度器将多个请求组成动态 batch
↓
Prefill:处理输入上下文,建立 KV Cache,生成首 token 所需状态
↓
Decode:基于 KV Cache 逐 token 生成输出
↓
返回推理结果扩展知识
Prefill 和 Decode 的区别:
| 阶段 | 做什么 | 主要瓶颈 | 用户感知 |
|---|---|---|---|
| Prefill | 处理完整输入上下文,建立 KV Cache | 计算量、长上下文、模型规模 | 首 token 延迟 |
| Decode | 基于已有 KV Cache 逐 token 生成 | 显存带宽、调度、并发、通信 | 输出速度 tokens/s |
模型参数量、上下文长度和硬件资源的关系可以概括为:
权重显存 + KV Cache 显存 + 运行时开销
↓
决定能否启动、能支持多长上下文、能承载多少并发
计算卡算力 + 显存带宽 + 通信带宽
↓
决定首 token 延迟、每秒输出 token、扩展效率参数量决定权重显存,粗略估算:
权重显存 ≈ 参数量 × 每个参数字节数| 精度 | 每参数字节 | 70B 权重粗略显存 |
|---|---|---|
| FP16 / BF16 | 2 bytes | 约 140 GB |
| INT8 / W8A8 | 约 1 byte | 约 70 GB |
| INT4 / W4A16 | 约 0.5 byte | 约 35 GB |
实际还会有量化 scale、embedding、运行时 buffer、通信 buffer 等额外开销。
上下文长度和并发主要决定 KV Cache 显存:
KV Cache 显存 ∝ 层数 × KV 头数 × head_dim × 上下文 token 数 × 并发序列数 × 数据类型字节数直观理解:
| 变量 | 越大意味着 |
|---|---|
max-model-len | 单个请求可用上下文更长,但每条序列 KV Cache 上限更大 |
| 并发请求数 | 同时保留更多序列的 KV Cache |
| 输出长度 | Decode 过程中 KV Cache 继续增长 |
| 模型层数 / hidden size | 每个 token 需要保存的 KV 更多 |
所以同一个模型:
上下文 32K 比 8K 更吃显存
并发 16 路比并发 1 路更吃显存
输出 4096 tokens 比输出 256 tokens 占用更久长上下文有两重影响:
- Prefill 要处理更多输入 token,首 token 延迟上升;
- KV Cache 要保存更多 token 的 K/V 状态,显存占用上升。
因此生产部署通常不会盲目把上下文开到模型理论最大值,而是按业务实际选择,并通过 max-model-len、max-num-seqs、max-num-batched-tokens、gpu-memory-utilization 等参数控制容量。
扩展提问 3.1:同一个权重在不同硬件上推理是否只影响速度、不影响精度?
我的思考 / 问题
同一个权重在不同算力、显存的显卡上做推理,理论上只会影响推理速度,不影响推理精度,是吗?
答疑结论
理论上,同一份权重、同一种数值精度、同一套推理算法和同样的解码参数,换到不同算力或不同显存容量的计算卡上,主要影响的是:
- 推理速度;
- 可承载的并发;
- 可配置的上下文长度;
- batch 大小;
- 是否能完整加载模型。
在这些条件完全一致时,模型能力和评测精度原则上不应因为“卡更快或更慢”而改变。
但工程上不能简单说“硬件只影响速度,完全不影响输出”。不同硬件、不同驱动和不同推理后端可能导致数值路径不同,从而带来微小差异,极端情况下会影响生成结果或评测分数。
扩展知识
影响输出一致性的常见因素:
| 因素 | 是否可能影响精度/输出 | 说明 |
|---|---|---|
| 权重文件不同 | 会 | 哪怕模型名相同,不同量化版本或 checkpoint 也可能不同 |
| 数值精度不同 | 会 | FP16、BF16、FP8、INT8、INT4 的数值误差不同 |
| 量化方式不同 | 会 | W8A8、W4A16、GPTQ、AWQ、SmoothQuant 等误差不同 |
| 算子实现不同 | 可能会 | 不同硬件 kernel、融合算子、attention 实现会有数值差异 |
| 并行策略不同 | 可能会 | TP/DP/EP、通信规约顺序不同,浮点累加顺序可能不同 |
| 解码参数不同 | 会 | temperature、top_p、top_k、seed 改变会直接影响输出 |
| 上下文被截断 | 会 | 显存不足导致降低 max-model-len 或裁剪输入,会明显影响回答 |
| batch 调度不同 | 可能会 | 某些后端在不同 batch 下数值路径略有差异 |
更准确的说法是:
同权重 + 同精度 + 同推理后端 + 同解码参数 + 输入未截断
↓
理论精度基本一致,硬件主要影响速度和容量
只要精度、量化、算子、并行方式、上下文裁剪或采样参数变化
↓
就可能影响输出一致性,甚至影响评测精度生产评测时建议固定:
- 权重路径和版本;
- tokenizer / chat template;
- 推理镜像和框架版本;
- 精度与量化配置;
max-model-len,确保输入未被截断;temperature=0或固定seed;- 评测脚本和数据集版本。
对于确定性评测,通常使用:
{
"temperature": 0,
"max_tokens": 512
}即使如此,不同硬件或不同推理框架之间仍可能存在极小数值差异。大多数场景下,这种差异不会改变整体能力判断;但在边界 token、长推理链、数学题、多选题等场景,微小差异可能放大成不同最终答案。
问题 4:主流大模型高并发推理架构是什么?
我的思考 / 问题
目前主流的大模型高并发推理架构是怎样的?
答疑结论
主流架构通常是分层的:入口网关负责统一接入和鉴权,调度层负责模型路由和负载均衡,推理层负责高效批处理和多卡/多机并行,监控层负责容量和稳定性。
用户 / 应用 / OpenAI SDK
↓
API Gateway / LiteLLM / Kong / APISIX
- TLS
- API Key / JWT
- 限流
- 审计
- model 路由
↓
推理服务池
- vLLM / TensorRT-LLM / SGLang / TGI
- continuous batching
- KV Cache 管理
↓
计算资源
- 单机多卡 TP / EP
- 多机 DP / PP / PD
- NPU / GPU 通信网络
↓
监控与运维
- Prometheus / Grafana
- 日志 / tracing
- autoscaling / 容量管理扩展知识
多模型场景一般不会让业务直接记多个 IP,而是通过统一入口按 model 路由:
model=glm-5 → GLM-5.1 推理集群
model=deepseek-v4 → DeepSeek-V4-Flash 推理集群
model=embedding → Embedding 服务同一个模型需要高并发时,常见做法是部署多个副本:
┌─ vLLM replica 1
Gateway ───┼─ vLLM replica 2
└─ vLLM replica 3当模型太大,单卡装不下时,需要模型并行:
| 并行方式 | 作用 |
|---|---|
| TP(Tensor Parallel) | 将单层矩阵切到多张卡 |
| PP(Pipeline Parallel) | 将不同层切到不同卡 |
| EP(Expert Parallel) | MoE 专家分布到不同卡 |
| DP(Data Parallel) | 多个完整副本处理不同请求 |
当前 GLM-5.1 两节点部署中的 DP=2, TP=8, EP 可以理解为:
- 每个节点 8 卡组成一个 TP 组;
- 两个节点形成两个 DP rank;
- MoE 专家使用 EP 分布;
- 对外通常只访问 Master API。
更高阶的高并发架构会把 Prefill 和 Decode 拆开:
请求输入长上下文
↓
Prefill 节点:处理输入、生成 KV
↓
Decode 节点:持续生成输出 token这种架构常用于长上下文、高并发场景,因为 Prefill 和 Decode 的资源特征不同:
| 阶段 | 更需要 |
|---|---|
| Prefill | 计算能力、大 batch 吞吐 |
| Decode | 显存带宽、KV Cache 容量、低延迟调度 |
但 PD 分离会带来 KV 传输、调度系统、网络和部署复杂度,一般不是最小生产架构的第一步。
扩展提问 4.1:TP、PP、EP、DP 分别是什么?vLLM 支持哪些并行方式?
我的思考 / 问题
详细说说 TP、PP、EP、DP 等并行方式,vLLM 支持哪些并行方式?
答疑结论
TP、PP、EP、DP 解决的是不同维度的问题:
| 并行方式 | 中文名 | 主要解决什么问题 |
|---|---|---|
| TP | 张量并行 | 单层太大,切分矩阵计算和权重 |
| PP | 流水线并行 | 模型层数太多,按层切到不同设备/节点 |
| EP | 专家并行 | MoE 专家太多,把不同专家分到不同设备 |
| DP | 数据并行 | 多个副本处理不同请求,提高吞吐和并发 |
vLLM 主线支持的核心并行能力包括:
| 能力 | vLLM 参数 / 形式 | 说明 |
|---|---|---|
| TP | --tensor-parallel-size, -tp | 常用;适合单机多卡或每节点多卡 |
| PP | --pipeline-parallel-size, -pp | 支持;常用于模型跨节点或 GPU 切层 |
| DP | --data-parallel-size, -dp 等 | 支持;用于多个 DP rank 承接请求 |
| EP | --enable-expert-parallel, -ep | 支持;仅对 MoE 模型有意义 |
| DCP / PCP | --decode-context-parallel-size、--prefill-context-parallel-size | 新版本中的上下文并行能力;用于 MLA(Multi-Latent Attention)等架构下,沿 KV 维度对长序列做并行切分,适合特定长上下文场景 |
| PD 分离 | disaggregated serving / KV transfer | 更高阶部署形态,不是简单一个并行参数 |
当前项目中的 GLM-5.1 两节点部署使用的是:
DP = 2
TP = 8
EP = enabled
PP = 1(未启用)也就是:每台 8 张 NPU 组成一个 TP 组,两台机器形成两个 DP 副本,MoE 专家层启用 EP,对外只访问 Master API。
扩展知识
TP:Tensor Parallel,张量并行
TP 是把同一层里的大矩阵切到多张卡上。
例如 Transformer 里的线性层权重矩阵很大:
Y = X × WTP 会把 W 沿某个维度切成多份,每张卡负责一部分计算,然后通过 AllReduce / AllGather 等通信把结果合并。
直观理解:
同一层
├─ 卡 0 算一部分 hidden / head
├─ 卡 1 算一部分 hidden / head
├─ 卡 2 算一部分 hidden / head
└─ 卡 3 算一部分 hidden / headTP 的特点:
| 项 | 说明 |
|---|---|
| 优点 | 能把单个大模型切到多卡上,降低单卡权重压力 |
| 缺点 | 每层都需要卡间通信,依赖高速互联 |
| 适合 | 单机多卡、NVLink/HCCS/HCCL 通信较好的环境 |
| 不适合 | 跨慢速网络做太细粒度 TP,通信开销会很高 |
当前项目单机 8 卡常用:
--tensor-parallel-size 8PP:Pipeline Parallel,流水线并行
PP 是按模型层切分,把不同层放到不同设备或节点。
例如 80 层模型,可以切成 2 段:
卡组 / 节点 0:第 1~40 层
卡组 / 节点 1:第 41~80 层推理时,请求先经过前半层,再把中间激活传给后半层。
PP 的特点:
| 项 | 说明 |
|---|---|
| 优点 | 适合模型太大,单节点放不下完整层集合 |
| 缺点 | 存在流水线气泡;单请求需要跨 stage 串行通过 |
| 适合 | 模型跨节点、GPU 数不能均匀做 TP、节点间通信相对较慢但可承受激活传输 |
| 不适合 | 追求极低单请求延迟且模型能用 TP 装下的场景 |
vLLM 中可用:
--pipeline-parallel-size 2官方常见建议是:如果 2 台机器、每台 8 卡,可考虑:
--tensor-parallel-size 8
--pipeline-parallel-size 2含义是每个节点内部做 TP,节点之间按层做 PP。
当前项目 GLM-5.1 两节点没有用 PP,而是用 DP。也就是说两台机器不是前后层关系,而是两个数据并行副本。
DP:Data Parallel,数据并行
DP 是部署多个模型副本,每个副本处理不同请求。
请求 A → DP rank 0
请求 B → DP rank 1
请求 C → DP rank 0
请求 D → DP rank 1DP 的特点:
| 项 | 说明 |
|---|---|
| 优点 | 提高吞吐和并发,架构直观 |
| 缺点 | 每个副本都需要一份模型资源,总显存消耗随副本数增加 |
| 适合 | 模型单副本能装下,但业务请求多 |
| 不适合 | 单副本都装不下的大模型;这种要先用 TP/PP/EP 切模型 |
vLLM 在线服务中常见参数:
--data-parallel-size 2
--data-parallel-size-local 1
--data-parallel-address <master_ip>
--data-parallel-rpc-port 13389多节点时还会用:
--data-parallel-start-rank 1
--headless当前项目 GLM-5.1 就是:
节点 0:DP rank 0,TP=8,对外提供 API
节点 1:DP rank 1,TP=8,headless,只参与计算因此 DP=2 不表示单个请求跨两台机器共同推理;更常见的理解是有两个副本承接不同请求。低并发时可能只看到一个 rank 忙,高并发时两个 rank 都会被调度。
EP:Expert Parallel,专家并行
EP 主要用于 MoE(Mixture of Experts)模型。
MoE 模型不是每个 token 都走所有 FFN,而是通过 router 选择部分专家:
token
↓
router 选择 top-k experts
↓
只计算被选中的专家EP 会把不同专家放到不同设备上:
卡 0:expert 0, 1
卡 1:expert 2, 3
卡 2:expert 4, 5
卡 3:expert 6, 7EP 的特点:
| 项 | 说明 |
|---|---|
| 优点 | MoE 专家参数可分散到多卡,适合 DeepSeek、Mixtral、GLM MoE 等 |
| 缺点 | token 需要按专家路由,涉及 All2All 通信;负载可能不均 |
| 适合 | MoE 模型 |
| 不适合 | Dense 模型;开启也没有实际意义 |
vLLM 中常用:
--enable-expert-parallel新版本 vLLM 还包含 Expert Parallel Load Balancer(EPLB)等能力,用于缓解专家冷热不均问题;是否可用取决于 vLLM 版本、硬件平台和模型结构。
TP / PP / DP / EP 如何组合?
可以把它们理解成不同维度的切分:
单个模型副本内部:
- TP:切一层里的矩阵
- PP:切不同层
- EP:切 MoE 专家
多个模型副本之间:
- DP:复制多个副本处理不同请求常见组合:
| 组合 | 场景 |
|---|---|
| TP=1, DP=N | 小模型,多副本提升吞吐 |
| TP=8, DP=1 | 大模型单机 8 卡部署 |
| TP=8, DP=2 | 两台 8 卡,各一份 TP 副本,提高并发 |
| TP=8, PP=2 | 两台 8 卡共同承载一个更大的模型副本 |
| TP=8, DP=2, EP=on | MoE 模型,两副本,每副本 8 卡切分并启用专家并行 |
| TP + PP + EP + DP | 超大 MoE 模型的大规模集群部署 |
vLLM 支持情况总结
| 并行方式 | vLLM 是否支持 | 备注 |
|---|---|---|
| TP | 支持 | 最常用,--tensor-parallel-size |
| PP | 支持 | --pipeline-parallel-size,常用于跨节点或不均匀切分 |
| DP | 支持 | --data-parallel-size 等;在线服务有 internal/hybrid/external LB 等形态 |
| EP | 支持 | --enable-expert-parallel,仅 MoE 模型有意义 |
| Context Parallel | 新版本支持 | DCP/PCP,适合特定长上下文和注意力结构 |
| PD 分离 | 支持高级部署 | 依赖 KV transfer / disaggregated serving,部署复杂 |
需要注意:vLLM 主线支持不等于 vLLM-Ascend 当前镜像、当前模型、当前硬件平台都完整支持。昇腾环境应优先以 vLLM-Ascend 对应版本文档和实测为准。
扩展提问 4.2:为什么当前部署只切 TP,不切 PP?
我的思考 / 问题
为什么当前部署只切 TP,不切 PP?
答疑结论
当前 GLM-5.1 两节点部署选择 TP=8 + DP=2,而不是 TP=8 + PP=2,核心原因是:单台 8 卡已经能承载一个完整模型副本,当前更需要提升并发吞吐,而不是把一个更大的模型拆到两台机器上共同承载。
也就是说,当前两台机器的角色是:
节点 0:一份完整 TP=8 推理副本(DP rank 0)
节点 1:另一份完整 TP=8 推理副本(DP rank 1)不是:
节点 0:模型前半层
节点 1:模型后半层所以当前不是 PP 架构。
扩展知识
是否使用 PP,主要取决于“单个模型副本能不能在单节点内放下”,以及业务目标是“装下更大的模型”还是“提升请求并发”。
| 目标 | 更适合的方式 | 原因 |
|---|---|---|
| 单机 8 卡能放下一个完整模型副本 | TP | 节点内通信更快,部署更简单 |
| 两台机器都能各自跑一份完整副本 | DP | 提升并发和吞吐最直接 |
| 单台机器放不下完整模型 | PP 或 TP+PP | 必须跨节点切层才能装下 |
| 模型是 MoE,专家很多 | EP | 专家层适合按专家分布 |
当前不切 PP 的主要原因:
- 没有装不下的刚性需求 当前 GLM-5.1 在单节点 8 卡 TP 下可以启动,只是
max-model-len、KV Cache 和并发需要调优。既然单节点能承载一个完整副本,就没有必要为了“装下模型”引入 PP。 - DP 更符合当前高并发目标
DP=2表示有两个推理副本,可以处理不同请求: 这能直接提升吞吐。PP 则是一个请求必须依次经过节点 0 和节点 1: 对高并发服务来说,如果模型单副本能放下,DP 通常比 PP 更直接。 - PP 增加跨节点串行链路 PP 会在层与层之间传中间激活。单个请求必须经过多个 pipeline stage,跨节点网络成为请求路径的一部分。这样会增加:
- 首 token 延迟;
- 跨节点通信依赖;
- pipeline bubble;
- 故障面和排查复杂度。
- TP 更适合节点内 8 卡高速互联 当前每台机器内部 8 张 NPU 做 TP,通信主要发生在节点内高速互联上。相比跨节点 PP,节点内 TP 的工程路径更清晰,也更符合当前 vLLM-Ascend GLM 部署示例。
- PP 更适合“更大模型”而不是“更多请求” PP 的典型价值是让一个单节点放不下的模型跨多节点运行。例如: 但当前场景是:
一句话总结:TP 是为了让单个副本在一台机器的多卡上跑起来,DP 是为了多副本提升吞吐;PP 是当一个副本跨单机放不下时才更有价值。当前单机 TP 能跑,所以两机优先做 DP,而不是 PP。
扩展提问 4.3:如果 GLM 因显存不够降低了 max-model-len,两节点切 PP 是否能把上下文加大?
我的思考 / 问题
在部署 GLM 时,因为显存不够降低了 max-model-len。如果两节点切 PP,是不是就可以把 max-model-len 加大?
答疑结论
有可能,但不是无条件成立。
如果从当前的:
两节点 DP=2, TP=8, PP=1改成:
两节点 DP=1, TP=8, PP=2那么一个模型副本会跨两台机器按层切分。每个 PP stage 只承载一部分 Transformer 层,因此每张卡上的 权重显存 和 该 stage 对应层的 KV Cache 显存 都可能下降。这样理论上可以释放更多显存给 KV Cache,从而有机会把 max-model-len 调大。
但是代价也很明显:原来两台机器是两个 DP 副本,现在会变成一个跨节点副本。也就是说,用 PP 可能换来更长上下文,但会损失一部分并发吞吐和部署简单性。
扩展知识
当前 max-model-len 受限的主要不是权重是否能加载,而是 KV Cache:
KV Cache 显存 ∝ 层数 × KV 头数 × head_dim × 上下文 token 数 × 并发序列数PP 按层切分后,每个 stage 只保存自己负责层的 KV Cache。例如一个 80 层模型做 PP=2:
节点 0:第 1~40 层 → 保存这 40 层的 KV
节点 1:第 41~80 层 → 保存这 40 层的 KV因此单卡上的 KV 压力可能下降,max-model-len 有机会提升。
但需要同时考虑以下限制:
| 影响项 | 说明 |
|---|---|
| DP 副本数下降 | 两节点从 DP=2 改为 PP=2 后,通常只剩一个副本,并发吞吐会下降 |
| 单请求延迟上升 | 请求必须依次经过两个 PP stage,跨节点链路进入单请求关键路径 |
| 通信复杂度增加 | stage 之间要传中间激活,依赖节点间网络稳定性 |
| Pipeline bubble | 小 batch / 低并发时,PP stage 可能互相等待,利用率不一定高 |
| vLLM-Ascend 支持度 | vLLM 主线支持 PP,但昇腾镜像、模型结构和版本是否稳定支持要实测 |
| 模型理论上限 | max-model-len 不能超过模型 RoPE / position embedding / 配置支持范围 |
| 并发参数仍会影响 KV | 即使用 PP,max-num-seqs、max-num-batched-tokens 过大仍可能 OOM |
可以这样理解两种方案的取舍:
| 方案 | 资源形态 | 优点 | 代价 |
|---|---|---|---|
DP=2, TP=8, PP=1 | 两个完整副本,每副本 8 卡 | 并发吞吐更好,架构简单 | 单副本上下文受单节点显存限制 |
DP=1, TP=8, PP=2 | 一个副本跨两节点 16 卡 | 单副本显存压力下降,可能支持更长上下文 | 并发副本少,延迟和跨节点复杂度上升 |
如果既想保留 DP=2,又想每个副本 PP=2,理论上需要更多节点:
每个副本:2 个节点做 PP
两个副本:DP=2
总节点数:2 × 2 = 4 节点也就是:
TP=8, PP=2, DP=2 → 通常需要 4 台 8 卡节点所以,对当前两节点环境来说:
想要更长上下文 → 可以探索 TP=8, PP=2, DP=1
想要更高并发吞吐 → 保持 TP=8, DP=2, PP=1生产建议是先明确业务目标:
- 如果业务主要是长文档、长上下文、低并发,可以评估 PP。
- 如果业务主要是多用户并发问答,优先保留 DP。
- 如果只是略微 OOM,优先尝试降低
max-num-seqs、max-num-batched-tokens、开启 chunked prefill / prefix caching,而不是直接切 PP。 - 如果要验证 PP,应单独做一组压测,对比
max-model-len、首 token 延迟、tokens/s、并发吞吐和稳定性。
一句话总结:两节点切 PP 确实可能把 max-model-len 加大,因为 KV Cache 会按层分散到两个 PP stage;但它会把两节点从两个并发副本变成一个跨节点副本,属于用吞吐和复杂度换上下文长度。
结合当前环境,初期生产建议:
- 不急于做复杂 PD 分离;
- 先用 LiteLLM 统一入口;
- 每个模型先跑清楚单模型容量;
- 记录平均输入/输出 token、并发、首 token 延迟和 tokens/s;
- 根据压测结果决定是加副本、降上下文、调 batch 参数,还是做更复杂的多机架构。
问题 5:对外 API 如何做内容审查?暂无审查 API 时怎么办?
我的思考 / 问题
当前已有 GLM-5.1 和 DeepSeek-V4-Flash 两个模型可对外提供 API 调用,如何对问答内容进行安全审查过滤?主流方案是什么?若接入文本审查 API,整个调用流程逻辑是怎样的?如果暂时没有审查 API,能否在模型层做基础关键词过滤?
答疑结论
内容审查应放在 vLLM 推理服务之外(网关层或应用层),在 请求进模型前 和 模型返回给用户前 各做一道检查。vLLM / LiteLLM 本身不负责业务级内容安全。
主流做法是:鉴权 → 拼上下文 → 输入审查 →(通过才)调 LiteLLM/vLLM → 输出审查 → 返回。暂无审查 API 时,可用 关键词 + 正则 在同样位置做过渡期基础过滤,不要依赖改 vLLM 内核或在 system prompt 里仅靠「请遵守规范」。
扩展知识
5.1 审查放在哪一层
客户端 / 业务应用
↓
LiteLLM 网关(鉴权、按 model 路由 glm-5 / deepseek-v4)
或
业务后端(拼 messages、会话管理)
↓
【输入审查】
↓
vLLM(10.143.91.2 / 10.143.91.3:8077)
↓
【输出审查】
↓
返回客户端| 接入位置 | 适用场景 |
|---|---|
| 业务应用层 | 最灵活;会话、拒答文案、审计、多租户策略 |
| LiteLLM 前自建代理 / callbacks | 多模型统一策略,业务方无需各自实现 |
| 独立 moderation-service | GLM / DeepSeek 共用一套审查接口 |
| vLLM 进程内部 | 不推荐;升级难、与推理耦合 |
鉴权(LITELLM_MASTER_KEY 等)与内容安全正交:有 Key 的用户仍可能提交违规内容。
5.2 主流方案对比
| 方案 | 优点 | 缺点 |
|---|---|---|
| 云文本审核 API(国内厂商绿网/天御/易盾等) | 成熟、类别全、中文场景多 | 按量计费、数据出境与合规需评估 |
| OpenAI Moderation 等 | 接入简单 | 偏英文、国内合规需评估 |
| 规则 + 关键词 + 正则 | 快、可控、可内网、无第三方依赖 | 误杀/漏杀,难防变体与越狱 |
| 自建小分类模型 | 数据不出域、可定制 | 需训练与运维 |
| LLM 二次审核 | 语义理解强 | 延迟高、成本高,宜作补充 |
| 仅 system prompt 约束 | 零开发 | 不可靠,不能当安全手段 |
生产常见组合:规则预筛 + 云/自建审查 API + 输出再检;暂无 API 时先用 关键词/正则双检,后续把 check() 换成 HTTP 审查 API,流程不变。
5.3 审查什么
输入侧(进模型前)
- 违法违规、色情暴力、仇恨等;
- 个人隐私(身份证、手机号等,可脱敏后再送模型);
- Prompt 注入 / 越狱(「忽略上文规则」等,规则只能挡典型句式);
- 超长、异常编码等。
输出侧(返回用户前)
- 同上有害类别;
- 幻觉式高风险承诺(医疗/法律/投资等,可按业务拒答或加免责声明);
- 泄露系统 prompt、内网 IP(如
10.143.91、8077)、密钥片段等; - 与业务策略不符的内容。
建议 输入拦一次、输出再拦一次;只拦输入无法防止模型自己生成违规内容。
5.4 接入文本审查 API 后的调用流程
① 客户端 POST /v1/chat/completions
② 网关鉴权(API Key / 用户身份)
③ 应用拼 messages(历史、system、本轮 user)
④ 输入审查 API(direction=input)
├─ 不通过 → 返回固定拒答,不调 vLLM
└─ 通过 → 继续
⑤ 转发 LiteLLM / vLLM(model=glm-5 或 deepseek-v4)
⑥ 获得 assistant 回复
⑦ 输出审查 API(direction=output)
├─ 不通过 → 替换为安全话术,记审计
└─ 通过 → 返回用户(可选脱敏)
⑧ 可选:仅将审查通过的轮次写入会话库时序关系:
Client → App/Gateway → Moderation(input) → LiteLLM → vLLM
↓ pass
Client ← App/Gateway ← Moderation(output) ← assistant text审查 API 示例(概念):
// 请求
{ "text": "...", "direction": "input", "user_id": "u1", "model": "glm-5.1" }
// 响应
{ "pass": false, "action": "block", "labels": ["..."], "request_id": "..." }| 阶段 | 典型动作 | 是否再调 vLLM |
|---|---|---|
| 输入 fail | 固定拒答文案 | 否 |
| 输出 fail | 安全替代文案 + 审计 | 已调过,一般不重试 |
| 审查服务不可用 | fail-closed(拒答,生产更安全)或 fail-open(放行,风险大) | 视策略 |
两个模型 共用同一套审查策略;仅在转发时改 model,审查层不感知 TP/DP。
多轮对话:至少审查 本轮 user 新消息 与 本轮 assistant 新回复;严格模式可审查整段即将送入模型的文本。
5.5 暂无审查 API:关键词与规则过滤
可以在 调用 vLLM 之前/之后 做基础过滤,不要改 vLLM-Ascend 镜像或在推理内核里插 hook。
拼 messages → 【输入:词库+正则】→ 未命中才调 vLLM → 【输出:词库+正则】→ 返回| 检查项 | 输入 | 输出 |
|---|---|---|
| 违禁/敏感词表 | 建议 | 建议 |
| 身份证、手机号等正则 | 可选 | 可选 |
| 典型越狱片段 | 建议 | — |
| 内网 IP、端口、密钥样式 | 可选 | 建议 |
实现位置优先级:业务后端 > LiteLLM 前自建代理 > Nginx lua(能力有限)。
实践建议:
- 词库放 yaml/json,支持热更新,不写死在代码里;
- 输入命中 → 不调 vLLM,返回统一拒答;
- 输出命中 → 替换为安全话术,日志记
blocked,避免明文存完整违禁内容; - 审查/词库服务异常时,生产倾向 fail-closed;
- GLM-5 与 DeepSeek-V4-Flash 共用同一套 filter。
拒答响应示例(OpenAI 兼容形态):
{
"choices": [{
"message": {
"role": "assistant",
"content": "该问题涉及受限内容,无法提供回答。"
}
}]
}关键词过滤的局限:谐音/拆字/多语言绕过、误杀、难以挡住精心设计的注入。定位应为 过渡期护栏;后续接入审查 API 时仅替换 check() 实现,不改整体链路。
system 中写「请遵守法律法规」可与硬过滤并存,不能替代硬过滤。
5.6 与当前拓扑的关系
| 组件 | 内容安全职责 |
|---|---|
LiteLLM :4000 | 鉴权、多模型路由;可挂统一代理或 callbacks |
vLLM 91.2 / 91.3:8077 | 仅推理,不做业务审查 |
| 业务应用 / filter-proxy | 会话、输入/输出审查、拒答与审计(推荐) |
初期建议:
- 短期:业务或 LiteLLM 前增加 关键词 + 正则,输入/输出双检;
- 中期:接入一家 文本审核 API 或自建 moderation-service,两模型共用;
- 长期:补充注入检测、领域拒答、泄密规则;高风险类目人工复核。
一句话总结:内容安全是网关/应用层能力,不是模型层能力;标准链路是「输入审 → 推理 → 输出审」,暂无审查 API 时用本地词库顶替同一位置,以后再换 API。
问题 6:Open WebUI 联网搜索的逻辑是什么?准确率受哪些因素影响?
我的思考 / 问题
本地部署的大模型在 Open WebUI 中开启 Web Search 后,仍可能出现:
- 问「今天几号」回答错误日期;
- 问「深圳市长是谁」时,引用来源多为无关新闻,最终答案不准;
- SearXNG 本机
curl搜索正常,但对话里「未找到引用来源」或引用与问题无关。
这是否说明模型需要继续调参?联网搜索应该如何理解与优化?
答疑结论
联网搜索不是让模型「自动上网」,而是一条独立的检索增强(RAG)链路;准确率取决于搜索词、引擎结果、网页抓取、上下文拼装和模型综合,与 temperature 等推理参数几乎无关。
核心认知:
- vLLM 无实时时钟、无互联网;日期、现任职务等不能指望权重「记得」。
- 「有引用来源」≠「来源能回答问题」;搜索引擎前排常是新闻,不等于百科或政府公开页。
- 开放网页搜索适合时效信息(天气、新闻、政策动态);事实型短问答(几号、谁任职)应优先系统注入时间、百科/知识库,而非单靠爬新闻。
标准链路(Open WebUI + SearXNG 典型部署):
用户问题 +(可选)模型改写搜索词
↓
① SearXNG 聚合搜索(多引擎 JSON 结果,含 title / url / snippet)
↓
② Open WebUI Web Loader 抓取每条 URL 正文(safe_web / playwright 等)
↓
③ 分块、(可选)写入临时向量库,拼入本次 prompt
↓
④ 调用 vLLM,模型基于「问题 + 检索片段」生成回答并展示引用任一步失败或质量差,最终答案都会偏:① 搜偏、② 抓失败、③ 片段噪声大、④ 模型忽略引用,表现各不相同,需分层排查。
扩展知识
6.1 各环节对准确率的影响
| 环节 | 做什么 | 不准时的常见原因 |
|---|---|---|
| 搜索词生成 | Open WebUI / 模型将用户问题改写成查询串 | 词过宽(「深圳市长」→ 大量深圳新闻);未加「现任」「百科」等限定 |
| SearXNG 引擎 | 多引擎合并排序(如 baidu、bing、sogou、bing news、sogou wechat) | 新闻/公众号引擎对 人物职务、统计事实 类问题噪声大;境外引擎被墙时只剩国内引擎,结果风格单一 |
| 结果条数 | 只取前 N 条 URL(Admin 里 Search Result Count,常见 3~5) | 前几条全是新闻时,后面百科/政府网进不了上下文 |
| 网页抓取 | 对 URL 拉 HTML 正文 | 403 反爬、0.9.5 loader bug 导致 6/6 全失败 → 无引用;能抓但正文冗长,关键句占比低 |
| 片段与上下文 | 切块塞进 prompt,受 max-model-len 限制 | 片段过多挤占 token,模型未读到有效句;snippet 与全文不一致 |
| 模型综合 | 阅读引用后生成答案 | 仍幻觉、张冠李戴;或检索失败时纯靠参数记忆胡编 |
「检索到来源」只说明 ①② 部分成功;是否答对还要看来源是否与问题类型匹配、以及模型是否遵循引用。
6.2 典型失效场景
(1)「今天几号」——日历型
| 现象 | 原因 |
|---|---|
| 未开搜索也答错 | 模型无实时日期,按训练截止或幻觉作答 |
| 开了搜索仍错 | 搜索可能返回天气/新闻页,不一定含明确日期;抓取失败时仍无上下文 |
建议:在 System Prompt 或网关 注入服务器当前时间(时区写清);日历问题 不要 单靠 Web Search。参见 deploy-open-webui.md 应用层说明。
(2)「深圳市长是谁」——事实型
| 现象 | 原因 |
|---|---|
| 引用多为无关新闻 | 搜索词 + 引擎偏向「深圳」「市长」关键词匹配的新闻稿,而非「现任市长」权威简介页 |
| 有百科链接但答案仍错 | 条数少未抓中;正文太长模型未提取;或抓取失败 |
建议:
- SearXNG:事实类可减少 bing news、sogou wechat 等新闻向引擎权重,保留 baidu / bing / sogou / wikipedia(若有百度百科引擎可优先人物类);见仓库
settings.yml/1-settings.yml。 - 提问可更明确:「深圳市现任市长是谁,请依据百科或政府公开信息」。
- 系统提示要求:未在引用中明确写出的职务 不得猜测。
- 组织内高频事实(人事、制度)更适合 自建知识库 RAG,而非开放爬网。
(3)「未找到任何引用来源」——链路中断型
| 现象 | 原因 |
|---|---|
| SearXNG curl 正常,Open WebUI 无引用 | 多为 ② 网页抓取全失败(如 0.9.5 safe_web bug、403、容器 loader 异常),与 SearXNG 无关 |
详见 faq.md · FAQ-005 / FAQ-006、deploy-searxng-open-webui.md §6。
6.3 问题类型与手段对照
| 问题类型 | 示例 | 优先手段 | 联网搜索角色 |
|---|---|---|---|
| 日历 | 今天几号 | 系统注入当前时间 | 不推荐依赖 |
| 事实职务 / 常量 | 市长是谁、人口多少 | 百科/政府站/知识库;商用 API 或收紧新闻引擎 | 辅助;商用 API 通常优于纯自抓新闻 |
| 时效 | 今日天气、最新政策 | Web Search + 抓取 | 主手段 |
| 观点 / 综合 | 近期热点解读 | Web Search | 主手段,需用户甄别 |
6.4 排查顺序(实操)
1. 宿主机 curl SearXNG:?q=与问题相关的查询&format=json
→ 看 results 前几条 title/url 是否已经偏新闻
2. docker logs open-webui:Error fetching、TypeError、allow_redirects
→ 判断是「搜偏」还是「抓失败」
3. Admin → Web Search:Query URL 是否为 .../search?q=<query>(勿用空 q=)
→ Result Count、Web Loader Engine(playwright 可缓解部分 403)
4. 同一问题对比:关 Web Search + 系统注入时间 / 知识库
→ 区分模型幻觉与检索噪声6.5 提升准确率的手段(由易到难)
| 手段 | 适用 |
|---|---|
| System Prompt 注入时间、要求「仅依据引用、无法确定则说明」 | 日历、防幻觉 |
优化 SearXNG settings.yml 引擎列表(减新闻、留百科/通用) | 事实型检索 |
| 提高 Search Result Count(注意变慢、占上下文) | 给优质链接更多进榜机会 |
| 固定 Open WebUI 版本 / 换 Playwright loader | 抓取成功率 |
| 商用搜索 API 替换/补充自建抓取(如博查 Bocha、Tavily、Firecrawl) | 相关性、摘要质量、抓取稳定性(见 §6.5.1) |
| 外部 Reranker、自建知识库 | 生产级事实问答 |
调优边界:调整 temperature、max_tokens 不能 系统性解决「搜到无关新闻」;应在 检索链路与应用提示 上优化。
6.5.1 使用商用搜索 API 替换(或补充)SearXNG + 自抓取
当自建链路出现 搜到无关新闻、抓取 6/6 失败、摘要噪声大 时,可改用面向 LLM 的 商用 Web Search API,在 Open WebUI 中把 Web Search Engine 从 searxng 换成对应提供商(如 Bocha / 博查),不再依赖「SearXNG 出链接 → 容器内逐页抓 HTML」。
链路对比
| 阶段 | SearXNG + Open WebUI 自抓取 | 商用 API(以博查为例) |
|---|---|---|
| 检索 | 多引擎合并,偏关键词排序 | 厂商索引 + 常带 语义重排(如 bocha-semantic-reranker) |
| 正文 | 自己对每个 URL 发 HTTP/Playwright | API 直接返回 snippet / summary,可设 summary: true |
| 失败点 | 403、loader bug、反爬 | 主要为 API 限流、密钥、网络到厂商 |
| 成本 | 自建机时与运维 | 按次/按量计费,需申请 API Key |
| 合规 | 数据在自建边界内 | 查询与结果经 第三方,内网需评估 |
【自建】用户问题 → SearXNG → URL 列表 → Web Loader 抓页 → 拼 prompt → vLLM
【商用 API】用户问题 → POST api.../web-search → 标题+URL+摘要 → 拼 prompt → vLLM
↑ 通常可跳过整页抓取相对自建,改善通常较明显的方面
- 事实/时效问答(政策、天气、新闻):摘要更贴问题,少整页新闻噪声。
- 引用稳定性:不再依赖
langchain web_base抓 6 个站,「无引用来源」类问题往往减少。 - 国内场景:博查等强调百科、新闻、天气等 中文源,对「深圳市长」类比纯新闻排序更友好(仍非 100% 准确)。
商用 API 仍不能单独解决的
| 场景 | 说明 |
|---|---|
| 「今天几号」 | 仍应在 System Prompt / 网关 注入当前时间,勿只靠搜索。 |
| 组织内部人事/制度 | 宜 自建知识库,而非公网搜索。 |
| 模型胡编 | 需在提示中要求 仅依据返回摘要,无法从引用推出则明确说不知道。 |
Open WebUI 接入博查(示例)
- 在 博查 AI 开放平台 注册并获取 API Key。
- Admin Panel → Settings → Web Search:
- Enable Web Search:开启
- Web Search Engine:Bocha(若当前镜像下拉无此项,需使用带博查集成的 Open WebUI 版本,如 BochaAI/open-webui-Bocha,或升级到官方已内置该引擎的版本)
- 填写 Bocha Search API Key
- 保存后对话中开启 Web Search 复测;可停用或保留 SearXNG 作备用(二选一即可,避免重复配置混淆)。
博查 Web Search 请求形态(供联调参考,非 Open WebUI 必填):
curl --location 'https://api.bochaai.com/v1/web-search' \
--header 'Authorization: Bearer <API-KEY>' \
--header 'Content-Type: application/json' \
--data '{
"query": "深圳市现任市长是谁",
"summary": true,
"freshness": "noLimit",
"count": 8
}'同类可选:Tavily、Firecrawl、Bing Web Search API 等,Open WebUI 管理端「Web Search Engine」下拉中若有对应项,原理相同——由厂商完成检索与摘要,应用侧少一道自抓取。
选型建议
| 诉求 | 建议 |
|---|---|
| 零成本、数据不出内网 | 继续 SearXNG + 收紧引擎 + 修抓取/版本 |
| 快速提高问答引用质量、少运维抓取 | 商用 API 替换 searxng 引擎项 |
| 高合规、固定人事事实 | 商用 API + 自建知识库 + 系统注入时间 |
部署侧可选说明见 deploy-searxng-open-webui.md §6.7。
6.6 相关文档
| 文档 | 内容 |
|---|---|
| deploy-searxng-open-webui.md | SearXNG 部署、Query URL、引擎与 JSON |
| deploy-open-webui.md | Open WebUI 部署与 vLLM 对接 |
| faq.md · FAQ-005 / FAQ-006 | 搜索 400、无引用 / 抓取失败 |
| deploy-searxng-open-webui.md §6.7 | 商用搜索 API 替代 SearXNG 的配置入口 |
一句话总结:联网搜索 = 检索 +(可选)抓取 + 拼进 prompt;自建 SearXNG 易遇新闻噪声与抓页失败,商用 API(如博查)可跳过自抓取并改善摘要相关性;日历类仍须注入时间,内部事实宜知识库。
问题 7:大模型推理调优涉及哪些层面?
我的思考 / 问题
完成大模型部署推理后,常说的「大模型推理调优」是什么?调优一般涉及哪些层面?分别涉及哪些具体的工作步骤和方法?(微调相关内容见问题 8。)
答疑结论
推理调优是指在不改变模型权重的前提下,通过优化推理引擎参数、Prompt 策略、量化、并行配置等手段,提升推理速度、吞吐和稳定性。
推理调优:让同一个模型跑得更快、更省、更稳调优按从上到下分为四个层面,越往上改动成本越低、见效越快:
① 应用与 Prompt 层 —— 减少无效 token,提升有效信息密度
↓
② 服务与调度层 —— 优化批处理、缓存、并发策略
↓
③ 模型与精度层 —— 量化、蒸馏、减少计算量
↓
④ 硬件与并行层 —— 通信、并行策略、硬件利用率扩展知识
7.1 推理调优的四个层面
推理调优可以按从上到下的顺序分为四个层面:
① 应用与 Prompt 层 —— 减少无效 token,提升有效信息密度
↓
② 服务与调度层 —— 优化批处理、缓存、并发策略
↓
③ 模型与精度层 —— 量化、蒸馏、减少计算量
↓
④ 硬件与并行层 —— 通信、并行策略、硬件利用率越往上,改动成本越低、见效越快;越往下,改动越深、收益上限越高。生产调优通常从上往下逐步推进。
7.2 应用与 Prompt 层
这一层的核心思想:减少送给模型的无效 token,让每次推理都尽可能「值」。
| 调优项 | 做什么 | 具体方法 |
|---|---|---|
| Prompt 精简 | 缩短 system / 上下文长度,减少 Prefill 耗时和 KV Cache 占用 | 去掉冗余说明、压缩历史摘要、限制 RAG 注入条数 |
| 输出长度控制 | 避免模型输出过长,浪费 Decode 算力 | 设置合理的 max_tokens;prompt 中要求「简洁回答」 |
| 历史裁剪策略 | 多轮对话不带全部历史 | 保留最近 N 轮 + 更早历史压缩成摘要 |
| RAG 优化 | 检索结果质量直接影响模型输出和 token 消耗 | 调整 top-k、重排序、截断过长片段 |
| Prompt Caching | 复用相同前缀的 KV Cache | vLLM 自动 Prefix Cache;system prompt 保持稳定 |
当前项目相关的实践建议:
- system prompt 尽量固定不变,利用 Prefix Cache 加速;
- RAG 注入的参考资料不要全部塞进 prompt,按相关性截断;
- 多轮对话设置合理的滑动窗口(如保留最近 5-10 轮)。
7.3 服务与调度层
这一层优化的是推理引擎自身的运行策略,不改变模型权重。
| 调优项 | 做什么 | 具体方法 |
|---|---|---|
| Continuous Batching | 动态组 batch,提高吞吐 | vLLM 默认开启;调整 max-num-seqs 控制并发上限 |
| KV Cache 管理 | 合理分配显存给 KV Cache | gpu-memory-utilization(如 0.9)、max-model-len、max-num-batched-tokens |
| Prefix Caching | 复用相同前缀的计算 | --enable-prefix-caching;保持 system prompt 稳定 |
| Chunked Prefill | 长输入分块 Prefill,避免阻塞 Decode | --enable-chunked-prefill + --max-num-batched-tokens |
| 调度策略 | 控制请求优先级和排队 | vLLM 的调度器参数;或在网关层做限流、优先级队列 |
| 采样参数优化 | 降低采样开销 | temperature=0 时可跳过部分采样计算;top_k=1 时直接取最大概率 |
当前项目的典型参数调优(以 vLLM-Ascend 为例):
# 常用调度相关参数
--max-num-seqs 16 # 最大并发序列数,过大会 OOM
--max-model-len 8192 # 最大上下文长度,按业务实际设置
--max-num-batched-tokens 32768 # 单 batch 最大 token 数
--gpu-memory-utilization 0.9 # 显存利用率
--enable-prefix-caching # 开启前缀缓存
--enable-chunked-prefill # 开启分块 Prefill调优步骤:
- 先跑基准测试,记录当前吞吐、延迟、显存占用;
- 逐步调整单个参数,每次只改一个变量;
- 观察指标变化,找到业务场景下的最优组合;
- 压力测试验证稳定性。
7.4 模型与精度层
这一层通过改变模型的计算方式来提升推理效率,不改变模型能力(理论上),但可能有微小精度损失。
| 调优项 | 做什么 | 精度影响 | 速度影响 |
|---|---|---|---|
| 量化(Quantization) | 降低权重和/或激活的数值精度 | 有一定损失,视量化方式和模型而定 | 显著减少显存和计算量 |
| 剪枝(Pruning) | 去掉不重要的权重或注意力头 | 可能有损失 | 减少计算量 |
| 蒸馏(Distillation) | 用大模型教小模型 | 小模型能力接近大模型 | 推理速度显著提升 |
量化是最常用的推理调优手段,常见量化方式对比:
| 量化方式 | 权重精度 | 激活精度 | 说明 |
|---|---|---|---|
| FP16 / BF16 | 16 bit | 16 bit | 基线,无量化 |
| W8A8 | 8 bit | 8 bit | 显存减半,速度提升,精度损失较小 |
| W4A16 | 4 bit | 16 bit | 显存大幅减少,速度取决于硬件支持 |
| FP8 | 8 bit | 8 bit | 较新,精度优于 INT8,需硬件支持 |
| GPTQ | 4 bit | 16 bit | 离线量化,精度较好 |
| AWQ | 4 bit | 16 bit | 保护重要权重,精度较好 |
昇腾环境的量化建议:
- vLLM-Ascend 支持的量化方式取决于版本和模型,应以官方文档和实测为准;
- W8A8 是昇腾上比较成熟的量化方案;
- 量化前后的精度需用 AISBench 等评测工具对比验证。
量化的一般步骤:
- 选择量化方式(如 W8A8);
- 准备校准数据集(部分量化方式需要);
- 执行量化,生成量化权重;
- 用量化权重启动推理服务;
- 跑评测对比精度,确认可接受;
- 对比推理性能(吞吐、延迟、显存)。
7.5 硬件与并行层
这一层优化的是硬件资源的使用方式和多卡/多机的通信效率。
| 调优项 | 做什么 | 具体方法 |
|---|---|---|
| 并行策略调整 | 选择合适的 TP/DP/EP/PP 组合 | 参见问题 4;根据模型大小和并发需求选择 |
| TP 大小调整 | 节点内 TP 越大,单层通信越多,但单卡显存压力越小 | 单机 8 卡通常 TP=8;小模型可 TP=4+DP=2 |
| 通信优化 | 减少卡间/节点间通信开销 | 使用高速互联(HCCS);避免跨节点做细粒度 TP |
| Batch 大小调优 | 找到吞吐和延迟的平衡点 | max-num-seqs 过大会增加排队延迟,过小会浪费算力 |
| 内存管理 | 避免 OOM,最大化利用显存 | gpu-memory-utilization、swap space(--swap-space) |
并行策略选择的决策逻辑:
模型单卡能放下?
├─ 是 → TP=1,按并发需求做 DP
└─ 否 → 单机能放下?
├─ 是 → TP=8(节点内),按并发需求做 DP
└─ 否 → TP=8 + PP=2(跨节点切层)
或更大 TP(如果硬件支持)
模型是 MoE?
└─ 是 → 开启 EP,专家层按专家分布7.6 推理调优的整体流程
1. 建立基准
- 固定模型、权重、精度、硬件
- 跑标准评测集,记录精度基线
- 跑压测,记录吞吐 / 延迟 / 显存基线
2. 从上往下逐层调优
- 先优化 Prompt 和上下文(零成本)
- 再调服务参数(低成本)
- 再评估量化(中等成本,需评测精度)
- 最后调整并行和硬件配置(高成本)
3. 每次只改一个变量
- 记录改动前后的指标对比
- 精度不能接受则回退
4. 压力测试验证
- 高并发、长上下文、长时间运行
- 观察稳定性、显存泄漏、调度异常
5. 上线后持续监控
- Prometheus + Grafana 监控吞吐、延迟、错误率
- 根据实际业务负载微调参数7.7 与当前项目的关系
当前项目已完成基础部署(GLM-5.1、DeepSeek-V4-Flash),下一步的调优方向建议:
| 优先级 | 方向 | 具体工作 |
|---|---|---|
| 高 | Prompt 与上下文 | 精简 system prompt、优化 RAG 注入策略、历史裁剪 |
| 高 | 服务参数 | 调整 max-num-seqs、max-model-len、开启 Prefix Cache |
| 中 | 量化评估 | 测试 W8A8 对 GLM-5.1 精度和性能的影响 |
| 中 | 并行策略 | 根据压测结果评估 DP/TP 是否需要调整 |
| 低 | 微调 | 如果通用模型无法满足业务需求,再考虑 LoRA 微调 |
问题 8:大模型微调是什么?有哪些方式、步骤和注意事项?
我的思考 / 问题
大模型微调(Fine-tuning)具体是什么?和推理调优的本质区别是什么?有哪些主流微调方式?各自适合什么场景?微调的完整流程是怎样的?有哪些常见坑和注意事项?
答疑结论
微调是在预训练模型的基础上,用特定任务或领域的数据继续训练,修改模型权重,使模型适配业务需求。 它与推理调优的本质区别是:
推理调优:不改权重,优化「怎么跑」—— 速度、吞吐、成本(见问题 7)
微调:改权重,改变「是什么」—— 能力、知识、风格、输出格式微调解决的核心问题是:通用基座模型在特定场景下「不够好用」——可能是不懂领域知识、输出格式不对、风格不符合要求、或者在特定任务上精度不够。
扩展知识
8.1 什么时候需要微调,什么时候不需要
微调不是万能的,在决定微调之前,应先评估是否有更低成本的替代方案:
| 需求 | 是否需要微调 | 更好的替代方案 |
|---|---|---|
| 模型不了解最新信息 | 不需要 | RAG(检索增强生成)注入知识 |
| 输出格式不统一 | 不一定需要 | Prompt 中给 few-shot 示例、或用结构化输出约束 |
| 模型风格不符合要求 | 可以微调,但先试 Prompt | System Prompt 中详细描述风格要求 |
| 特定任务精度不够 | 优先 Prompt + RAG | 不够再考虑微调 |
| 需要学习全新领域知识 | 需要微调 | RAG 只能注入检索到的内容,无法改变模型的内在理解 |
| 需要改变模型行为模式 | 需要微调 | 如拒绝特定类型问题、特定回答结构等 |
| 降低推理成本 | 蒸馏微调 | 用大模型数据训练小模型 |
决策流程:
需求明确
↓
先用 Prompt Engineering 尝试解决
↓
不够?→ 加 RAG / 知识库
↓
还不够?→ 考虑微调
↓
评估数据、算力、时间成本是否可接受
↓
选择微调方式8.2 主流微调方式详解
| 微调方式 | 原理 | 可训练参数量 | 资源需求 | 适用场景 |
|---|---|---|---|---|
| 全量微调 | 更新模型所有参数 | 100% | 极高,需多卡/多机 | 数据量大、任务与预训练差异极大 |
| LoRA | 在权重矩阵旁插入低秩分解的旁路矩阵,只训练旁路 | 约 0.1%~1% | 较低,单卡或多卡 | 最主流的高效微调方式 |
| QLoRA | LoRA + 4-bit 量化加载基座 | 约 0.1%~1% | 最低,单卡即可 | 显存极其有限时 |
| Prefix Tuning | 在每层输入前加可训练的连续向量 | 极少 | 低 | 简单任务适配 |
| Adapter | 在 Transformer 层间插入小型可训练模块 | 较少 | 低 | 多任务切换 |
| Prompt Tuning | 只训练输入层的 prompt embedding | 极少 | 最低 | 最简单的任务适配 |
LoRA 是目前最主流的微调方式,值得重点理解。
8.3 LoRA 的核心原理
全量微调会更新模型的每一个参数。对于 70B 模型来说,这意味着训练和存储 700 亿个参数的梯度和优化器状态,资源消耗巨大。
LoRA 的核心思想:大模型微调时,权重的变化量通常是低秩的(low-rank),不需要更新全部参数。
具体做法:
原始权重矩阵 W(冻结,不训练)
↓
增加旁路:W + ΔW = W + B × A
↑ ↑
低秩矩阵 低秩矩阵
(d × r) (r × d)
r << d
只训练 A 和 B,不训练 W例如,一个 4096×4096 的权重矩阵:
- 全量微调:训练 4096 × 4096 = 16,777,216 个参数
- LoRA(r=8):训练 4096 × 8 + 8 × 4096 = 65,536 个参数
- 参数量减少 256 倍
LoRA 的关键超参数:
| 参数 | 含义 | 典型值 | 影响 |
|---|---|---|---|
r(rank) | 旁路矩阵的秩 | 8、16、32、64 | 越大表达能力越强,但参数越多 |
lora_alpha | 缩放系数 | 通常 = 2r 或与 r 相同 | 控制旁路对原始权重的影响幅度 |
target_modules | 对哪些权重矩阵加 LoRA | q_proj, v_proj, k_proj, o_proj, gate_proj, up_proj, down_proj | 加的越多效果越好,但显存和时间增加 |
lora_dropout | Dropout 比率 | 0.05~0.1 | 防止过拟合 |
8.4 微调的完整流程
1. 明确目标
- 希望模型在什么任务上表现更好?
- 期望的输出格式、风格、行为是什么?
- 如何量化「更好」?(评测指标)
2. 准备数据
- 收集、清洗、格式化训练数据
- 数据格式通常是 instruction-input-output 三元组
- 数据量:LoRA 通常几百到几万条即可
- 数据质量 > 数据数量
- **划分训练集和评测集**(通常 8:2 或 9:1),评测集不能参与训练,用于验证微调效果
3. 选择基座模型和微调方式
- 基座模型:选与目标任务能力接近的(如中文任务选 GLM/Qwen)
- 微调方式:LoRA 最常用;显存不够用 QLoRA
4. 配置训练超参数
- 学习率(learning_rate):通常 1e-4 ~ 5e-5
- 训练轮数(epochs):1~5 轮,过多会过拟合
- batch size:根据显存调整
- 序列长度:与业务最大输入长度匹配
5. 启动训练
- 监控 loss 曲线:正常应下降并趋于平稳
- 监控 eval loss:如果 train loss 下降但 eval loss 上升,说明过拟合
- 定期保存 checkpoint
6. 评测
- 对比微调前后的精度(用标准评测集或业务测试集)
- 检查是否有灾难性遗忘(原本会的能力退化了)
- 检查输出风格、格式是否符合预期
- 安全性检查
7. 合并权重
- LoRA 训练完成后,将旁路矩阵合并回基座权重
- 合并后的模型与普通模型无区别,可以直接用 vLLM 加载
8. 量化 + 部署
- 对合并后的模型做量化(如 W8A8)
- 用 vLLM-Ascend 部署
- 推理调优(参见问题 7)8.5 微调数据的格式与质量
数据格式通常遵循 Chat 模板或 Alpaca 格式:
// Alpaca 格式(单轮)
{
"instruction": "请将以下英文翻译成中文",
"input": "Large language models are transforming the world.",
"output": "大语言模型正在改变世界。"
}
// Chat 格式(多轮)
{
"conversations": [
{"role": "user", "content": "什么是 KV Cache?"},
{"role": "assistant", "content": "KV Cache 是 Transformer 注意力计算中..."},
{"role": "user", "content": "它和上下文长度有什么关系?"},
{"role": "assistant", "content": "KV Cache 的显存占用与上下文长度成正比..."}
]
}数据质量的关键原则:
| 原则 | 说明 |
|---|---|
| 质量 > 数量 | 1000 条高质量数据 > 10000 条低质量数据 |
| 多样性 | 覆盖目标任务的各种变体,避免模型只学会少数模式 |
| 一致性 | 同类问题的回答风格和标准要统一 |
| 格式规范 | 输出格式要稳定,不要有时 JSON 有时纯文本 |
| 去噪 | 去除错误标注、矛盾数据、重复数据 |
| 平衡 | 不同类别/难度的数据比例要合理 |
8.6 微调的常见问题与避坑
| 问题 | 表现 | 原因 | 解决方法 |
|---|---|---|---|
| 过拟合 | train loss 下降,eval loss 上升;训练集表现好,新数据差 | 数据量太少、训练轮数太多、学习率太大 | 减少 epochs、增加数据、加 dropout、降低学习率 |
| 灾难性遗忘 | 微调后原本会的能力退化了 | 训练数据只覆盖了新任务,模型遗忘了通用能力 | 混入一部分通用数据;降低学习率;减少训练轮数 |
| 欠拟合 | 微调后没有明显改善 | 数据量不够、rank 太低、学习率太小 | 增加数据量、增大 rank、调整学习率 |
| 输出格式不稳定 | 有时 JSON 有时纯文本 | 训练数据格式不统一 | 清洗数据,确保输出格式一致 |
| 重复生成 | 模型输出不断重复同一段内容 | 训练数据中存在重复模式、解码参数不当 | 数据去重;推理时设 repetition_penalty |
| 安全性下降 | 微调后模型更容易生成不当内容 | 训练数据中可能包含敏感内容 | 数据审查;训练后做安全对齐 |
8.7 微调后的部署流程
微调完成(LoRA 权重)
↓
合并权重:LoRA + 基座 → 完整模型
↓
(可选)量化:W8A8 / W4A16 等
↓
转换为 vLLM-Ascend 可加载的格式
↓
部署推理服务
↓
推理调优(参见问题 7)
↓
评测验证(AISBench 或业务测试集)
↓
上线合并权重的注意事项:
- 合并后的模型文件通常和原始模型一样大(如 70B 合并后仍是 70B);
- 合并前确认 LoRA 权重与基座版本匹配(不同版本的基座不能混用 LoRA);
- vLLM 也支持直接加载 LoRA 权重而不合并(
--enable-lora),适合多 LoRA 切换场景。
8.8 微调与推理调优的关系
两者是串联关系,不是替代关系:
基座模型
↓ 微调(改变能力)
微调后模型
↓ 量化(降低显存)
量化后模型
↓ 推理调优(提升性能)
生产部署| 维度 | 微调 | 推理调优 |
|---|---|---|
| 改变什么 | 模型权重 | 运行参数、硬件配置 |
| 是否需要训练数据 | 是 | 否 |
| 是否改变模型能力 | 是 | 否 |
| 代价 | 训练算力 + 数据 + 时间 | 工程与运维 |
| 典型产出 | 新的模型权重文件 | 最优的启动参数组合 |
| 执行顺序 | 先 | 后 |
8.9 与当前项目的关系
当前项目(GLM-5.1、DeepSeek-V4-Flash)处于基础推理部署阶段。微调的适用时机:
| 阶段 | 是否需要微调 | 建议 |
|---|---|---|
| 当前 | 暂不需要 | 先完成推理调优,验证通用模型在业务场景的表现 |
| Prompt + RAG 能解决 | 不需要 | 通过优化 Prompt 和 RAG 注入知识 |
| 特定任务精度不够 | 考虑微调 | 准备数据,用 LoRA 微调 GLM-5.1 |
| 需要小模型降成本 | 考虑蒸馏 | 用 GLM-5.1 生成数据,训练小模型 |
「特定任务精度不够」的典型场景举例:
假设内部有一个工单分类系统,需要将用户提交的工单自动归类到「网络故障」「账号问题」「费用争议」「功能建议」等类别。直接用 GLM-5.1 做 zero-shot 分类,准确率只有 75% 左右,达不到上线要求的 92%。尝试了优化 Prompt(给出类别定义和 few-shot 示例)和 RAG(注入历史工单样本),准确率提升到 83%,仍然不够。此时就适合用历史工单数据(几千条已标注的工单-类别对)对 GLM-5.1 做 LoRA 微调,让模型学会内部工单的分类逻辑和表述习惯,通常可以将准确率提升到 90% 以上。
8.10 RAG 与微调的对比
RAG 和微调是提升模型在特定场景表现的两种主要手段,解决的问题有重叠但本质不同:
| 维度 | RAG(检索增强生成) | 微调(Fine-tuning) |
|---|---|---|
| 做什么 | 推理时检索相关知识,注入到 prompt 中 | 训练时修改模型权重,让模型内化知识 |
| 是否改权重 | 否 | 是 |
| 知识更新方式 | 更新知识库即可,无需重新训练 | 需要用新数据重新训练或增量训练 |
| 部署复杂度 | 需要维护向量库、检索服务、文档管线 | 训练阶段复杂,部署后与普通模型无异 |
| 适用场景 | 知识频繁变化、需要引用来源、私有知识注入 | 需要改变模型行为模式、风格、格式、内在理解 |
| 成本结构 | 基础设施成本(向量库、存储、检索) | 训练算力成本 + 数据标注成本 |
| 响应延迟 | 检索会增加额外延迟 | 推理时无额外延迟 |
| 可解释性 | 高,可以展示引用来源 | 低,无法追溯知识来源 |
核心区别在于知识的「存放位置」:
RAG:知识存在外部知识库里,推理时临时塞进 prompt
微调:知识被训练进了模型权重里,模型「记住」了两者的选择决策:
需求是什么?
├─ 注入外部知识(政策、文档、产品手册)→ RAG
├─ 需要引用来源、可追溯 → RAG
├─ 知识频繁更新 → RAG
├─ 改变模型输出风格/格式 → 微调
├─ 提升特定任务精度 → 先试 RAG,不够再微调
├─ 让模型理解全新领域概念 → 微调
└─ 两者都需要 → RAG + 微调组合使用两者也可以组合使用:
微调:让模型学会「怎么用」检索到的知识(如特定领域的推理方式、回答结构)
RAG:提供「用什么」知识(如最新的政策文档、产品信息)组合后的典型链路:
用户问题
↓
RAG 检索相关知识片段
↓
拼入 prompt
↓
微调后的模型基于检索结果生成回答
↓
返回(附引用来源)| 组合方式 | 适用场景 |
|---|---|
| 仅 RAG | 知识注入、问答系统、文档助手 |
| 仅微调 | 风格迁移、格式约束、任务精度提升 |
| RAG + 微调 | 专业领域问答(如医疗、法律),既需要最新知识,又需要专业化的回答方式 |
问题 9:KV Cache 可以理解为一次完整上下文长度的缓存吗?和最大上下文长度是什么关系?
我的思考 / 问题
KV Cache 可以理解为一次完整上下文长度的缓存吗?KV Cache 是不是和大模型推理的最大上下文长度强相关?
答疑结论
不完全是。 KV Cache 不是「缓存了一整段上下文」,而是缓存了每个 token 在每一层的 Key 和 Value 两个向量。它是 Transformer 注意力计算的中间产物,不是上下文本身的副本。
是强相关的。 KV Cache 的显存占用与上下文长度(token 数)成正比,是决定 max-model-len 能设多大的最核心因素。
一句话理解:
KV Cache = 每个已处理 token × 每层 × 每个 KV 头 × head_dim × 数据类型字节数它不是「记住了文本」,而是「记住了每个 token 在注意力机制中的 Key 和 Value 状态」,这样 Decode 阶段生成新 token 时,不需要重新计算之前所有 token 的 K 和 V。
扩展知识
9.1 KV Cache 到底缓存了什么
Transformer 的 Self-Attention 机制中,每个 token 会生成三个向量:
| 向量 | 作用 |
|---|---|
| Q(Query) | 当前 token「在找什么」 |
| K(Key) | 每个 token「能提供什么」 |
| V(Value) | 每个 token「实际携带的信息」 |
注意力计算的核心是:
Attention(Q, K, V) = softmax(Q × K^T / √d_k) × V在 Prefill 阶段,模型一次性处理所有输入 token,为每个 token 计算出 K 和 V。如果不缓存,Decode 阶段每生成一个新 token,就要重新算一遍之前所有 token 的 K 和 V —— 这是巨大的重复计算。
KV Cache 的作用就是:把 Prefill 阶段算好的 K 和 V 存起来,Decode 阶段只需要算新 token 的 K 和 V,追加到缓存里,然后用完整的 K、V 做注意力。
Prefill 阶段(处理 N 个输入 token):
token_0 → K_0, V_0 ─┐
token_1 → K_1, V_1 │ 全部存入 KV Cache
... │
token_N → K_N, V_N ─┘
Decode 阶段(生成第 N+1 个 token):
只算 token_N+1 → K_N+1, V_N+1
追加到 KV Cache
用完整的 [K_0..K_N+1] × [V_0..V_N+1] 做注意力所以 KV Cache 缓存的不是「上下文文本」,而是每个 token 在每一层的注意力状态。
9.2 KV Cache 的显存公式
单个请求的 KV Cache 显存:
KV Cache 显存 = 2 × 层数 × KV 头数 × head_dim × 序列长度 × 数据类型字节数
↑ ↑ ↑
K 和 V 各一份 每层的 KV 头维度 当前已处理的 token 数其中 KV 头数 × head_dim = KV 维度。如果模型使用 GQA(Grouped Query Attention),KV 头数会少于 Q 头数,KV Cache 也会更小。
用具体数字感受一下:
| 模型参数 | 典型值 |
|---|---|
| 层数(layers) | 80~96 |
| KV 头数(num_kv_heads) | 8~16(GQA 模型) |
| head_dim | 128 |
| 数据类型 | FP16(2 bytes) |
假设 80 层、8 个 KV 头、head_dim=128、FP16:
每个 token 的 KV Cache = 2 × 80 × 8 × 128 × 2 bytes = 327,680 bytes ≈ 0.31 MB那么:
| 序列长度 | 单请求 KV Cache |
|---|---|
| 2,048 | 约 0.64 GB |
| 8,192 | 约 2.56 GB |
| 32,768 | 约 10.24 GB |
| 131,072 | 约 40.96 GB |
这只是单个请求。 如果同时服务 16 个并发请求,KV Cache 总量还要乘以 16。
9.3 为什么 KV Cache 和最大上下文长度强相关
显存的分配大致是:
总可用显存 = 权重显存 + KV Cache 显存 + 运行时开销其中:
- 权重显存是固定的,加载后不会变化;
- 运行时开销相对较小且固定;
- KV Cache 显存是唯一随请求动态增长的部分。
因此:
KV Cache 显存预算 = 总可用显存 - 权重显存 - 运行时开销
能支持的最大上下文长度 ≈ KV Cache 显存预算 ÷ 单 token KV 显存 ÷ 并发请求数这就是为什么 max-model-len 的上限由显存决定——不是模型「不认识」更长的文本,而是放不下更长的 KV Cache。
9.4 KV Cache 与并发的关系
KV Cache 不是全局共享的——每个并发请求都有自己的 KV Cache。
请求 A:自己的 KV Cache(长度取决于 A 的 token 数)
请求 B:自己的 KV Cache(长度取决于 B 的 token 数)
请求 C:自己的 KV Cache(长度取决于 C 的 token 数)所以:
总 KV Cache 显存 ≈ Σ(每个请求的序列长度 × 单 token KV 显存)这意味着 max-model-len 和 max-num-seqs(最大并发数)是互相制约的:
| 场景 | max-model-len | max-num-seqs | KV Cache 显存压力 |
|---|---|---|---|
| 长上下文、低并发 | 大(如 32K) | 小(如 4) | 中等 |
| 短上下文、高并发 | 小(如 4K) | 大(如 32) | 中等 |
| 长上下文、高并发 | 大 | 大 | 很大,容易 OOM |
生产部署中常见的调优就是在这两者之间找平衡:
显存预算固定
→ 想要更长上下文 → 降低并发数
→ 想要更高并发 → 缩短上下文长度9.5 KV Cache 的生命周期
请求到达
↓
Prefill:处理所有输入 token,生成完整 KV Cache
↓
Decode:每生成一个 token,追加一个位置的 K、V
↓
生成完成或达到 max_tokens
↓
KV Cache 释放,显存归还KV Cache 是请求级别的临时数据,不会跨请求保留(Prefix Cache 除外,见下文)。请求结束后立即释放。
9.6 KV Cache 的优化手段
既然 KV Cache 是显存的主要消费者,围绕它的优化手段有很多:
| 优化手段 | 原理 | 效果 |
|---|---|---|
| GQA / MQA | 减少 KV 头数(多个 Q 头共享一组 K、V) | KV Cache 缩小 4~8 倍 |
| 量化 KV Cache | 把 KV 从 FP16 降到 FP8 或 INT8 | KV Cache 显存减半 |
| Prefix Cache | 相同前缀的请求复用已有 KV Cache | 减少重复 Prefill,节省显存和时间 |
| Prompt Caching(云服务) | 云服务商在服务端暂存请求的 KV Cache(如 Anthropic 约 5 分钟),相同前缀的后续请求直接复用 | 降低首 token 延迟和成本;与 vLLM Prefix Cache 原理相同,但由服务端管理生命周期 |
| PagedAttention | KV Cache 按页分配,避免碎片化 | 提高显存利用率(vLLM 核心技术) |
| Chunked Prefill | 长输入分块处理,与 Decode 交错 | 降低峰值显存 |
| KV Cache 压缩 | 丢弃不重要的 KV 条目(如 H2O、StreamingLLM) | 在可接受精度损失下扩展有效上下文 |
当前项目相关的要点:
- vLLM 的 PagedAttention 是 KV Cache 管理的基础,按 block 分配,不需要连续内存;
--enable-prefix-caching可以让相同 system prompt 的请求共享 KV Cache 前缀;- GQA 是模型结构层面的优化,由模型设计决定(如 GLM-5.1、DeepSeek 已使用 GQA);
- 量化 KV Cache 是否支持取决于 vLLM-Ascend 版本。
9.7 直观类比
可以把 KV Cache 比作「演算草稿纸」:
| 类比 | 对应 |
|---|---|
| 上下文文本 | 题目本身 |
| KV Cache | 做完每一步后记录的中间结果草稿 |
| 重新计算 K、V | 没有草稿纸,每次加一步都要从头算 |
| 有 KV Cache | 草稿纸在,新一步只需要在末尾追加 |
| 草稿纸太大放不下 | 限制了能做的题目长度(max-model-len) |
| 多个人同时做题 | 每人需要自己的草稿纸(并发 × 序列长度) |
一句话总结:KV Cache 是 Transformer 注意力计算中 K、V 向量的增量缓存,不是上下文文本的副本;它与最大上下文长度强相关——上下文越长,KV Cache 越大,是决定 max-model-len 上限的核心因素;同时它也与并发数互相制约,两者共同决定显存需求。
问题 10:昇腾部署推理必须对权重进行量化处理吗?
我的思考 / 问题
昇腾部署推理必须对权重进行量化处理吗?我目前是直接下载 W8A8 量化版本的权重,理论上是否所有模型都可以自行进行权重量化后即可适配昇腾芯片?
答疑结论
不是必须的。 量化不是昇腾部署的前提条件,而是一种优化手段。是否量化取决于模型大小、NPU 显存容量和性能需求。
是否能量化 ≠ 是否必须量化
模型能装进显存 → 可以不量化,直接用原始精度部署
模型装不下 / 想要更高吞吐 → 量化减少显存和计算量直接下载 W8A8 量化权重是最常见、最省事的做法,只要该量化权重是由可信来源(如官方或社区验证过的)产出的,就可以直接用于部署。
但「所有模型都可以自行量化后适配昇腾」这个说法过于乐观,存在多个限制条件。
扩展知识
10.1 量化到底在做什么
模型权重本质上是一堆数值(浮点数)。原始训练出来的权重通常是 FP32(32 bit)或 FP16(16 bit),每个参数占用 4 字节或 2 字节。
量化的核心就是:用更少的 bit 来表示这些数值。
FP16 权重:每个参数 16 bit(2 字节)
↓ 量化
INT8 权重:每个参数 8 bit(1 字节)具体过程:
原始权重(连续的浮点数值,如 -0.037, 0.125, -0.008, 0.501, ...)
↓
找到这组数值的范围(min, max),比如 [-0.037, 0.501]
↓
把这个范围映射到 INT8 的离散整数(-128 到 127,共 256 个级别)
↓
每个浮点数 → 一个最接近的整数 + 一个缩放系数(scale)
↓
存储时只需存整数(8 bit)和少量 scale 参数直观理解:
原始 FP16:0.125(16 bit)
量化后 INT8:整数 63(8 bit)+ scale = 0.00198
推理时:63 × 0.00198 ≈ 0.125(近似还原)这个过程是有损的——从 65536 种可能的值(FP16)压缩到 256 种(INT8),精度必然下降。但好的量化策略能让这种下降尽可能小。
W8A8 的含义:
| 符号 | 含义 |
|---|---|
| W8 | Weights(权重)量化为 8 bit |
| A8 | Activations(激活值)也量化为 8 bit |
同理:
- W4A16:权重 4 bit,激活保持 16 bit
- W8A16:权重 8 bit,激活保持 16 bit
量化带来两个核心收益:
| 收益 | 原因 |
|---|---|
| 显存减半(W8A8) | 每个参数从 2 字节变成 1 字节,权重显存直接减半 |
| 计算加速 | INT8 矩阵乘法比 FP16 更快(硬件有原生加速,如昇腾 Cube Core) |
代价是精度有一定损失,需要通过评测验证是否可接受。
10.2 昇腾部署是否必须量化
不是必须的。是否量化取决于以下因素:
| 条件 | 是否需要量化 | 说明 |
|---|---|---|
| 模型较小,单卡能装下(FP16) | 不需要 | 直接用 FP16/BF16 部署 |
| 模型较大,FP16 装不下 | 需要量化或其他手段 | 量化、TP 切分、PP 切分都是可选方案 |
| 想要更高吞吐和更低延迟 | 建议量化 | 量化后计算量减少,Decode 速度提升 |
| 想要支持更长上下文 | 建议量化 | 权重显存减少,留给 KV Cache 的空间更大 |
| 想要更高并发 | 建议量化 | 同理,释放显存给更多并发请求的 KV Cache |
决策逻辑:
模型 FP16 能装进显存?
├─ 是 → 不量化也可以,先跑起来再说
│ 想要更快 → 再考虑量化
└─ 否 → 必须量化或切分
├─ 量化(W8A8 / W4A16)→ 单卡能装下?
│ ├─ 是 → 量化后部署
│ └─ 否 → TP/PP 切分 + 可选量化
└─ TP/PP 切分 → 不量化也能装下?
├─ 是 → 不量化,多卡部署
└─ 否 → 切分 + 量化当前项目中,你使用 W8A8 量化版本是合理的选择,因为:
- GLM-5.1 和 DeepSeek-V4-Flash 参数量较大;
- W8A8 在昇腾上是比较成熟的量化方案;
- 量化后显存减半,可以支持更长上下文和更高并发。
10.3 量化的两种路径
| 路径 | 做什么 | 优缺点 |
|---|---|---|
| 直接下载量化权重 | 别人已经量化好的权重,直接加载使用 | 最省事;但依赖权重来源的质量和兼容性 |
| 自行量化 | 用量化工具对原始 FP16 权重进行量化 | 可控性高;但需要工具、校准数据、时间和验证 |
直接下载量化权重是生产中最常见的做法,特别是当模型官方或社区已经提供了验证过的量化版本时。
自行量化适用于以下场景:
- 没有现成的量化权重可用;
- 需要特定量化方式(如自定义的 smooth quant 策略);
- 需要对量化过程完全可控(安全合规等);
- 想要针对特定数据分布做校准优化。
10.4 自行量化的流程与限制
自行量化的一般流程:
1. 获取原始 FP16 权重
↓
2. 选择量化方式(W8A8 / W4A16 / FP8 等)
↓
3. 准备校准数据集(部分量化方式需要)
- 通常几百到几千条有代表性的文本
- 覆盖目标任务的典型输入分布
↓
4. 运行量化工具
- 生成量化后的权重文件
- 生成量化参数(scale、zero_point 等)
↓
5. 转换为推理框架支持的格式
- vLLM-Ascend 需要特定的权重格式
↓
6. 加载验证
- 用量化权重启动推理服务
- 跑评测对比精度
- 确认性能提升但自行量化存在以下限制和注意事项:
| 限制项 | 说明 |
|---|---|
| 量化工具兼容性 | 不是所有量化工具都支持昇腾。需要使用昇腾兼容的量化工具链(如 ATC、MindSpore 量化工具、或 vLLM-Ascend 支持的量化方式) |
| vLLM-Ascend 支持的量化格式 | vLLM-Ascend 不是支持所有量化格式。W8A8 是比较成熟的方案,W4A16、FP8 等取决于版本 |
| 模型结构限制 | 某些模型结构中的特殊算子(如自定义的 attention 变体、MoE router)可能不被量化工具良好支持 |
| 校准数据质量 | 校准数据分布与实际推理数据差距大时,量化后精度可能明显下降 |
| 精度不可预测 | 不同模型对量化的敏感度不同。有的模型 W8A8 几乎无损,有的会有明显精度下降 |
| 格式转换 | 量化后的权重格式需要与推理框架匹配,可能存在格式不兼容的问题 |
10.5 是否所有模型都可以量化后适配昇腾
理论上大部分主流模型都可以量化,但实际操作中存在多个门槛:
| 门槛 | 说明 |
|---|---|
| 模型结构 | 主流 Transformer 架构(如 GLM、Qwen、LLaMA、DeepSeek)的量化支持较好。非主流或自定义结构可能缺少量化工具支持 |
| MoE 模型 | MoE 模型(如 DeepSeek-V4-Flash、Mixtral)的专家层量化需要特殊处理,不是所有工具都能良好支持 |
| 量化工具链 | 需要量化工具能在昇腾上正确运行,并输出 vLLM-Ascend 可加载的格式 |
| 精度验证 | 量化后必须跑评测确认精度可接受。不能假设所有模型量化后精度都 OK |
| vLLM-Ascend 版本 | 不同版本支持的量化方式不同。新模型可能需要更新版本才能支持 |
更准确的说法是:
大部分主流模型(GLM、Qwen、LLaMA、DeepSeek 等)
→ 可以通过量化适配昇腾
→ 但需要验证:工具支持、格式兼容、精度可接受
非主流或自定义模型
→ 可能缺少现成的量化方案
→ 需要更多适配工作
所有模型
→ 不能假设「量化一下就行」
→ 量化是工程实践,不是万能药10.6 W8A8 在昇腾上的特殊地位
W8A8 之所以在昇腾生态中被广泛使用,是因为:
| 原因 | 说明 |
|---|---|
| 硬件原生支持 | 昇腾 NPU 的 Cube Core 对 INT8 矩阵乘法有原生加速支持 |
| CANN 算子支持 | 昇腾的 CANN(Compute Architecture for Neural Networks)算子库对 W8A8 有完善的优化 |
| 社区和官方验证 | 大量模型的 W8A8 版本已经在昇腾上经过验证 |
| 精度损失可控 | 对于大多数主流模型,W8A8 的精度损失在可接受范围内 |
相比之下:
| 量化方式 | 昇腾支持程度 | 说明 |
|---|---|---|
| W8A8 | 成熟 | 硬件原生支持,社区验证充分 |
| W4A16 | 逐步完善 | 部分模型支持,需要验证 |
| FP8 | 逐步完善 | 需要较新的昇腾硬件和软件栈支持 |
| GPTQ / AWQ | 有限 | 这些是 GPU 生态中常见的量化方式,在昇腾上的兼容性取决于 vLLM-Ascend 版本 |
10.7 实践建议
针对当前项目的量化决策:
| 场景 | 建议 |
|---|---|
| 已有官方/社区验证的 W8A8 权重 | 直接使用,这是最省事且风险最低的方式 |
| 没有现成量化权重 | 评估是否真的需要量化;如果 FP16 能装下,可以先不量化 |
| 需要自行量化 | 使用昇腾兼容的量化工具;准备高质量校准数据;量化后必须跑评测 |
| 量化后精度不可接受 | 先尝试优化校准数据;不行则回退到更高精度或不量化 |
| 想要尝试 W4A16 等更激进的量化 | 在 W8A8 基础上进一步评估;精度损失通常更大 |
一句话总结:量化不是昇腾部署的必要条件,而是优化显存和性能的手段;直接下载 W8A8 量化权重是最常见的实践方式;但并非所有模型都能「量化一下就行」,需要考虑量化工具兼容性、模型结构支持、精度验证等多个环节,不能一概而论。
问题 11:大模型推理后端是怎么管理不同用户的上下文的?后端能识别不同用户的请求吗?
我的思考 / 问题
大模型推理后端(vLLM)是怎么管理不同用户的上下文的?后端能识别不同用户的请求吗?
答疑结论
问题 1 已说明后端不存记忆。本问题进一步聚焦:后端能否识别不同用户、如何隔离并发请求。
推理后端不管理用户上下文,也不能识别用户身份。 vLLM 是一个纯粹的推理执行引擎——它只看到一个一个独立的请求,每个请求包含完整的 messages 数组。它不知道请求来自谁,也不会为任何用户维护状态。
vLLM 看到的世界:
请求 1:带完整 messages 的推理任务
请求 2:带完整 messages 的推理任务
请求 3:带完整 messages 的推理任务
它不关心这些请求来自同一个用户还是不同用户用户的识别、会话管理、上下文拼装,全部由上游的应用层或网关层负责。推理后端只是「执行者」,不是「管理者」。
扩展知识
11.1 推理后端的视角
vLLM 在处理每个请求时,唯一关心的是:
| 项 | 说明 |
|---|---|
messages 数组 | 包含完整的对话上下文(system、user、assistant 历史轮次) |
model | 模型名称 |
| 生成参数 | max_tokens、temperature、top_p 等 |
| 可选字段 | request_id、user 等(用于日志和计费,不参与推理逻辑) |
OpenAI 兼容 API 中确实有一个 user 字段:
{
"model": "glm-5.1",
"messages": [...],
"user": "user_12345"
}但这个字段在 vLLM 中仅用于日志记录和限流统计,不会影响推理行为。vLLM 不会根据 user 字段做上下文隔离、历史记忆或个性化处理。
11.2 用户上下文管理的职责分工
客户端
↓ 携带 user_id、session_id、认证 token
网关 / 应用层
├─ 鉴权:确认用户身份
├─ 会话管理:按 session_id 读取历史消息
├─ 上下文拼装:把历史 + RAG + system prompt 组装成 messages
└─ 发送请求给 vLLM(请求体中包含完整 messages)
↓
vLLM 推理服务
├─ Tokenize messages
├─ Prefill + Decode
└─ 返回 assistant 回复
↓
网关 / 应用层
├─ 保存本轮对话
├─ 输出审查
└─ 返回给客户端各层职责:
| 层级 | 是否知道用户身份 | 是否管理上下文 | 是否存记忆 |
|---|---|---|---|
| 客户端 | 是 | 否(仅展示) | 否 |
| 网关 / 应用层 | 是 | 是 | 是 |
| vLLM | 否 | 否 | 否 |
11.3 为什么后端不管理用户上下文
这不是设计缺陷,而是合理的架构分工:
| 原因 | 说明 |
|---|---|
| 关注点分离 | 推理引擎专注计算效率,业务逻辑由应用层处理 |
| 多租户灵活 | 不同应用可以有不同的用户模型、会话策略、记忆机制 |
| 性能 | 推理服务保持无状态,易于水平扩展和负载均衡 |
| 安全 | 推理层不接触用户身份信息,减少泄露风险 |
11.4 并发请求的隔离
当多个用户的请求同时到达 vLLM 时,隔离是天然的——因为每个请求自带完整的 messages,彼此之间没有任何数据依赖。
时间线:
T1:用户 A 的请求到达 → messages_A 进入 Prefill
T2:用户 B 的请求到达 → messages_B 进入 Prefill
T3:用户 A 的请求 Decode → 生成 A 的回复
T4:用户 B 的请求 Decode → 生成 B 的回复
两个请求的 KV Cache 完全独立,互不干扰vLLM 的 Continuous Batching 会把多个请求合并成一个 batch 提高 GPU/NPU 利用率,但每个请求的 KV Cache 是独立分配和管理的(通过 PagedAttention 的 block 机制)。不会出现用户 A 的输出混入用户 B 的情况。
11.5 容易混淆的点
Prefix Cache 不等于跨用户共享上下文
vLLM 的 Prefix Cache 可以复用相同前缀的 KV Cache 计算结果。如果用户 A 和用户 B 有相同的 system prompt,Prefill 阶段可以复用这部分计算。但这只是计算优化,不是「共享上下文」——两个请求仍然有各自独立的 KV Cache,生成结果互不影响。
user 字段不等于用户管理
OpenAI API 规范中的 user 字段是一个可选标识,用于:
- 日志追踪(哪个请求来自哪个用户)
- 限流(按用户限制请求频率)
- 计费(按用户统计 token 消耗)
但它不参与推理逻辑。vLLM 不会因为 user 字段不同而改变推理行为。
11.6 与问题 1 的关系
问题 1 已经说明了「后端不存记忆」。问题 11 进一步说明:
后端不仅不存记忆,它甚至不知道「谁在问」。
所有用户相关的能力:
- 用户识别 → 应用层
- 会话管理 → 应用层
- 历史消息 → 应用层读取后拼入 messages
- 长期记忆 → 应用层检索后注入 prompt
- 个性化 → 应用层根据用户画像调整 system prompt
vLLM 只做一件事:根据收到的 messages 做推理,返回结果。一句话总结:vLLM 推理后端不管理用户上下文,也不能识别用户身份——它只看到一个个独立的、自带完整 messages 的推理请求。用户的识别、会话管理、上下文拼装和记忆维护全部由上游的网关或应用层负责。并发请求之间通过独立的 KV Cache 天然隔离,不会串话。
后续关于大模型推理、网关、上下文、多用户、评测、容量规划、联网搜索与 RAG 等思考型问题,都继续追加到本文,格式为:
## 问题 N:标题
### 我的思考 / 问题
### 答疑结论
### 扩展知识