跳转至

MiniMax-M3

1 简介

MiniMax-M3 是一个支持文本、图像和视频输入的多模态大型语言模型。在 Ascend 上,它支持 A2/A3 上的 BF16 和 W8A8、Atlas 800 A3(BF16)和 950DT 产品(MXFP8)上的 Prefill-Decode 分离、思考模式、推理解析、工具调用解析以及多模态输入。

本文档涵盖支持的特性、环境和模型准备、单节点部署、多节点部署、PD 分离、思考与解析器配置、功能验证、精度评估和故障排查。

本文档基于 vLLM-Ascend v0.27.1 版本编写。该模型在此版本中受支持。

2 支持特性

模型支持矩阵请参考 支持特性列表。

功能配置说明请参考 特性指南。

3 前提条件

3.1 模型权重

  • MiniMax-M3(BF16):需要 16 颗 64 GB NPU 芯片。Prefill-Decode 分离使用 2 台 Atlas 800 A3(64GB × 16)。下载模型权重。
  • MiniMax-M3-w8a8(W8A8):至少需要 8 颗 64 GB NPU 芯片。推荐用于 Atlas 800 A3(64GB × 16)和 Atlas 800 A2(64GB × 8)。下载模型权重。
  • MiniMax-M3-MXFP8(MXFP8):用于 950DT 产品(96GB × 8)的 PD 分离(2 节点,1P1D)。下载模型权重。
  • MiniMax-M3-EAGLE3-GQA:EAGLE3 草稿模型,用作投机解码(eagle3 方法)的草稿模型以加速生成。与原始 MiniMax-M3-EAGLE3 相比,该草稿模型采用分组查询注意力(GQA)以提高推理效率并兼容目标模型。下载模型权重。

建议将模型权重放置在共享缓存目录中。

3.2 验证多节点通信(可选)

对于多节点部署,请按照 验证多节点通信环境 验证通信环境。

4 安装

4.1 Docker 镜像安装

您可以使用官方一体化 Docker 镜像。有关可用的镜像标签和已发布版本,请参考 使用 Docker。

步骤 1: 下载最新的 Docker 镜像

docker pull quay.io/ascend/vllm-ascend:{tag}

步骤 2: 启动 Docker 容器

根据您的硬件平台选择对应的 docker run 命令:

# Set the vLLM Ascend image name.
export IMAGE=quay.io/ascend/vllm-ascend:{tag}
export NAME=minimax-m3-dev

# Start the container with the variables defined above.
docker run --rm \
--name $NAME \
--net=host \
--shm-size=100g \
--device /dev/davinci0 \
--device /dev/davinci1 \
--device /dev/davinci2 \
--device /dev/davinci3 \
--device /dev/davinci4 \
--device /dev/davinci5 \
--device /dev/davinci6 \
--device /dev/davinci7 \
--device /dev/davinci8 \
--device /dev/davinci9 \
--device /dev/davinci10 \
--device /dev/davinci11 \
--device /dev/davinci12 \
--device /dev/davinci13 \
--device /dev/davinci14 \
--device /dev/davinci15 \
--device /dev/davinci_manager \
--device /dev/devmm_svm \
--device /dev/hisi_hdc \
-v /usr/local/dcmi:/usr/local/dcmi \
-v /usr/local/Ascend/driver/tools/hccn_tool:/usr/local/Ascend/driver/tools/hccn_tool \
-v /usr/local/bin/npu-smi:/usr/local/bin/npu-smi \
-v /usr/local/Ascend/driver/lib64/:/usr/local/Ascend/driver/lib64/ \
-v /usr/local/Ascend/driver/version.info:/usr/local/Ascend/driver/version.info \
-v /etc/ascend_install.info:/etc/ascend_install.info \
-v /root/.cache:/root/.cache \
-it $IMAGE bash
# Set the vLLM Ascend image name.
export IMAGE=quay.io/ascend/vllm-ascend:{tag}
export NAME=minimax-m3-dev

# 950DT products have 8 NPUs and use Device UB.
docker run --rm \
--name $NAME \
--net=host \
--privileged=true \
--shm-size=60g \
--device /dev/davinci_manager \
--device /dev/hisi_hdc \
--device /dev/ummu \
--device /dev/uburma \
--device /dev/davinci0 \
--device /dev/davinci1 \
--device /dev/davinci2 \
--device /dev/davinci3 \
--device /dev/davinci4 \
--device /dev/davinci5 \
--device /dev/davinci6 \
--device /dev/davinci7 \
-v /usr/local/Ascend/driver:/usr/local/Ascend/driver \
-v /usr/local/Ascend/firmware:/usr/local/Ascend/firmware \
-v /usr/local/dcmi:/usr/local/dcmi \
-v /usr/local/bin/npu-smi:/usr/local/bin/npu-smi \
-v /etc/ascend_install.info:/etc/ascend_install.info \
-v /etc/hixlep:/etc/hixlep \
-v /etc/hccn.conf:/etc/hccn.conf \
-v /var/log/npu/:/usr/slog \
-v /root/.cache:/root/.cache \
-it $IMAGE bash

请根据您的环境调整数据卷挂载(例如模型权重和数据集)。在 950DT 产品上,不要添加主机上不存在的 Atlas A3 挂载,例如 /dev/devmm_svm。

预期结果:容器以 Up 状态列出。您还可以在容器内验证 vllm-ascend 版本:

pip show vllm-ascend

预期结果:显示版本信息,与所拉取镜像的版本一致。

5 在线服务部署

使用以下命令启动在线服务:

有关部署示例中使用的标准 vllm serve 参数说明,请参考 vLLM 服务参数文档。有关通过 --additional-config 传入的 Ascend 专用选项,请参考 附加配置。有关 Ascend 专用的环境变量,请参考 环境变量。

5.1 单节点部署

单节点部署在同一节点内同时完成 Prefill 和 Decode。MiniMax-M3(BF16)模型可部署在 1 台 Atlas 800 A3(64GB × 16)上,但对于 A3 系列上的 BF16,建议使用双节点部署;不建议单节点部署。MiniMax-M3-w8a8(W8A8)量化模型建议在 1 台 Atlas 800 A3(64GB × 16)或 1 台 Atlas 800 A2(64GB × 8)上进行单节点部署。MiniMax-M3-MXFP8(MXFP8)量化模型可部署在 1 台 950DT 产品(96GB × 8)上。

Note

--quantization 参数仅对量化权重需要,并且必须与权重变体匹配:

  • BF16 权重(MiniMax-M3):不要添加 --quantization;模型将以浮点格式加载。
  • W8A8 权重(MiniMax-M3-w8a8,ModelSlim 格式):使用 --quantization ascend。vLLM Ascend 可以从检查点文件自动检测该配置,但为清晰起见,建议显式指定。
  • MXFP8 权重(MiniMax-M3-MXFP8):使用 --quantization mxfp8。

添加错误的 --quantization 值(或将其添加到 BF16 权重)会导致检查点被错误解释并加载失败。

export HCCL_OP_EXPANSION_MODE="AIV"
export LD_PRELOAD=/usr/lib/aarch64-linux-gnu/libjemalloc.so.2:$LD_PRELOAD
export PYTORCH_NPU_ALLOC_CONF=expandable_segments:True

vllm serve ${WEIGHT_PATH} \
  --host 0.0.0.0 \
  --port 11223 \
  --served-model-name minimax-m3 \
  --trust-remote-code \
  --max-model-len 43008 \
  --tensor-parallel-size 16 \
  --enable-expert-parallel \
  --max-num-seqs 16 \
  --distributed_executor_backend "mp" \
  --gpu-memory-utilization 0.92 \
  --reasoning-parser minimax_m3 \
  --limit-mm-per-prompt '{"image":1,"video":0}' \
  --enable-chunked-prefill \
  --enable-prefix-caching \
  --additional-config '{
      "enable_cpu_binding": true,
      "enable_flashcomm1": true,
      "ascend_compilation_config": {
      "fuse_norm_quant": false
      },
      "multistream_overlap_shared_expert": true,
      "weight_nz_mode": 2
  }' > ${LOG_PATH} 2>&1 &
export HCCL_OP_EXPANSION_MODE="AIV"
export LD_PRELOAD=/usr/lib/aarch64-linux-gnu/libjemalloc.so.2:$LD_PRELOAD
export PYTORCH_NPU_ALLOC_CONF=expandable_segments:True

vllm serve ${WEIGHT_PATH} \
  --host 0.0.0.0 \
  --port 11223 \
  --served-model-name minimax-m3 \
  --trust-remote-code \
  --quantization ascend \
  --max-model-len 131072 \
  --tensor-parallel-size 4 \
  --data-parallel-size 4 \
  --api-server-count 1 \
  --max-num-batched-tokens 32768 \
  --long-prefill-token-threshold 4096 \
  --enable-expert-parallel \
  --max-num-seqs 32 \
  --distributed_executor_backend "mp" \
  --gpu-memory-utilization 0.92 \
  --reasoning-parser minimax_m3 \
  --limit-mm-per-prompt '{"image":1,"video":0}' \
  --enable-chunked-prefill \
  --enable-prefix-caching \
  --speculative-config '{"model":"${EAGLE3_WEIGHT_PATH}", "method":"eagle3", "num_speculative_tokens":3}' \
  --additional-config '{
      "enable_cpu_binding": true,
      "enable_flashcomm1": true,
      "ascend_compilation_config": {
        "fuse_norm_quant": false
          },
      "multistream_overlap_shared_expert": true,
      "enable_shared_expert_dp": true,
      "weight_nz_mode": 2
  }' > ${LOG_PATH} 2>&1 &
nic_name="xxxx"  # NIC corresponding to local_ip
export GLOO_SOCKET_IFNAME=$nic_name
export HCCL_SOCKET_IFNAME=$nic_name
export HCCL_OP_EXPANSION_MODE="AIV"
export LD_LIBRARY_PATH=/usr/local/Ascend/cann-9.1.0/opp/vendors/experimental_950_transformer/op_api/lib/:${LD_LIBRARY_PATH}
export PYTORCH_NPU_ALLOC_CONF=expandable_segments:True

vllm serve ${WEIGHT_PATH} \
  --host 0.0.0.0 \
  --port 11223 \
  --served-model-name minimax-m3 \
  --trust-remote-code \
  --distributed-executor-backend mp \
  --tensor-parallel-size 4 \
  --data-parallel-size 2 \
  --enable-expert-parallel \
  --dtype bfloat16 \
  --quantization mxfp8 \
  --max-model-len 140000 \
  --max-num-batched-tokens 16384 \
  --kv-cache-dtype fp8 \
  --max-num-seqs 500 \
  --enable-chunked-prefill \
  --enable-prefix-caching \
  --async-scheduling \
  --reasoning-parser minimax_m3 \
  --limit-mm-per-prompt '{"image":1,"video":0}' \
  --gpu-memory-utilization 0.92 \
  --additional-config '{"enable_cpu_binding":true,"ascend_compilation_config":{"fuse_qknorm_rope":false,"fuse_norm_quant":false},"multistream_overlap_shared_expert":true,"enable_shared_expert_dp":true,"enable_flashcomm1":true}' \
  --speculative-config '{"method":"eagle3","model":"${EAGLE3_WEIGHT_PATH}","num_speculative_tokens":3,"kv_cache_dtype": "bfloat16"}' \
  --safetensors-load-strategy prefetch > ${LOG_PATH} 2>&1 &

注意:在上述脚本中,max-num-seqs 表示调度器在单次迭代中可处理的最大序列数。请根据实际业务动态调整 max-num-seqs 参数。

对于纯文本部署,可以省略 --limit-mm-per-prompt。对于多模态部署,请根据实际请求形态配置该参数。例如,对于双图像请求,使用 --limit-mm-per-prompt '{"image":2,"video":0}';对于单视频请求,使用 --limit-mm-per-prompt '{"image":0,"video":1}'。

5.2 多节点部署

在 Ascend A2 服务器上部署浮点模型至少需要两个节点。不建议在 A3 服务器上执行未进行 Prefill–Decode 分离的多节点部署。请根据实际环境更新 WEIGHT_PATH、EAGLE3_WEIGHT_PATH、LOG_PATH、local_ip、node0_ip 和 IFNAME。

在节点 0 上运行以下命令:

    local_ip="${NODE0_IP}"
    node0_ip="${NODE0_IP}"

    export HCCL_IF_IP=$local_ip
    export IFNAME="${NETWORK_INTERFACE}"
    export HCCL_OP_EXPANSION_MODE="AIV"
    export HCCL_SOCKET_IFNAME="$IFNAME"
    export LD_PRELOAD=/usr/lib/aarch64-linux-gnu/libjemalloc.so.2:$LD_PRELOAD
    export GLOO_SOCKET_IFNAME="$IFNAME"
    export PYTORCH_NPU_ALLOC_CONF=expandable_segments:True

    vllm serve ${WEIGHT_PATH} \
      --host 0.0.0.0 \
      --port 11223 \
      --served-model-name minimax-m3 \
      --trust-remote-code \
      --max-model-len 65920 \
      --tensor-parallel-size 8 \
      --enable-expert-parallel \
      --max-num-seqs 32 \
      --data-parallel-size 4 \
      --data-parallel-size-local 2 \
      --data-parallel-start-rank 0 \
      --data-parallel-address $node0_ip \
      --distributed_executor_backend "mp" \
      --gpu-memory-utilization 0.92 \
      --reasoning-parser minimax_m3 \
      --limit-mm-per-prompt '{"image":1,"video":0}' \
      --speculative-config '{"model":"${EAGLE3_WEIGHT_PATH}","method":"eagle3","num_speculative_tokens":3}' \
      --enable-chunked-prefill \
      --enable-prefix-caching \
      --additional-config '{"enable_cpu_binding":true, "ascend_compilation_config":{"fuse_norm_quant":false}, "enable_shared_expert_dp":true,"multistream_overlap_shared_expert": true, "weight_nz_mode": 2,"enable_flashcomm1":true}' > ${LOG_PATH} 2>&1 &
    ```

    在节点 1 上运行以下命令:

    ```bash
    local_ip="${NODE1_IP}"
    node0_ip="${NODE0_IP}"

    export HCCL_IF_IP=$local_ip
    export IFNAME="${NETWORK_INTERFACE}"
    export HCCL_OP_EXPANSION_MODE="AIV"
    export HCCL_SOCKET_IFNAME="$IFNAME"
    export LD_PRELOAD=/usr/lib/aarch64-linux-gnu/libjemalloc.so.2:$LD_PRELOAD
    export GLOO_SOCKET_IFNAME="$IFNAME"
    export PYTORCH_NPU_ALLOC_CONF=expandable_segments:True

    vllm serve ${WEIGHT_PATH} \
      --host 0.0.0.0 \
      --port 11223 \
      --served-model-name minimax-m3 \
      --trust-remote-code \
      --headless \
      --max-model-len 65920 \
      --tensor-parallel-size 8 \
      --enable-expert-parallel \
      --max-num-seqs 32 \
      --data-parallel-size 4 \
      --data-parallel-size-local 2 \
      --data-parallel-start-rank 2 \
      --data-parallel-address $node0_ip \
      --distributed_executor_backend "mp" \
      --gpu-memory-utilization 0.92 \
      --reasoning-parser minimax_m3 \
      --limit-mm-per-prompt '{"image":1,"video":0}' \
      --speculative-config '{"model":"${EAGLE3_WEIGHT_PATH}","method":"eagle3","num_speculative_tokens":3}' \
      --enable-chunked-prefill \
      --enable-prefix-caching \
      --additional-config '{"enable_cpu_binding":true, "ascend_compilation_config":{"fuse_norm_quant":false}, "enable_shared_expert_dp":true,"multistream_overlap_shared_expert": true, "weight_nz_mode": 2,"enable_flashcomm1":true}' > ${LOG_PATH} 2>&1 &
    ```

=== "A3 series(W8A8)"

    在节点 0 上运行以下命令:

    ```bash
    local_ip="${NODE0_IP}"
    node0_ip="${NODE0_IP}"

    export HCCL_IF_IP=$local_ip
    export IFNAME="${NETWORK_INTERFACE}"
    export HCCL_OP_EXPANSION_MODE="AIV"
    export HCCL_SOCKET_IFNAME="$IFNAME"
    export LD_PRELOAD=/usr/lib/aarch64-linux-gnu/libjemalloc.so.2:$LD_PRELOAD
    export GLOO_SOCKET_IFNAME="$IFNAME"
    export PYTORCH_NPU_ALLOC_CONF=expandable_segments:True

    vllm serve ${WEIGHT_PATH} \
      --host 0.0.0.0 \
      --port 11223 \
      --served-model-name minimax-m3 \
      --trust-remote-code \
      --quantization ascend \
      --max-model-len 131072 \
      --tensor-parallel-size 4 \
      --enable-expert-parallel \
      --max-num-seqs 32 \
      --data-parallel-size 8 \
      --data-parallel-size-local 4 \
      --data-parallel-start-rank 0 \
      --data-parallel-address $node0_ip \
      --distributed_executor_backend "mp" \
      --gpu-memory-utilization 0.92 \
      --reasoning-parser minimax_m3 \
      --limit-mm-per-prompt '{"image":1,"video":0}' \
      --speculative-config '{"model":"${EAGLE3_WEIGHT_PATH}", "method":"eagle3", "num_speculative_tokens":3}' \
      --enable-chunked-prefill \
      --enable-prefix-caching \
      --additional-config '{"enable_cpu_binding":true, "ascend_compilation_config":{"fuse_norm_quant":false}, "enable_shared_expert_dp":true,"multistream_overlap_shared_expert": true, "weight_nz_mode": 2,"enable_flashcomm1":true}' > ${LOG_PATH} 2>&1 &
    ```

    在节点 1 上运行以下命令:

    ```bash
    local_ip="${NODE1_IP}"
    node0_ip="${NODE0_IP}"

    export HCCL_IF_IP=$local_ip
    export IFNAME="${NETWORK_INTERFACE}"
    export HCCL_OP_EXPANSION_MODE="AIV"
    export HCCL_SOCKET_IFNAME="$IFNAME"
    export LD_PRELOAD=/usr/lib/aarch64-linux-gnu/libjemalloc.so.2:$LD_PRELOAD
    export GLOO_SOCKET_IFNAME="$IFNAME"
    export PYTORCH_NPU_ALLOC_CONF=expandable_segments:True

    vllm serve ${WEIGHT_PATH} \
      --host 0.0.0.0 \
      --port 11223 \
      --served-model-name minimax-m3 \
      --trust-remote-code \
      --quantization ascend \
      --headless \
      --max-model-len 131072 \
      --tensor-parallel-size 4 \
      --enable-expert-parallel \
      --max-num-seqs 32 \
      --data-parallel-size 8 \
      --data-parallel-size-local 4 \
      --data-parallel-start-rank 4 \
      --data-parallel-address $node0_ip \
      --distributed_executor_backend "mp" \
      --gpu-memory-utilization 0.92 \
      --reasoning-parser minimax_m3 \
      --limit-mm-per-prompt '{"image":1,"video":0}' \
      --speculative-config '{"model":"${EAGLE3_WEIGHT_PATH}", "method":"eagle3", "num_speculative_tokens":3}' \
      --enable-chunked-prefill \
      --enable-prefix-caching \
      --additional-config '{"enable_cpu_binding":true, "ascend_compilation_config":{"fuse_norm_quant":false}, "enable_shared_expert_dp":true,"multistream_overlap_shared_expert": true, "weight_nz_mode": 2,"enable_flashcomm1":true}' > ${LOG_PATH} 2>&1 &
    ```

### 5.3 预填充-解码分离 {#5.3-prefill-decode-disaggregation}

下面将展示 MiniMax-M3 在 1P1D 多节点环境下的部署指南,以获得更好的性能。



PD 分离将 Prefill 和 Decode 拆分到不同的服务组。Prefill 节点处理大型 prompt 块,Decode 节点负责 token 生成,代理在两者之间转发请求。使用 Mooncake 进行 KV 缓存传输。有关通用 PD 分离工作流,请参阅 [Mooncake](../features/pd_disaggregation_mooncake_multi_node.md)。

启动方式为:在每个节点上准备 `launch_online_dp.py` 和按角色区分的 `run_dp_template.sh`,然后在每个引擎打印 `Application startup complete` 后启动负载均衡代理。下面的启动器在仓库示例基础上扩展了 `--pp-size`:在 A3 上,Prefill 使用流水线并行(`PP=2`),将 60 个 transformer 层按 `30,30` 划分;而 950DT 产品的 MXFP8 启动在两个角色上均使用 `PP=1` 和 `DP=2`。每个 DP rank 占用 `tp_size * pp_size` 个 NPU。

**常见问题提示:** 对于 PD 分离问题(例如 KV 传输超时或 Mooncake 连接错误),请参阅 [公共常见问题](../../faqs.md)。对于 MiniMax 特有问题,请参阅 [第 10 章常见问题](#10-faq)。

开始之前,请在每个节点上准备脚本 `launch_online_dp.py`:

```python
import argparse
import multiprocessing
import os
import subprocess
import sys


def parse_args():
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "--dp-size",
        type=int,
        required=True,
        help="Data parallel size."
    )
    parser.add_argument(
        "--tp-size",
        type=int,
        default=1,
        help="Tensor parallel size."
    )
    parser.add_argument(
        "--pp-size",
        type=int,
        default=1,
        help="Pipeline parallel size."
    )
    parser.add_argument(
        "--dp-size-local",
        type=int,
        default=-1,
        help="Local data parallel size."
    )
    parser.add_argument(
        "--dp-rank-start",
        type=int,
        default=0,
        help="Starting rank for data parallel."
    )
    parser.add_argument(
        "--dp-address",
        type=str,
        required=True,
        help="IP address for data parallel master node."
    )
    parser.add_argument(
        "--dp-rpc-port",
        type=str,
        default="12321",
        help="Port for data parallel master node."
    )
    parser.add_argument(
        "--vllm-start-port",
        type=int,
        default=8000,
        help="Starting port for the engine."
    )
    return parser.parse_args()


args = parse_args()
dp_size = args.dp_size
tp_size = args.tp_size
pp_size = args.pp_size
dp_size_local = args.dp_size_local
if dp_size_local == -1:
    dp_size_local = dp_size
dp_rank_start = args.dp_rank_start
dp_address = args.dp_address
dp_rpc_port = args.dp_rpc_port
vllm_start_port = args.vllm_start_port
gpus_per_dp_rank = tp_size * pp_size


def run_command(visible_devices, dp_rank, vllm_engine_port):
    command = [
        "bash",
        "./run_dp_template.sh",
        visible_devices,
        str(vllm_engine_port),
        str(dp_size),
        str(dp_rank),
        dp_address,
        dp_rpc_port,
        str(tp_size),
        str(pp_size),
    ]
    subprocess.run(command, check=True)


if __name__ == "__main__":
    template_path = "./run_dp_template.sh"
    if not os.path.exists(template_path):
        print(f"Template file {template_path} does not exist.")
        sys.exit(1)

    processes = []
    for i in range(dp_size_local):
        dp_rank = dp_rank_start + i
        vllm_engine_port = vllm_start_port + i
        visible_devices = ",".join(
            str(x) for x in range(i * gpus_per_dp_rank, (i + 1) * gpus_per_dp_rank)
        )
        process = multiprocessing.Process(
            target=run_command,
            args=(visible_devices, dp_rank, vllm_engine_port),
        )
        processes.append(process)
        process.start()

    for process in processes:
        process.join()

launch_online_dp.py 会将可见设备、端口、DP 规模、DP rank、DP 地址、DP RPC 端口、TP 规模和 PP 规模作为 $1 到 $8 传递。

然后在每个节点上准备 run_dp_template.sh 并启动引擎。

Prefill-Decode 分离可部署在 2 台 Atlas 800 A3(64GB × 16)上,用于 MiniMax-M3-w8a8(W8A8),并搭配 MiniMax-M3-EAGLE3-GQA。

显式声明该限制(--limit-mm-per-prompt '{"image":1,"video":0}')可改善调度器侧内存规划和端到端吞吐量。请根据实际请求形态调整(例如,双图像请求使用 {"image":2,"video":0},单视频请求使用 {"image":0,"video":1});对于纯文本部署,可以省略该参数。

  1. Prefill 节点
unset http_proxy https_proxy ftp_proxy

nic_name="xxxx"                 # NIC corresponding to local_ip
local_ip="xxxx"                 # Prefill node IP
model_path="xxxx"               # MiniMax-M3 model path
EAGLE3_WEIGHT_PATH="xxxx"       # MiniMax-M3-EAGLE3-GQA path

export VLLM_PP_LAYER_PARTITION="30,30"
export HCCL_BUFFSIZE=1024
export HCCL_IF_IP=$local_ip
export HCCL_OP_EXPANSION_MODE="AIV"
export ASCEND_RT_VISIBLE_DEVICES=$1
export HCCL_SOCKET_IFNAME=$nic_name
export GLOO_SOCKET_IFNAME=$nic_name
export PYTORCH_NPU_ALLOC_CONF=expandable_segments:True

# If Mooncake is installed in a non-standard path, set this before startup.
if [ -n "${MOONCAKE_LIB_DIRS:-}" ]; then
    export LD_LIBRARY_PATH="${MOONCAKE_LIB_DIRS}:${LD_LIBRARY_PATH:-}"
fi

vllm serve "$model_path" \
    --host 0.0.0.0 \
    --port $2 \
    --data-parallel-size $3 \
    --data-parallel-rank $4 \
    --data-parallel-address $5 \
    --data-parallel-rpc-port $6 \
    --tensor-parallel-size $7 \
    --pipeline-parallel-size $8 \
    --enforce-eager \
    --distributed-executor-backend mp \
    --served-model-name minimax-m3 \
    --enable-expert-parallel \
    --seed 1024 \
    --max-model-len 133000 \
    --max-num-seqs 32 \
    --max-num-batched-tokens 32768 \
    --long-prefill-token-threshold 2048 \
    --enable-chunked-prefill \
    --enable-prefix-caching \
    --trust-remote-code \
    --quantization ascend \
    --gpu-memory-utilization 0.92 \
    --limit-mm-per-prompt '{"image":1,"video":0}' \
    --reasoning-parser minimax_m3 \
    --additional-config '{"enable_cpu_binding":true,"ascend_compilation_config":{"fuse_norm_quant":false},"multistream_overlap_shared_expert":true,"weight_nz_mode":2,"enable_shared_expert_dp":true,"enable_flashcomm1":true}' \
    --speculative-config '{"method":"eagle3","model":"${EAGLE3_WEIGHT_PATH}","num_speculative_tokens":3}' \
    --kv-transfer-config \
    '{
        "kv_connector":"MooncakeConnectorV1",
        "kv_role":"kv_producer",
        "kv_port":"36000",
        "engine_id":"0",
        "kv_connector_extra_config":{
            "use_ascend_direct":true,
            "prefill":{"dp_size":2,"tp_size":4,"pp_size":2,"pp_layer_partition":"30,30"},
            "decode":{"dp_size":4,"tp_size":4,"pp_size":1}
        }
    }'
  1. 解码节点

    unset http_proxy https_proxy ftp_proxy
    
    nic_name="xxxx"                 # NIC corresponding to local_ip
    local_ip="xxxx"                 # Decode node IP
    model_path="xxxx"               # MiniMax-M3 model path
    EAGLE3_WEIGHT_PATH="xxxx"       # MiniMax-M3-EAGLE3-GQA path
    
    export HCCL_BUFFSIZE=2048
    export HCCL_IF_IP=$local_ip
    export HCCL_OP_EXPANSION_MODE="AIV"
    export ASCEND_RT_VISIBLE_DEVICES=$1
    export HCCL_SOCKET_IFNAME=$nic_name
    export GLOO_SOCKET_IFNAME=$nic_name
    export PYTORCH_NPU_ALLOC_CONF=expandable_segments:True
    
    # If Mooncake is installed in a non-standard path, set this before startup.
    if [ -n "${MOONCAKE_LIB_DIRS:-}" ]; then
        export LD_LIBRARY_PATH="${MOONCAKE_LIB_DIRS}:${LD_LIBRARY_PATH:-}"
    fi
    
    vllm serve "$model_path" \
        --host 0.0.0.0 \
        --port $2 \
        --data-parallel-size $3 \
        --data-parallel-rank $4 \
        --data-parallel-address $5 \
        --data-parallel-rpc-port $6 \
        --tensor-parallel-size $7 \
        --pipeline-parallel-size $8 \
        --enable-expert-parallel \
        --seed 1024 \
        --served-model-name minimax-m3 \
        --reasoning-parser minimax_m3 \
        --distributed-executor-backend mp \
        --max-model-len 133000 \
        --max-num-batched-tokens 32768 \
        --trust-remote-code \
        --quantization ascend \
        --max-num-seqs 64 \
        --gpu-memory-utilization 0.92 \
        --limit-mm-per-prompt '{"image":1,"video":0}' \
        --additional-config '{"enable_cpu_binding":true,"ascend_compilation_config":{"fuse_norm_quant":false},"multistream_overlap_shared_expert":true,"weight_nz_mode":2,"enable_shared_expert_dp":true}' \
        --speculative-config '{"method":"eagle3","model":"${EAGLE3_WEIGHT_PATH}","num_speculative_tokens":3}' \
        --kv-transfer-config \
        '{
            "kv_connector":"MooncakeConnectorV1",
            "kv_role":"kv_consumer",
            "kv_port":"36100",
            "engine_id":"1",
            "kv_connector_extra_config":{
                "use_ascend_direct":true,
                "prefill":{"dp_size":2,"tp_size":4,"pp_size":2,"pp_layer_partition":"30,30"},
                "decode":{"dp_size":4,"tp_size":4,"pp_size":1}
            }
        }'
    

    准备工作完成后,在每个节点上使用以下命令启动服务器:

    1. 预填充节点
    python launch_online_dp.py \
        --dp-size 2 --tp-size 4 --pp-size 2 \
        --dp-size-local 2 --dp-rank-start 0 \
        --dp-address $node_p_ip --dp-rpc-port 6884 \
        --vllm-start-port 31050
    

    这将在端口 31050 和 31051 上启动两个预填充 API 服务器。等待两个 rank 都打印 Application startup complete。

    1. 解码节点
    python launch_online_dp.py \
        --dp-size 4 --tp-size 4 --pp-size 1 \
        --dp-size-local 4 --dp-rank-start 0 \
        --dp-address $node_d_ip --dp-rpc-port 5964 \
        --vllm-start-port 31060
    

    这将在端口 31060 到 31063 上启动四个解码 API 服务器。

    要设置请求转发,请在能够访问所有预填充和解码 API 端口的节点上运行以下脚本。你可以从仓库的示例中获取代理程序:load_balance_proxy_server_example.py。对于 A3 1P1D,代理会将请求转发到 2 个预填充 rank 和 4 个解码 rank。

    unset http_proxy
    unset https_proxy
    unset ftp_proxy
    
    python load_balance_proxy_server_example.py \
    --port 8009 \
    --host $node_p_ip \
    --prefiller-hosts \
        $node_p_ip $node_p_ip \
    --prefiller-ports \
        31050 31051 \
    --decoder-hosts \
        $node_d_ip $node_d_ip $node_d_ip $node_d_ip \
    --decoder-ports \
        31060 31061 31062 31063
    

    然后服务可通过 <proxy_ip>:8009 访问。对于 PD 分离,请在第 7 节中使用此代理端点。

=== "950DT 产品" {#950dt-products}

对于 `MiniMax-M3-MXFP8` 配合 `MiniMax-M3-EAGLE3-GQA`,预填充-解码分离可以部署在 2 个 950DT 产品(96GB × 8)上。在容器中挂载 `/etc/hixlep/`,用于 UBOE / Ascend 直接 KV 传输。

1. 预填充节点

```bash
unset ftp_proxy https_proxy http_proxy all_proxy
unset FTP_PROXY HTTPS_PROXY HTTP_PROXY ALL_PROXY

nic_name="xxxx"                 # NIC corresponding to local_ip
local_ip="xxxx"                 # Prefill node IP
model_path="xxxx"               # MiniMax-M3-MXFP8 model path
EAGLE3_WEIGHT_PATH="xxxx"       # MiniMax-M3-EAGLE3-GQA path

export HCCL_BUFFSIZE=256
export HCCL_IF_IP=$local_ip
export HCCL_OP_EXPANSION_MODE="AIV"
export ASCEND_RT_VISIBLE_DEVICES=$1
export HCCL_SOCKET_IFNAME=$nic_name
export GLOO_SOCKET_IFNAME=$nic_name
export PYTORCH_NPU_ALLOC_CONF=expandable_segments:True

export LD_LIBRARY_PATH=/usr/local/Ascend/ascend-toolkit/latest/lib64:$LD_LIBRARY_PATH
export LD_LIBRARY_PATH=/usr/local/Ascend/ascend-toolkit/latest/python/site-packages/mooncake:$LD_LIBRARY_PATH

vllm serve "$model_path" \
    --host 0.0.0.0 \
    --port $2 \
    --data-parallel-size $3 \
    --data-parallel-rank $4 \
    --data-parallel-address $5 \
    --data-parallel-rpc-port $6 \
    --tensor-parallel-size $7 \
    --pipeline-parallel-size $8 \
    --served-model-name minimax-m3 \
    --trust-remote-code \
    --dtype bfloat16 \
    --max-num-seqs 128 \
    --max-num-batched-tokens 32768 \
    --max-model-len 133000 \
    --enable-expert-parallel \
    --quantization mxfp8 \
    --gpu-memory-utilization 0.92 \
    --distributed-executor-backend mp \
    --kv-cache-dtype fp8 \
    --reasoning-parser minimax_m3 \
    --safetensors-load-strategy prefetch \
    --speculative-config '{"method":"eagle3","model":"${EAGLE3_WEIGHT_PATH}","num_speculative_tokens":3,"kv_cache_dtype":"bfloat16"}' \
    --enforce-eager \
    --enable-chunked-prefill \
    --enable-prefix-caching \
    --additional-config '{"enable_cpu_binding":true,"ascend_compilation_config":{"fuse_qknorm_rope":true,"fuse_norm_quant":true},"multistream_overlap_shared_expert":true,"enable_shared_expert_dp":true,"enable_flashcomm1":true}' \
    --kv-transfer-config \
    '{
        "kv_connector":"MooncakeConnectorV1",
        "kv_role":"kv_producer",
        "kv_port":"30000",
        "engine_id":"0",
        "kv_connector_extra_config":{
            "use_ascend_direct":true,
            "ascend_local_comm_res_path":"/etc/hixlep",
            "prefill":{"dp_size":2,"tp_size":4,"pp_size":1},
            "decode":{"dp_size":2,"tp_size":4,"pp_size":1}
        }
    }'
```
  1. Decode 节点

    unset ftp_proxy https_proxy http_proxy all_proxy
    unset FTP_PROXY HTTPS_PROXY HTTP_PROXY ALL_PROXY
    
    nic_name="xxxx"                 # NIC corresponding to local_ip
    local_ip="xxxx"                 # Decode node IP
    model_path="xxxx"               # MiniMax-M3-MXFP8 model path
    EAGLE3_WEIGHT_PATH="xxxx"       # MiniMax-M3-EAGLE3-GQA path
    
    export HCCL_BUFFSIZE=2048
    export HCCL_IF_IP=$local_ip
    export HCCL_OP_EXPANSION_MODE="AIV"
    export HCCL_SOCKET_IFNAME=$nic_name
    export GLOO_SOCKET_IFNAME=$nic_name
    export ASCEND_RT_VISIBLE_DEVICES=$1
    export LD_LIBRARY_PATH=/usr/local/Ascend/ascend-toolkit/latest/lib64:$LD_LIBRARY_PATH
    export LD_LIBRARY_PATH=/usr/local/Ascend/ascend-toolkit/latest/python/site-packages/mooncake:$LD_LIBRARY_PATH
    export PYTORCH_NPU_ALLOC_CONF=expandable_segments:True
    
    vllm serve "$model_path" \
        --host 0.0.0.0 \
        --port $2 \
        --data-parallel-size $3 \
        --data-parallel-rank $4 \
        --data-parallel-address $5 \
        --data-parallel-rpc-port $6 \
        --tensor-parallel-size $7 \
        --pipeline-parallel-size $8 \
        --enable-expert-parallel \
        --seed 1024 \
        --served-model-name minimax-m3 \
        --reasoning-parser minimax_m3 \
        --distributed-executor-backend mp \
        --max-model-len 133000 \
        --max-num-batched-tokens 32768 \
        --trust-remote-code \
        --max-num-seqs 256 \
        --gpu-memory-utilization 0.92 \
        --dtype bfloat16 \
        --quantization mxfp8 \
        --kv-cache-dtype fp8 \
        --speculative-config '{"method":"eagle3","model":"${EAGLE3_WEIGHT_PATH}","num_speculative_tokens":3,"kv_cache_dtype":"bfloat16"}' \
        --additional-config '{"enable_cpu_binding":true,"ascend_compilation_config":{"fuse_norm_quant":false},"multistream_overlap_shared_expert":true,"enable_shared_expert_dp":true}' \
        --kv-transfer-config \
        '{
            "kv_connector":"MooncakeConnectorV1",
            "kv_role":"kv_consumer",
            "kv_port":"26900",
            "engine_id":"1",
            "kv_connector_extra_config":{
                "use_ascend_direct":true,
                "ascend_local_comm_res_path":"/etc/hixlep",
                "prefill":{"dp_size":2,"tp_size":4,"pp_size":1},
                "decode":{"dp_size":2,"tp_size":4,"pp_size":1}
            }
        }'
    

    准备工作完成后,在每个节点上使用以下命令启动服务器:

    1. Prefill 节点
    python launch_online_dp.py \
        --dp-size 2 --tp-size 4 --pp-size 1 \
        --dp-size-local 2 --dp-rank-start 0 \
        --dp-address $node_p_ip --dp-rpc-port 6884 \
        --vllm-start-port 31050
    

    这将启动两个 Prefill API 服务器,端口为 31050 和 31051。等待两个 rank 都打印 Application startup complete。

    1. Decode 节点
    python launch_online_dp.py \
        --dp-size 2 --tp-size 4 --pp-size 1 \
        --dp-size-local 2 --dp-rank-start 0 \
        --dp-address $node_d_ip --dp-rpc-port 5964 \
        --vllm-start-port 31060
    

    这将启动两个 Decode API 服务器,端口为 31060 和 31061。

    要设置请求转发,请在一个能够访问所有 Prefill 和 Decode API 端口的节点上运行以下脚本。你可以在仓库的 examples 中获取代理程序:load_balance_proxy_server_example.py。对于 950DT 产品 1P1D,代理会将请求转发到 2 个 Prefill rank 和 2 个 Decode rank。

    unset ftp_proxy
    unset https_proxy
    unset http_proxy
    
    python load_balance_proxy_server_example.py \
    --port 8009 \
    --host $node_p_ip \
    --prefiller-hosts \
        $node_p_ip $node_p_ip \
    --prefiller-ports \
        31050 31051 \
    --decoder-hosts \
        $node_d_ip $node_d_ip \
    --decoder-ports \
        31060 31061
    

    之后,服务可通过 <proxy_ip>:8009 访问。对于 PD 分离,请在第 7 节中使用此代理端点。

关键参数说明:

launch_online_dp.py 参数:

参数 类型 是否必填 默认值 描述
--dp-size int 是 - 数据并行大小(所有节点上 DP rank 的总数)。
--tp-size int 否 1 每个 DP rank 内的张量并行大小。
--pp-size int 否 1 每个 DP rank 内的流水线并行大小。每个 rank 占用 tp_size * pp_size 个 NPU。
--dp-size-local int 否 (与 --dp-size 相同) 当前节点上的 DP rank 数量。
--dp-rank-start int 否 0 本节点上数据并行 rank 的起始 rank 偏移。
--dp-address str 是 - 数据并行主节点的 IP 地址。
--dp-rpc-port str 否 12321 数据并行主通信的 RPC 端口。
--vllm-start-port int 否 8000 本节点上每个 vLLM 引擎实例的起始端口。每个 DP rank 的引擎端口 = vllm_start_port + 本地 rank 索引。

Prefill 节点特定配置:

  • --pipeline-parallel-size(A3 Prefill:2):将 MiniMax-M3 的 60 层分配到两个流水线阶段。A3 会设置 VLLM_PP_LAYER_PARTITION=30,30,并将 pp_layer_partition 写入 Mooncake 额外配置。950DT 产品启动在 Prefill 和 Decode 上都使用 --pp-size 1(不使用流水线并行),因此无需层分区。
  • --enforce-eager:Prefill 节点不捕获 CUDA/ACL 图。
  • --speculative-config '{"method":"eagle3", ...}':启用 MiniMax-M3-EAGLE3-GQA 草稿模型。不要将其替换为 GLM MTP 选项。

Decode 节点特定配置:

  • --max-num-seqs 256:经验证的 950DT 产品 1P1D 启动所使用的 Decode 并发数。A3 使用 64。

Mooncake KV 传输配置(--kv-transfer-config):

  • "kv_connector": "MooncakeConnectorV1":使用 Mooncake 作为 prefill 节点和 decode 节点之间的 KV 缓存传输连接器。
  • "kv_role": "kv_producer" / "kv_consumer":prefill 节点使用 kv_producer,decode 节点使用 kv_consumer。
  • "kv_port":Mooncake KV 传输端口。prefill 和 decode 需使用不同端口。经验证的值为 A3 36000/36100,950DT 产品 30000/26900。
  • "use_ascend_direct": true:为 KV 缓存启用 Ascend 直接传输。
  • "ascend_local_comm_res_path": "/etc/hixlep"(仅限 950DT 产品):950DT 产品上 UBOE / Ascend 直接通信所需。
  • "prefill" / "decode" 部分:dp_size、tp_size 和 pp_size 必须与两个节点上的实际全局布局一致。A3 使用 prefill: dp2 tp4 pp2 和 decode: dp4 tp4 pp1。950DT 产品使用 prefill: dp2 tp4 pp1 和 decode: dp2 tp4 pp1。

请求转发(代理):

  • 在启动代理之前,请等待所有 Prefill 和 Decode rank 打印 Application startup complete。
  • 代理将每个 prefill 引擎端点和每个 decode 引擎端点映射到端口 8009 上的单一入口点。
  • 如果请求到达代理但没有返回输出,请检查代理主机列表是否包含所有健康的 Prefill 和 Decode 端口,并且在上一次运行后两个节点仍有空闲 NPU 内存。

有关上述环境变量的进一步说明和限制,请参阅 envs.py。

5.4 Prefill-Decode 分离与 KV Cache 池

本节基于 第 5.3 节。复用相同的拓扑、launch_online_dp.py、代理映射以及 MiniMax vllm serve 标志。Prefill 和 Decode 切换到 MultiConnector,以便实时 P→D KV 传输和 Mooncake KV Cache 池协同工作:

  • MooncakeConnectorV1 实时将 KV 从 Prefill 传输到 Decode(与第 5.3 节中的角色相同)。
  • AscendStoreConnector 将 KV 存储在 Mooncake KV Cache 池中,以便后续具有相同前缀的 Prefill 可以命中池,而无需重新计算。
  • 默认情况下,Prefill 节点执行池查找、加载和写入。

有关后端选择、mooncake.json、Mooncake Master、驱逐和租户选项,请参阅 KV Cache 池部署指南。有关池化所需的硬件/通信环境变量,请参阅 环境变量说明。有关 MiniMax 特定问题,请参阅 第 10 章常见问题。

与第 5.3 节中的普通 PD 相比,请注意以下仅适用于池化的要求:

项目 普通 PD(5.3) 池化 PD(本节)
连接器 单个 MooncakeConnectorV1 MultiConnector 封装 MooncakeConnectorV1 + AscendStoreConnector
Mooncake Master 不需要 必须在 Decode / Prefill 之前启动 mooncake_master
mooncake.json 不需要 每个 rank 都需要;Prefill 提供内存,Decode 设置 global_segment_size=0
engine_id / lookup_rpc_port 固定示例 ID 可以 必须每个 DP rank 唯一(37000/37100 + DP_RANK),以避免端口 / 引擎冲突
额外环境变量 仅第 5.3 节的 HCCL_* 保留 5.3 环境变量,然后添加来自 kv_pool.md §5.1 的池 fabric/UB 导出
容器挂载 950DT 需要 /etc/hixlep/ 还需挂载 /etc/hccn.conf;在 950DT 上保留 /etc/hixlep/
启动顺序 Decode → Prefill → 代理 Mooncake Master → Decode → Prefill → 代理
验证 仅 P→D KV 传输 在重复前缀预热后,还需检查 Prefill 池 lookup/get/put 命中

5.4.1 先决条件

将主机 HCCN 配置挂载到每个参与池化的容器中:

-v /etc/hccn.conf:/etc/hccn.conf:ro

在 950DT 产品上,还需保留第 5.3 节中的 /etc/hixlep/ 挂载,用于 Ascend 直接 KV 传输。

在每个节点上,将 mooncake.json 放在 run_dp_template.sh 旁边。Prefill 提供池内存;Decode 不提供。将 xxxx 替换为运行 Mooncake Master 的 Prefill 节点 IP。非零的 global_segment_size 必须对齐到 1GB。

A3 Prefill mooncake.json:

{
  "metadata_server": "P2PHANDSHAKE",
  "protocol": "ascend",
  "device_name": "",
  "master_server_address": "xxxx:50088",
  "global_segment_size": "64GB",
  "preferred_segment": true,
  "prefer_alloc_in_same_node": true,
  "enable_ssd_offload": false,
  "tenant_id": "default"
}

950DT Prefill mooncake.json:

{
  "metadata_server": "P2PHANDSHAKE",
  "protocol": "ascend",
  "device_name": "",
  "master_server_address": "xxxx:50088",
  "global_segment_size": "128GB",
  "preferred_segment": true,
  "prefer_alloc_in_same_node": true,
  "enable_ssd_offload": false,
  "tenant_id": "default"
}

两个平台上的 Decode mooncake.json(相同 Master 和 tenant_id,无捐赠段):

{
  "metadata_server": "P2PHANDSHAKE",
  "protocol": "ascend",
  "device_name": "",
  "master_server_address": "xxxx:50088",
  "global_segment_size": 0,
  "preferred_segment": true,
  "prefer_alloc_in_same_node": true,
  "enable_ssd_offload": false,
  "tenant_id": "default"
}

5.4.2 环境变量

从第 5.3 节的环境变量块(HCCL_IF_IP、HCCL/GLOO/TP socket IFNAME、HCCL_BUFFSIZE、HCCL_OP_EXPANSION_MODE、PYTORCH_NPU_ALLOC_CONF、ASCEND_RT_VISIBLE_DEVICES,以及 A3 Prefill VLLM_PP_LAYER_PARTITION="30,30")开始。然后添加以下池化所需变量。根据你的机器和链路类型,从 环境变量说明 中选择硬件/通信导出。

每个 Prefill 和 Decode rank 上通用的池化导出:

export PYTHONHASHSEED=0
export MOONCAKE_CONFIG_PATH=./mooncake.json
# Optional: only if Mooncake is installed in a non-standard path (same as Section 5.3).
if [ -n "${MOONCAKE_LIB_DIRS:-}" ]; then
    export LD_LIBRARY_PATH="${MOONCAKE_LIB_DIRS}:${LD_LIBRARY_PATH:-}"
fi
硬件 依赖项 必需的池化导出 说明
800I/T A3(HCCS,推荐) HDK >= 26.0,或 HDK >= 25.5 且 mooncake >= v0.3.11;CANN >= 9.0.0 export ACL_OP_INIT_MODE=1 和 export ASCEND_ENABLE_USE_FABRIC_MEM=1 保留第 5.3 节的 HCCL_IF_IP / socket IFNAME 导出。
800I/T A3(RoCE)或 800I/T A2 A2:推荐 HDK >= 25.5 export HCCL_INTRA_ROCE_ENABLE=1,外加 HCCL_IF_IP / socket IFNAME,以及 nr_hugepages=200000 使用 KV Cache 池指南中的 RoCE 路径。
950PR/DT(Device UB) HDK >= 25.6 且 mooncake >= v0.3.11;CANN >= 9.1.0 export ASCEND_LOCAL_COMM_RES_PATH=/etc/hixlep/ 和 export ASCEND_LOCAL_COMM_RES='{"version":"1.3"}';unset ASCEND_GLOBAL_RESOURCE_CONFIG 挂载 /etc/hixlep/。若改用 UBOE,请按 KV Cache 池指南中的说明使用 ASCEND_GLOBAL_RESOURCE_CONFIG。
硬件 依赖项 必需的池化导出项 备注

5.4.3 预填充 / 解码脚本

复用第 5.3 节的 launch_online_dp.py。将每个角色的 run_dp_template.sh 替换为下面的池化版本。保持第 5.3 节 vllm serve 的参数风格;仅 --kv-transfer-config 切换为 MultiConnector。根据 $4(DP rank)展开 engine_id 和 lookup_rpc_port — 不要在不同 rank 中硬编码相同值。

在 2 台 Atlas 800 A3(64GB × 16)上,针对 MiniMax-M3-w8a8(W8A8)并使用 MiniMax-M3-EAGLE3-GQA,实现带 KV 缓存池的预填充-解码分离。

  1. 预填充节点
unset http_proxy https_proxy ftp_proxy

nic_name="xxxx"                 # NIC corresponding to local_ip
local_ip="xxxx"                 # Prefill node IP
model_path="xxxx"               # MiniMax-M3 model path
EAGLE3_WEIGHT_PATH="xxxx"       # MiniMax-M3-EAGLE3-GQA path

export VLLM_PP_LAYER_PARTITION="30,30"
export HCCL_BUFFSIZE=1024
export HCCL_IF_IP=$local_ip
export HCCL_OP_EXPANSION_MODE="AIV"
export ASCEND_RT_VISIBLE_DEVICES=$1
export HCCL_SOCKET_IFNAME=$nic_name
export GLOO_SOCKET_IFNAME=$nic_name
export PYTORCH_NPU_ALLOC_CONF=expandable_segments:True
export PYTHONHASHSEED=0

# Pooling extras on top of Section 5.3 (kv_pool.md §5.1, A3 HCCS)
export ACL_OP_INIT_MODE=1
export ASCEND_ENABLE_USE_FABRIC_MEM=1
export MOONCAKE_CONFIG_PATH=./mooncake.json
if [ -n "${MOONCAKE_LIB_DIRS:-}" ]; then
    export LD_LIBRARY_PATH="${MOONCAKE_LIB_DIRS}:${LD_LIBRARY_PATH:-}"
fi

vllm serve "$model_path" \
    --host 0.0.0.0 \
    --port $2 \
    --data-parallel-size $3 \
    --data-parallel-rank $4 \
    --data-parallel-address $5 \
    --data-parallel-rpc-port $6 \
    --tensor-parallel-size $7 \
    --pipeline-parallel-size $8 \
    --enforce-eager \
    --distributed-executor-backend mp \
    --served-model-name minimax-m3 \
    --enable-expert-parallel \
    --seed 1024 \
    --max-model-len 133000 \
    --max-num-seqs 32 \
    --max-num-batched-tokens 32768 \
    --long-prefill-token-threshold 2048 \
    --enable-chunked-prefill \
    --enable-prefix-caching \
    --trust-remote-code \
    --quantization ascend \
    --gpu-memory-utilization 0.92 \
    --reasoning-parser minimax_m3 \
    --additional-config '{"enable_cpu_binding":true,"ascend_compilation_config":{"fuse_norm_quant":false},"multistream_overlap_shared_expert":true,"weight_nz_mode":2,"enable_shared_expert_dp":true,"enable_flashcomm1":true}' \
    --speculative-config '{"method":"eagle3","model":"${EAGLE3_WEIGHT_PATH}","num_speculative_tokens":3}' \
    --kv-transfer-config \
    '{
        "kv_connector":"MultiConnector",
        "kv_role":"kv_producer",
        "engine_id":"minimax-m3-prefill-dp'"$4"'",
        "kv_connector_extra_config":{
            "connectors":[
                {
                    "kv_connector":"MooncakeConnectorV1",
                    "kv_buffer_device":"npu",
                    "kv_role":"kv_producer",
                    "kv_port":"36000",
                    "kv_connector_extra_config":{
                        "use_ascend_direct":true,
                        "prefill":{"dp_size":2,"tp_size":4,"pp_size":2,"pp_layer_partition":"30,30"},
                        "decode":{"dp_size":4,"tp_size":4,"pp_size":1}
                    }
                },
                {
                    "kv_connector":"AscendStoreConnector",
                    "kv_role":"kv_producer",
                    "kv_connector_extra_config":{
                        "backend":"mooncake",
                        "lookup_rpc_port":'$((37000 + $4))'
                    }
                }
            ]
        }
    }'
  1. 解码节点
unset http_proxy https_proxy ftp_proxy

nic_name="xxxx"                 # NIC corresponding to local_ip
local_ip="xxxx"                 # Decode node IP
model_path="xxxx"               # MiniMax-M3 model path
EAGLE3_WEIGHT_PATH="xxxx"       # MiniMax-M3-EAGLE3-GQA path

export HCCL_BUFFSIZE=2048
export HCCL_IF_IP=$local_ip
export HCCL_OP_EXPANSION_MODE="AIV"
export ASCEND_RT_VISIBLE_DEVICES=$1
export HCCL_SOCKET_IFNAME=$nic_name
export GLOO_SOCKET_IFNAME=$nic_name
export PYTORCH_NPU_ALLOC_CONF=expandable_segments:True
export PYTHONHASHSEED=0

# Pooling extras on top of Section 5.3 (kv_pool.md §5.1, A3 HCCS)
export ACL_OP_INIT_MODE=1
export ASCEND_ENABLE_USE_FABRIC_MEM=1
export MOONCAKE_CONFIG_PATH=./mooncake.json
if [ -n "${MOONCAKE_LIB_DIRS:-}" ]; then
    export LD_LIBRARY_PATH="${MOONCAKE_LIB_DIRS}:${LD_LIBRARY_PATH:-}"
fi

vllm serve "$model_path" \
    --host 0.0.0.0 \
    --port $2 \
    --data-parallel-size $3 \
    --data-parallel-rank $4 \
    --data-parallel-address $5 \
    --data-parallel-rpc-port $6 \
    --tensor-parallel-size $7 \
    --pipeline-parallel-size $8 \
    --enable-expert-parallel \
    --seed 1024 \
    --served-model-name minimax-m3 \
    --reasoning-parser minimax_m3 \
    --distributed-executor-backend mp \
    --max-model-len 133000 \
    --max-num-batched-tokens 32768 \
    --trust-remote-code \
    --quantization ascend \
    --max-num-seqs 64 \
    --gpu-memory-utilization 0.92 \
    --additional-config '{"enable_cpu_binding":true,"ascend_compilation_config":{"fuse_norm_quant":false},"multistream_overlap_shared_expert":true,"weight_nz_mode":2,"enable_shared_expert_dp":true}' \
    --speculative-config '{"method":"eagle3","model":"${EAGLE3_WEIGHT_PATH}","num_speculative_tokens":3}' \
    --kv-transfer-config \
    '{
        "kv_connector":"MultiConnector",
        "kv_role":"kv_consumer",
        "engine_id":"minimax-m3-decode-dp'"$4"'",
        "kv_connector_extra_config":{
            "connectors":[
                {
                    "kv_connector":"MooncakeConnectorV1",
                    "kv_buffer_device":"npu",
                    "kv_role":"kv_consumer",
                    "kv_port":"36100",
                    "kv_connector_extra_config":{
                        "use_ascend_direct":true,
                        "prefill":{"dp_size":2,"tp_size":4,"pp_size":2,"pp_layer_partition":"30,30"},
                        "decode":{"dp_size":4,"tp_size":4,"pp_size":1}
                    }
                },
                {
                    "kv_connector":"AscendStoreConnector",
                    "kv_role":"kv_consumer",
                    "kv_connector_extra_config":{
                        "backend":"mooncake",
                        "lookup_rpc_port":'$((37100 + $4))'
                    }
                }
            ]
        }
    }'

A3 上的启动顺序:Mooncake Master → Decode → Prefill → Proxy。使用与 5.3 节 A3 相同的 launch_online_dp.py 和 proxy 命令(--vllm-start-port 31050 / 31060,proxy 位于 8009)。

在 2 个 950DT 产品(96GB × 8)上,为 MiniMax-M3-MXFP8 搭配 MiniMax-M3-EAGLE3-GQA 实现带 KV Cache Pool 的 Prefill-Decode 分离。挂载 /etc/hixlep/ 和 /etc/hccn.conf。将 5.4.1 节中的 Prefill / Decode mooncake.json 放置在 run_dp_template.sh 旁边(Prefill 上的 global_segment_size 为 128GB,Decode 上为 0)。将 master_server_address 设置为 Prefill Mooncake Master,例如 xxxx:50088。

  1. Prefill 节点
unset ftp_proxy https_proxy http_proxy all_proxy
unset FTP_PROXY HTTPS_PROXY HTTP_PROXY ALL_PROXY

nic_name="xxxx"                 # NIC corresponding to local_ip
local_ip="xxxx"                 # Prefill node IP
model_path="xxxx"               # MiniMax-M3-MXFP8 model path
EAGLE3_WEIGHT_PATH="xxxx"       # MiniMax-M3-EAGLE3-GQA path

export HCCL_BUFFSIZE=256
export HCCL_IF_IP=$local_ip
export HCCL_OP_EXPANSION_MODE="AIV"
export ASCEND_RT_VISIBLE_DEVICES=$1
export HCCL_SOCKET_IFNAME=$nic_name
export GLOO_SOCKET_IFNAME=$nic_name
export PYTORCH_NPU_ALLOC_CONF=expandable_segments:True
export PYTHONHASHSEED=0
export LD_LIBRARY_PATH=/usr/local/Ascend/ascend-toolkit/latest/lib64:$LD_LIBRARY_PATH
export LD_LIBRARY_PATH=/usr/local/Ascend/ascend-toolkit/latest/python/site-packages/mooncake:$LD_LIBRARY_PATH

# Pooling extras on top of Section 5.3 (kv_pool.md §5.1, Device UB)
export MOONCAKE_CONFIG_PATH=./mooncake.json
export ASCEND_LOCAL_COMM_RES_PATH=/etc/hixlep/
export ASCEND_LOCAL_COMM_RES='{"version":"1.3"}'
unset ASCEND_GLOBAL_RESOURCE_CONFIG

vllm serve "$model_path" \
  --host 0.0.0.0 \
  --port $2 \
  --data-parallel-size $3 \
  --data-parallel-rank $4 \
  --data-parallel-address $5 \
  --data-parallel-rpc-port $6 \
  --tensor-parallel-size $7 \
  --pipeline-parallel-size $8 \
  --served-model-name minimax-m3 \
  --trust-remote-code \
  --dtype bfloat16 \
  --max-num-seqs 128 \
  --max-num-batched-tokens 32768 \
  --max-model-len 133000 \
  --enable-expert-parallel \
  --quantization mxfp8 \
  --gpu-memory-utilization 0.92 \
  --distributed-executor-backend mp \
  --kv-cache-dtype fp8 \
  --reasoning-parser minimax_m3 \
  --safetensors-load-strategy prefetch \
  --speculative-config '{"method":"eagle3","model":"${EAGLE3_WEIGHT_PATH}","num_speculative_tokens":3,"kv_cache_dtype":"bfloat16"}' \
  --enforce-eager \
  --enable-chunked-prefill \
  --enable-prefix-caching \
  --additional-config '{"enable_cpu_binding":true,"ascend_compilation_config":{"fuse_qknorm_rope":true,"fuse_norm_quant":true},"multistream_overlap_shared_expert":true,"enable_shared_expert_dp":true,"enable_flashcomm1":true}' \
  --kv-transfer-config \
  '{
      "kv_connector":"MultiConnector",
      "kv_role":"kv_producer",
      "engine_id":"minimax-m3-prefill-dp'"$4"'",
      "kv_connector_extra_config":{
          "connectors":[
              {
                  "kv_connector":"MooncakeConnectorV1",
                  "kv_buffer_device":"npu",
                  "kv_role":"kv_producer",
                  "kv_port":"30000",
                  "kv_connector_extra_config":{
                      "use_ascend_direct":true,
                      "ascend_local_comm_res_path":"/etc/hixlep",
                      "prefill":{"dp_size":2,"tp_size":4,"pp_size":1},
                      "decode":{"dp_size":2,"tp_size":4,"pp_size":1}
                  }
              },
              {
                  "kv_connector":"AscendStoreConnector",
                  "kv_role":"kv_producer",
                  "kv_connector_extra_config":{
                      "backend":"mooncake",
                      "lookup_rpc_port":'$((37000 + $4))'
                  }
              }
          ]
      }
  }'
  1. Decode 节点
unset ftp_proxy https_proxy http_proxy all_proxy
unset FTP_PROXY HTTPS_PROXY HTTP_PROXY ALL_PROXY

nic_name="xxxx"                 # NIC corresponding to local_ip
local_ip="xxxx"                 # Decode node IP
model_path="xxxx"               # MiniMax-M3-MXFP8 model path
EAGLE3_WEIGHT_PATH="xxxx"       # MiniMax-M3-EAGLE3-GQA path

export HCCL_BUFFSIZE=2048
export HCCL_IF_IP=$local_ip
export HCCL_OP_EXPANSION_MODE="AIV"
export HCCL_SOCKET_IFNAME=$nic_name
export GLOO_SOCKET_IFNAME=$nic_name
export ASCEND_RT_VISIBLE_DEVICES=$1
export LD_LIBRARY_PATH=/usr/local/Ascend/ascend-toolkit/latest/lib64:$LD_LIBRARY_PATH
export LD_LIBRARY_PATH=/usr/local/Ascend/ascend-toolkit/latest/python/site-packages/mooncake:$LD_LIBRARY_PATH
export PYTORCH_NPU_ALLOC_CONF=expandable_segments:True
export PYTHONHASHSEED=0

# Pooling extras on top of Section 5.3 (kv_pool.md §5.1, Device UB)
export MOONCAKE_CONFIG_PATH=./mooncake.json
export ASCEND_LOCAL_COMM_RES_PATH=/etc/hixlep/
export ASCEND_LOCAL_COMM_RES='{"version":"1.3"}'
unset ASCEND_GLOBAL_RESOURCE_CONFIG

vllm serve "$model_path" \
    --host 0.0.0.0 \
    --port $2 \
    --data-parallel-size $3 \
    --data-parallel-rank $4 \
    --data-parallel-address $5 \
    --data-parallel-rpc-port $6 \
    --tensor-parallel-size $7 \
    --pipeline-parallel-size $8 \
    --enable-expert-parallel \
    --seed 1024 \
    --served-model-name minimax-m3 \
    --reasoning-parser minimax_m3 \
    --distributed-executor-backend mp \
    --max-model-len 133000 \
    --max-num-batched-tokens 32768 \
    --trust-remote-code \
    --max-num-seqs 256 \
    --gpu-memory-utilization 0.92 \
    --dtype bfloat16 \
    --quantization mxfp8 \
    --kv-cache-dtype fp8 \
    --speculative-config '{"method":"eagle3","model":"${EAGLE3_WEIGHT_PATH}","num_speculative_tokens":3,"kv_cache_dtype":"bfloat16"}' \
    --additional-config '{"enable_cpu_binding":true,"ascend_compilation_config":{"fuse_norm_quant":false},"multistream_overlap_shared_expert":true,"enable_shared_expert_dp":true}' \
    --kv-transfer-config \
    '{
        "kv_connector":"MultiConnector",
        "kv_role":"kv_consumer",
        "engine_id":"minimax-m3-decode-dp'"$4"'",
        "kv_connector_extra_config":{
            "connectors":[
                {
                    "kv_connector":"MooncakeConnectorV1",
                    "kv_buffer_device":"npu",
                    "kv_role":"kv_consumer",
                    "kv_port":"26900",
                    "kv_connector_extra_config":{
                        "use_ascend_direct":true,
                        "ascend_local_comm_res_path":"/etc/hixlep",
                        "prefill":{"dp_size":2,"tp_size":4,"pp_size":1},
                        "decode":{"dp_size":2,"tp_size":4,"pp_size":1}
                    }
                },
                {
                    "kv_connector":"AscendStoreConnector",
                    "kv_role":"kv_consumer",
                    "kv_connector_extra_config":{
                        "backend":"mooncake",
                        "lookup_rpc_port":'$((37100 + $4))'
                    }
                }
            ]
        }
    }'

950DT 上的启动顺序:Mooncake Master → Decode → Prefill → Proxy。请使用与第 5.3 节 950DT 相同的 launch_online_dp.py 和代理命令(两个角色均使用 DP2,代理使用 8009)。

5.4.4 启动服务

在每个平台上按以下顺序启动:

Mooncake Master
    ↓
Decode
    ↓
Prefill
    ↓
Proxy (:8009)
  1. 在 Prefill 节点上启动 mooncake_master,并确认端口 50088 可访问:
mooncake_master \
  --port 50088 \
  --eviction_high_watermark_ratio 0.9 \
  --eviction_ratio 0.1 \
  --default_kv_lease_ttl 11000 \
  --enable_offload=false \
  --client_ttl=120
  1. 使用第 5.3 节中适用于你平台的 launch_online_dp.py 命令启动 Decode。等待每个 Decode rank 打印 Application startup complete。

  2. 以相同方式启动 Prefill。等待每个 Prefill rank 打印 Application startup complete。

  3. 启动第 5.3 节的代理。此时服务可通过 <proxy_ip>:8009 访问。在第 7 节中使用此代理端点。

5.4.5 验证

  1. 确认 Mooncake Master 端口 50088 可访问。
  2. 确认每个 Decode 引擎端口就绪,然后确认每个 Prefill 引擎端口就绪。
  3. 仅向端口 8009 上的代理发送请求。
  4. 使用重复前缀请求进行预热,然后再次发送请求,并检查 Prefill 日志中的 KV Pool 查找/获取/写入及命中信息。
  5. 确认 Decode 日志仍显示正常的 Mooncake P→D KV 传输。

5.5 多模态与 ViT DP(可选)

MiniMax-M3 在 Ascend 上支持图像和视频输入。上述部署示例将 --limit-mm-per-prompt '{"image":1,"video":0}' 保留为默认的多模态容量假设,因为其他服务参数是针对单图像路径调优的。

MiniMax-M3 的图像和视频输入共享同一个 Vision Tower。如果服务只需要一种模态,请显式将未使用的模态设置为 0;例如,仅图像服务使用 {"image":1,"video":0},仅视频服务使用 {"image":0,"video":1}。只要图像或视频中任意一种保持启用,共享的 Vision Tower 就会保留。将未使用的模态设置为 0 比省略它更清晰,因为被省略的模态仍可能参与多模态容量和 profiling 规划。

对于 ViT / 多模态编码器部分,支持数据并行执行,并可通过以下方式启用:

--mm-encoder-tp-mode data

默认部署示例未启用该选项,因为它可能增加单卡内存使用。启用 ViT DP 时,请针对目标工作负载重新评估内存相关参数,例如 --max-model-len、--max-num-seqs 和 --gpu-memory-utilization。

对于视频或图像-视频混合请求,请根据实际请求形态调整多模态限制,而不是盲目修改默认模板:

# one video
--limit-mm-per-prompt '{"image":0,"video":1}'

# one image and one video
--limit-mm-per-prompt '{"image":1,"video":1}'

在请求中使用本地媒体路径(例如 file:///path/to/video.mp4)时,请添加显式允许列表路径:

--allowed-local-media-path /

如果未指定采样的视频帧数,vLLM 会使用其默认视频采样策略,默认采样 32 帧。对于快速功能冒烟测试,可以在请求或评估配置中设置更小的帧数,例如 8 或 16。对于基准测试运行,请遵循数据集协议。

在 MiniMax-M3 服务中,不应同时启用 FLASHCOMM1 和仅语言模型模式。FLASHCOMM1 通过 additional_config.enable_flashcomm1 启用,而仅语言模型模式通过 --language-model-only 启用。

# Enable FLASHCOMM1.
--additional-config '{"enable_flashcomm1": true}'

# Enable language-model-only mode.
--language-model-only

VLLM_ASCEND_ENABLE_FLASHCOMM1=1 仅为兼容性保留,但推荐使用 additional_config.enable_flashcomm1。

6 思考与解析器配置

6.1 思考模式

MiniMax-M3 支持三种思考模式,通过 chat_template_kwargs 中的 thinking_mode 控制:

模式 行为 使用场景
enabled 模型在每次响应前都会思考,包括在工具结果之后 复杂推理、智能体
disabled 不思考;模型直接回答 对延迟敏感的轮次
adaptive 模型根据任务决定是否思考(未设置时的默认值) 通用场景

6.1.1 请求示例

禁用思考(curl):

curl http://{ip}:{port}/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "minimax-m3",
    "messages": [{"role": "user", "content": "who are you?"}],
    "max_tokens": 100,
    "stream": false,
    "top_p": 0.95,
    "top_k": 40,
    "temperature": 1.0,
    "chat_template_kwargs": {"thinking_mode": "disabled"}
  }'

根据需要,将 "thinking_mode" 更改为 "enabled" 或 "adaptive"。已弃用的 enable_thinking 参数(等同于 thinking_mode: "enabled")也受到支持。

启用思考(Python SDK):

from openai import OpenAI

client = OpenAI(api_key="EMPTY", base_url="http://localhost:8000/v1")

response = client.chat.completions.create(
    model="minimax-m3",
    messages=[{"role": "user", "content": "Prove there are infinitely many primes."}],
    extra_body={"chat_template_kwargs": {"thinking_mode": "enabled"}},
)
msg = response.choices[0].message
print(getattr(msg, "reasoning", None))  # the <mm:think> block
print(msg.content)                       # the final answer

6.2 推理解析器

MiniMax-M3 推理解析器(--reasoning-parser minimax_m3)会从模型输出中提取思考块 <mm:think>...</mm:think>,并将其作为 reasoning 字段暴露。其余文本作为 content 返回。

6.2.1 服务器配置

--reasoning-parser minimax_m3 标志启用 MiniMax-M3 推理解析器,该解析器使用 <mm:think>...</mm:think> 分隔符将模型输出拆分为推理和正文:

vllm serve ${WEIGHT_PATH} \
  --reasoning-parser minimax_m3 \
  ...

6.2.2 输出格式

MiniMax-M3 使用显式的思考分隔符:

<mm:think>reasoning process...</mm:think>final answer

6.2.3 解析器行为

  • thinking_mode="enabled":聊天模板会在提示词中预填 <mm:think>。生成的文本从推理块内部开始,并在 </mm:think> 之后过渡到内容。
  • thinking_mode="disabled" 或默认:模型输出被视为纯内容。如果出现 <mm:think>,解析器会按分隔符进行拆分。
  • 流式输出:推理和内容通过 DeltaMessage.reasoning 和 DeltaMessage.content 逐 token 增量流式传输。
  • Token 计数:<mm:think> 块内的推理 token 会被正确计数。

6.3 工具调用解析器

MiniMax-M3 使用命名空间分隔的 XML 格式进行工具调用。通过 --tool-parser minimax_m3 启用。

6.3.1 服务端配置

当同时指定 --reasoning-parser minimax_m3 和 --tool-call-parser minimax_m3 时,解析器会自动协同工作,以处理同时包含推理块和工具调用的响应:

vllm serve ${WEIGHT_PATH} \
  --reasoning-parser minimax_m3 \
  --enable-auto-tool-choice \
  --tool-call-parser minimax_m3 \
  ...

6.3.2 工具调用格式

每个结构标签前面都有 ]<]minimax[>[ 命名空间标记:

]<]minimax[>[<tool_call>
]<]minimax[>[<invoke name="create_order">
]<]minimax[>[<user_id>42]<]minimax[>[</user_id>
]<]minimax[>[<shipping>
]<]minimax[>[<city>Singapore]<]minimax[>[</city>
]<]minimax[>[<zip>018956]<]minimax[>[</zip>
]<]minimax[>[</shipping>
]<]minimax[>[</invoke>
]<]minimax[>[</tool_call>

6.3.3 主要特性

  • 递归参数解析:支持嵌套对象和数组(例如,包含 city/zip 的 shipping)。
  • Schema 感知的类型强制转换:根据函数的 JSON Schema 定义,字符串参数值会自动转换为正确的类型(整数、布尔值、对象、数组)。
  • 多次调用:单个 <tool_call> 块可以包含多个 <invoke> 块。
  • 流式输出:随着 <invoke> 块的接收,工具名称和参数片段会增量流式传输。

6.3.4 请求示例(curl)

curl http://{ip}:{port}/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "minimax-m3",
    "messages": [{"role": "user", "content": "What'\''s the weather like in Shanghai?"}],
    "max_tokens": 300,
    "stream": false,
    "tool_choice": "auto",
    "tools": [
        {
            "type": "function",
            "function": {
                "name": "get_weather",
                "description": "Get current weather for a city",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "location": {
                            "type": "string",
                            "description": "City or country name"
                        }
                    },
                    "required": ["location"],
                    "additionalProperties": false
                }
            }
        }
    ],
    "chat_template_kwargs": {"thinking_mode": "disabled"}
  }'

7 功能验证

7.1 文本

curl http://{ip}:{port}/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d @- <<EOF
{
  "model": "minimax-m3",
  "messages": [
    {
      "role": "user",
      "content": "Answer the following multiple choice question. The last line of your response should be of the following format: 'Answer: LETTER' (without quotes) where LETTER is one of ABCD. Think step by step before answering.\n\nA student regrets that he fell asleep during a lecture in electrochemistry, facing the following incomplete statement in a test:\nThermodynamically, oxygen is a …oxidant in basic solutions. Kinetically, oxygen reacts …in acidic solutions.\nWhich combination of weaker/stronger and faster/slower is correct?\n\nA) weaker —faster\nB) stronger —faster\nC) weaker - slower\nD) stronger —slower"
    }
  ],
  "max_tokens": 8000,
  "temperature": 1.0
}
EOF

预期结果:答案为 C。

7.2 单张图片

以启用图片输入的方式启动服务,例如 --limit-mm-per-prompt '{"image":1,"video":0}'。在客户端将 ${IMAGE_PATH} 替换为本地图片路径。

IMAGE_PATH=/path/to/image.jpg
IMAGE_BASE64="$(base64 -w 0 "${IMAGE_PATH}")"

curl http://{ip}:{port}/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d @- <<EOF
{
  "model": "minimax-m3",
  "messages": [
    {
      "role": "user",
      "content": [
        {"type": "image_url", "image_url": {"url": "data:image/jpeg;base64,${IMAGE_BASE64}"}},
        {"type": "text", "text": "Briefly describe this image."}
      ]
    }
  ],
  "max_tokens": 512,
  "temperature": 0
}
EOF

预期结果:HTTP 200 响应,JSON 响应体包含非空的 choices 以及描述图片的生成文本。

7.3 单个视频

以启用视频输入的方式启动服务,例如 --limit-mm-per-prompt '{"image":0,"video":1}'。如果请求使用 file:// 本地视频路径,还需添加 --allowed-local-media-path / 或更受限的允许目录。如果未指定 media_io_kwargs.video.num_frames,vLLM 默认采样 32 帧。

curl http://{ip}:{port}/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "minimax-m3",
    "messages": [
      {
        "role": "user",
        "content": [
          {
            "type": "video_url",
            "video_url": {
              "url": "file:///path/to/video.mp4"
            }
          },
          {
            "type": "text",
            "text": "Briefly describe the main content of this video."
          }
        ]
      }
    ],
    "max_tokens": 512,
    "temperature": 0
  }'

Expected result: HTTP 200 response with a JSON body containing non-empty choices and generated text describing the video content.

7.4 混合图像和视频请求

启动服务时同时启用图像和视频输入。对于以下请求,使用 --limit-mm-per-prompt '{"image":1,"video":1}'。如果请求使用 file:// 本地视频路径,还需添加 --allowed-local-media-path / 或更窄的允许目录。

IMAGE_BASE64="$(base64 -w 0 /path/to/image.jpg)"

curl http://{ip}:{port}/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d @- <<EOF
{
  "model": "minimax-m3",
  "messages": [
    {
      "role": "user",
      "content": [
        {"type": "image_url", "image_url": {"url": "data:image/jpeg;base64,${IMAGE_BASE64}"}},
        {"type": "video_url", "video_url": {"url": "file:///path/to/video.mp4"}},
        {"type": "text", "text": "Describe the image and video separately, and explain whether they are related."}
      ]
    }
  ],
  "max_tokens": 512,
  "temperature": 0
}
EOF

预期结果:HTTP 200 响应,JSON 响应体中包含非空的 choices 以及分别描述图像和视频并解释它们是否相关的生成文本。

8 准确性评估

8.1 使用 AISBench

有关详细说明,请参阅 使用 AISBench 进行准确性评估。

8.2 文本评估

数据集 硬件 分数 max-model-len max-num-seqs max_out_len batch_size generation_kwargs
GSM8K 8 H20 (96G × 8) 96.72 65536 16 49152 16 temperature=1.0, top_p=0.95
GSM8K 8 Atlas 800 A3 (64GB × 16) 96.36 10240 16 9500 20 temperature=1.0, top_p=0.95
AIME2025 8 H20 (96G × 8) 95@repeat4 - - - - -
AIME2025 8 Atlas 800 A3 (64GB × 16) 93.3@repeat2 131072 32 65536 8 temperature=1.0, top_p=0.95
GPQA-Diamond 8 H20 (96G × 8) 92.42 81920 64 75776 8 temperature=0.6, top_p=0.95
GPQA-Diamond 8 Atlas 800 A3 (64GB × 16) 92.42 131072 32 65536 8 temperature=0.6, top_p=0.95
GPQA-Diamond 8 950DT 产品 (96GB × 8) 92.9 133000 128 131072 128 temperature=0.6, top_p=0.95
MMMU-pro 8 950DT 产品 (96GB × 8) 78.9 133000 128 131072 50 temperature=0.6, top_p=0.95

8.3 多模态评估

MiniMax-M3 的多模态准确性使用 AISBench 进行评估。ViT DP 路径是可选的,可以通过在服务命令中添加 --mm-encoder-tp-mode data 来启用,但并非所有多模态准确性运行都需要它。对于视频评估,如果请求或评估配置中未指定帧数,vLLM 默认采样 32 帧。

下面的 Video-MME 结果是在 chunk1 和 chunk2 上测量的,而不是完整数据集。

对于 Video-MME 评估,请运行启用视频输入的 vLLM OpenAI 兼容服务,并使用 AISBench 发送 Video-MME 请求。官方 AISBench 指南可能未将 Video-MME 列为内置示例,因此此处使用的关键 MiniMax-M3 设置如下:

  • 使用 --limit-mm-per-prompt '{"image":0,"video":1}' 提供服务;
  • 不要设置 media_io_kwargs.video.num_frames,以便 vLLM 使用默认的 32 个采样帧;
  • 使用 max-model-len=90112 和 max_out_len=8192;
  • 评估 Video-MME chunk1 和 chunk2,而不是完整数据集。

用于 Video-MME chunk1+chunk2 评估的 AISBench 命令如下:

ais_bench \
  --models vllm_api_general_chat \
  --datasets videomme_subset_1_2.py \
  --mode all \
  --dump-eval-details \
  --merge-ds

videomme_subset_1_2.py 是一个本地 AISBench 数据集配置,派生自原始 Video-MME 配置,例如 videomme_gen.py。它将 path 指向根据本地可用的 chunk1/chunk2 视频从完整 Video-MME 元数据中过滤得到的 parquet 文件,并将 video_path 指向提取的 chunk1/chunk2 .mp4 目录。这可以在保持标准 Video-MME 请求和评分流程的同时,使评估更轻量。

数据集 模态 工具 硬件 ViT DP max-model-len max_out_len 输入配置 generation_kwargs 分数
TextVQA 图像 AISBench GPU 禁用 65536 512 --limit-mm-per-prompt '{"image":1,"video":0}' temperature=1.0, top_p=0.95 70.82
TextVQA 图像 AISBench NPU 禁用 65536 512 --limit-mm-per-prompt '{"image":1,"video":0}' temperature=1.0, top_p=0.95 72.75
Video-MME chunk1+chunk2 视频 AISBench GPU - 90112 8192 --limit-mm-per-prompt '{"image":0,"video":1}',默认 32 帧 temperature=1.0, top_p=0.95 73.41
Video-MME chunk1+chunk2 视频 AISBench NPU - 90112 8192 --limit-mm-per-prompt '{"image":0,"video":1}',默认 32 帧 temperature=1.0, top_p=0.95 74.21

9 性能调优

注意:以下配置已在特定测试环境中验证,仅供参考。最优配置取决于最大输入/输出长度、前缀缓存命中率、精度要求以及部署机器比例等因素。建议参考第 9.2 节,根据实际条件进行调优。

推荐配置与第 5 章“在线服务部署”中指定的配置相同。

9.2 调优指南

9.2.1 通用调优参考

有关通用调优方法,请参阅 公开性能调优文档。

有关详细功能描述,请参阅 功能矩阵。

10 常见问题

  • 问:如何重新安装 vLLM Ascend?

答:使用以下命令重新安装 vLLM Ascend,并使用当前 Python 环境中的依赖项进行构建:

pip install -v --no-build-isolation -e . -i http://mirrors.aliyun.com/pypi/simple --trusted-host mirrors.aliyun.com
  • 问:当未设置 media_io_kwargs.video.num_frames 时,如果视频请求缓慢或超时,应该怎么办?

答:默认情况下,vLLM 在读取视频时会采样 32 帧。MiniMax-M3 每帧会产生大量视觉 Token,因此 32 帧视频会显著增加预填充计算。如果请求缓慢或超时,请显式将 media_io_kwargs.video.num_frames 设置为较小的值,例如 8 或 16 帧:

{
  "media_io_kwargs": {
    "video": {
      "num_frames": 8
    }
  }
}

本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 vl/vllm-ascend