外观
部署与推理优化实战
部署与推理优化实战的目标,是把一个选型、微调好的 LLM 变成稳定、便宜、够快的生产服务。 概念页《推理优化与量化》讲透 GPU 上推理为什么慢、量化与 KV cache 的原理;本页解决落地问题:该走云 API 还是自部署、模型怎么导出与量化、服务怎么起、API 怎么接、缓存与限流怎么配、线上怎么监控——每一步都给可复制的最小配置。
先建立一个清醒的预期:LLM 部署和传统 Web 服务有本质区别——它在 GPU 上跑、有概率性输出、延迟和吞吐随负载剧烈波动。因此本页所有实践的隐含目标都只有三个:把延迟压下去、把吞吐提上来、把成本降下来,并在三者之间找到你的业务平衡点。
本文的阅读顺序
建议先读推理优化与量化建立机制层面的认知,再按本文从「部署路径决策」往下走。如果你连模型都还没选好,先看 模型与榜单速查;如果模型还没微调,先看微调你自己的 LLM。部署完成后,评测与回归交给搭建一套 LLM 评估。
一、部署路径决策:云 API 还是自部署?
第一步不是写配置,而是决定模型跑在哪。两条路线的本质区别不是"贵或便宜",而是你在成本、控制、延迟、数据安全四个维度上的优先级。
| 决策维度 | 云 API(OpenAI、Anthropic、国内大模型平台等) | 自部署(vLLM / Ollama / TensorRT-LLM) |
|---|---|---|
| 前期成本 | 几乎为零,按 token 付费 | 高:GPU 硬件或云 GPU 租用 + 运维人力 |
| 边际成本 | 每次调用都付费,量越大越贵 | 固定成本,容量打满后边际成本趋近零 |
| 延迟 | 网络 + 排队,波动较大(共享算力) | 内网直连,可预测,独占算力可极致优化 |
| 控制力 | 低:不能改采样细节、不能插桩、版本受制于人 | 高:解码参数、批处理、前缀缓存全部可控 |
| 数据安全 | 数据出境与留存政策受制于服务商 | 数据不出内网,满足私有化/合规要求 |
| 弹性 | 天然弹性,突发流量扛得住 | 需要自己扩容,容量规划是常态工作 |
| 运维成本 | 服务商承担 | 全栈自管:GPU、驱动、框架、版本升级 |
| 定制化 | 只能用厂商模型 | 可部署自己的微调模型 |
一句话判断(经验法则):
- 日均请求量小、模型不换、不想养 GPU → 云 API,把人力花在业务上;
- 调用量大到"云 API 月度账单超过一台 GPU 服务器"、或模型必须私有 → 自部署;
- 最经济的常见形态是混合:默认走自部署的开源模型,峰值或高难请求降级到云 API。
text
决策流程(从第一步开始,命中即走该路线):
1. 业务要求数据不出内网 / 通过合规审计? ──→ 自部署(无选择)
2. 需要部署自己微调的模型(LoRA 等)? ──→ 自部署
3. 单月预计调用量折合 token < 某阈值(如数亿)? ──→ 云 API
4. 延迟要求极苛刻(如首 token < 500ms)且量级大? ──→ 自部署
5. 都不满足? ──→ 混合:默认自部署 + 峰值走云 API别被"免费开源"骗了
开源模型本身免费,但跑它的 GPU 不免费。自部署的真实成本 = 硬件/租用 + 电力 + 运维人力,这三项加起来的隐性成本常被低估。模型选型的权衡详见 模型与榜单速查。
二、分步实战:从模型文件到生产服务
下面以自部署路线为主线(云 API 只是把"服务端"换成厂商端点),走完五步:导出量化 → vLLM 起服务 → OpenAI 兼容 API 接入 → 网关缓存 → 监控告警。
步骤 1:模型导出与量化
自部署前,先把模型权重导出成标准格式(safetensors),再按显存预算做量化。量化是自部署的"第一生产力":同样的显存,4-bit 量化能把可部署的模型规模放大接近一倍,原理见推理优化与量化的「量化」一节。
主流量化方案对比(截至 2025 年年中):
| 方案 | 类型 | 特点 | 适用场景 |
|---|---|---|---|
| AWQ | 4-bit 权重量化(激活感知) | 校准数据少、精度损失小、vLLM 原生支持 | 一般生产默认 |
| GPTQ | 4-bit 权重量化(基于 Hessian) | 老牌方案,生态成熟 | 兼容旧工具链 |
| FP8 | 8-bit 浮点 | H100 等新卡硬件加速 | 显存宽裕、要精度 |
| GGUF(Q4_K_M 等) | 4-bit 量化 | Ollama / llama.cpp 生态 | 个人机、边缘设备 |
以 AWQ 为例,用 AutoAWQ 量化导出:
python
from awq import AutoAWQForCausalLM
from transformers import AutoTokenizer
model_path = "Qwen/Qwen2.5-7B-Instruct" # 或你的微调模型路径
quant_path = "./qwen2.5-7b-awq" # 量化后输出目录
model = AutoAWQForCausalLM.from_pretrained(model_path)
tokenizer = AutoTokenizer.from_pretrained(model_path)
# AWQ 需要少量校准数据(128~512 条即可,用目标任务的代表性样本)
calib_data = [
"公司的退款政策是什么?",
"请总结这份合同的核心条款。",
# ... 约 128~512 条
]
model.quantize(tokenizer, quant_config={"zero_point": True, "q_group_size": 128, "w_bit": 4})
model.save_quantized(quant_path)
tokenizer.save_pretrained(quant_path)量化前先想清楚三件事
- 量化不可逆:原始权重要留档,量化后的模型没法无损还原;
- 精度损失要验证:量化前后在评测集上跑一遍对比,掉点超阈值(如 >1%)就考虑换更宽的位宽,详见 LLM 评估与基准;
- 优先量化推理侧:训练/微调用 FP16,部署用 INT4,两套权重各司其职。
步骤 2:vLLM 启动服务
vLLM 是目前自部署 LLM 的事实标准:支持 PagedAttention 的 KV cache 管理、连续批处理(continuous batching)与 OpenAI 兼容 API,吞吐通常比朴素 Transformers 推理高一个数量级。最小启动命令:
bash
# 安装(需 CUDA 环境,详见官方文档)
pip install vllm
# 启动 OpenAI 兼容服务(显存按卡预留、输出长度设上限)
vllm serve Qwen/Qwen2.5-7B-Instruct \
--quantization awq \
--gpu-memory-utilization 0.90 \
--max-model-len 8192 \
--port 8000更复杂的生产配置写进启动参数或环境变量:
| 参数 | 建议值 | 说明 |
|---|---|---|
--gpu-memory-utilization | 0.85~0.95 | 显存利用率,太高会 OOM,太低浪费显存 |
--max-model-len | 按业务定(如 8192) | 决定最大上下文长度;注意它同时约束 KV cache 预留 |
--max-num-seqs | 64~256 | 单次批处理的最大序列数,越大吞吐越高但延迟抬升 |
--tensor-parallel-size | 卡数(多卡时) | 张量并行切分 |
--enable-prefix-caching | 开启 | 前缀缓存,RAG 场景命中率可观 |
--served-model-name | 自定义 | 暴露给客户端的模型名,可指向真实路径的别名 |
yaml
# docker-compose.yaml 示例:单卡部署 + 健康检查
services:
llm:
image: vllm/vllm-openai:latest
command:
- "--model"
- "/models/qwen2.5-7b-awq"
- "--quantization"
- "awq"
- "--gpu-memory-utilization"
- "0.90"
- "--max-model-len"
- "8192"
- "--enable-prefix-caching"
volumes:
- ./models:/models
ports:
- "8000:8000"
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]启动后自检:curl http://localhost:8000/v1/models 应返回模型列表;/health 或 /v1/chat/completions 可做探活。
步骤 3:OpenAI 兼容 API 接入
vLLM 暴露的是 OpenAI 兼容接口,意味着客户端代码与云 API 几乎完全一致,切换端点即可。这也让"混合部署"(自部署 + 云 API 降级)的实现成本趋近于零。
python
from openai import OpenAI
# 只需改 base_url 与 api_key,代码其他部分与调用 OpenAI 云端完全一致
client = OpenAI(
base_url="http://localhost:8000/v1",
api_key="not-needed", # vLLM 默认不校验
timeout=60.0,
)
resp = client.chat.completions.create(
model="qwen2.5-7b-awq",
messages=[
{"role": "system", "content": "你是知识库问答助手。"},
{"role": "user", "content": "公司的退款政策是什么?"},
],
temperature=0.2,
max_tokens=512,
stream=False,
)
print(resp.choices[0].message.content)流式输出(首 token 延迟更低,对话类体验更好):
python
stream = client.chat.completions.create(
model="qwen2.5-7b-awq",
messages=messages,
stream=True,
)
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="")接入层的"最小三条铁律"
- 永远设置 timeout 与 max_tokens:模型卡死或失控时不至于拖死整个调用链;
- 流式与超时重试要分开设计:流式场景下超时重试容易产生重复输出,建议只在非流式 + 幂等场景自动重试;
- 模型名走别名:
--served-model-name让业务端与底层模型解耦,换模型不动业务代码。
步骤 4:网关与缓存
服务起来了,接下来是保护它、榨干它的网关层。两件事必做:语义/精确缓存(减少重复计算)和限流(防止单点打满)。
Redis 缓存:对完全相同的请求直接返回历史结果,能省掉大量重复推理。更进阶的语义缓存用向量相似度匹配相近问题(向量检索原理见向量数据库与语义检索),命中率更高但要承担误召回风险。
python
import hashlib
import redis
r = redis.Redis(host="localhost", port=6379, decode_responses=True)
def cached_chat(messages, ttl=3600):
# 精确缓存:对"相同的消息序列"直接命中
key = "llm:cache:" + hashlib.sha256(
repr(messages).encode("utf-8")
).hexdigest()
hit = r.get(key)
if hit:
return hit
resp = llm_client.chat.completions.create(
model=MODEL, messages=messages, temperature=0
)
text = resp.choices[0].message.content
r.setex(key, ttl, text) # TTL 避免缓存无限膨胀
return text限流(网关层,用令牌桶限 QPS 与并发):
yaml
# Nginx + lua-resty-limit-traffic 示例(或直接用网关产品如 Kong/APISIX)
limit_req_zone $binary_remote_addr zone=llm_per_ip:10m rate=10r/s;
server {
location /v1/chat/completions {
limit_req zone=llm_per_ip burst=20 nodelay;
proxy_pass http://vllm-svc:8000;
proxy_read_timeout 300s; # 长输出需要放宽读超时
}
}网关层值得做的三件小事
- 队列与背压:并发超过模型处理能力时排队而不是直接打挂服务,配合
max-num-seqs限住模型侧并发; - 降级开关:自部署挂了自动切云 API(
base_url换成云端端点即可); - 记录每次调用的输入输出与元数据——这既是审计日志,也是后续评测与提示词迭代的数据资产(搭建一套 LLM 评估 会用到)。
步骤 5:监控告警
LLM 服务必须监控三层:系统层(GPU 利用率、显存、温度)、推理指标层(TTFT / TPOT / tokens/s)、质量层(输出是否符合预期)。推理指标是 LLM 特有的,定义如下(详细口径见 LLM 评估与基准):
| 指标 | 全称 / 含义 | 量纲 | 说明 |
|---|---|---|---|
| TTFT | Time To First Token,首个 token 耗时 | ms | 用户感知的"响应快不快",越低越好 |
| TPOT | Time Per Output Token,每个输出 token 的平均耗时 | ms/token | 生成阶段的"速度感" |
| tokens/s | 吞吐:每秒生成的 token 数 | tok/s | 批量负载下容量有多高 |
| QPS | 每秒请求数 | 次/s | 业务负载 |
| KV cache 利用率 | 前缀缓存命中率 / 显存占用 | % | 决定吞吐上限 |
把 vLLM 的 Prometheus 指标接进监控体系(vLLM 自带 /metrics 端点,暴露 vllm:num_requests_running、vllm:time_to_first_token_seconds、vllm:generation_tokens_total 等):
yaml
# prometheus.yml 片段
scrape_configs:
- job_name: vllm
metrics_path: /metrics
static_configs:
- targets: ["vllm-svc:8000"]yaml
# alertmanager 告警规则示例(按 99 分位设阈值,先跑基线再定)
groups:
- name: llm
rules:
- alert: TTFTHigh
expr: histogram_quantile(0.99, vllm_time_to_first_token_seconds_bucket) > 2
for: 5m
annotations:
summary: "首 token 延迟超过 2s,检查 GPU 负载与队列长度"
- alert: GPUOOMRisk
expr: (1 - vllm_gpu_cache_usage_perc) < 0.1
for: 3m
annotations:
summary: "KV cache 使用率超过 90%,存在 OOM 风险"监控要"先有基线,再设阈值"
不要凭感觉拍告警阈值。先裸跑一周收集 99 分位 TTFT、TPOT 的正常波动范围,再设告警线——否则要么天天误报被忽略,要么真出事时不报警。阈值与评测口径的完整方法论见 LLM 评估与基准。
三、性能调优清单:四个旋钮的顺序
性能优化有四个主要旋钮,请按下面的顺序拧——这个顺序来自"改动代价从小到大、收益确定性从高到低":
| 顺序 | 旋钮 | 做法 | 收益与代价 |
|---|---|---|---|
| 1 | 批处理(batching) | 打开连续批处理(vLLM 默认),合并小请求;RAG 场景共享前缀 | 吞吐提升最显著、近乎零代价;代价是单请求延迟略升 |
| 2 | KV cache 优化 | 开 --enable-prefix-caching;多轮对话用 chat 接口而非每次重建历史 | 长上下文 + 多用户场景吞吐大增;几乎零成本 |
| 3 | 并发与队列 | 调 --max-num-seqs,压测找吞吐/延迟平衡点;网关侧限流保护 | 要压测数据支撑,不然会顾此失彼 |
| 4 | 显存与量化 | --gpu-memory-utilization 提到安全上限;必要时 AWQ/GPTQ 量化 | 直接扩容能力上限;量化有精度风险需验证 |
一句话判断
先调批处理,再碰量化。80% 的吞吐问题靠"把请求合并起来跑"就能解决,轮不到动量化——量化是最后的手段,因为它有精度风险。
四、成本优化:让每一块钱都出 token
推理成本优化的核心是减少无效计算,四条路按优先级排列:
- 缓存命中率:精确缓存 + 语义缓存(见步骤 4),常见 FAQ 场景命中率可达 30%~50%,对应成本直接砍掉同样比例;
- 模型降级(routing):简单请求走小模型、复杂请求走大模型——这是性价比最高、也最容易被忽略的一招。用一个小分类器或规则先判断"这题难不难",难的问题才上大模型;
- 量化:INT4 推理对成本与显存的削减立竿见影(推理优化与量化);
- 输出长度治理:限制
max_tokens、提示词里给字数约束、对过长输出做截断——token 是按生成量付费的,压输出长度就是直接省钱。
python
# 模型降级骨架:简单问题走小模型,复杂问题走大模型
def route(query: str) -> str:
complexity = estimate_complexity(query) # 规则/小分类器/启发式
model = "qwen2.5-7b-awq" if complexity < 0.5 else "qwen2.5-32b-awq"
return call_llm(model, query)五、安全与合规:部署的最后一道闸门
模型上线即面向攻击面,至少要做四件事(完整治理框架见 AI 安全与治理):
| 措施 | 做法 | 场景 |
|---|---|---|
| 内容过滤 | 输出侧接审核模型/规则,拦截违规内容 | 面向 C 端的对话、写作类产品 |
| 数据脱敏 | 请求侧先做 PII 检测与替换(手机号、身份证、邮箱等),响应后再还原 | 处理用户隐私数据的任何场景 |
| 审计日志 | 记录输入输出、模型版本、时间、用户标识;敏感操作留痕 | 合规审计、问题溯源 |
| 权限与隔离 | Agent 类应用限制工具权限、高危操作人审 | Agent 应用与工具调用场景 |
python
# 最小脱敏示例:请求进模型前替换敏感信息,响应后还原
import re
def mask_pii(text: str):
text = re.sub(r"1[3-9]\d{9}", "[手机号]", text) # 手机号
text = re.sub(r"\d{17}[\dXx]", "[身份证号]", text) # 身份证
return text自部署不等于天然安全
自部署只是把数据留在内网,内容安全与提示注入防护一点都不能省——提示注入、越狱等攻击对自部署模型同样有效,甚至因为模型更"裸"而更容易中招。安全是部署上线的前置条件,不是上线后的补丁。
六、常见坑:上线前先看这张清单
| 坑 | 症状 | 预防与修复 |
|---|---|---|
| 显存 OOM | 服务启动崩溃或运行中 CUDA out of memory | 先量化再上线;gpu-memory-utilization 从 0.85 起调;KV cache 与 max-model-len 一起预算显存 |
| 超时 | 客户端先于服务端超时,用户看到"无响应" | 流式输出降低首 token 感知延迟;网关 proxy_read_timeout 放宽;按 max_tokens 设客户端 timeout |
| 并发打满 | 一个用户的批量请求占满全部并发,其他人排队 | 网关限流 + 每用户配额;max-num-seqs 设上限;队列与背压机制兜底 |
| 输出长度失控 | 回答无限膨胀,token 账单爆炸、接口超时 | 提示词加字数约束(见提示词实战手册);max_tokens 硬上限;超长输出截断 |
| 幻觉上生产 | 回答自信地编造事实,用户投诉 | RAG 接入 + "只依据材料回答"(从零搭建 RAG 应用);输出侧加校验 |
| 精度掉点不自知 | 量化后效果变差,用户流失 | 量化前后用同一评测集对比(搭建一套 LLM 评估) |
| 版本管理缺失 | 换了模型/量化版本,无法回滚 | 模型文件与配置进版本管理;served-model-name 别名化,支持一键切换 |
更完整的"反模式"清单(包括评估泄漏、数据污染等部署之外的坑)见常见陷阱与反模式。
七、延伸阅读
- 推理优化与量化 —— 本页实战的机制底座:为什么慢、量化与 KV cache 原理
- 模型与榜单速查 —— 模型选型与自部署/云 API 的成本权衡数据
- 大语言模型(LLM) —— 部署对象的架构与能力边界
- 微调你自己的 LLM —— 部署的上游:模型从哪里来
- 从零搭建 RAG 应用 —— RAG 应用的端到端部署(含检索服务)
- 搭建一套 LLM 评估 —— 部署上线前的评测与回归
- LLM 评估与基准 —— TTFT/TPOT 之外的指标口径与基准
- AI 安全与治理 —— 内容过滤、提示注入、数据合规全景
- 提示词实战手册 —— 上线后的提示词迭代与 A/B
- AI 智能体(Agent) —— Agent 类应用的部署与工具权限隔离
- 术语表 —— 部署相关术语口径统一
参考资料
- vLLM 官方文档 —— 部署、性能调优、Prometheus 指标、量化支持的权威参考
- vLLM: Engine Arguments(官方参数参考) ——
gpu-memory-utilization、max-model-len、max-num-seqs等参数的完整说明 - Ollama 官方文档 —— GGUF 量化与本地部署的轻量方案
- AutoAWQ 官方仓库 —— AWQ 量化实现与使用示例
- GPTQ: Accurate Post-Training Quantization for Generative Pre-trained Transformers(论文) —— GPTQ 量化算法原文
- Lin et al. AWQ: Activation-aware Weight Quantization for LLM Compression and Acceleration(MLSys 2024) —— AWQ 量化算法原文
- OpenAI: Production best practices —— 延迟、并发、监控的最佳实践(云 API 路线)
- NVIDIA: LLM 推理性能优化指南(TensorRT-LLM 文档) —— 更极致性能场景的参考方案
- MLPerf Inference —— 推理性能基准,评估你的服务在同级别硬件中的位置