在 A100 / 80 GB 显存上运行 vLLM 0.4 服务 Llama-3-70B 时,客户端发送一个 12000 token 的请求,服务端返回 This model's maximum context length is 8192 tokens. However, you requested 12000 tokens。但 Llama-3-70B 的原生上下文长度是 131072 token,8192 这个限制明显不对——这是 vLLM 启动时 --max-model-len 参数被自动截断的经典表现。
常见原因
1. vLLM 自动将 max_model_len 截断到可用 KV cache 范围
vLLM 在启动时会根据可用显存计算能够分配的 KV cache 块数,若显存不足以支撑模型声明的最大上下文长度,会自动降低 max_model_len 并打印警告。这个警告往往被淹没在启动日志里,运维人员没有注意到。
怎么判断:查看 vLLM 启动日志,搜索 WARNING: Reducing max_model_len 或 max_model_len 字样;或用 curl http://localhost:8000/v1/models 查看返回的 context_window 字段是否远小于模型设计值。
2. —max-model-len 被明确设置为较小值
部署脚本中可能残留了早期测试时设置的 --max-model-len 8192,在后续切换到更大模型时没有更新。
怎么判断:查看 vLLM 启动命令或 systemd 服务文件,搜索 --max-model-len 参数值。
3. KV cache 块数不足以支撑请求的上下文长度
vLLM 使用 PagedAttention 分块管理 KV cache,每个块默认 16 token。若 --gpu-memory-utilization 设置较低(如 0.7),可用显存少,KV cache 块数可能不足以处理长请求,即使 max_model_len 设置正确也会在运行时报错。
怎么判断:vLLM 启动日志中查找 # GPU blocks: 行;用公式估算 最大支持序列长度 = GPU blocks × block_size (16),若小于请求长度就会报错。
4. 多卡 tensor parallel 下 KV cache 显存计算不准确
使用 --tensor-parallel-size 4 等多卡配置时,vLLM 对每卡可用显存的估算有时会偏低,导致实际可用 KV cache 块数比单卡估算少,进而触发更严格的 max_model_len 限制。
怎么判断:比较单卡运行(--tensor-parallel-size 1)和多卡运行时日志里的 # GPU blocks 数量,若多卡总块数远小于单卡块数的倍数,就是这个问题。
5. prefix caching 开启时内存分区导致可用 KV cache 减少
vLLM 0.4 引入了 --enable-prefix-caching 功能,该功能会预留一部分显存用于前缀 KV cache,进一步压缩普通请求可用的 KV cache 空间。
怎么判断:检查启动参数中是否有 --enable-prefix-caching;临时关闭后对比 # GPU blocks 数量变化。
6. 模型 config.json 中的 max_position_embeddings 设置异常
部分社区上传的模型权重将 config.json 里的 max_position_embeddings 改为较小值(如 8192),vLLM 读取此值作为上限,不会超过它即使显存充足。
怎么判断:查看模型目录下的 config.json,检查 max_position_embeddings 和 rope_scaling 字段是否与官方模型一致。
最短修复路径
Step 1:查看 vLLM 实际使用的 max_model_len
# 方法 1:查询 API
curl http://localhost:8000/v1/models | python3 -m json.tool | grep context
# 方法 2:查看启动日志
journalctl -u vllm -n 200 | grep -i 'max_model_len\|context\|WARNING'
Step 2:显式设置 max_model_len 并增加 KV cache 显存配额
# 停止现有服务
sudo systemctl stop vllm
# 以更大上下文长度重新启动
python -m vllm.entrypoints.openai.api_server \
--model meta-llama/Meta-Llama-3-70B-Instruct \
--max-model-len 32768 \
--gpu-memory-utilization 0.92 \
--tensor-parallel-size 2 \
--host 0.0.0.0 \
--port 8000
将 --gpu-memory-utilization 从默认 0.90 提高到 0.92-0.95(注意:过高会导致 CUDA OOM,建议每次增加 0.02 测试)。
Step 3:计算最优 max_model_len
# 估算公式(A100 80GB,70B 模型,bf16 权重)
# 模型权重约占 140 GB(需要 2 卡),每卡 70 GB 权重
# 每卡剩余约 10 GB 用于 KV cache
# KV cache 每 token 约 0.5 MB(70B,32 head,128 head_dim,bf16)
# 可支持约 10000 / 0.5 = 20000 token 上下文
# 两卡合计约 40000 token
# 实际验证:启动后查看日志
grep "# GPU blocks" /var/log/vllm.log
# 假设输出 "# GPU blocks: 2048"
echo "最大支持 token 数 = 2048 × 16 = $((2048 * 16))"
Step 4:使用 —swap-space 扩展有效上下文容量
# 用 CPU 内存作为 KV cache 溢出区(性能有损但可处理更长请求)
python -m vllm.entrypoints.openai.api_server \
--model meta-llama/Meta-Llama-3-70B-Instruct \
--max-model-len 65536 \
--gpu-memory-utilization 0.90 \
--swap-space 32 \
--tensor-parallel-size 2
# --swap-space 单位为 GB,使用系统 RAM 作为溢出区
Step 5:验证长上下文请求可以正常处理
# 发送一个接近目标长度的测试请求
python3 - << 'PYEOF'
import openai
client = openai.OpenAI(base_url="http://localhost:8000/v1", api_key="dummy")
# 构造约 10000 token 的消息
long_text = "Summarize the following text: " + ("The quick brown fox. " * 500)
response = client.chat.completions.create(
model="meta-llama/Meta-Llama-3-70B-Instruct",
messages=[{"role": "user", "content": long_text}],
max_tokens=200
)
print(response.choices[0].message.content)
PYEOF
预防建议
- 启动脚本中始终显式设置
--max-model-len,不依赖自动推断,避免切换模型时残留旧值。 - 在 CI/CD 流程中加入 vLLM 启动后的
/v1/models接口检查,断言context_window满足预期。 - 对于 70B 以上的大模型,提前用
--dry-run选项(vLLM 0.5 支持)估算 GPU blocks 数量再决定上下文配置。 - 记录每个模型部署的
--max-model-len、--gpu-memory-utilization、GPU blocks 数量到运维文档,下次变更时有参考。 - 客户端发送请求前统计 token 数量并与服务端 context_window 对比,在应用层提前截断而非让服务端报错。
- 定期监控 vLLM 的
vllm:num_requests_running、vllm:gpu_cache_usage_perc指标,提前发现 KV cache 瓶颈。
常见问答 (FAQ)
Q: --max-model-len 设大了,但实际请求超过 GPU blocks 上限会怎样?
A: 请求会报 Cannot allocate memory in the KV cache 或直接挂起等待 KV cache 空间释放。开启 --swap-space 可以缓解,但会导致该请求延迟显著增加。
Q: prefix caching 真的值得开吗?会影响 context length 吗? A: 对于 system prompt 固定的场景(如 chatbot),prefix caching 能减少 30-50% 的首 token 延迟,非常值得。它会占用部分 GPU blocks,但通常只减少 5-10% 的有效上下文容量,性价比高。
Q: vLLM 和 llama.cpp 的 max context 行为有什么区别?
A: llama.cpp 的 --ctx-size 在运行时可以直接指定,超出显存时部分 KV cache 会溢出到系统内存(如果开启了 mmap)。vLLM 是服务端集中管理 KV cache,启动时就确定好池子大小,运行时不允许超过。
Q: 为什么同一份代码在 A100 80GB 上没问题,换到 A6000 48GB 后就报 context exceeded?
A: A6000 显存减半,KV cache 块数也减半,有效最大上下文相应减半。需要同比降低 --max-model-len 或换更小的模型,或者切换到多卡配置。
相关阅读
标签: #local-llm #vllm #排查