☰
Windows本地跑Qwen3小模型:WSL2+vLLM+Open WebUI部署全流程
2026/10/21 14:07:56 网站建设 项目流程

前阵子手头只有一台装了 Windows 的笔记本,想在公司晚上值班时本地起一个能对话的小模型玩。几番对比之后把目标锁定在 Qwen3-0.6B:参数只有 6 亿出头,权重文件一两个 GB,一张 4GB 以上显存的卡就能动起来。但真正动手时才发现,问题不在模型本身,而在 Windows 这层的环境。vLLM 这个推理框架很多依赖默认只提供 Linux 构建产物,Open WebUI 的部署也绕不开容器或 Python 环境,直接写在 Windows 桌面上会踩到各种补丁、编译器、路径分隔符组成的坑。于是我把方案改成了 WSL2 里建 Ubuntu 实例,串起 vLLM 推理服务和 Open WebUI 页面,整条链路跑通后,从浏览器打开对话页面到拿到第一个字,非常顺滑。

这篇文章就把完整过程写出来。目标读者是手里只有一台 Windows 电脑、想跑个开源小模型当私人聊天助手的人。0.6B 虽然能力不算强,但作为本地部署的第一站非常合适:显存门槛低、下载快、踩坑成本小。等你把这套链路跑熟,以后换更大的模型只是换目录和启动参数的事。

1. 为什么绕道 WSL2:Windows 原生跑大模型的四个现实障碍

先说结论:如果你认定了 vLLM + Open WebUI 这条技术栈,Windows 原生环境基本是走不通或者很难走的。这不是 Windows 不行,而是这套开源生态从设计上就没把 Windows 当一等公民。

1.1 vLLM 并没有为 Windows 原生环境提供完整构建产物

vLLM 是目前本地推理服务里很常用的框架,它提供 OpenAI 兼容的 /v1 接口,后面挂 Open WebUI 这类前端非常省事。但 vLLM 有大量算子和底层内核依赖 Linux 的 CUDA 编译链,很多预编译 wheel 没有 Windows 版本。你在 Windows 上pip install vllm,常见结局是"找不到匹配的 wheel"或者编译到一半报错,错误信息指向 MSVC、CMake、ninja 这些衍生依赖。

为跑一个 0.6B 的小模型去编译整个框架,性价比实在太低。很多人就是卡在这一步才转投 WSL2 的——不是 Windows 跑不了 AI,是不值得为它折腾编译链。

1.2 CUDA 生态在 Linux 侧更完整

PyTorch 在 Windows 上确实有官方 CUDA wheel,日常推理没问题,但 vLLM 这类框架不一样。它底层用到的部分算子、注意力实现、Triton 内核,主线支持平台就是 Linux。Windows 上即便勉强装上了,也容易在运行时遇到算子不支持、设备断言失败、莫名崩溃之类的问题。

WSL2 提供的是完整 Linux 用户态,Ubuntu 实例里跑的 vLLM 和服务器上跑的没有任何区别,pip 拉下来的就是标准构建,行为一致。以后你遇到问题去翻 issue、搜资料,别人给的方案在 WSL2 里能直接照抄,这个优势在折腾阶段非常关键。

1.3 文件系统性能差距比你想的大

WSL2 底层用 ext4 虚拟磁盘,模型权重和缓存放在 Linux 文件系统内,IO 路径比 NTFS 短。模型加载时要从磁盘读 1GB 左右权重到大块内存,IO 快了,冷启动时间能短不少。

反过来,/mnt/c这种跨文件系统访问速度明显慢,尤其大量小文件(tokenizer 的 JSON、各类配置文件)读起来拖沓。所以模型文件放 WSL2 家目录,不放 Windows 桌面,看起来是小事,实际影响启动体验。

1.4 后续接 Docker 和其他工具顺手

Open WebUI 经常以容器方式部署,WSL2 里的 Docker 引擎和 Linux 生态天然一致,端口映射、数据卷挂载都比 Windows 原生容器模式省心。另外,如果你以后想换别的推理框架——比如更轻量的 llama.cpp、sglang,或者试 DeepSpeed、TensorRT-LLM 这类工程方案——它们也都先支持 Linux。

当然也有不需要 WSL2 的场景:如果只玩 llama.cpp 的 Windows 原生 exe,或只用 Ollama,Windows 直跑没问题。但目标明确是 vLLM + Open WebUI 这条链路时,WSL2 是绕开了大量麻烦的最优路线。

2. 环境搭建:WSL 实例、驱动透传与磁盘规划的具体操作

2.1 安装并确认 WSL2

安装 WSL2 本身不复杂,但版本坑特别多。我在一台 Windows 11 机器上操作,装的是 Ubuntu 22.04:

wsl --install -d Ubuntu-22.04

安装完成后重启,首次进入 Ubuntu 会要求设置用户名和密码。注意这个用户名会成为 WSL 实例的默认用户,密码忘了会有点麻烦。

关键一步是确认版本:

wsl -l -v

输出里的 VERSION 列必须是 2。如果是 1,执行:

wsl --set-version Ubuntu-22.04 2

这个转换可能要等几分钟。另外,如果你之前装过旧版 WSL,先跑一下wsl --update,把 WSL 内核升级到商店版本,否则有些行为和老版本不一致,后面排错容易混乱。

2.2 NVIDIA 驱动与 WSL CUDA 透传

WSL2 的 GPU 透传逻辑很简单:Windows 侧安装好 NVIDIA 驱动后,WSL2 内的 Linux 可以直接访问 GPU,不需要在 Ubuntu 里再装一次驱动。

进入 WSL 执行:

nvidia-smi

如果能正常显示显卡信息,驱动版本和 Windows 侧一致,透传就成功了。这一步失败最可能的原因是 Windows 驱动太旧。我最早在一台老机器上试过,WSL 内 nvidia-smi 直接提示找不到设备,升级到新版驱动后立刻恢复。

这里补充一个常见误解:日常跑 vLLM 不是非要手动安装完整 CUDA Toolkit。vLLM 的 pip 依赖里已经包含它需要的 CUDA 运行库(一堆 nvidia-*-cu12 开头的 wheel),装完 vllm 就等于带上了 CUDA runtime。只有需要从源码编译 vLLM 时才要手动装完整 Toolkit。

2.3 WSL 虚拟磁盘位置与空间规划

WSL 默认把虚拟磁盘放在 C 盘用户目录下,文件名类似 ext4.vhdx。模型文件、虚拟环境、日志全塞进去之后,C 盘空间会被吃掉不少。我的建议是把虚拟磁盘迁到其他盘符,操作流程如下:

wsl --shutdown wsl --export Ubuntu-22.04 D:\wsl-backup\ubuntu.tar wsl --unregister Ubuntu-22.04 wsl --import Ubuntu-22.04 D:\wsl D:\wsl-backup\ubuntu.tar --version 2

注意--unregister会删除原实例,操作前确认没有需要保留的数据。import 之后默认用户会变成 root,可以通过编辑 Ubuntu 里的/etc/wsl.conf来改回原用户:

[user] default=你的用户名

空间预估方面:Qwen3-0.6B 权重约 1.2GB,vLLM 虚拟环境加 CUDA 相关依赖 8-10GB,Open WebUI 源码版也会吃掉几百 MB,再加上系统缓存,整体留 20GB 以上比较稳妥。

2.4 用 .wslconfig 限制资源占用

很多人跑 WSL 时风扇狂转、系统卡死,原因是 WSL2 默认会尽量吃满可用的 CPU 和内存。通过 Windows 用户目录下的.wslconfig文件可以限制:

[wsl2] memory=12GB processors=4 swap=4GB localhostForwarding=true

改完执行wsl --shutdown再重启 WSL,配置生效。memory 不建议写得太低,比如 4GB 的话跑 vLLM 时可能内存不够;12GB 是我用得比较舒服的值。

3. 模型文件怎么拿到:下载源、目录摆放与完整性核对

3.1 为什么先下载模型再启动服务

vLLM 的 serve 命令本身支持直接从模型仓库拉取,也可以传本地目录。但把下载这件事交给 vLLM,中断后要重新来,而且日志里混着下载信息,不利于排错。我习惯先把模型完整下到本地固定目录,再启动服务。这样管理更清晰,以后多个模型并列存在~/models下面,也方便切换。

3.2 下载命令

在 WSL2 内执行:

pip install -U "huggingface_hub[cli]" export HF_ENDPOINT=https://hf-mirror.com hf download Qwen3/Qwen3-0.6B --local-dir ~/models/qwen3-0.6b

设置HF_ENDPOINT是因为直接从境外模型仓库下载速度不稳定,换成国内加速镜像后,1.2GB 的权重几分钟就能拉完。如果你所在的网络环境访问境外仓库很快,这步可以跳过。

喜欢用 git 的话也可以:

git lfs install git clone https://huggingface.co/Qwen3/Qwen3-0.6B ~/models/qwen3-0.6b

但 git 方式务必先确保 git-lfs 已经安装,否则拉下来的不是权重文件而是 LFS 指针文件,加载到一半会报错,或者文件大小只有几百 KB。

3.3 目录结构与完整性检查

下载完成后先看一下目录:

ls -lh ~/models/qwen3-0.6b du -sh ~/models/qwen3-0.6b

正常会看到 config.json、generation_config.json、model.safetensors.index.json、若干 safetensors 权重分片、tokenizer.json 等文件。总大小应该在 1.2GB 左右,如果只有几 MB,说明拉到了 LFS 指针文件,需要重新执行git lfs pull或检查下载源。

为什么权重是 1.2GB 而不是别的数字?0.6B 参数约 6 亿个,BF16 格式每个参数占 2 字节,6 亿乘以 2 字节等于 1.2GB。权重体积的估算公式就这么简单,选显卡时也能用:参数规模乘以精度字节数,FP16/BF16 按 2 算,INT4 量化按 0.5 算。

3.4 模型存放目录的选择

模型放在~/models/qwen3-0.6b,不要图方便放到/mnt/c/下面。WSL2 访问 Windows 文件系统要走 9p 协议,大量小文件 IO 明显慢,模型加载阶段要顺序读大文件,虽然差距没有小文件那么夸张,但没必要给自己添堵。

4. vLLM 启动参数拆解:让 Qwen3-0.6B 变成可服务的 API

4.1 安装 vLLM

在 WSL2 内创建虚拟环境安装,Ubuntu 22.04 自带 Python 3.10,vLLM 对 3.10-3.12 都有支持,用 3.10 最省心:

cd ~ python3 -m venv vllm-env source vllm-env/bin/activate pip install --upgrade pip pip install vllm

pip 安装 vllm 时会拉取大量 nvidia 开头的 CUDA 依赖,虚拟环境总体积可能到 8-10GB,磁盘要留够。装完后快速验证:

python -c "import vllm; print(vllm.__version__)"

如果导入时报 libcuda 相关错误,基本是 nvidia-smi 不可见,回到 2.2 节查驱动透传。

4.2 启动命令与参数含义

vllm serve ~/models/qwen3-0.6b \ --served-model-name qwen3-0.6b \ --host 0.0.0.0 \ --port 8000 \ --max-model-len 8192 \ --gpu-memory-utilization 0.8

逐个说参数:

  • --served-model-name:对外暴露的模型名称,Open WebUI 里下拉选择时看到的就是它。如果目录名和想要的模型名不一致,这里必须显式指定,否则 API 的 model 字段会和页面展示对不上。
  • --host 0.0.0.0:监听所有网卡,让 WSL2 内的其他进程、Docker 容器都能访问。如果只本机访问,设成 127.0.0.1 也行,但接 Open WebUI 时建议保持 0.0.0.0。
  • --max-model-len 8192:最大上下文长度。0.6B 模型本身的上下文能力不低,但 8192 是安全和显存友好的折中。改大了,KV cache 预占会上升;改小了,长对话容易被截断。
  • --gpu-memory-utilization 0.8:vLLM 预占 80% 显存作为 KV cache 池,留 20% 给 CUDA context 和系统余量。显存充裕可以调到 0.9,但 8GB 的卡我不建议超过 0.85。

启动成功的标志是日志里出现:

INFO: Uvicorn running on http://0.0.0.0:8000 INFO: (Base) http://0.0.0.0:8000/v1

看到 /v1 路径,说明 OpenAI 兼容 API 已经就绪。如果日志提示模型仓库里有自定义代码需要信任,再补一个--trust-remote-code。

4.3 为什么小模型也要用 vLLM

有人会问:0.6B 的模型用 transformers 直接加载对话不就行了?何必上 vLLM?我实际对比过,一段 500 字的中文回答,transformers 在长上下文下逐 token 生成时速度衰减明显,用 vLLM 则稳定得多。

原因在于 vLLM 的 PagedAttention 机制。它把 KV cache 分成固定大小的块,按需申请、动态管理,避免显存碎片化。你可以理解成操作系统把内存分页管理,程序按页申请,不用的页可以回收。传统方式是一次性为整段对话分配连续显存,容易浪费和碎片化。vLLM 这样设计后,单并发下内存抖动小,并发请求还能用 continuous batching 把多个请求拼在一起推理,吞吐更高。

对个人玩机来说更重要的是,vLLM 提供了一个标准化的 OpenAI 兼容接口。Open WebUI 可以直接接入,不需要自己写 FastAPI 封装,省掉大量兼容性工作。

4.4 快速验证 API

服务起来后,先别急着开 Web UI,用 curl 验证一下:

curl http://localhost:8000/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{ "model": "qwen3-0.6b", "messages": [{"role": "user", "content": "用一句话介绍你自己"}], "max_tokens": 128 }'

返回 JSON 里choices[0].message.content就是模型回答。如果返回 404,检查 model 字段是否和--served-model-name一致;返回 503 一般是模型还在加载,等日志出现 ready 再试。

5. Open WebUI 这层的坑:API 对接、容器网络与模型列表为空

5.1 选择容器版还是源码版

Open WebUI 有两条路可走:

  • Docker 容器版:干净、升级方便,但要求先装好 Docker,而且容器访问宿主 vLLM 的地址有讲究。
  • 源码版:直接pip install open-webui,在 WSL2 里起一个 Python 进程,访问 vLLM 用 localhost 就行,少一层网络隔阂。

对于只想在一台机器上跑小模型的人来说,我推荐源码版,问题最少。如果你已经装了 Docker 桌面版,容器版也顺手,但要注意 5.3 节的网络配置。

5.2 源码运行与 API 配置

pip install open-webui open-webui serve --host 0.0.0.0 --port 8080

第一次启动会初始化本地 SQLite 数据库,浏览器访问http://localhost:8080,注册管理员账号后进入主界面。

接下来在页面右上角的"设置"里找到连接配置,添加一个 OpenAI 兼容端点:

  • API Base URL:http://localhost:8000/v1
  • API Key:任意占位符,比如vllm-does-not-check,vLLM 默认不校验 key

保存后,页面模型列表会从/v1/models拉取,显示qwen3-0.6b。

5.3 模型列表为空的排查链路

这问题我踩过好几次,按出现频率排:

  1. vLLM 还没启动完成就点保存。先用curl http://localhost:8000/v1/models确认有返回再连。
  2. API Base URL 写成了http://localhost:8000,漏了/v1。Open WebUI 期望的地址是带 /v1 的 base,漏掉会在连接测试时报错或拉不到模型。
  3. 如果 Open WebUI 跑在 Docker 容器里,容器内部的 localhost 是容器自己,不是 WSL2 里的 vLLM。此时要用 WSL2 的 IP,通过wsl hostname -I查出来,然后填http://<WSL2-IP>:8000/v1。
  4. Windows Defender 防火墙在某些网络配置下会拦截 WSL2 入站访问。如果从浏览器能打开 Open WebUI,但容器或源码进程连不上 vLLM,检查防火墙规则,或者确保 vLLM 监听 0.0.0.0。

5.4 Docker 版补充命令

如果坚持用容器版,参考这个:

docker run -d \ -p 3000:8080 \ -e OPENAI_API_BASE_URL=http://<WSL2-IP>:8000/v1 \ -e OPENAI_API_KEY=whatever \ -v open-webui-data:/app/backend/data \ --name open-webui \ --restart=always \ open-webui/open-webui:latest

浏览器访问http://localhost:3000。这个方案里的关键是把OPENAI_API_BASE_URL指向 WSL2 的 IP,而不是写 localhost。WSL2 的 IP 每次重启可能变化,如果发现连接断了,先重新查一下 IP。

6. 实测记录:显存占用、生成速度与那几次重启后的教训

6.1 显存占用和响应速度

整个链路跑起来后,用nvidia-smi观察显存。Qwen3-0.6B 的 BF16 权重约 1.2GB,加上 CUDA context、激活值、KV cache,8GB 显存的卡整体占用大概 3-4GB。如果是 4GB 显存的老卡,把--gpu-memory-utilization调到 0.6、--max-model-len降到 4096,通常也能跑起来。

我实测的单客户端响应速度:首 token 约 0.3-0.5 秒,后续生成速度在 30-50 token/s 之间,日常聊天完全够用。想要更快可以换量化版,但 0.6B 小模型没必要一上来就量化,BF16 已经很好。

6.2 模型加载慢、OOM 和无输出的排查

  • 模型加载慢:确认权重在 WSL2 ext4 内部而不是/mnt/c,同时看虚拟磁盘剩余空间是否充足。
  • 启动阶段报 CUDA out of memory:多半是gpu-memory-utilization太高或max-model-len太大,调低重试。
  • 有输入无输出:检查请求是否超出上下文上限,客户端max_tokens不要设得过大,建议 512 以内。

6.3 端口冲突与 WSL2 的 localhost 转发

vLLM 占用 8000,Open WebUI 占用 8080,端口冲突时用ss -tlnp查看占用进程,改端口后注意同步 Open WebUI 里的 API 配置。

WSL2 默认开启 localhostForwarding,Windows 侧浏览器可以直接访问 WSL2 里的localhost:8000和localhost:8080。如果手动关掉了这个配置,就要改用 WSL2 的 IP 访问,地址变成http://<WSL2-IP>:8080。

6.4 一次标准的启动顺序

我每次重新启动的固定顺序:

  1. 进入 WSL2,激活 vllm-env,启动 vLLM
  2. 另开一个终端,启动 Open WebUI(源码版直接open-webui serve)
  3. 浏览器打开页面,登录并发起对话
  4. 会话结束后关闭两个进程,需要释放资源时wsl --shutdown

按照这个顺序走,基本不会出现 Open WebUI 连不上 vLLM 的问题。

最后补一个个人经验:.wslconfig里限制 12GB 内存、模型固定在~/models、vLLM 的gpu-memory-utilization保持 0.8、Open WebUI 跑源码版,这套组合我已经稳定用了很久。如果你是第一次在 Windows 上部署 Qwen3 这类小模型,建议先照这套跑通,再考虑调参。0.6B 的能力确实有限,但它是最适合练手的一条链路——等这台机器承载不了更大模型时,你至少已经知道瓶颈出在哪个环节了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询