本文定位:低显存设备「从 0 到 1 跑通 vLLM + 多模态模型」的实测记录

  • 核心目标:在 GTX 1650(4GB 显存)上成功部署 Qwen2.5-VL-3B;
    操作指引:每一步均标注执行终端(PowerShell / WSL / Docker),照抄即可复现。
  • 适用环境:Win10 22H2 + WSL2 + Ubuntu 24.04 + Docker + vLLM
  • 记录时间:2026-08-09(本机实测,全部命令已验证)

💡 为什么要这么做?

  • 目前 DeepSeek API 逻辑推理能力极强,但原生不支持图片输入。本教程旨在构建一种混合推理架构:
  • 利用本机 GTX 1650 运行 Qwen2.5-VL 充当 “视觉前端”,负责处理图片并生成描述;再将结果传递给 DeepSeek API 充当 “逻辑后端”,负责深度思考与问答。
    这意味着,只需一张入门级显卡,就能让 DeepSeek 瞬间拥有识图能力。

目录


⚡ 快速上手(三步走)

整篇文档其实就是这三件事,其余都是补充。① ② 只做一次,③ 每次使用都做。

步骤干什么命令(在哪个终端)详见
① 装 vLLM建虚拟环境 + 装 vllm/home/vllm_serve/venv/bin/pip install vllm==0.26.0 (WSL)第 2 部分
② 下模型下 AWQ 量化版,复制进 WSL先跑 download_model.ps1(PowerShell)→ 再 cp/home/vllm_serve/models/(WSL)第 3 部分
③ 起服务 + 验证启动 OpenAI 兼容 API,确认在线bash /home/vllm_serve/start_vllm.sh(WSL)→ curl.exe http://localhost:8000/v1/models(PowerShell)第 4 部分

唯一前置:显卡驱动 ≥ 610.88(第 1 部分),否则 torch 起不来。

卡住时先看第 6 部分踩坑全记录——4GB 显存的所有坑都在那里。

全文路径约定(每条命令处另有就地注释,按命令旁的注释改即可):本文命令统一放在 /home/vllm_serve/(部署目录,不污染 /root);/root/ 是 root 用户家目录,非 root 用户运行系统命令时换成 echo $HOME 的结果;WSL 侧部署路径别放 /mnt/...(那是 Windows 盘,走 9P 桥接,模型会加载不动)。

你会得到什么(成果速览)

项目结果
模型Qwen2.5-VL-3B-Instruct(AWQ Q4 量化,权重 3.32 GiB)
服务vLLM 0.26.0,监听 http://localhost:8000(OpenAI 兼容 API)
硬件GTX 1650 4GB 显存(实际可用 ~3.2 GiB,Windows 桌面占 ~0.8 GiB)
能力中文 OCR、图片描述、图表理解、文档识别
实测图片识别 ✅ 文字 OCR ✅ 中文回答 ✅
限制单次请求图片+文字总 token ≤ 512(图需 ≤ 448px),并发 1

Windows 侧直接访问 http://localhost:8000 即可调用(WSL2 自动转发端口),无需任何额外配置。


第 1 部分:前置准备(适用环境 + WSL2 + 显卡驱动)

这部分的活只干一次:确认机器达标 → 装好 WSL2 和 Ubuntu → 升级显卡驱动。

1.1 适用环境

  • Windows 10 22H2(19045)或 Windows 11,已开启 WSL2
  • NVIDIA 独显:本文实测为 GTX 1650(Turing,compute capability 7.5,4GB 显存)
  • 显卡驱动:610.88 及以上(支持 CUDA 14,torch cu130 需要)
  • 磁盘:模型权重 ~3.4 GB + 虚拟环境 ~6 GB,建议放非 C 盘

⚠️ 4GB 显存是本文最大的坑:AWQ 权重 3.32 GiB 几乎占满整张卡,全靠第 6.7 节的技巧才塞进去。显存 ≥ 6GB 的机器可跳过极限压榨部分,直接加大 --max-model-len

1.2 WSL2 + Ubuntu 24.04

详细安装流程见同目录《WSL-Ubuntu-Docker-安装全流程.md》,这里只列结论:

  • Ubuntu 虚拟磁盘在 E:\WSL\Ubuntu\ext4.vhdx(Linux 的"物理存储",/ 根目录)
  • Windows 的 D 盘在 Linux 里挂在 /mnt/d,C 盘在 /mnt/c
  • 打开 Ubuntu:PowerShell 里敲 wsl 回车
[PowerShell]
wsl --status          # 确认默认分发是 Ubuntu、版本 2
wsl                   # 进入 Ubuntu

1.3 显卡驱动(关键前提)

vLLM 的 torch 2.11+cu130 需要较新驱动。旧驱动(如 526.56)会报:

RuntimeError: The NVIDIA driver on your system is too old (found version 12000)

升级到 610.88(实测通过):

  1. 下载驱动(需完整浏览器 UA + Referer,否则 403):
    [PowerShell]
    curl.exe -L -A "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" -e "https://www.nvidia.com/" -o "E:\WSL\downloads\drivers\610.88.exe" "https://us.download.nvidia.cn/Windows/610.88/610.88-notebook-win10-win11-64bit-international-dch-whql.exe"
    # 上面 -o 后面的保存目录可改:换成你想要的任意 Windows 路径即可(只影响驱动安装包放哪)
    
  2. 双击 exe 安装(会弹 UAC,点"是";装完可能需要重启)
  3. 验证:
    [PowerShell]
    nvidia-smi        # 显示 Driver Version: 610.88 即成功
    

1.4 确认 WSL 里 CUDA 可用

[WSL]
nvidia-smi          # 应显示和 Windows 一样的 610.88

第 2 部分:安装 vLLM 运行环境

2.1 创建虚拟环境

[WSL]
sudo apt update && sudo apt install -y python3-venv python3-pip

python3 -m venv /home/vllm_serve/venv   # 虚拟环境路径可改:换成 ~/venv-vllm 等任意 WSL 目录,但下文所有 /home/vllm_serve/venv/ 前缀要同步改

/home/vllm_serve/venv/bin/pip install --upgrade pip

💡 想让命令短写?激活 venv 即可:执行 source /home/vllm_serve/venv/bin/activate 后,pip / python 直接可用(不用再写 /home/vllm_serve/venv/bin/ 前缀)。但激活只对当前终端窗口有效,换窗口要重新激活;想自动激活就执行 echo 'source /home/vllm_serve/venv/bin/activate' >> ~/.bashrc
本文后续命令仍写全路径,是为了不依赖激活状态、保证一定装进 venv(直接 pip install 会调用系统 pip,可能装错地方)。你激活后把 /home/vllm_serve/venv/bin/ 前缀去掉即可。

2.2 安装 vLLM

[WSL]
/home/vllm_serve/venv/bin/pip install vllm==0.26.0
# 已激活 venv 的话直接: pip install vllm==0.26.0

会自动装 torch 2.11.0+cu130。装完验证:

/home/vllm_serve/venv/bin/python -c "import torch; print(torch.__version__, torch.cuda.is_available())"
# 期望: 2.11.0+cu130 True

cuda.is_available() 是 False,几乎都是驱动问题,回 1.3。


第 3 部分:下载模型(AWQ 量化版)

3.1 为什么选 AWQ 版

模型版本权重大小说明
官方原版(FP16)7.5 GB4GB 显存直接出局
Qwen2.5-VL-3B-Instruct-AWQ(本文选用)3.4 GBQ4 量化,4GB 显存的极限选择
GGUF(Q4_K_M)~2.3 GB最小,但 vLLM 0.26 不支持多模态 GGUF,放弃

3.2 下载(本机实测:hf-mirror + curl 逐个下载)

模型仓库:Qwen/Qwen2.5-VL-3B-Instruct-AWQ(HF 镜像:hf-mirror.com

需要下载 14 个文件(含 model.safetensors 3.17 GB 主权重),共 ~3.4 GB:

.gitattributes  LICENSE  README.md  added_tokens.json  chat_template.json
config.json  generation_config.json  merges.txt  model.safetensors (3.17 GB)
preprocessor_config.json  special_tokens_map.json  tokenizer.json (11 MB)
tokenizer_config.json  vocab.json (2.7 MB)

下载脚本(Windows 侧执行,存到 E:\WSL\downloads\models\):

# download_model.ps1
$files = @(
  ".gitattributes", "LICENSE", "README.md", "added_tokens.json",
  "chat_template.json", "config.json", "generation_config.json",
  "merges.txt", "model.safetensors", "preprocessor_config.json",
  "special_tokens_map.json", "tokenizer.json", "tokenizer_config.json", "vocab.json"
)
$dest = "E:\WSL\downloads\models\Qwen2.5-VL-3B-Instruct-AWQ"   # 下载目录可改:任意 Windows 目录,但 3.3 的 cp 源路径要跟着改
New-Item -ItemType Directory -Path $dest -Force | Out-Null
$base = "https://hf-mirror.com/Qwen/Qwen2.5-VL-3B-Instruct-AWQ/resolve/main/"
foreach ($f in $files) {
  $out = Join-Path $dest $f
  if ((Test-Path $out) -and ((Get-Item $out).Length -gt 0)) { Write-Host "SKIP $f"; continue }
  curl.exe -sL -C - -A "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" -o $out "$base$f"
  Write-Host "$f -> $((Get-Item $out -ErrorAction SilentlyContinue).Length) bytes"
}
Write-Host "ALL DONE"
[PowerShell]
powershell -ExecutionPolicy Bypass -File .\download_model.ps1

⚠️ model.safetensors 下载完后必须确认后 5 个小文件也下载成功(本机曾因流程提前报完成漏掉 tokenizer 等 5 个文件,导致启动报 vocab and merges must be both be from memory or both filenames)。检查:

[PowerShell]
Get-ChildItem "E:\WSL\downloads\models\Qwen2.5-VL-3B-Instruct-AWQ" | Select Name, Length   # 路径随上面脚本的 $dest

3.3 复制进 WSL

WSL 直接读 /mnt/e(9P 桥接)极慢,必须复制到 Linux 原生目录

[WSL]
# 源路径 = 你在 3.2 下载脚本里设的 $dest(Windows 盘,经 /mnt 访问)
# 目标路径 = 你选的模型运行目录:任意 WSL 原生路径都行(如 ~/models/...),但 4.1 启动脚本里的模型路径要同步改
mkdir -p /home/vllm_serve/models/Qwen2.5-VL-3B-Instruct-AWQ
cp /mnt/e/WSL/downloads/models/Qwen2.5-VL-3B-Instruct-AWQ/* /home/vllm_serve/models/Qwen2.5-VL-3B-Instruct-AWQ/
ls -la /home/vllm_serve/models/Qwen2.5-VL-3B-Instruct-AWQ/

3.4 GB 复制约需 5 分钟。复制期间 WSL 可能报资源不足崩溃(见 6.8),崩溃后 wsl --shutdown 重启再继续。


第 4 部分:启动并验证 vLLM 服务

起服务 → 确认在线 → 跑两个测试(OCR + 真实图片),一气呵成。

4.1 启动脚本(最终版,已调通)

把下面内容存为 /home/vllm_serve/start_vllm.sh

#!/bin/bash
export VLLM_WSL2_ENABLE_PIN_MEMORY=1
# GTX 1650 4GB 极限压榨: AWQ 权重 3.32GiB 用 weights 专用池加载
# KV cache 手动指定 128MiB 绕过 vLLM 的显存计算 bug (free-weights 为负)
# 路径可改: venv 随 2.1, 模型目录随 3.3, 日志重定向路径见下方 > /home/vllm_serve/vllm.log 处
source /home/vllm_serve/venv/bin/activate
cd /home/vllm_serve/models

echo "=============================================="
echo " vLLM 启动中... 日志实时显示在下面 (同时写入 /home/vllm_serve/vllm.log)"
echo " 出现 'Application startup complete.' = 就绪"
echo " 就绪后访问: http://localhost:8000   Ctrl+C 停止服务"
echo " 启动约需 1.5~2 分钟,请耐心等待..."
echo "=============================================="

# 模型路径换成你在 3.3 复制到的目录;--port / --served-model-name 也可自定义
exec vllm serve /home/vllm_serve/models/Qwen2.5-VL-3B-Instruct-AWQ \
  --served-model-name qwen2.5-vl-3b \
  --quantization awq \
  --gpu-memory-utilization 0.80 \
  --kv-cache-memory-bytes 134217728 \
  --max-model-len 512 \
  --max-num-seqs 1 \
  --limit-mm-per-prompt '{"image": 1}' \
  --enforce-eager \
  --host 0.0.0.0 \
  --port 8000 \
  2>&1 | tee /home/vllm_serve/vllm.log  # 日志路径可改,4.3/4.4 的查看命令要同步改

4.2 启动

[WSL]
chmod +x /home/vllm_serve/start_vllm.sh
bash /home/vllm_serve/start_vllm.sh   # 脚本路径可改:若你在 4.1 另存了位置,这里和速查表都要跟着改

启动耗时约 1.5~2 分钟(权重加载 17s + 编译/profile 10s + API 就绪)。

4.3 常用配套命令

[WSL]
tail -f /home/vllm_serve/vllm.log          # 实时看日志(路径随 4.1 的重定向)
pgrep -af "vllm serve"          # 确认进程活着

若想让服务在 Windows 后台常驻,在 PowerShell 用后台任务启动:

[PowerShell]
wsl -e bash -c "bash /home/vllm_serve/start_vllm.sh"   # 保持这个窗口开着即可;脚本路径同 4.2

4.4 确认 API 就绪

日志出现以下行即成功:

[WSL]
grep -E "Application startup complete|Uvicorn" /home/vllm_serve/vllm.log
# 期望输出: Application startup complete.

4.5 查询模型列表

[PowerShell]
curl.exe http://localhost:8000/v1/models
# 期望返回: {"object":"list","data":[{"id":"qwen2.5-vl-3b",...}]}

4.6 OCR 实测(生成本机测试图)

把下面脚本存为 /home/vllm_serve/test_ocr.py 并执行(脚本路径可改,保存位置和下面执行命令保持一致即可):

from PIL import Image, ImageDraw, ImageFont
import base64, io, json, urllib.request

img = Image.new("RGB", (224, 224), "white")
d = ImageDraw.Draw(img)
try:
    font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 28)
except Exception:
    font = ImageFont.load_default()
d.text((20, 40), "HELLO VLLM", fill="black", font=font)
d.text((20, 90), "Qwen2.5-VL-3B", fill="black", font=font)
d.text((20, 140), "GTX 1650 4GB", fill="black", font=font)
d.text((20, 190), "12345", fill="black", font=font)
img.save("/home/vllm_serve/test_ocr.png")

buf = io.BytesIO(); img.save(buf, format="PNG")
b64 = base64.b64encode(buf.getvalue()).decode()
payload = {
    "model": "qwen2.5-vl-3b",
    "messages": [{"role": "user", "content": [
        {"type": "image_url", "image_url": {"url": f"data:image/png;base64,{b64}"}},
        {"type": "text", "text": "请逐行读出图片里的所有文字。"}
    ]}],
    "max_tokens": 200,
}
req = urllib.request.Request("http://127.0.0.1:8000/v1/chat/completions",
    data=json.dumps(payload).encode(), headers={"Content-Type": "application/json"})
with urllib.request.urlopen(req, timeout=180) as resp:
    print(json.loads(resp.read().decode())["choices"][0]["message"]["content"])
[WSL]
/home/vllm_serve/venv/bin/python /home/vllm_serve/test_ocr.py   # 路径随你保存的位置;venv 前缀随 2.1

实测输出(基本全对):

HELLO VLLM
Qwen2.5-VL-1      # "3B" 读成 "1",小图分辨率低所致,可忽略
GTX 1650 4G
12345

4.7 真实图片测试

用任意真实图片(如 D:\数据存储\Pictures\情头\男生.jpeg):

[WSL]
cp "/mnt/d/数据存储/Pictures/情头/男生.jpeg" /home/vllm_serve/real.jpg

注意:原图必须先缩放到 ≤448px 再传(原因见 6.9),缩放用 Windows 侧脚本最方便(见第 5 部分)。


第 5 部分:从 Windows 调用测试

vLLM 在 WSL 里监听 0.0.0.0:8000WSL2 自动把端口转发到宿主机,所以 Windows 直接访问 http://localhost:8000

5.1 方式一:一键测试脚本(推荐)

test_vllm.ps1(见附录)放在任意目录,PowerShell 进入该目录执行(下面 \.\ 换成你的实际路径):

[PowerShell]
# .\ 表示当前目录:先 cd 到 test_vllm.ps1 所在目录,或直接写完整路径 C:\...\test_vllm.ps1
.\test_vllm.ps1                                              # 纯文本对话
.\test_vllm.ps1 -Image "D:\数据存储\Pictures\情头\男生.jpeg"   # 图片识别(自动缩放 448px)
.\test_vllm.ps1 -Image "...\图.jpg" -Prompt "这张图是什么风格?"  # 图片 + 自定义问题

脚本内置:服务健康检查、图片自动缩放、UTF-8 中文输出修复。实测输出:

== 4. 结果 (用时 1.7s) ==
这张图片的主角是一只棕色的小狗。

5.2 方式二:浏览器 Swagger 调试页

浏览器打开 http://localhost:8000/docs —— vLLM 自带的可视化 API 页面,填参数点按钮即可发请求。

image-20260809123857480

例如调试/v1/chat/completionsate接口

image-20260809124527769

5.3 方式三:curl 命令行

[PowerShell]
# 方式一:内联(单引号包裹,JSON 内部的双引号必须写成 \" 转义形式)
curl.exe http://localhost:8000/v1/chat/completions -H "Content-Type: application/json" -d '{\"model\":\"qwen2.5-vl-3b\",\"messages\":[{\"role\":\"user\",\"content\":\"你好,用一句话介绍自己\"}]}'

# 方式二:文件方式(最稳,绕开所有引号/编码问题)
$body = '{"model":"qwen2.5-vl-3b","messages":[{"role":"user","content":"你好,用一句话介绍自己"}]}'
[IO.File]::WriteAllText("$env:TEMP\vllm_body.json", $body, (New-Object System.Text.UTF8Encoding($false)))
curl.exe http://localhost:8000/v1/chat/completions -H "Content-Type: application/json" -d "@$env:TEMP\vllm_body.json"

⚠️ PowerShell 5.1 引号坑(实测):PowerShell 5.1 调原生程序时不转义参数内部的双引号,-d '{"model":...}'(纯单引号)会把 JSON 拆碎,vLLM 报 JSON decode error / Expecting property name enclosed in double quotes

  • 方式一必须把 JSON 里的 " 写成 \"(单引号包裹 + 反斜杠转义),curl 会把它还原成正常 JSON。这是本机实测唯一能一行跑通的内联写法。
  • 方式二文件方式最稳:UTF-8 无 BOM 的 JSON 文件(WriteAllText + UTF8Encoding($false)),绕开命令行引号与编码的一切问题。
  • 中文实测正常:curl.exe 按 UTF-8 处理命令行,vLLM 返回干净 UTF-8;若响应中文在 GBK 控制台显示乱码,只是显示问题——改用 .\test_vllm.ps1 查看即可。
  • 如果用的是 PowerShell 7.3+,纯单引号 '{"model":...}' 也可以直接工作。

5.4 接口速查

接口路径用途
模型列表GET /v1/models确认服务在线
对话(OpenAI 格式)POST /v1/chat/completions文本 + 图片(image_url 传 base64)
补全POST /v1/completions纯文本

完全兼容 OpenAI SDK,任何语言/工具(Python、Node、Postman)都能调。


第 6 部分:踩坑全记录(重点)

按遇到顺序排列,每条含:现象 → 原因 → 解决。本部分是最有价值的经验。

6.1 驱动太老:found version 12000

  • 现象torch.cuda.is_available() 返回 False,报驱动太老
  • 原因:旧驱动 526.56 只支持 CUDA 12.0,而 torch 2.11+cu130 需要新驱动
  • 解决:升级到 610.88(见 1.3)

6.2 FA2 报错:FA2 is only supported on devices with compute capability >= 8

  • 现象:日志大量 ERROR,但不影响启动
  • 原因:GTX 1650 是 Turing 架构(7.5),vLLM 想用 FlashAttention-2(需 8.0+)失败后自动回退
  • 解决:忽略即可

6.3 UVA 不可用:RuntimeError: UVA is not available

  • 现象:引擎初始化直接失败
  • 原因:vLLM 0.26 的 staged-writes 需要 CUDA pinned memory,而 vLLM 在 WSL2 下默认禁用 pinned memory
  • 解决:启动脚本加环境变量:
    export VLLM_WSL2_ENABLE_PIN_MEMORY=1
    

6.4 KV cache 负内存:Available KV cache memory: -0.09 GiB

  • 现象:权重加载成功(Model loading took 3.32 GiB),但 KV cache 内存为负数,启动失败
  • 原因:vLLM 用 初始化后剩余显存(3.22GiB) - 权重(3.32GiB) 计算 KV cache 空间——4GB 卡上权重本身就超了"初始化后可用",公式结果为负
  • 解决:见 6.7(--kv-cache-memory-bytes 绕过)

6.5 --cpu-offload-gb 无效(AWQ 模型)

  • 现象:加 --cpu-offload-gb 1.2 后,Model loading took 3.32 GiB 不变,KV cache 依旧负数
  • 原因:vLLM 0.26 的 offloader 在模型构建时make_layers)wrap 模块,此时参数还在 CPU 上直接被跳过;真正的权重在后续 load_weights 阶段直接进显存,offload 根本没机会生效。实测对 AWQ/多模态模型无效
  • 解决:放弃 offload,改用 6.7 的方案

6.6 GGUF 方案否决

  • 现象Qwen2.5-VL-3B-Instruct-GGUF 的 Q4 版最小(~2.3GB)
  • 原因vLLM 0.26 不支持多模态模型的 GGUF--quantization gguf 对 VL 模型直接报错);hf-mirror 也没有该仓库的 GGUF 文件
  • 解决:回到 AWQ 路线,用 6.7 极限压榨

6.7 ⭐ 最终突破:--kv-cache-memory-bytes 手动指定

  • 原理:vLLM 0.26 加载权重用的是专用 weights 内存池_maybe_get_memory_pool_context),能用到接近 4GB 全部;加载后实际剩余 ~143 MB。但 KV cache 计算仍用 初始化后free - 权重 的错误公式 → 负数
  • 解决:手动指定 KV cache 大小,跳过这个公式
    --kv-cache-memory-bytes 134217728    # 128 MiB
    
    同时把 --max-model-len 降到 512、--max-num-seqs 1--enforce-eager(省 CUDA graph 显存),把显存全部让给权重
  • 效果init engine (profile, create kv cache, warmup model) took 23.50 s 成功,API 正常服务

6.8 WSL 崩溃:0x800705aa(系统资源不足)

  • 现象:复制 3.4GB 模型文件或服务运行时,WSL 突然挂掉,所有 wsl 命令报 Wsl/Service/CreateInstance/CreateVm/HCS/0x800705aa
  • 原因:WSL2 虚拟机瞬时资源不足(大文件复制 + 多进程同时进行)
  • 解决
    [PowerShell]
    wsl --shutdown        # 彻底重启 WSL 虚拟机
    
    等 10 秒后再用 wsl 即恢复。注意:这会杀掉 WSL 里所有进程(包括 vLLM),需要重新启动服务

6.9 图片 token 超限:Input length (767) exceeds model's maximum context length (512)

  • 现象:传真实图片(1000×968)报 400 Bad Request
  • 原因:Qwen2.5-VL 把图切成 28×28 的 patch,每 patch = 1 token。1000×968 的图缩放 768px 后 = ~760 token,超 512 上限
  • 解决图片缩放 ≤448px(448/28 = 16 列 × 16 行 = 256 token,留足文本余量)。test_vllm.ps1 已内置自动缩放

6.10 PowerShell 脚本中文乱码(两个坑)

  • 坑 1.ps1 文件里有中文注释,直接运行报语法错误
    • 原因:Windows PowerShell 5.1 按 GBK 解析无 BOM 的 UTF-8 文件,中文乱码导致引号配对错乱
    • 解决:文件存为 UTF-8 with BOM(VSCode 右下角编码选"UTF-8 with BOM")
  • 坑 2:脚本里 Invoke-RestMethod 拿到的中文回答变乱码
    • 原因:PowerShell 5.1 对无 charset 的 JSON 响应默认按 Latin-1 解码
    • 解决:改用 Invoke-WebRequest 拿原始字节手动按 UTF-8 解码:
      $rawResp = Invoke-WebRequest -Uri $url -Method Post -ContentType "application/json; charset=utf-8" -Body $body
      $resp = [System.Text.Encoding]::UTF8.GetString($rawResp.RawContentStream.ToArray()) | ConvertFrom-Json
      

6.11 漏下载小文件:vocab and merges must be both be from memory or both filenames

  • 现象:启动时 tokenizer 加载报错
  • 原因:下载流程在 model.safetensors 完成后提前报"完成",漏了最后 5 个小文件(tokenizer.jsonvocab.json 等)
  • 解决:检查 Windows 侧文件清单(3.2),缺哪个补哪个,再复制进 WSL

第 7 部分:已知限制

限制说明
上下文长度固定 512 token(图片+文字总长),长文档识别不支持
图片大小需 ≤448px(28×28=1 token 折算),大图自动缩放会丢细节
并发1 个请求(--max-num-seqs 1
每请求图片数1 张(--limit-mm-per-prompt '{"image": 1}'
速度生成约 1~5 token/s(受显存极限制约,可用但不算快)
稳定性WSL 虚拟机可能因资源不足崩溃,崩溃后需重启服务(见 6.8)
显存已被权重占满(~3.9/4.0 GiB),不能再开其他占用显存的程序

第 8 部分:命令速查

操作终端命令
启动 vLLMWSLbash /home/vllm_serve/start_vllm.sh
后台启动(Windows 侧)PowerShellwsl -e bash -c "bash /home/vllm_serve/start_vllm.sh"
看日志WSLtail -f /home/vllm_serve/vllm.log
确认进程WSLpgrep -af "vllm serve"
确认服务PowerShellcurl.exe http://localhost:8000/v1/models
文本测试PowerShell.\test_vllm.ps1
图片测试PowerShell.\test_vllm.ps1 -Image "D:\...\图.jpg"
查显存PowerShell/WSLnvidia-smi
WSL 崩了重启PowerShellwsl --shutdownwsl
停服务WSLpkill -9 -f "vllm serve"

附录:本机文件与脚本清单

文件位置用途
start_vllm.sh/home/vllm_serve/(WSL)vLLM 启动脚本(最终版)
vllm.log/home/vllm_serve/(WSL)服务日志
test_ocr.py/home/vllm_serve/(WSL)生成本机 OCR 测试图并调用
test_vllm.ps1C:\Users\Ameng\Desktop\claude_woker\vllm_windows\Windows 一键测试(文本/图片)
download_model.ps1Windows 任意目录hf-mirror 下载模型
模型权重E:\WSL\downloads\models\Qwen2.5-VL-3B-Instruct-AWQ\(下载源)→ /home/vllm_serve/models/Qwen2.5-VL-3B-Instruct-AWQ\(运行目录)模型文件
驱动E:\WSL\downloads\drivers\610.88-notebook-win10-win11-64bit-international-dch-whql.exe显卡驱动安装包

虚拟环境/home/vllm_serve/venv(Python 3.12.3,vllm 0.26.0,torch 2.11.0+cu130)

最终启动参数摘要(一句话记住):AWQ 权重 + VLLM_WSL2_ENABLE_PIN_MEMORY=1 + --kv-cache-memory-bytes 134217728 + --max-model-len 512 + --enforce-eager + --max-num-seqs 1

番外:Ollama 轻量替代方案实测记录(同一模型)

本篇正文用 vLLM 极限压榨跑通 Qwen2.5-VL-3B;若你需要更大的上下文(如长文档、长截图分析),Ollama 是更省心的轻量替代——同一个模型(qwen2.5vl:3b,Q4_K_M 量化,~2.7 GB),Windows 原生运行,一条命令常驻。以下为 2026-08-09 本机实测记录。

番外 1:部署概况

项目
Ollama 版本0.32.6(Windows 原生,非 WSL)
模型qwen2.5vl:3b(Q4_K_M 量化,权重 ~2.7 GB)
模型库E:\model\ollama_model(环境变量 OLLAMA_MODELS 指定)
服务地址http://localhost:11434(OpenAI 兼容:/v1/chat/completions
热启动~6 s(模型已加载时)

番外 2:Ollama 是常驻服务,不需要每次推理都启动

很多人的误解(包括当时的我):以为 Ollama 每次推理都要"启动"。实际上:

ollama.exe serve      ← 常驻服务,像 vLLM 的进程一样一直挂着(监听 11434)
   └─ llama-server    ← 模型进程,第一次请求时加载(冷启动 ~10~20s),
                        之后 OLLAMA_KEEP_ALIVE(默认 5 分钟)内保持"热",
                        连续请求秒级响应,不会重复加载
[PowerShell]
ollama serve                    # 启动/恢复常驻服务(或用桌面托盘图标)
ollama ps                       # 查看当前加载的模型(还在内存里就是"热"的)

体验要点:模型加载一次后,5 分钟内连续用都很快;隔久了模型自动卸载,下次请求重新加载(1020s)。想模型一直驻留可设 OLLAMA_KEEP_ALIVE=-1(永久驻留,占 ~2.7GB 显存 + RAM)。

番外 3:上下文 vs 速度(实测数据,核心结论)

同一个模型,上下文大小直接决定速度——这是"感觉慢"的真正元凶,不是 Ollama 本身:

上下文长度纯文本生成速度说明
819213.4 token/s默认 f16 KV,日常推荐
16384~5 token/s明显变慢,长文档才值得
[PowerShell]
# 测速命令(生成 220 token 计时,看响应里 eval_count / 耗时)
curl.exe http://localhost:11434/api/generate -H "Content-Type: application/json" -d '{"model":"qwen2.5vl:3b","prompt":"请写一篇关于夏天的短文,大约200字。","options":{"num_ctx":8192,"num_predict":220},"stream":false}'

番外 4:q8_0 KV 量化实验(踩坑:别设!)

想让 KV cache 减半、提速——实测无效且有害

  • 现象:设置 OLLAMA_KV_CACHE_TYPE=q8_0 后,纯文本 12 token/s(与默认 13.4 无本质差异),但图片请求直接把服务卡死(llama-server 进程活着、占着显存不释放,API 不再响应任何请求)
  • 原因:llama.cpp 的多模态(vision)路径与 KV 量化的兼容问题
  • 处理:回退默认配置,恢复正常:
[PowerShell]
# 删除 q8_0 设置(恢复默认 f16 KV)
[Environment]::SetEnvironmentVariable('OLLAMA_KV_CACHE_TYPE', $null, 'User')
# 然后杀干净重启服务(务必连残留的 llama-server 一起杀)
Stop-Process -Name "ollama*","llama-server*" -Force -ErrorAction SilentlyContinue
Start-Sleep -Seconds 3
ollama serve

⚠️ 踩过的坑:残留的 llama-server 进程会导致新 Ollama 实例卡死(监听 11434 但 API 无响应)。重启服务前一定要用 Stop-Process -Name "llama-server*" 清干净。

番外 5:Ollama vs vLLM 怎么选(本机 4GB 卡实测)

维度vLLM(WSL,本篇正文)Ollama(Windows 原生)
上下文上限512(极限压榨后的妥协)8192+ 随意设置
图片上限≤448px≤1024px
纯文本速度快(GPU 直算)8192 上下文下 ~13 token/s
冷启动1.5~2 分钟1020s
运维复杂度手动启停、日志排查一条命令常驻,托盘管理
适用场景高频 OCR 短任务(快)需要大上下文的长文档/长截图

一句话选择:主要是 OCR 识别、追求速度 → vLLM;要看长文档、要省心 → Ollama(上下文设 8192 即可,别贪大)。

番外 6:Ollama 推荐配置(Cherry Studio 侧)

配置项推荐值理由
上下文长度8192实测 13.4 token/s;16384 会掉到 ~5
最大输出20485 token/s 下 2048 ≈ 7 分钟上限,再大没人等
温度不动(默认 0.0001)对 OCR/文字提取极稳,调高会开始"脑补"错字
图片尺寸≤1024px(OCR 建议 448~768px)768px 图 ≈ 760 token,8192 内绰绰有余

Cherry Studio 路径:设置 → 模型服务 → Ollama → qwen2.5vl:3b → 上下文长度填 8192

下期预告:我们将基于本篇部署的 vLLM 服务,编写中间件将 Qwen2.5-VL 封装为 API,并与 DeepSeek API 串联,实现真正的“看图说话”智能助手。

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐