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 镜像
步骤 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 版本:
预期结果:显示版本信息,与拉取的镜像版本一致。
5 在线服务部署¶
使用以下命令启动在线推理服务:
有关部署示例中使用的标准 vllm serve 参数的说明,请参考 vLLM Serving 参数文档。有关通过 --additional-config 传递的昇腾特定选项,请参考附加配置。有关昇腾特定的环境变量,请参考环境变量。
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 多节点部署¶
在昇腾 A2 服务器上部署 BF16 模型至少需要两个节点。不建议在没有 prefill-decode 分离的情况下在 A3 服务器上进行多节点部署。请根据实际环境更新 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 上运行以下命令:
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 &
在节点 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 \
--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 上运行以下命令:
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 Prefill-Decode 分离¶
我们将展示 MiniMax-M3 在多节点环境中采用 1P1D 部署以获得更佳性能的指南。
PD 分离将 Prefill 和 Decode 分离到不同的服务组中。Prefill 节点处理大型提示块,Decode 节点负责 token 生成,代理在两者之间转发请求。使用 Mooncake 进行 KV 缓存传输。通用 PD 分离工作流程请参考 Mooncake。
启动模式为:在每个节点上准备 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。
常见问题提示: 对于 KV 传输超时或 Mooncake 连接错误等 PD 分离问题,请参考公共 FAQ。对于 MiniMax 特有问题,请参考第 10 章 FAQ。
开始之前,请在每个节点上准备脚本 launch_online_dp.py:
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});对于纯文本部署,可以省略此参数。
- Prefill 节点
unset http_proxy https_proxy ftp_proxy
export VLLM_USE_V2_MODEL_RUNNER=1
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 263000 \
--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}
}
}'
- Decode 节点
unset http_proxy https_proxy ftp_proxy
export VLLM_USE_V2_MODEL_RUNNER=1
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 263000 \
--max-num-batched-tokens 32768 \
--trust-remote-code \
--quantization ascend \
--max-num-seqs 128 \
--compilation-config '{"cudagraph_mode":"FULL_DECODE_ONLY"}' \
--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}
}
}'
准备完成后,在每个节点上使用以下命令启动服务:
- Prefill 节点
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 上启动两个 Prefill API 服务。等待两个 rank 均打印 Application startup complete。
- Decode 节点
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 上启动四个 Decode API 服务。
要设置请求转发,请在能够访问所有 Prefill 和 Decode API 端口的节点上运行以下脚本。您可以在仓库的示例中获取代理程序:load_balance_proxy_server_example.py。对于 A3 1P1D,代理将请求转发到 2 个 Prefill rank 和 4 个 Decode 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 节中使用此代理端点。
Prefill-Decode 分离可部署在 2 台 950DT 产品(96GB × 8)上,用于 MiniMax-M3-MXFP8 配合 MiniMax-M3-EAGLE3-GQA。在容器中挂载 /etc/hixlep/ 以进行 UBOE / Ascend 直接 KV 传输。
- 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 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 263000 \
--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}
}
}'
- 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 263000 \
--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}
}
}'
准备完成后,在每个节点上使用以下命令启动服务:
- 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
这将在端口 31050 和 31051 上启动两个 Prefill API 服务。等待两个 rank 均打印 Application startup complete。
- 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
这将在端口 31060 和 31061 上启动两个 Decode API 服务。
要设置请求转发,请在能够访问所有 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 节点专属配置:
--language-model-only(optional): For text-only tests, add this flag as needed based on the available KV cache capacity to avoid reserving memory for multimodal processing. Follow the FLASHCOMM1 compatibility guidance in Section 5.5 when enabling this flag.--pipeline-parallel-size(A3 Prefill:2):将 60 层 MiniMax-M3 拆分到两个流水线阶段。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 节点专属配置:
--language-model-only(optional): For text-only tests, add this flag as needed based on the available KV cache capacity to avoid reserving memory for multimodal processing."max_cudagraph_capture_size"(optional, omitted by default): Limits the maximum decode batch size covered by ACL graph capture and the graph memory reserved for it; the program default is512. With EAGLE3 speculative decoding, each request is expanded to1 + num_speculative_tokenstokens in one decode step (4tokens whennum_speculative_tokens=3). Since DP distributes requests per rank, the per-rank batch size matters: for example, withDP2and--max-num-seqs 256, each DP rank handles 128 requests, producing4 × 128 = 512tokens per step — exactly at the default limit. If concurrency rises to 257, one DP rank handles 129 requests, giving4 × 129 = 516 > 512; batches above 512 skip graph capture and fall back to eager execution, lowering decode throughput. In that case set"max_cudagraph_capture_size":1024in--compilation-config(e.g.,--compilation-config '{"max_cudagraph_capture_size":1024}'). Because a larger capture size reserves additional NPU memory, the configurations in this tutorial keep the default; add this option only when your target concurrency requires it.--max-num-seqs 256: Decode concurrency used by the verified 950DT products 1P1D launch. A3 uses128.
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 请使用不同的端口。已验证的取值为 A336000/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 章 FAQ。
与第 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 → Proxy | Mooncake Master → Decode → Prefill → Proxy |
| 验证 | 仅 P→D KV 传输 | 还需在重复前缀预热后检查 Prefill 池的 lookup/get/put 命中情况 |
5.4.1 前置条件¶
将主机 HCCN 配置挂载到每个参与池化的容器中:
在 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 Prefill / Decode 脚本¶
复用第 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 Cache Pool 的 Prefill-Decode 分离部署。
- Prefill 节点
unset http_proxy https_proxy ftp_proxy
export VLLM_USE_V2_MODEL_RUNNER=1
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 263000 \
--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))'
}
}
]
}
}'
- Decode 节点
unset http_proxy https_proxy ftp_proxy
export VLLM_USE_V2_MODEL_RUNNER=1
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 263000 \
--max-num-batched-tokens 32768 \
--trust-remote-code \
--quantization ascend \
--max-num-seqs 128 \
--compilation-config '{"cudagraph_mode":"FULL_DECODE_ONLY"}' \
--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 和代理命令(--vllm-start-port 31050 / 31060,代理在 8009)。
在 2 台 950DT 产品(96GB × 8)上为 MiniMax-M3-MXFP8 配合 MiniMax-M3-EAGLE3-GQA 进行带 KV Cache 池的 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。
- 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 263000 \
--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))'
}
}
]
}
}'
- 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 263000 \
--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 启动服务¶
在每个平台上按以下顺序启动:
-
在 Prefill 节点上启动
mooncake_master,并确认端口50088可达: -
使用第 5.3 节中适用于您平台的
launch_online_dp.py命令启动 Decode。等待每个 Decode rank 都打印出Application startup complete。 -
以相同方式启动 Prefill。等待每个 Prefill rank 都打印出
Application startup complete。 -
启动第 5.3 节的代理。随后可通过
<proxy_ip>:8009访问该服务。请在第 7 节中使用此代理端点。
5.4.5 验证¶
- 确认 Mooncake Master 端口
50088可访问。 - 确认每个 Decode 引擎端口均已就绪,然后确认每个 Prefill 引擎端口。
- 仅向端口
8009上的代理发送请求。 - 使用重复前缀请求进行预热,然后再次发送,并检查 Prefill 日志中的 KV Pool 查找/获取/写入以及命中信息。
- 确认 Decode 日志仍显示正常的 Mooncake P→D KV 传输。
5.5 多模态与 ViT DP(可选)¶
MiniMax-M3 在昇腾上支持图像和视频输入。上述部署示例将 --limit-mm-per-prompt '{"image":1,"video":0}' 作为默认的多模态容量假设,因为其他服务参数是针对单图像路径调优的。
MiniMax-M3 的图像和视频输入共享同一个 Vision Tower。如果服务只需要一种模态,请将未使用的模态显式设置为 0;例如,仅图像服务使用 {"image":1,"video":0},仅视频服务使用 {"image":0,"video":1}。只要图像或视频任一保持启用,共享的 Vision Tower 就会被保留。将未使用的模态设置为 0 比省略它更清晰,因为省略的模态仍可能参与多模态容量和性能分析规划。
对于 ViT / 多模态编码器部分,支持数据并行执行,可以通过以下方式启用:
默认部署示例中未启用此选项,因为它可能会增加每卡内存使用量。启用 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)时,请添加显式的白名单路径:
如果未指定采样的视频帧数,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
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> 分隔符将模型输出拆分为推理和内容:
6.2.2 输出格式¶
MiniMax-M3 使用显式的思考分隔符:
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)。 - 模式感知类型转换:根据函数的 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
}'
预期结果:HTTP 200 响应,JSON 响应体包含非空的 choices 以及描述该视频内容的生成文本。
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 products (96GB × 8) | 92.9 | 133000 | 128 | 131072 | 128 | temperature=0.6, top_p=0.95 |
| MMMU-pro | 8 950DT products (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 请求和评分流程。
| Dataset | Modality | Tool | Hardware | ViT DP | max-model-len | max_out_len | Input Config | generation_kwargs | Score |
|---|---|---|---|---|---|---|---|---|---|
| TextVQA | Image | AISBench | GPU | disabled | 65536 | 512 | --limit-mm-per-prompt '{"image":1,"video":0}' |
temperature=1.0, top_p=0.95 | 70.82 |
| TextVQA | Image | AISBench | NPU | disabled | 65536 | 512 | --limit-mm-per-prompt '{"image":1,"video":0}' |
temperature=1.0, top_p=0.95 | 72.75 |
| Video-MME chunk1+chunk2 | Video | AISBench | GPU | - | 90112 | 8192 | --limit-mm-per-prompt '{"image":0,"video":1}', default 32 frames |
temperature=1.0, top_p=0.95 | 73.41 |
| Video-MME chunk1+chunk2 | Video | AISBench | NPU | - | 90112 | 8192 | --limit-mm-per-prompt '{"image":0,"video":1}', default 32 frames |
temperature=1.0, top_p=0.95 | 74.21 |
9 性能调优¶
注意:以下配置在特定测试环境中经过验证,仅供参考。最佳配置取决于最大输入/输出长度、前缀缓存命中率、精度要求以及部署机器比例等因素。建议根据实际情况参考第 9.2 节进行调优。
9.1 推荐配置¶
推荐配置与第 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 帧: