更新时间:2026-03-14 GMT+08:00
分享

准备训练脚本

在VeOmni中不同类型的模型,训练步骤和脚本有所差异。在此分Dense模型,Moe模型,VL模型进行介绍。更多详细内容可以参考官网内容,参数设置可以参考 Arguments API Reference — VeOmni v0.1.0 documentation 获取更多详细信息。

如直接复制本文档提供的命令或脚本,请注意不同操作系统下换行符的差异。可以使用dos2unix xxx.sh命令将脚本内换行符转换为linux系统的换行符。

Dense模型

Qwen3-32B的训练任务为例,准备对应模型的训练脚本,并上传至OBS桶中。

训练启动命令

ModelArts平台,训练任务启动命令如下:

cd /home/ma-user/VeOmni
bash "${input_dir}/train.sh" /home/ma-user/VeOmni/tasks/train_torch.py /home/ma-user/VeOmni/configs/sft/qwen3_sft.yaml \
    --model.model_path "${model_dir}/Qwen3-32B" \
    --data.train_path "${dataset_dir}/tulu-first2000.parquet" \
    --train.data_parallel_mode fsdp2 \
    --train.init_device meta \
    --train.output_dir "${output_dir}/Qwen3-32B-Base-sft-tulu" \
    --train.save_epochs 3 \
    --train.num_train_epochs 3 \
    --train.ulysses_parallel_size 1 \
    --data.max_seq_len 4096 \
    --train.global_batch_size 16 \
    --train.enable_activation_offload true

命令中参数详解如下:

  • ${input_dir} :训练脚本的输入路径。配置obs路径到输入的环境变量input_dir,例如/home/ma-user/modelarts/user-job-dir/veomni/input。
  • ${output_dir} :训练或者格式转化的输出路径。配置obs路径到输出的环境变量output_dir,例如/home/ma-user/modelarts/user-job-dir/veomni/output/veomni-output。
  • ${model_dir}:训练模型的路径。
  • ${dataset_dir} :处理过后的数据集的路径。

如直接复制本脚本,请注意不同操作系统下换行符的差异。可以使用dos2unix xxx.sh命令将脚本内换行符转换为linux系统的换行符。

训练脚本train.sh

Qwen3-32B 为例,需要准备训练启动脚本train.sh。train.sh包含处理多节点通信逻辑、训练任务启动、转化为hf格式模型权重等内容。

train.sh脚本内容如下,脚本中需要修改的参数请参见脚本中的注释。准备好的train.sh脚本放在自己的OBS桶内的相应位置。
#!/bin/bash
set -x
set -o pipefail
export HCCL_IF_BASE_PORT=61000
export HCCL_NPU_SOCKET_PORT_RANGE="61000-61050"
export HCCL_CONNECT_TIMEOUT=7200   # 最大超时时间,可以根据实际情况调整
export HCCL_EXEC_TIMEOUT=7200
unset https_proxy http_proxy proxy ASCEND_RT_VISIBLE_DEVICES
source /usr/local/Ascend/ascend-toolkit/set_env.sh
source /usr/local/Ascend/nnal/atb/set_env.sh
export TOKENIZERS_PARALLELISM=false
export TORCH_NCCL_AVOID_RECORD_STREAMS=1
# 获取本机 IP
get_current_ip() {
    local ip
    ip=$(ip -4 addr | grep -v '127.0.0.1' | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | head -n1 2>/dev/null)
    if [[ -z "$ip" ]]; then
        ip=$(ip -4 addr | grep -v '127.0.0.1' | awk '/inet/ {gsub(/\/.*/,""); print $2}' | head -n1 2>/dev/null)
    fi
    echo "${ip:-}"
}
CURRENT_IP=$(get_current_ip)
export NNODES=${VC_WORKER_NUM:-1}
export MASTER_PORT=${MASTER_PORT:=12345}
export CHECK_LOG_DIR="/home/ma-user/logs"
export CHECK_LOG_PATH="${CHECK_LOG_DIR}/veomni.log" #检查日志路径,无需修改
mkdir -p "$CHECK_LOG_DIR"
# 自动计算 NODE_RANK 和 MASTER_ADDR
if [ "$NNODES" -gt 1 ]; then
    MASTER_ADDR_RAW="${VC_WORKER_HOSTS%%,*}"
    export MASTER_ADDR=$(python3 -c "import socket; print(socket.gethostbyname('${MASTER_ADDR_RAW}'))")
    export NODE_RANK=$(python3 -c "import os, socket; hosts = os.environ.get('VC_WORKER_HOSTS', '').split(','); cur='${CURRENT_IP}'; print(next((i for i, h in enumerate(hosts) if socket.gethostbyname(h.strip()) == cur), 0))")
    echo "[INFO] Multi-Node: Master=$MASTER_ADDR, Rank=$NODE_RANK, Total=$NNODES"
    additional_args="--rdzv_endpoint=${MASTER_ADDR}:${MASTER_PORT}"
else
    export MASTER_ADDR="localhost"
    export NODE_RANK=0
    echo "[INFO] Single-Node Mode"
    additional_args="$additional_args --standalone"
fi
if command -v nvidia-smi &> /dev/null && nvidia-smi --list-gpus &> /dev/null; then
  # GPU
  if [[ -n "${CUDA_VISIBLE_DEVICES}" ]]; then
    NPROC_PER_NODE=${NPROC_PER_NODE:=$(echo "${CUDA_VISIBLE_DEVICES}" | tr ',' '\n' | wc -l)}
  else
    NPROC_PER_NODE=${NPROC_PER_NODE:=$(nvidia-smi --list-gpus | wc -l)}
  fi
  export NCCL_DEBUG=WARN
else
  # NPU
  if [[ -n "${ASCEND_RT_VISIBLE_DEVICES}" ]]; then
    NPROC_PER_NODE=${NPROC_PER_NODE:=$(echo "${ASCEND_RT_VISIBLE_DEVICES}" | tr ',' '\n' | wc -l)}
  else
    NPROC_PER_NODE=${NPROC_PER_NODE:=$(ls -l /dev/davinci* | grep -v "davinci_manager" | wc -l)}
  fi
  # NPU env that may optimize performance
  export PYTORCH_NPU_ALLOC_CONF=${PYTORCH_NPU_ALLOC_CONF:='expandable_segments:True'}
fi
torchrun \
  --nnodes=$NNODES \
  --nproc-per-node=$NPROC_PER_NODE \
  --node-rank=$NODE_RANK \
  $additional_args $@ 2>&1 | tee "$CHECK_LOG_PATH" && \
  echo "Training completes" >> "$CHECK_LOG_PATH"
sleep 60s
tail -n 500 "$CHECK_LOG_PATH" | grep -q -E "Training completes"  || exit 1 #异常状态校验,返回状态码给平台

# 主节点进行hf格式转化
if [ "$NODE_RANK" -eq 0 ]; then
    echo "[Master] Master. Doing Convert ckpt to hf models"
    #此处与平台启动命令的$output_dir/Qwen3-32B-Base-sft-tulu保持一致
    CHECKPOINT_ROOT="$output_dir/Qwen3-32B-Base-sft-tulu/checkpoints"
    #获取global_step最大的一次保存的ckpt结果转化为hf格式
    LATEST_STEP_DIR=$(ls -v "$CHECKPOINT_ROOT" | grep "global_step_" | tail -n 1)
    FULL_LOAD_PATH="${CHECKPOINT_ROOT}/${LATEST_STEP_DIR}"
    echo "[INFO] Detected latest checkpoint: $FULL_LOAD_PATH"
    # 执行ckpt转化为hf格式,model-assets-dir是模型路径,load-dir是训练完保存的dcp路径,save-dir是最后保存的hf路径
    if python /home/ma-user/VeOmni/scripts/merge_dcp_to_hf.py \
        --model-assets-dir "$model_dir/Qwen3-32B" \
        --save-dir "$output_dir/qwen3-32b-saved-hf_ckpt" \
        --load-dir "$FULL_LOAD_PATH"; then
        echo "转换成功"
        echo "All steps completes" >> "$CHECK_LOG_PATH"
    else
        echo "[ERROR] Master. Convert ckpt to hf models FAILED!" >> "$CHECK_LOG_PATH"
        exit 1  # 失败则显式退出
    fi
else
    echo "[Worker $NODE_RANK] Task finished. Waiting for Master to complete conversion..."
    echo "All steps completes" >> "$CHECK_LOG_PATH"
fi
sleep 10s
tail -n 500 "$CHECK_LOG_PATH" | grep -q -E "All steps completes"  && exit 0 || exit 1 #异常状态校验,返回状态码给平台

Moe模型

Qwen3-30B-A3B的训练任务为例,准备对应模型的训练脚本train.sh,并上传至OBS桶中。

训练启动命令

ModelArts平台,Moe模型训练任务启动命令如下:

cd /home/ma-user/VeOmni
python3 /home/ma-user/VeOmni/scripts/moe_ckpt_merge/moe_merge.py \
    --raw_hf_path "${model_dir}/Qwen3-30B-A3B"  \
    --merge_hf_path "${model_dir}/Qwen3-30B-A3B-merge"
bash "${input_dir}/train.sh" /home/ma-user/VeOmni/tasks/train_torch.py /home/ma-user/VeOmni/configs/sft/qwen3_sft.yaml \
    --model.model_path "${model_dir}/Qwen3-30B-A3B-merge" \
    --model.moe_implementation fused \
    --data.train_path "${dataset_dir}/tulu-first2000.parquet" \
    --train.data_parallel_mode fsdp2 \
    --train.init_device meta \
    --train.output_dir "${output_dir}/Qwen3-30B-A3B-Base-sft-tulu" \
    --train.save_epochs 3 \
    --train.num_train_epochs 3 \
    --train.expert_parallel_size 8 \
    --data.max_seq_len 4096 \
    --train.global_batch_size 16

命令中参数详解如下:

  • ${input_dir} :训练脚本的输入路径。配置obs路径到输入的环境变量input_dir,例如/home/ma-user/modelarts/user-job-dir/veomni/input。
  • ${output_dir} :训练或者格式转化的输出路径。配置obs路径到输出的环境变量output_dir,例如/home/ma-user/modelarts/user-job-dir/veomni-moe/output/veomni-output
  • ${model_dir}:训练模型的路径
  • ${dataset_dir}: 处理过后的数据集的路径。

训练脚本

Qwen3-30B-A3B 为例,准备训练启动脚本train.sh,并放在自己的OBS桶内的相应位置。

train.sh包含处理MoE模型专家合并、多节点通信逻辑、训练任务启动、转化为hf格式模型权重、MoE权重专家拆分等内容。

train.sh脚本内容如下,脚本中需要修改的参数请参见脚本中的注释。其中脚本中的注释仅为说明参数含义,实际使用时请去除在每行的\后不要留有空格
#!/bin/bash
set -x
set -o pipefail
export HCCL_IF_BASE_PORT=61000
export HCCL_NPU_SOCKET_PORT_RANGE="61000-61050"
export HCCL_CONNECT_TIMEOUT=7200   # 最大超时时间,可以根据实际情况调整
export HCCL_EXEC_TIMEOUT=7200
unset https_proxy http_proxy proxy ASCEND_RT_VISIBLE_DEVICES
source /usr/local/Ascend/ascend-toolkit/set_env.sh
source /usr/local/Ascend/nnal/atb/set_env.sh
export TOKENIZERS_PARALLELISM=false
export TORCH_NCCL_AVOID_RECORD_STREAMS=1
# 获取本机 IP
get_current_ip() {
    local ip
    ip=$(ip -4 addr | grep -v '127.0.0.1' | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | head -n1 2>/dev/null)
    if [[ -z "$ip" ]]; then
        ip=$(ip -4 addr | grep -v '127.0.0.1' | awk '/inet/ {gsub(/\/.*/,""); print $2}' | head -n1 2>/dev/null)
    fi
    echo "${ip:-}"
}
CURRENT_IP=$(get_current_ip)
export NNODES=${VC_WORKER_NUM:-1}
export MASTER_PORT=${MASTER_PORT:=12345}
export CHECK_LOG_DIR="/home/ma-user/logs"
export CHECK_LOG_PATH="${CHECK_LOG_DIR}/veomni.log" #检查日志路径,无需修改
mkdir -p "$CHECK_LOG_DIR"
# 自动计算 NODE_RANK 和 MASTER_ADDR
if [ "$NNODES" -gt 1 ]; then
    MASTER_ADDR_RAW="${VC_WORKER_HOSTS%%,*}"
    export MASTER_ADDR=$(python3 -c "import socket; print(socket.gethostbyname('${MASTER_ADDR_RAW}'))")
    export NODE_RANK=$(python3 -c "import os, socket; hosts = os.environ.get('VC_WORKER_HOSTS', '').split(','); cur='${CURRENT_IP}'; print(next((i for i, h in enumerate(hosts) if socket.gethostbyname(h.strip()) == cur), 0))")
    echo "[INFO] Multi-Node: Master=$MASTER_ADDR, Rank=$NODE_RANK, Total=$NNODES"
    additional_args="--rdzv_endpoint=${MASTER_ADDR}:${MASTER_PORT}"
else
    export MASTER_ADDR="localhost"
    export NODE_RANK=0
    echo "[INFO] Single-Node Mode"
    additional_args="$additional_args --standalone"
fi
if command -v nvidia-smi &> /dev/null && nvidia-smi --list-gpus &> /dev/null; then
  # GPU
  if [[ -n "${CUDA_VISIBLE_DEVICES}" ]]; then
    NPROC_PER_NODE=${NPROC_PER_NODE:=$(echo "${CUDA_VISIBLE_DEVICES}" | tr ',' '\n' | wc -l)}
  else
    NPROC_PER_NODE=${NPROC_PER_NODE:=$(nvidia-smi --list-gpus | wc -l)}
  fi
  export NCCL_DEBUG=WARN
else
  # NPU
  if [[ -n "${ASCEND_RT_VISIBLE_DEVICES}" ]]; then
    NPROC_PER_NODE=${NPROC_PER_NODE:=$(echo "${ASCEND_RT_VISIBLE_DEVICES}" | tr ',' '\n' | wc -l)}
  else
    NPROC_PER_NODE=${NPROC_PER_NODE:=$(ls -l /dev/davinci* | grep -v "davinci_manager" | wc -l)}
  fi
  # NPU env that may optimize performance
  export PYTORCH_NPU_ALLOC_CONF=${PYTORCH_NPU_ALLOC_CONF:='expandable_segments:True'}
fi
torchrun \
  --nnodes=$NNODES \
  --nproc-per-node=$NPROC_PER_NODE \
  --node-rank=$NODE_RANK \
  $additional_args $@ 2>&1 | tee "$CHECK_LOG_PATH" && \
  echo "Training completes" >> "$CHECK_LOG_PATH"
sleep 30s
tail -n 500 "$CHECK_LOG_PATH" | grep -q -E "Training completes"  || exit 1 #异常状态校验,返回状态码给平台
# 主节点进行hf格式转化
if [ "$NODE_RANK" -eq 0 ]; then
    echo "[Master] Master. Doing Convert ckpt to hf models"
    # 与平台启动命令的$output_dir/Qwen3-30B-A3B-Base-sft-tulu保持一致
    CHECKPOINT_ROOT="$output_dir/Qwen3-30B-A3B-Base-sft-tulu/checkpoints"
    # 获取global_step最大的一次保存的ckpt结果转化为hf格式
    LATEST_STEP_DIR=$(ls -v "$CHECKPOINT_ROOT" | grep "global_step_" | tail -n 1)
    FULL_LOAD_PATH="${CHECKPOINT_ROOT}/${LATEST_STEP_DIR}"
    echo "[INFO] Detected latest checkpoint: $FULL_LOAD_PATH"
    # 执行ckpt转化为hf格式,其中save-dir为hf格式权重的保存路径,包含模型的其他配置文件
    python /home/ma-user/VeOmni/scripts/merge_dcp_to_hf.py \
    --model-assets-dir "$model_dir/Qwen3-30B-A3B" \
    --save-dir "$output_dir/qwen3-30b-A3B-saved-hf_ckpt" \
    --load-dir "$FULL_LOAD_PATH" 2>&1 | tee "$CHECK_LOG_PATH" && \
    echo "Converting ckpt to hf completes" >> "$CHECK_LOG_PATH"
    sleep 5s
    tail -n 500 "$CHECK_LOG_PATH" | grep -q -E "Converting ckpt to hf completes"  || exit 1 #异常状态校验,返回状态码给平台    
    # 对得到的hf权重拆分MoE专家
    sleep 10s
    python /home/ma-user/VeOmni/scripts/moe_ckpt_merge/moe_spilt.py \
    --merge_hf_path "$output_dir/qwen3-30b-A3B-saved-hf_ckpt" \
    --split_hf_path "$output_dir/qwen3-30b-saved-hf_ckpt_split" 2>&1 | tee "$CHECK_LOG_PATH" && \
    echo "All steps completes" >> "$CHECK_LOG_PATH"
else
    echo "[Worker $NODE_RANK] Task finished. Waiting for Master to complete conversion..."
    echo "All steps completes" >> "$CHECK_LOG_PATH"
fi
sleep 30s
tail -n 500 "$CHECK_LOG_PATH" | grep -q -E "All steps completes"  && exit 0 || exit 1 #异常状态校验,返回状态码给平台

VL模型

Qwen3-VL-8B 的训练任务为例,准备对应模型的训练脚本,并上传至OBS桶中。

训练启动命令

ModelArts平台,VL模型训练任务启动命令如下:

cd /home/ma-user/VeOmni
bash "${input_dir}/train.sh" /home/ma-user/VeOmni/tasks/omni/train_qwen_vl.py /home/ma-user/VeOmni/configs/multimodal/qwen3_vl/qwen3_vl_dense.yaml \
    --model.model_path "${model_dir}/Qwen3-VL-8B" \
    --data.train_path "${dataset_dir}/sharegpt4v_instruct_gpt4-vision_cap100k_coco.json" \
    --train.output_dir ${output_dir}/qwen3_vl_dense_sft \
    --data.dataloader_type native \
    --data.datasets_type iterable \
    --data.source_name sharegpt4v_sft \
    --data.num_workers 8 \
    --train.micro_batch_size 1

命令中参数详解如下:

  • ${input_dir} :训练脚本的输入路径。配置obs路径到输入的环境变量input_dir,例如/home/ma-user/modelarts/user-job-dir/veomni-vl/input
  • ${output_dir} :训练或者格式转化的输出路径。配置obs路径到输出的环境变量output_dir,例如/home/ma-user/modelarts/user-job-dir/veomni-vl/output
  • ${model_dir}:训练模型的路径。
  • ${dataset_dir}: 处理过后的数据集的路径。

训练启动脚本

Qwen3-VL-8B 为例,准备训练脚本train.sh,并放在自己的OBS桶内的相应位置。

train.sh是训练启动脚本,包含处理多节点通信逻辑、训练任务启动、转化为hf格式模型权重等内容。

train.sh脚本内容如下,脚本中需要修改的参数请参见脚本中的注释。
#!/bin/bash
set -x
set -o pipefail
export HCCL_IF_BASE_PORT=61000
export HCCL_NPU_SOCKET_PORT_RANGE="61000-61050"
export HCCL_CONNECT_TIMEOUT=7200   # 最大超时时间,可以根据实际情况调整
export HCCL_EXEC_TIMEOUT=7200
unset https_proxy http_proxy proxy ASCEND_RT_VISIBLE_DEVICES
source /usr/local/Ascend/ascend-toolkit/set_env.sh
source /usr/local/Ascend/nnal/atb/set_env.sh
export TOKENIZERS_PARALLELISM=false
export TORCH_NCCL_AVOID_RECORD_STREAMS=1
# 获取本机 IP
get_current_ip() {
    local ip
    ip=$(ip -4 addr | grep -v '127.0.0.1' | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | head -n1 2>/dev/null)
    if [[ -z "$ip" ]]; then
        ip=$(ip -4 addr | grep -v '127.0.0.1' | awk '/inet/ {gsub(/\/.*/,""); print $2}' | head -n1 2>/dev/null)
    fi
    echo "${ip:-}"
}
CURRENT_IP=$(get_current_ip)
export NNODES=${VC_WORKER_NUM:-1}
export MASTER_PORT=${MASTER_PORT:=12345}
export CHECK_LOG_DIR="/home/ma-user/logs"
export CHECK_LOG_PATH="${CHECK_LOG_DIR}/veomni.log" # 检查日志路径,无需修改
mkdir -p "$CHECK_LOG_DIR"
# 自动计算 NODE_RANK 和 MASTER_ADDR
if [ "$NNODES" -gt 1 ]; then
    MASTER_ADDR_RAW="${VC_WORKER_HOSTS%%,*}"
    export MASTER_ADDR=$(python3 -c "import socket; print(socket.gethostbyname('${MASTER_ADDR_RAW}'))")
    export NODE_RANK=$(python3 -c "import os, socket; hosts = os.environ.get('VC_WORKER_HOSTS', '').split(','); cur='${CURRENT_IP}'; print(next((i for i, h in enumerate(hosts) if socket.gethostbyname(h.strip()) == cur), 0))")
    echo "[INFO] Multi-Node: Master=$MASTER_ADDR, Rank=$NODE_RANK, Total=$NNODES"
    additional_args="--rdzv_endpoint=${MASTER_ADDR}:${MASTER_PORT}"
else
    export MASTER_ADDR="localhost"
    export NODE_RANK=0
    echo "[INFO] Single-Node Mode"
    additional_args="$additional_args --standalone"
fi
if command -v nvidia-smi &> /dev/null && nvidia-smi --list-gpus &> /dev/null; then
  # GPU
  if [[ -n "${CUDA_VISIBLE_DEVICES}" ]]; then
    NPROC_PER_NODE=${NPROC_PER_NODE:=$(echo "${CUDA_VISIBLE_DEVICES}" | tr ',' '\n' | wc -l)}
  else
    NPROC_PER_NODE=${NPROC_PER_NODE:=$(nvidia-smi --list-gpus | wc -l)}
  fi
  export NCCL_DEBUG=WARN
else
  # NPU
  if [[ -n "${ASCEND_RT_VISIBLE_DEVICES}" ]]; then
    NPROC_PER_NODE=${NPROC_PER_NODE:=$(echo "${ASCEND_RT_VISIBLE_DEVICES}" | tr ',' '\n' | wc -l)}
  else
    NPROC_PER_NODE=${NPROC_PER_NODE:=$(ls -l /dev/davinci* | grep -v "davinci_manager" | wc -l)}
  fi
  # NPU env that may optimize performance
  export PYTORCH_NPU_ALLOC_CONF=${PYTORCH_NPU_ALLOC_CONF:='expandable_segments:True'}
fi
torchrun \
  --nnodes=$NNODES \
  --nproc-per-node=$NPROC_PER_NODE \
  --node-rank=$NODE_RANK \
  $additional_args $@ 2>&1 | tee "$CHECK_LOG_PATH" && \
  echo "Training completes" >> "$CHECK_LOG_PATH"
sleep 20s
tail -n 500 "$CHECK_LOG_PATH" | grep -q -E "Training completes"  || exit 1 # 异常状态校验,返回状态码给平台
# 主节点进行hf格式转化
if [ "$NODE_RANK" -eq 0 ]; then
    echo "[Master] Master. Doing Convert ckpt to hf models"
    CHECKPOINT_ROOT="$output_dir/qwen3_vl_dense_sft/checkpoints" # 与平台启动命令的$output_dir/qwen3_vl_dense_sft保持一致
    LATEST_STEP_DIR=$(ls -v "$CHECKPOINT_ROOT" | grep "global_step_" | tail -n 1) # 获取global_step最大的一次保存的ckpt结果转化为hf格式
    FULL_LOAD_PATH="${CHECKPOINT_ROOT}/${LATEST_STEP_DIR}"
    echo "[INFO] Detected latest checkpoint: $FULL_LOAD_PATH"
    # 执行ckpt转化为hf格式,model-assets-dir是config的路径,load-dir是训练完保存的dcp路径,save-dir是最后保存的hf路径
    python /home/ma-user/VeOmni/scripts/merge_dcp_to_hf.py \
    --model-assets-dir "$model_dir/Qwen3-VL-8B" \
    --save-dir "$output_dir/qwen3-vl-8b-saved-hf_ckpt" \
    --load-dir "$FULL_LOAD_PATH" 2>&1 | tee "$CHECK_LOG_PATH" && \
    echo "All steps completes" >> "$CHECK_LOG_PATH"
else
    echo "[Worker $NODE_RANK] Task finished. Waiting for Master to complete conversion..."
    echo "All steps completes" >> "$CHECK_LOG_PATH"
fi
sleep 10s
tail -n 500 "$CHECK_LOG_PATH" | grep -q -E "All steps completes"  && exit 0 || exit 1 # 异常状态校验,返回状态码给平台

相关文档