TellWorldAI 世界档案
问档案English

首页›实践›实践

实践

MiniMax-M2 用 vLLM 部署

MiniMax-M2 的 vLLM 部署实战:220GB 权重的显存账、4 卡与 8 卡两种并行方案、minimax_m2 双解析器,以及面向 Agent 场景的工具调用实测。

MiniMax-M2 是一个为 Agent 和代码而生的开源大模型:规划并执行长链条工具调用、协同 Shell、浏览器、Python 执行器和各类 MCP 工具是它的主战场 [1]。把它部署到 vLLM 上,第一笔账不是参数而是显存——权重约 220GB,每 1M 上下文 token 还要再加约 240GB [1]。这篇按 96GB×4 卡与 144GB×8 卡两档配置走完部署全程,并以工具调用实测收尾。

面向 Agent 场景直接决定了部署形态:这类负载里模型要频繁地在思考和工具调用之间切换,响应结构必须被服务端正确解析才能被上层框架消费。所以启动命令里两个解析器缺一不可——没有工具解析器,模型的调用意图会以纯文本形式出现;没有思考解析器,推理过程会和答案混在一起。上层框架拿到的是结构化的 tool_calls 和分开的思考内容,编排逻辑才写得下去。

显存账与卡数选择

显存算清楚,卡数就定了 [1]:96G×4 卡支持约 40 万 token 总上下文,144G×8 卡能到约 300 万。两档对应两种并行策略:4 卡走纯张量并行,8 卡额外开专家并行。参考环境是 8 张 RTX PRO 6000(每卡 96GB)[ ^1]。如果显存吃紧,调低 --mem-fraction-static 或缩 --max-model-len 都能换出空间 [1]。

并发和上下文的规划值得单独想一遍:40 万 token 的总上下文是全实例共享的预算,不是单请求上限。假设每个会话平均占用 8K 上下文,4 卡档大约承载 50 个并发会话;再往上要么牺牲单请求长度,要么上 8 卡档。Agent 负载的特点恰恰是多轮长对话,历史消息不断累积,会话数要按这个口径打折——规划时按平均上下文的 1.5 到 2 倍预留才稳妥。

环境与安装

依赖建议装在虚拟环境里避免冲突 [1]。安装用 uv 锁版本,vLLM 走 nightly 源 [1]:

pip install --upgrade pip
pip install uv==0.9.7
[ -d ".venv" ] || uv venv
source .venv/bin/activate
uv pip install modelscope==1.31.0
uv pip install openai==2.6.1
uv pip install 'triton-kernels @ git+https://github.com/triton-lang/triton.git@v3.5.0#subdirectory=python/triton_kernels'  vllm --extra-index-url https://wheels.vllm.ai/nightly --prerelease=allow

triton-kernels 从 GitHub 源码安装,这一步依赖网络质量,失败多半卡在克隆仓库上。

模型下载

vLLM 首次启动会自动从 Hugging Face 拉取并缓存模型;网络受限时可以先用 modelscope 手动下载,模型名 MiniMaxAI/MiniMax-M2 [1]:

# model_download.py
from modelscope import snapshot_download

model_dir = snapshot_download('MiniMaxAI/MiniMax-M2', cache_dir='/root/autodl-tmp', revision='master')
print(f"模型下载成功,保存到: {model_dir}")

权重很大,带宽不足时可以先在高带宽机器下载再拷贝到目标机 [1]。

脚本方式的价值在工程侧:参数固化在代码里,改配置走代码评审而不是口头约定;launch_server_cmd 返回进程句柄,配套的 wait_for_server 阻塞到服务就绪,启动脚本可以直接嵌入健康检查流程。日志重定向、异常重启、就绪探测这些运维要素都有了挂载点。命令行方式则适合临时验证——一行起服务,观察日志,用完即走。

服务启动:按卡数自动选命令

启动方式有两种,脚本方式便于固定参数与日志。脚本按检测到的 GPU 数量选命令,不是 4 卡或 8 卡就直接报错 [1]:

import torch
from vLLM.utils import launch_server_cmd, wait_for_server

gpu_count = torch.cuda.device_count() if torch.cuda.is_available() else 0
if gpu_count == 4:
    cmd = (
        "SAFETENSORS_FAST_GPU=1 vllm serve "
        "MiniMaxAI/MiniMax-M2 --trust-remote-code "
        "--tensor-parallel-size 4 "
        "--enable-auto-tool-choice --tool-call-parser minimax_m2 "
        "--reasoning-parser minimax_m2_append_think"
    )
elif gpu_count == 8:
    cmd = (
        "SAFETENSORS_FAST_GPU=1 vllm serve "
        "MiniMaxAI/MiniMax-M2 --trust-remote-code "
        "--enable_expert_parallel --tensor-parallel-size 8 "
        "--enable-auto-tool-choice --tool-call-parser minimax_m2 "
        "--reasoning-parser minimax_m2_append_think"
    )
else:
    raise RuntimeError(f"建议使用 4 或 8 张 GPU,当前检测到: {gpu_count}")
server_process, port = launch_server_cmd(cmd, port=8000)
wait_for_server(f"http://127.0.0.1:{port}")
print(f"vLLM Server started: http://127.0.0.1:{port}")

命令行等价写法,4 卡版 [1]:

SAFETENSORS_FAST_GPU=1 vllm serve \
    MiniMaxAI/MiniMax-M2 --trust-remote-code \
    --tensor-parallel-size 4 \
    --enable-auto-tool-choice --tool-call-parser minimax_m2 \
    --reasoning-parser minimax_m2_append_think

8 卡版多一个 --enable_expert_parallel [1]:

SAFETENSORS_FAST_GPU=1 vllm serve \
    MiniMaxAI/MiniMax-M2 --trust-remote-code \
    --enable_expert_parallel --tensor-parallel-size 8 \
    --enable-auto-tool-choice --tool-call-parser minimax_m2 \
    --reasoning-parser minimax_m2_append_think

两个解析器是启动命令的固定搭配:--tool-call-parser minimax_m2 处理工具调用格式,--reasoning-parser minimax_m2_append_think 处理思考内容。模型大,首次加载可能超过半小时,服务起来后监听 http://127.0.0.1:8000/v1 [1]。起好后先用 curl 快速验证链路 [1]:

curl http://localhost:8000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "MiniMaxAI/MiniMax-M2",
    "messages": [
      {"role": "system", "content": [{"type": "text", "text": "You are a helpful assistant."}]},
      {"role": "user", "content": [{"type": "text", "text": "Which team won the worldcup in 2022?"}]}
    ]
  }'

采样参数里 temperature 1.0 看似激进,实则是推理模型的常态:思考阶段需要足够的随机性探索解题路径,压得太低反而让模型陷入重复推理。真正要确定性的是参数抽取和分类这类结构化任务,那时再单独压低。分层使用采样参数——Agent 编排调用用官方推荐值,抽取类调用用低温——是一个值得写进调用规范的细节。

三种调用姿势

补全、聊天、流式各来一遍,全部走 OpenAI SDK。补全接口用官方推荐采样参数 [1]:

# test_completion.py
from openai import OpenAI

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

response = client.completions.create(
    model="MiniMaxAI/MiniMax-M2",
    prompt="简要介绍一下 MiniMax M2 模型的特点。",
    max_tokens=8192,
    top_p=0.95,
    temperature=1.0,
)
print(response)

聊天接口的回复里,思考过程包在 <think> 标签里,最终答案在其后 [1]:

# test_chat.py
from openai import OpenAI

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

response = client.chat.completions.create(
    model="MiniMaxAI/MiniMax-M2",
    messages=[
        {"role": "user", "content": "MiniMAX公司的愿景是什么?你觉得他们会成功吗,请作出详细分析。"}
    ],
    max_tokens=8192,
    top_p=0.95,
    temperature=1.0,
)

msg = response.choices[0].message
print("MiniMax-M2:", msg.content)

流式接口适合长生成任务,逐块打印 delta [1]:

# test_streaming.py
from openai import OpenAI

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

stream = client.chat.completions.create(
    model="MiniMaxAI/MiniMax-M2",
    messages=[{"role": "user", "content": "请写一篇题为Agent时代大模型应用落地要点的调研报告。"}],
    stream=True,
    max_tokens=32768,
    top_p=0.95,
    temperature=1.0,
)

for chunk in stream:
    delta = chunk.choices[0].delta
    if delta and delta.content:
        print(delta.content, end="", flush=True)

流式跑长报告这类任务时注意 max_tokens 要给足,32768 的预算在思考加正文两者之间分配;给小了会在正文写完前截断。

工具描述的质量直接决定调用准确率:参数说明里的示例值会被模型当作格式参照,枚举类型比自由文本更可控,必填字段要让模型无从跳过。多工具场景下还要注意函数命名——名字本身承载语义,含糊的名字会诱发误选。每次调用结束后把结果以工具消息回传并进入下一轮,是 Agent 循环的标准节奏,客户端只需维护好消息列表的顺序。

工具调用实测

M2 在 vLLM 部署时默认启用工具调用,模型自己判断何时需要外部工具并输出结构化参数 [1]。示例定义一个天气查询函数和对应的工具描述 [1]:

# test_tool_calling.py
from openai import OpenAI
import json

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

def get_weather(location: str, unit: str):
    return f"Getting the weather for {location} in {unit}..."

tool_functions = {"get_weather": get_weather}
tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "Get the current weather in a given location",
        "parameters": {
            "type": "object",
            "properties": {
                "location": {"type": "string", "description": "City and state, e.g., 'San Francisco, CA'"},
                "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
            },
            "required": ["location", "unit"]
        }
    }
}]

请求里的模型名直接从模型列表接口动态读取,避开写死路径的坑 [1]:

response = client.chat.completions.create(
    model=client.models.list().data[0].id,
    messages=[{"role": "user", "content": "What's the weather like in San Francisco? use celsius."}],
    tools=tools,
    tool_choice="auto"
)

print(response)

tool_call = response.choices[0].message.tool_calls[0].function
print(f"Function called: {tool_call.name}")
print(f"Arguments: {tool_call.arguments}")
print(f"Result: {get_weather(**json.loads(tool_call.arguments))}")

实测结果干净利落:模型选中 get_weather,参数为 {"location": "San Francisco, CA", "unit": "celsius"}——问题里只说了 San Francisco,模型按工具描述里的示例补全了州名缩写,单位严格按用户要求取摄氏度 [1]。

参数速查

收个尾 [1]:请求里的 model 必须与启动时指定一致;--tensor-parallel-size 常设为 GPU 数;8 卡示例开 --enable_expert_parallel;--enable-auto-tool-choice 让模型自主发起工具调用;max_tokens 过大徒增显存和时延;要确定性输出就压低 temperature 和 top_p。采样参数官方推荐 temperature=1.0、top_p=0.95、top_k=20 [1]。

本文改编自 Datawhale 开源教程《self-llm》的 MiniMax-M2 vLLM 部署一节[1],原文以 Apache-2.0 许可证发布。

来源

  1. 1
    2-MiniMax-M2 vLLM 部署调用 | self-llmDatawhale self-llm (Apache-2.0)

引用本页: MiniMax-M2 用 vLLM 部署. TellWorld, https://www.tellworld.net/zh/practice/practice-minimax-m2-vllm. 更新于 2026-11-14

相关动态