把大语言模型接入公司内部系统,难点通常不在于启动一个推理进程,而在于让它像数据库、消息队列一样成为稳定的基础设施:调用方不必关心模型部署在哪里,平台团队能够控制成本、延迟和权限,业务团队也能持续替换模型而不重写应用。围绕 “In-House LLM Serving at Netflix” 这一主题,可以把内部 LLM Serving 理解为一套完整的平台工程问题,而不是单个模型服务。
Serving 的核心边界
一个可用的内部 LLM 平台,至少要划分出四层职责:
- 模型运行层:负责加载模型、批处理请求、管理 GPU 或其他加速资源。
- 服务接入层:提供统一的 HTTP 或兼容 OpenAI 风格的 API。
- 治理层:处理身份认证、配额、审计、敏感数据过滤和模型访问策略。
- 业务适配层:把推荐、搜索、内容理解、客服等场景转换成稳定的提示词和结构化输入输出。
这种分层很重要。业务代码不应该直接连接某台 GPU 机器,也不应该把模型名称、上下文长度和重试策略散落在各个仓库里。调用方更适合依赖一个稳定的逻辑模型名,例如 text-summary-v1,平台再把它映射到具体模型版本和推理后端。
内部平台还需要同时支持两类请求:
- 在线请求关注首 token 延迟、整体响应时间和并发控制,适合交互式产品。
- 离线请求关注吞吐量和单位成本,适合批量生成摘要、标签或向量。
如果把所有请求都放到同一个队列中,长上下文的离线任务很容易阻塞交互式请求。因此,按优先级、上下文长度和业务类型拆分队列,往往比单纯增加副本更有效。
统一 API,隔离模型变化
可以这样实践:在模型后端前面放置一个很薄的内部网关。网关负责鉴权、请求校验、路由和指标采集,模型服务只处理推理。下面是一个可运行的 FastAPI 示例。它用一个简单的伪模型响应模拟后端,方便先验证接口契约;接入真实推理引擎时,只需要替换 generate_text 函数。
运行前安装依赖:
python -m venv .venv
. .venv/bin/activate
pip install fastapi uvicorn
uvicorn app:app --reload --port 8000
将下面内容保存为 app.py:
import os
import time
from typing import Literal
from fastapi import FastAPI, Header, HTTPException
from pydantic import BaseModel, Field
app = FastAPI(title="Internal LLM Gateway")
INTERNAL_TOKEN = os.getenv("INTERNAL_LLM_TOKEN", "dev-token")
class GenerateRequest(BaseModel):
model: str = Field(pattern=r"^[a-z0-9-]+$")
prompt: str = Field(min_length=1, max_length=12000)
max_tokens: int = Field(default=256, ge=1, le=2048)
priority: Literal["online", "batch"] = "online"
class GenerateResponse(BaseModel):
model: str
text: str
latency_ms: int
def generate_text(model: str, prompt: str, max_tokens: int) -> str:
# 这里替换为 vLLM、TensorRT-LLM 或公司内部推理客户端。
return f"[{model}] response for: {prompt[:max_tokens]}"
@app.post("/v1/generate", response_model=GenerateResponse)
def generate(
request: GenerateRequest,
authorization: str | None = Header(default=None),
) -> GenerateResponse:
expected = f"Bearer {INTERNAL_TOKEN}"
if authorization != expected:
raise HTTPException(status_code=401, detail="invalid token")
started = time.perf_counter()
text = generate_text(request.model, request.prompt, request.max_tokens)
latency_ms = round((time.perf_counter() - started) * 1000)
return GenerateResponse(
model=request.model,
text=text,
latency_ms=latency_ms,
)
调用示例:
curl -s http://127.0.0.1:8000/v1/generate \
-H 'Authorization: Bearer dev-token' \
-H 'Content-Type: application/json' \
-d '{
"model": "text-summary-v1",
"prompt": "Summarize this incident report in three bullet points.",
"max_tokens": 128,
"priority": "online"
}'
这个示例刻意保留了几个平台边界:模型名由请求传入但受到格式约束,输出包含延迟字段,优先级也进入了 API 契约。生产实现中,可以基于 model 和 priority 路由到不同的服务池,并把用户、团队、请求 ID 和 token 使用量写入指标系统。
在容器平台上管理推理服务
如果使用 Kubernetes,可以为在线模型单独建立 Deployment 和 Service。下面的 YAML 只是最小部署骨架,镜像入口和资源数值需要按实际模型调整:
apiVersion: apps/v1
kind: Deployment
metadata:
name: text-summary-v1
labels:
app: text-summary-v1
spec:
replicas: 2
selector:
matchLabels:
app: text-summary-v1
template:
metadata:
labels:
app: text-summary-v1
spec:
containers:
- name: server
image: registry.example.com/llm/text-summary:v1
args: ["--port", "8080", "--max-batch-size", "8"]
ports:
- containerPort: 8080
env:
- name: MODEL_ID
value: "text-summary-v1"
resources:
requests:
cpu: "4"
memory: "16Gi"
nvidia.com/gpu: "1"
limits:
cpu: "8"
memory: "32Gi"
nvidia.com/gpu: "1"
readinessProbe:
httpGet:
path: /health/ready
port: 8080
periodSeconds: 5
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 10"]
---
apiVersion: v1
kind: Service
metadata:
name: text-summary-v1
spec:
selector:
app: text-summary-v1
ports:
- name: http
port: 80
targetPort: 8080
可以这样部署并检查:
kubectl apply -f text-summary.yaml
kubectl rollout status deployment/text-summary-v1
kubectl get pods -l app=text-summary-v1 -o wide
真正进入生产后,健康检查不能只表示进程还活着。/health/ready 应该在模型加载完成、权重可用、依赖连接正常并且服务能够接收新请求时才返回成功。滚动升级期间,还需要给正在生成的请求留出排空时间,否则发布会制造大量截断响应。
可观测性决定平台能否运营
LLM Serving 的平均延迟很容易掩盖问题。至少应记录以下指标:
- 请求数、错误率、超时率和按模型拆分的状态码。
- 首 token 延迟、完整响应延迟和 token 吞吐量。
- 输入 token、输出 token、上下文长度和拒绝原因。
- GPU 利用率、显存占用、批大小以及队列等待时间。
- 按团队、应用和模型版本统计的成本或资源消耗。
日志中不要直接写入完整提示词和模型输出。内部系统同样可能携带用户隐私、业务机密或未发布内容。更稳妥的做法是记录请求哈希、长度、分类结果和 trace ID;需要排查时,再通过受控的脱敏存储关联原始数据。
容量管理也要围绕队列来做。一个简单的排队模型可以帮助判断是否应该扩容:如果请求到达速度长期接近或超过服务处理速度,队列会不断增长,最终表现为延迟飙升和超时。扩容策略应结合队列等待时间、GPU 利用率和 SLO,而不是只看 CPU 使用率。
采用时的检查清单
内部 LLM Serving 适合分阶段推进:
- 先定义稳定的模型服务契约,明确输入、输出、错误码、超时和版本规则。
- 把在线与离线流量分池,配置独立的队列、配额和扩缩容策略。
- 在平台层统一鉴权、审计、敏感信息处理和成本统计。
- 用真实业务流量建立基准,分别测量短提示词、长上下文和高并发场景。
- 通过灰度路由验证新模型,不要让模型升级与业务代码发布强绑定。
内部部署的优势是控制数据边界、延迟和模型适配方式,但代价是需要承担 GPU 采购或租用、模型升级、容量规划、故障恢复和安全治理。决定是否自建时,不应只比较单次 API 价格,还要计算空闲资源、工程维护、峰值容量和合规要求。一个小而稳定的统一入口,通常比一开始建设覆盖所有模型和场景的“大平台”更容易验证价值。