把模型跑通和把模型做成服务是两件事。本地 python chat.py 能回答问题,不代表高峰期、多用户、长上下文下还能稳住延迟和成本。
本文从工程视角梳理推理服务的最小骨架,以及选型时真正该看的指标。
先定义服务边界
一个可用的 LLM 推理服务至少要回答:
- 入口:HTTP / gRPC?是否支持 SSE 流式?
- 契约:请求里带哪些字段(model、messages、max_tokens、temperature)?
- 隔离:多租户如何限流、鉴权、审计?
- 失败:超时、OOM、排队过长时返回什么?
先把接口定死,再谈引擎。接口一变,客户端、网关、评测脚本全要跟着改。
核心路径:Prefill 与 Decode
生成一段回复通常分两段:
- Prefill:把整段 Prompt 吃进模型,算首 token(TTFT)。
- Decode:逐 token 生成,吞吐受 KV cache 与 batch 影响更大。
优化往往不是「换更大卡」这么简单:
| 现象 | 常见原因 | 优先动作 |
|---|---|---|
| TTFT 很高 | Prompt 过长 / 无前缀缓存 | 截断、缓存系统提示、启用 prefix cache |
| 生成很慢 | batch 太小或显存碎片 | continuous batching、调整并发 |
| 偶发 OOM | 并发与 max_len 乘积过大 | 硬限上下文、排队、预估显存 |
引擎怎么选
常见开源路线:
- vLLM:continuous batching + PagedAttention,吞吐友好,生态活。
- TGI / TensorRT-LLM:在特定硬件与模型上可能更极致,运维成本更高。
- 自建 FastAPI + transformers:适合原型,不适合生产吞吐。
选型清单(按优先级):
- 目标 QPS 与 P95 延迟(含流式首包)
- 模型大小与量化策略(FP16 / INT8 / AWQ)
- 多 LoRA / 多模型是否要共卡
- 团队能否 hold 住升级与驱动兼容
上线前的五道闸
- 健康检查:
/healthz真实探测模型是否可推理,而不只是进程在。 - 超时分层:网关超时 > 服务超时 > 单次生成超时,避免僵尸请求占卡。
- 限流与排队:按 API Key / 租户限并发;排队深度有上限。
- 可观测性:TTFT、tokens/s、队列长度、GPU 利用率、OOM 次数。
- 灰度:先用小流量对照「旧服务 vs 新引擎」的答案质量与延迟。
# 粗测流式首包(示例)
curl -N -X POST "$ENDPOINT/v1/chat/completions" \
-H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-d '{"model":"demo","stream":true,"messages":[{"role":"user","content":"ping"}]}'
实践建议
- 先稳再快:P95 稳定比峰值吞吐更重要。
- 把评测绑进 CI:同一套 Prompt 集,对比延迟与答案回归。
- 成本按 token 记账:没有用量看板,优化就是玄学。
下一篇会聊 GPU 调度:多模型共卡、排队与抢占,怎样少浪费显存。