2×16G也能跑Qwen3.5:21GB模型实测120 tokens/s、262K上下文

2×16GB RTX 4080 的受限环境里,我最终把 Qwen3.5-35B-A3B 跑到了 120 tokens/s,并且把 262K 上下文稳定接进了 Dify 工作流。
这篇文章不是“理论解析”,而是一次完整的实战复盘:从 vLLM 踩坑到 llama.cpp 落地,再到 Dify 集成与排错闭环。

如果你也在做本地大模型部署,可以重点看这 4 块:

  • vLLM 为什么暂时不适合这次场景
  • llama.cpp + GGUF 在双 16G 卡上的参数组合
  • Dify 接入时 No user query found 的根因
  • 可直接复用的运维与排错命令

一、背景与硬件环境

1.1 部署背景

原有的dify日志分析工作流基于 Qwen3-32B-AWQ 模型,虽然能工作,但在处理超长上下文(如一天的全量日志)时显得力不从心。得知阿里新开源的 Qwen3.5-35B-A3B 模型支持 262K 上下文,我决定升级模型以获得更好的长文本处理能力。

1.2 硬件配置

我的部署环境是一台双卡服务器,但说实话配置并不豪华:

  • GPU:2 × NVIDIA GeForce RTX 4080(每张只有 16GB 显存,共 32GB)
  • 内存:128GB
  • 存储:4TB
  • 操作系统:Ubuntu 24.04

尴尬的现实:两张 16G 4080 总显存 32GB,而 Qwen3.5-35B 即使量化后也需要约 12-14GB。这意味着我必须精打细算每一兆显存,tensor-split 参数必须精确分配。

二、第一轮尝试:vLLM 部署

2.1 安装依赖

一开始我沿用之前部署 Qwen3-32B-AWQ 的经验,选择 vLLM:

# 创建虚拟环境(Ubuntu 24.04 强制要求)
cd /vllm
python3 -m venv vllm-env
source vllm-env/bin/activate

# 安装 vLLM
pip install vllm

2.2 下载模型(FP8 版)

从 ModelScope 下载 FP8 量化版,约 37.5GB:

# 安装 modelscope
pip install modelscope

# 下载模型到指定目录
modelscope download --model Qwen/Qwen3.5-35B-A3B-FP8 --local_dir ./Qwen/Qwen3.5-35B-A3B-FP8

下载过程:37.5GB 的文件,企业内网下载了约 40 分钟。

2.3 首次启动尝试

vllm serve /vllm/models/Qwen/Qwen3.5-35B-A3B-FP8 \
    --tensor-parallel-size 2 \
    --max-model-len 262144 \
    --gpu-memory-utilization 0.9

报错 1: Value error, The checkpoint you are trying to load has model type 'qwen3_5_moe' but Transformers does not recognize this architecture

解决: Transformers 版本太旧,需要升级

pip install --upgrade transformers

2.4 版本冲突的噩梦

升级后发现新问题:

vllm 0.16.0 requires transformers<5,>=4.56.0, but you have transformers 5.3.0

这就是 Python 依赖地狱的典型场景——vLLM 要求 transformers <5,但 Qwen3.5 需要较新的版本。我尝试了各种组合:

  • transformers 4.48.3 + vLLM 0.16.0(报错:缺少 Gemma3Config)
  • transformers 4.49.0 + vLLM 0.16.0(同样缺少 Gemma3Config)
  • transformers 5.3.0 + 升级 vLLM(但 vLLM 最新版仍未完全支持)

最终报错:

Model architectures ['Qwen3_5MoeForConditionalGeneration'] are not supported for now.

查看 vLLM 支持的模型列表,确实没有 Qwen3.5 的 MoE 架构。这意味着 vLLM 官方还未支持 Qwen3.5-35B-A3B

三、转向 llama.cpp(成功方案)

3.1 为什么选择 llama.cpp

  • 对新模型的支持通常比 vLLM 更快
  • GGUF 格式在社区中广泛使用,量化版本更成熟
  • 对双卡并行有完善支持,特别适合我这种 8G*2 的尴尬配置

3.2 编译安装

# 浅克隆(避免网络问题)
git clone --depth 1 https://github.com/ggml-org/llama.cpp
cd llama.cpp
mkdir build && cd build

# 配置 CUDA 支持(RTX 4080 是 sm89)
cmake .. -DGGML_CUDA=ON -DCMAKE_CUDA_ARCHITECTURES="89"

# 编译
make -j$(nproc)

# 编译完成后,可执行文件在 build/bin/ 下
ls -la bin/llama-server  # 确认编译成功

3.3 下载 GGUF 模型(详细过程)

根据社区反馈,unsloth 的 MXFP4_MOE 量化版效果最好,模型文件约 21GB,比 FP8 版小了近一半,更适合我的 16GB 显存环境。

方法一:使用 huggingface-cli

# 安装 huggingface-hub
pip install huggingface-hub

# 注意:这是 GGUF 格式,不是 FP8!
~/.local/bin/huggingface-cli download unsloth/Qwen3.5-35B-A3B-GGUF \
    --include "Qwen3.5-35B-A3B-MXFP4_MOE.gguf" \
    --local-dir /vllm/models/llama.cpp \
    --local-dir-use-symlinks False

方法二:使用 Python 脚本(支持断点续传,推荐)

# 创建 download_model.py 文件
cat > /vllm/download_model.py << 'EOF'
from huggingface_hub import hf_hub_download
import os

repo_id = "unsloth/Qwen3.5-35B-A3B-GGUF"
filename = "Qwen3.5-35B-A3B-MXFP4_MOE.gguf"  # GGUF 格式!
local_dir = "/vllm/models/llama.cpp"

os.makedirs(local_dir, exist_ok=True)
print(f"开始下载 {filename} (约 21GB,请耐心等待)...")

downloaded_path = hf_hub_download(
    repo_id=repo_id,
    filename=filename,
    local_dir=local_dir,
    local_dir_use_symlinks=False,
    resume_download=True  # 支持断点续传!
)
print(f"下载完成!文件保存在: {downloaded_path}")
EOF

# 执行下载脚本
python /vllm/download_model.py

下载过程实录

Qwen3.5-35B-A3B-MXFP4_MOE.gguf: 1%| 252M/21.6G [01:05<1:07:47, 5.24MB/s]
Qwen3.5-35B-A3B-MXFP4_MOE.gguf: 29%| 6.31G/21.6G [17:11<1:07:06, 3.79MB/s]
Qwen3.5-35B-A3B-MXFP4_MOE.gguf: 100%| 21.6G/21.6G [55:46<00:00, 6.45MB/s]

整整下载了 55 分钟,中间网络断了一次,好在 resume_download=True 自动续传。

3.4 启动服务(双卡并行,8G*2 的精确分配)

关键参数是 --tensor-split 8,8,将模型按显存比例分配到两张卡——每张卡刚好 8GB,这是为我的硬件量身定制的

# 进入 llama.cpp 的 build 目录
cd /vllm/llama.cpp/build

# 启动服务器(注意:模型名称去掉了 .gguf 后缀,只保留别名)
./bin/llama-server \
    --model /vllm/models/llama.cpp/Qwen3.5-35B-A3B-MXFP4_MOE.gguf \
    --alias "Qwen3.5-35B-A3B" \  # 这里去掉了 gguf,只保留模型名
    --host 0.0.0.0 \
    --port 8000 \
    --ctx-size 262144 \
    --n-gpu-layers 99 \
    --tensor-split 8,8 \  # 8G + 8G,精确分配!
    --main-gpu 0 \
    --flash-attn auto \
    --mlock \
    --no-mmap

成功标志:

ggml_cuda_init: found 2 CUDA devices:
  Device 0: NVIDIA GeForce RTX 4080, compute capability 8.9, VMM: yes
  Device 1: NVIDIA GeForce RTX 4080, compute capability 8.9, VMM: yes
llama_model_load: using multiple GPUs: tensor split 8,8
HTTP server listening on http://0.0.0.0:8000

3.5 性能验证

新开一个终端,用 nvidia-smi 查看显存占用:

nvidia-smi

真实占用情况

GPU 0: 15639MiB / 16376MiB  # 约 15.6GB
GPU 1: 14171MiB / 16376MiB  # 约 14.2GB

双卡均衡负载,各留约 1-2GB 余量——对于两张 8G 卡来说,这已经是极限压榨了。

测试推理速度:

curl http://localhost:8000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "Qwen3.5-35B-A3B",  # 注意:这里用的是 alias,不带 gguf
    "messages": [{"role": "user", "content": "你好,介绍一下你自己"}]
  }'

返回结果中包含 predicted_per_second: 120.71,即 120 tokens/s——这个速度让我很惊喜,毕竟显存已经快撑爆了。

3.6 设置系统服务(开机自启)

为了让模型服务在后台稳定运行,并确保服务器重启后自动启动,我配置了 systemd 服务。

创建服务配置文件

sudo vim /etc/systemd/system/llama-server.service

写入以下内容:

[Unit]
Description=llama.cpp server for Qwen3.5-35B-A3B
After=network.target

[Service]
Type=simple
User=root
WorkingDirectory=/vllm/llama.cpp/build
ExecStart=/vllm/llama.cpp/build/bin/llama-server \
    --model /vllm/models/llama.cpp/Qwen3.5-35B-A3B-MXFP4_MOE.gguf \
    --alias "Qwen3.5-35B-A3B" \
    --host 0.0.0.0 \
    --port 8000 \
    --ctx-size 262144 \
    --n-gpu-layers 99 \
    --tensor-split 8,8 \
    --main-gpu 0 \
    --flash-attn auto \
    --mlock \
    --no-mmap
Restart=on-failure
RestartSec=10
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target

启用并启动服务

# 重新加载 systemd 配置
sudo systemctl daemon-reload

# 启用开机自启
sudo systemctl enable llama-server

# 启动服务
sudo systemctl start llama-server

# 查看服务状态
sudo systemctl status llama-server

# 查看实时日志
sudo journalctl -u llama-server -f

服务管理常用命令:

sudo systemctl stop llama-server    # 停止服务
sudo systemctl restart llama-server # 重启服务
sudo journalctl -u llama-server --since "5 minutes ago"  # 查看最近5分钟日志

3.7 使用 screen 作为备选方案

如果不想配置 systemd,也可以使用 screen 临时后台运行:

# 安装 screen
apt install screen -y

# 创建新的 screen 会话
screen -S llama

# 在 screen 中启动服务
cd /vllm/llama.cpp/build
./bin/llama-server \
    --model /vllm/models/llama.cpp/Qwen3.5-35B-A3B-MXFP4_MOE.gguf \
    --alias "Qwen3.5-35B-A3B" \
    --host 0.0.0.0 \
    --port 8000 \
    --ctx-size 262144 \
    --n-gpu-layers 99 \
    --tensor-split 8,8 \
    --main-gpu 0 \
    --flash-attn auto \
    --mlock \
    --no-mmap

# 按 Ctrl+A 然后按 D 退出 screen(服务继续后台运行)

# 重新连接查看状态
screen -r llama

四、Dify 集成配置

4.1 添加模型供应商

在 Dify 中,选择 OpenAI-API-compatible 供应商:

配置项
模型名称 Qwen3.5-35B-A3B(和 --alias 保持一致)
API Key 任意(如 sk-no-key-required)
API Endpoint URL http://10.128.253.45:8000(注意不加 /v1)
模型上下文长度 262144(从模型元数据获取)
Function calling 根据需要选择

关键点

  • Endpoint URL 不要加 /v1,Dify 会自动拼接
  • 模型名称必须和启动时的 --alias 一致,我的是 Qwen3.5-35B-A3B不带 .gguf 后缀

4.2 模型参数配置

根据日志分析场景,我最终选择了以下参数:

  • Temperature: 0.6(平衡准确性与创造性)
  • Top P: 0.95
  • Top K: 20
  • Max Tokens: 4096

五、Dify 工作流中的关键问题

5.1 问题现象

在工作流中配置好 LLM 节点后,反复出现错误:

No user query found in messages
Error: Jinja Exception: No user query found in messages

5.2 排查过程

  1. 检查变量引用:前节点输出是 result,LLM 节点 context 已正确关联
  2. 添加调试代码节点:确认数据确实传递到了 LLM 节点
  3. 查看 Dify 日志:错误发生在模型调用前的模板渲染阶段

5.3 根本原因

Qwen3.5-35B-A3B 模型模板必须要求存在 user message。我的初始提示词配置只有 system role,缺少 user role。

5.4 解决方案

修改 LLM 节点的 prompt_template,显式添加 user role:

prompt_template:
- role: system
  text: |
    ###角色
    你是一名资深网络运维专家和日志分析专家。
    ...(详细的 system prompt)

- role: user
  text: |
    以下是网络设备异常日志的聚合 JSON 数据:

    {{#context#}}

    请按照 system 中的规则生成日志异常分析报告。

5.5 经验总结

对于 Qwen3.5 系列模型,在 Dify 中配置 LLM 节点时必须注意:

  1. 必须有 user message,不能只有 system message
  2. user message 中要明确说明输入数据是什么
  3. 变量引用 {{#context#}} 必须放在 user message 中
  4. 模型名称不要带 .gguf 后缀

六、最终工作流结构

我的完整工作流如下:

![流程图](/Users/jayce/Desktop/Jayce/公众号文章/21GB模型,120 tokenss,262K上下文:我的Qwen3.5部署成绩单.optimized.wechat-ready.assets/flowchart-1.png)

七、排错思路总结

7.1 模型部署阶段

问题 排查方向 解决方案
Transformers 版本不兼容 检查 vLLM 支持的模型 改用 llama.cpp
下载中断 网络稳定性 Python 脚本 + resume_download
双卡负载不均 检查 tensor-split 按显存精确分配 8,8
显存不足 查看 nvidia-smi 调整 n-gpu-layers,使用 GGUF
服务不能后台运行 进程管理 systemd 服务或 screen

7.2 Dify 集成阶段

问题 排查方向 解决方案
API Endpoint 错误 检查是否加了 /v1 去掉 /v1
模型调用失败 curl 直接测试 验证模型服务:curl http://localhost:8000/v1/models
变量引用错误 添加调试代码节点 打印实际传递的数据
No user query found 检查 prompt_template 添加 user role
模型名称错误 检查 alias 确保和启动时的 --alias 一致,不带 .gguf

7.3 服务运维常用命令

# 查看服务状态
systemctl status llama-server

# 查看实时日志
journalctl -u llama-server -f

# 测试模型服务
curl http://localhost:8000/v1/models

# 查看显存占用
watch -n 1 nvidia-smi

八、性能数据与结论

最终部署成功的 Qwen3.5-35B-A3B 在两张 8G 4080 上表现如下:

  • 模型格式:GGUF (MXFP4_MOE 量化),21GB
  • 服务别名:Qwen3.5-35B-A3B(不带 .gguf 后缀)
  • 推理速度:120 tokens/s
  • 上下文长度:262K(满血)
  • 显存占用:约 30GB(双卡分摊,GPU0 15.6G,GPU1 14.2G)
  • 服务稳定性:systemd 管理,开机自启,持续运行一周无崩溃

这次从 vLLM 转向 llama.cpp 的经历让我深刻体会到:

  1. 8G2 的显卡也能跑 35B 模型*,关键在于选对量化格式(GGUF)和精确分配 tensor-split
  2. 新模型发布初期,llama.cpp 往往比 vLLM 支持更快
  3. 模型名称不要带 .gguf 后缀,alias 要单独设置
  4. Dify 中配置 LLM 节点时,user message 是必须的
  5. systemd 服务配置对于生产环境至关重要

希望这份记录能帮助其他同样在探索 Qwen3.5 本地部署的同行少走弯路。


CTA:资料包领取

如果你需要我这次实战里的可复用材料,关注公众号后私信关键词:Qwen35部署,我会把以下资料发你:

  • Qwen3.5 的 LLM 提示词模板(含 system + user 结构)
  • llama.cpp 启动与 systemd 服务模板
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容