LLM 推理服务系列 · 第 2 篇
vLLM 的核心创新是 PagedAttention。但 PagedAttention 只解决了一个问题:显存管理。实际的 LLM 推理服务还需要:模型权重分片、量化、Token 化、LoRA 热切换、多模态输入、水印生成、语法约束输出……
TGI(Text Generation Inference)是 HuggingFace 的官方推理服务——它跟 vLLM 目标相同(高效推理),但切入点不同:vLLM 从 KV Cache 管理切入,TGI 从模型生态兼容性切入。它要把 HuggingFace Hub 上的几十万个模型都服务好——不只是 Llama 和 Mistral。
TGI 的架构:不止一个 Scheduler
HTTP/gRPC API
│
▼
┌──────────────────┐
│ Router │ ← 路由请求到对应模型
│ (可服务多个模型) │
└──────────────────┘
│
▼
┌──────────────────┐
│ Scheduler │ ← 类似 vLLM 的调度器
│ │ 但额外支持 Chunked Prefill
└──────────────────┘
│
▼
┌──────────────────┐
│ Model Runner │ ← 实际执行推理
│ (支持多种后端) │ 支持 PyTorch / TensorRT / FlashInfer
└──────────────────┘
│
┌───────────┼───────────┐
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌──────────┐
│ KV │ │ Weight │ │ Token │
│ Cache │ │ Loader │ │ izer │
└─────────┘ └─────────┘ └──────────┘
TGI vs vLLM:五个关键差异
1. Chunked Prefill(分块预填充)
第 1 篇提过——vLLM 没有 Chunked Prefill。TGI 有。
没有 Chunked Prefill(vLLM):
时间轴:
[Prefill: 处理一个 10K Token 的 Prompt, GPU 满负荷 200ms]
[Decode: 生成 Token]
问题:Prefill 200ms 期间,其他请求的 Decode 被阻塞
有 Chunked Prefill(TGI):
时间轴:
[P:2K][D:1步][P:2K][D:1步][P:2K][D:1步][P:2K][D:1步][P:2K][D:1步]
↑ ↑ ↑ ↑
P = Prefill 块, D = 所有请求的 Decode
效果:Prefill 和 Decode 交替执行,Decode 不会被长时间阻塞
对于超长 Prompt 场景(RAG 中把多篇文档塞进上下文),Chunked Prefill 至关重要——它保证了服务的 P99 延迟稳定。
2. 量化方案:GPT-Q 和 AWQ 是一等公民
vLLM 支持多种量化,但 TGI 对 HuggingFace 上最广泛使用的量化格式有更深入优化:
支持的量化:
- AWQ (Activation-aware Weight Quantization)
- GPT-Q
- BitsAndBytes (8-bit / 4-bit NF4)
- EETQ (Easy & Efficient Quantization)
- FP8 (H100 原生支持)
TGI 对 AWQ 的路由做了专门的 CUDA Kernel 优化(awq-kernel),量化模型的推理延迟比 vLLM 通常低 10-15%。因为 HuggingFace Hub 上大量模型以 AWQ 格式发布,TGI 对这些模型的"即开即用"体验碾压 vLLM。
3. LoRA 热切换:多租户场景的核心能力
有些场景:同一个 Base Model,不同租户需要不同的微调(电商客服 vs 金融客服 vs 技术支持)。每次都重新加载模型?太慢。
TGI 支持运行时加载/卸载 LoRA Adapter,无需重启:
# 启动 TGI 时开启 LoRA 支持
text-generation-launcher \
--model-id meta-llama/Llama-3.1-8B-Instruct \
--enable-lora \
--max-loras 10 \ # 最多同时加载 10 个 adapter
--max-cpu-loras 50 # CPU 内存里可以缓存 50 个 adapter 待命
不同请求在 API 里指定 lora_id——TGI 动态切换 Adapter 权重,只切换几 MB 的 LoRA 权重矩阵,不动几十 GB 的 Base Model。
请求A: "lora_id": "ecommerce-support" → GPU 加载 ecommerce LoRA
请求B: "lora_id": "finance-support" → GPU 卸载 ecommerce,加载 finance LoRA
请求C: 无 lora_id → 直接用 Base Model
vLLM 新版本也开始支持这个,但 TGI 的实现更早更成熟。
4. 语法约束输出(Structured Generation / Guided Decoding)
有些场景需要模型输出严格格式的 JSON:
// 必须严格遵循这个 schema
{
"sentiment": "positive" | "negative" | "neutral",
"reason": "string",
"confidence": 0.0-1.0
}
普通生成靠运气("请输出 JSON")。TGI 在 Decode 阶段实时约束 Token 候选集——每个位置只从符合 Grammar 的 Token 里抽样:
普通生成:
"I think the sentiment is... uh... positive!" ← 不严格
TGI Grammar 约束:
{"sentiment": "positive", "reason": "The product works well", "confidence": 0.92}
↑ token-by-token 被约束到 JSON schema 的子集
实现方式:在 Softmax 之后、采样之前,把不符合 Grammar 的 Token 概率全部置零。选项支持 JSON Schema、Regex、Context-Free Grammar。
5. 水印生成(Watermarking)
TGI 内置了 Watermark 功能——在生成的文本中嵌入可检测的数字签名。原理是在 Token 采样阶段对候选 Token 做两分法打标("绿色 Token"和"红色 Token"),采样时偏好"绿色 Token"——后续可以用统计方法检测一段文本是否由该 TGI 实例生成。这是 HuggingFace 独有的功能,vLLM 没有。
启动方式
# Docker 启动(推荐方式)
docker run --gpus all \
-p 8080:80 \
-v $PWD/models:/data \
ghcr.io/huggingface/text-generation-inference:latest \
--model-id meta-llama/Llama-3.1-8B-Instruct \
--max-total-tokens 4096 \
--max-batch-prefill-tokens 2048 # Chunked Prefill 的块大小
兼容 OpenAI Chat API,跟 vLLM 相同的调用方式。
什么时候选 TGI
| 场景 | TGI 适合? |
|---|---|
| 使用 HuggingFace Hub 模型 | 首选——无缝集成 |
| 超长 Prompt(>10K) | Chunked Prefill 降低 P99 延迟 |
| 多租户 LoRA 服务 | 最佳选择,LoRA 热切换成熟 |
| 需要 JSON/Regex 约束输出 | 原生支持 Grammar 约束 |
| 多模态模型(LLaVA, Idefics3) | 原生支持图像输入 |
| 单模型高并发推理 | 跟 vLLM 性能相当,选部署习惯 |
| 不想用 Docker | vLLM 的 Python 直接启动更简单 |
一句话总结
TGI 跟 vLLM 在性能上不相上下——两者的核心优化思路(PagedAttention + Continuous Batching)是一致的。TGI 的差异点在"模型生态"——量化(AWQ 优化)、多租户(LoRA 热切换)、多模态、结构化输出、水印——这些是 vLLM 没有或做得不如 TGI 深入的地方。如果你深度依赖 HuggingFace 生态——TGI 是自然的选择。
下一篇:NVIDIA Triton Inference Server——不是专门为 LLM 设计的,却成了推理服务的"瑞士军刀"。支持 PyTorch、TensorRT、ONNX、TensorFlow、XGBoost 甚至 Python 自定义代码——一个服务端统一所有模型。