> ## Content Index
> Fetch the complete content index at: https://lucent.blog/llms.txt
> Use this file to discover other available public pages before exploring further.

# 华为910B 通过 vllm 部署 Qwen3.5 系列模型
- URL: https://lucent.blog/zrlxrntd/
- Published: 2026-03-31T05:04:03.000Z
- Updated: 2026-08-20T09:20:55.000Z
- Author: Lucent
- Tags: AI, 大模型, vllm

最近折腾了一阵华为昇腾 910B，目标很简单：在自己的机器上把 Qwen3.5 系列模型跑起来，而且最好还能兼容 OpenAI API、支持工具调用、支持超长上下文。

最后我选择了 `vllm-ascend`。

这一套下来，最大的感受是：

- 能跑，而且跑得不算慢(至少在 Ascend上 不算慢)
- 但坑也不少
- 很多问题不是模型的问题，而是镜像、驱动、卡型、参数组合的问题

下面放一个性能测试图:

![](https://img-1251540275.cos.ap-shanghai.myqcloud.com/blog/6ed3d5f42de8cfb2b47a698cf5a35427-kmzckgfx.png)

# **一、环境说明**

我这里的环境大概是这样：

- 机器：华为 Ascend 910B
- 系统：openEuler / CentOS 类 Linux
- 驱动和 CANN：已经安装好
- 模型：Qwen3.5-27B
- 部署方式：Docker Compose
- 推理框架：vllm-ascend

先确认宿主机能看到 NPU：

```
npu-smi info
```

如果能看到设备列表，说明驱动基本没问题。

例如：

```
+------------------------------------------------------------------------------------------------+
| npu-smi 25.2.1                   Version: 25.2.1                                               |
+---------------------------+---------------+----------------------------------------------------+
| NPU   Name                | Health        | Power(W)    Temp(C)           Hugepages-Usage(page)|
| Chip                      | Bus-Id        | AICore(%)   Memory-Usage(MB)  HBM-Usage(MB)        |
+===========================+===============+====================================================+
| 0     910B2               | OK            | 97.2        41                0    / 0             |
| 0                         | 0000:C1:00.0  | 0           0    / 0          64231/ 65536         |
+===========================+===============+====================================================+
| 1     910B2               | OK            | 93.3        43                0    / 0             |
| 0                         | 0000:01:00.0  | 0           0    / 0          64231/ 65536         |
+===========================+===============+====================================================+
| 2     910B2               | OK            | 93.5        39                0    / 0             |
| 0                         | 0000:C2:00.0  | 0           0    / 0          64231/ 65536         |
+===========================+===============+====================================================+
| 3     910B2               | OK            | 102.5       43                0    / 0             |
| 0                         | 0000:02:00.0  | 0           0    / 0          64231/ 65536         |
+===========================+===============+====================================================+
| 4     910B2               | OK            | 97.8        41                0    / 0             |
| 0                         | 0000:81:00.0  | 0           0    / 0          3390 / 65536         |
+===========================+===============+====================================================+
| 5     910B2               | OK            | 97.5        43                0    / 0             |
| 0                         | 0000:41:00.0  | 0           0    / 0          3390 / 65536         |
+===========================+===============+====================================================+
| 6     910B2               | OK            | 88.6        40                0    / 0             |
| 0                         | 0000:82:00.0  | 0           0    / 0          3390 / 65536         |
+===========================+===============+====================================================+
| 7     910B2               | OK            | 97.5        43                0    / 0             |
| 0                         | 0000:42:00.0  | 0           0    / 0          3390 / 65536         |
+===========================+===============+====================================================+
+---------------------------+---------------+----------------------------------------------------+
| NPU     Chip              | Process id    | Process name             | Process memory(MB)      |
+===========================+===============+====================================================+
| 0       0                 | 1316011       | VLLMWorker_TP            | 60891                   |
+===========================+===============+====================================================+
| 1       0                 | 1316012       | VLLMWorker_TP            | 60891                   |
+===========================+===============+====================================================+
| 2       0                 | 1316013       | VLLMWorker_TP            | 60891                   |
+===========================+===============+====================================================+
| 3       0                 | 1316014       | VLLMWorker_TP            | 60891                   |
+===========================+===============+====================================================+
| No running processes found in NPU 4                                                            |
+===========================+===============+====================================================+
| No running processes found in NPU 5                                                            |
+===========================+===============+====================================================+
| No running processes found in NPU 6                                                            |
+===========================+===============+====================================================+
| No running processes found in NPU 7                                                            |
+===========================+===============+====================================================+
```

# **二、准备模型缓存**

我不想让容器每次启动都重新下载模型，所以先在宿主机把模型缓存好。

我用的是 ModelScope：

```
pip install modelscope
```

下载模型：

```
modelscope download --model Qwen/Qwen3.5-27B
```

默认会下载到：

```
~/.cache/modelscope/hub/models/Qwen/Qwen3.5-27B
```

如果你的磁盘空间比较紧张，也可以提前设置：

```
export MODELSCOPE_CACHE=/mnt/disk2/modelscope
```

这样模型就会下载到你指定的目录。

# **三、docker-compose 配置**

我最后用的是下面这份配置。

```
services:
  vllm-ascend:
    image: quay.io/ascend/vllm-ascend:v0.17.0rc1-openeuler
    container_name: vllm-ascend
    network_mode: host
    shm_size: "1g"
    restart: unless-stopped
​
    environment:
      VLLM_USE_MODELSCOPE: "true"
      PYTORCH_NPU_ALLOC_CONF: "expandable_segments:True"
      HCCL_BUFFSIZE: "512"
      OMP_PROC_BIND: "false"
      OMP_NUM_THREADS: "1"
      TASK_QUEUE_ENABLE: "1"
​
    devices:
      - /dev/davinci0:/dev/davinci0
      - /dev/davinci1:/dev/davinci1
      - /dev/davinci_manager:/dev/davinci_manager
      - /dev/devmm_svm:/dev/devmm_svm
      - /dev/hisi_hdc:/dev/hisi_hdc
​
    volumes:
      - /mnt/disk2/modelscope/models:/root/.cache/modelscope/hub/models
      - /usr/local/dcmi:/usr/local/dcmi
      - /usr/local/Ascend/driver/tools/hccn_tool:/usr/local/Ascend/driver/tools/hccn_tool
      - /usr/local/bin/npu-smi:/usr/local/bin/npu-smi
      - /usr/local/Ascend/driver/lib64/:/usr/local/Ascend/driver/lib64/
      - /usr/local/Ascend/driver/version.info:/usr/local/Ascend/driver/version.info
      - /etc/ascend_install.info:/etc/ascend_install.info
​
    command:
      - vllm
      - serve
      - Qwen/Qwen3.5-27B
      - --host
      - 0.0.0.0
      - --port
      - "8000"
      - --tensor-parallel-size
      - "2"
      - --served-model-name
      - Qwen
      - --max-model-len
      - "133000"
      - --max-num-batched-tokens
      - "8096"
      - --trust-remote-code
      - --gpu-memory-utilization
      - "0.95"
      - --reasoning-parser
      - qwen3
      - --tool-call-parser
      - qwen3_coder
      - --enable-auto-tool-choice
      - --async-scheduling
      - --compilation-config
      - '{"cudagraph_mode":"FULL_DECODE_ONLY"}'
```

启动：

```
docker-compose up -d
```

查看日志：

```
docker-compose logs -f
```

如果看到：

```
INFO:     Application startup complete.
```

说明服务已经起来了。

# **四、这些参数到底是干什么的？**

第一次看到这堆参数的时候，我的反应基本是：

这玩意儿每个都像是“先抄别人的，别问为什么”。

后来踩坑踩多了，终于知道它们各自负责什么。

## **1\.** `VLLM_USE_MODELSCOPE=true`

让 vLLM 不去 Hugging Face，而是优先从 ModelScope 找模型。

国内环境强烈建议开，不然根本无法下载模型。

## **2\.** `tensor-parallel-size=2`

表示两张卡一起跑。

比如 Qwen3.5-27B ，一张 910B 能跑但是上下文太小，两张卡正合适。

简单理解：

- 1：单卡
- 2：双卡
- 4：四卡
- 8：八卡

但不是越大越好。

我自己试过 8 卡，结果反而炸了：

```
aclnnCausalConv1d failed
blockDim is invalid
```

最后发现不是显存不够，而是某些算子在 8 卡切分下有兼容性问题。

所以我的建议是：

- 先 1 卡验证
- 再 2 卡
- 稳了之后再考虑 4 卡
- 8 卡别一上来就冲

## **3\.** `max-model-len=133000`

这个是上下文长度。

也就是模型一次最多能吃多少 token。

133000 已经属于超长上下文了。当然这个模型原生支持 262,144 个 token，可扩展至最多 1,010,000 个 token。

它的好处很明显：

- 可以塞超长文档
- 可以做长聊天记录
- PDF、会议纪要

但副作用也很明显：

- 占更多内存
- 并发会下降
- 首 token 会变慢

## **4\.** `gpu-memory-utilization=0.95`

虽然名字里写的是 GPU，但在 Ascend 上它其实表示“最多吃掉多少设备内存”。

```
0.95 = 吃掉 95%
```

这个值越大，模型越容易跑下更长上下文、更高并发。

但也越容易翻车。

我的经验：

- 0.90：稳
- 0.95：性能更好
- 0.98：准备随时爆炸

如果你经常碰到：

```
CUDA out of memory
NPU out of memory
```

先别急着怀疑模型，先把它降到：

```
0.90
```

## **5\.** `PYTORCH_NPU_ALLOC_CONF=expandable_segments:True`

这个参数我一开始完全没看懂。

后来才知道，它是为了减少内存碎片。

有时候明明设备还有内存，但模型就是报 OOM。

原因可能不是“没内存”，而是“剩下的内存被切得太碎了”。

这个参数会让内存分配更灵活，对长上下文、多并发特别有帮助。

建议直接开。

## **6\.** `--reasoning-parser qwen3`

这是给 Qwen3 系列准备的“推理格式解析器”。

不开的话，模型虽然也能输出，但有些 reasoning 字段、思考内容、结构化返回可能会不正常。

如果你部署的是 Qwen3.5，直接写：

```
--reasoning-parser qwen3
```

## **7\.** `--tool-call-parser qwen3_coder`

这是工具调用解析器。

如果你希望模型支持：

- function calling
- tool calling
- 自动调用天气、搜索、数据库、浏览器

那它就很重要。

再配合：

```
--enable-auto-tool-choice
```

模型就能自动决定：

“我应该去调用哪个工具，而不是直接瞎编。”

# **五、测试一下接口**

服务起来以后，可以直接用 curl 测。

最简单的聊天：

```
curl http://127.0.0.1:8000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "Qwen",
    "messages": [
      {
        "role": "user",
        "content": "你好，介绍一下你自己"
      }
    ]
  }'
```

# **六、测试视觉能力**

Qwen3.5 是统一多模态模型，原生支持视觉(图片、视频输入)。

```
curl http://127.0.0.1:8000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "Qwen",
    "messages": [
      {
        "role": "user",
        "content": [
          {
            "type": "text",
            "text": "图里有什么？"
          },
          {
            "type": "image_url",
            "image_url": {
              "url": "https://your-image-url"
            }
          }
        ]
      }
    ]
  }'
```

# **七、测试工具调用**

```
curl http://127.0.0.1:8000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "Qwen",
    "messages": [
      {
        "role": "user",
        "content": "帮我查一下北京今天的天气"
      }
    ],
    "tools": [
      {
        "type": "function",
        "function": {
          "name": "get_weather",
          "description": "获取天气",
          "parameters": {
            "type": "object",
            "properties": {
              "city": {
                "type": "string"
              }
            },
            "required": ["city"]
          }
        }
      }
    ],
    "tool_choice": "auto"
  }'
```

如果返回里出现：

```
"tool_calls": [
  {
    "function": {
      "name": "get_weather",
      "arguments": "{\"city\":\"北京\"}"
    }
  }
]
```

说明工具调用能力已经正常了。

---

# **八、我踩过的几个坑**

## **1\. 镜像不对**

910B2 和 910B3 对应的镜像可能不一样。

镜像不匹配时，最常见的症状是：

```
worker 一直起不来
设备识别失败
推理一执行就崩
```

## **2\.** `compilation-config` **JSON 写错**

这个参数必须是真正的 JSON。

错误：

```
cudagraph_mode:FULL_DECODE_ONLY
```

正确：

```
{"cudagraph_mode":"FULL_DECODE_ONLY"}
```

否则 vLLM 会直接启动失败。

## **3\. 视觉模型必须关掉 Flash Comm V1**

如果你部署的是 VL 模型(Qwen3.5 原生就是 VL 模型)，可能会看到：

```
Flash Comm V1 is not supported for VL models
```

这时候加上：

```
environment:
  VLLM_ASCEND_ENABLE_FLASHCOMM1: "0"
```