周红伟:Qwen3.5-Plus - 企业级部署案例实操,Qwen3.5 LLM,包括 Qwen3.5-397B-A17B

Qwen3.5-Plus - 企业级部署案例实操

在本地设备上运行新的 Qwen3.5 LLM,包括 Qwen3.5-397B-A17B!

Qwen3.5 是阿里巴巴的新一代模型家族,包括 Qwen3.5-397B-A17B,一款具有 397B 参数(17B 活跃)的多模态推理模型,其性能可与 Gemini 3 Pro、Claude Opus 4.5 和 GPT-5.2 相媲美。它支持 256K 上下文(可扩展至 1M)覆盖 201 种语言,提供思考与非思考模式,并在编码、视觉、代理、对话和长上下文任务中表现出色。

完整的 Qwen3.5-397B-A17B 模型大小为 ~807GB 在磁盘上,你可以在 192GB Mac / RAM 设备上运行 3-bit,或在 256GB Mac : 上运行 4-bit MXFP4 的 Qwen3.5-397B-A17B GGUF

所有上传均使用 Unsloth Dynamic 2.0 以实现最先进的量化性能 ------ 因此 4-bit 的重要层会提升为 8 或 16 位。感谢 Qwen 在第一天就为 Unsloth 提供访问权限。

⚙️ 使用指南

Unsloth 的 4-bit 动态量化 UD-Q4_K_XL 使用 214GB 磁盘空间 ------ 这可以直接适配到一台 256GB M3 Ultra ,并且在 1x24GB 卡和 256GB 内存 配合 MoE 卸载时,能够达到 25+ tokens/s。3**-bit**量化可以在 192GB 内存上运行,而 8-bit 需要 512GB 的内存/显存。

为获得最佳性能,请确保您的总可用内存(显存 + 系统内存)超过您正在下载的量化模型文件的大小。如果不满足,llama.cpp 仍可通过 SSD/HDD 卸载运行,但推理会更慢。

推荐设置

由于 Qwen3.5 是混合推理模型,思考和非思考模式需要不同的设置:

思考模式

非思考模式

temperature = 0.6

temperature = 0.7

top_p = 0.95

top_p = 0.8

tok_k = 20

tok_k = 20

min_p = 0

min_p = 0

repeat penalty = disabled or 1.0

repeat penalty = disabled or 1.0

  • 最大上下文窗口: 262,144

  • presence_penalty = 0.0 到 2.0 默认此项为关闭,但若要减少重复可使用此项,然而使用更高的值可能会导致性能略有下降

  • 思考: temperature=0.6, top_p=0.95, top_k=20, min_p=0

  • 非思考: temperature=0.7, top_p=0.8, top_k=20, min_p=0

  • 适当的输出长度 : 32,768 适用于大多数查询的 tokens 数量

Qwen3.5-397B-A17B 教程:

在本指南中我们将使用 Dynamic MXFP4_MOE 其在 256GB 内存 / Mac 设备上非常适合用于快速推理:

✨ 在 llama.cpp 中运行

获取最新的 llama.cpp 此处的 GitHub。你也可以按照下面的构建说明。将 -DGGML_CUDA=ON 改为 -DGGML_CUDA=OFF 如果你没有 GPU 或仅想使用 CPU 推理。

复制

复制代码
apt-get update
apt-get install pciutils build-essential cmake curl libcurl4-openssl-dev -y
git clone https://github.com/ggml-org/llama.cpp
cmake llama.cpp -B llama.cpp/build \
    -DBUILD_SHARED_LIBS=OFF -DGGML_CUDA=ON
cmake --build llama.cpp/build --config Release -j --clean-first --target llama-cli llama-mtmd-cli llama-server llama-gguf-split
cp llama.cpp/build/bin/llama-* llama.cpp

如果你想直接使用 llama.cpp 来加载模型,你可以如下操作:(:Q3_K_XL)是量化类型。你也可以通过 Hugging Face(第 3 点)下载。这类似于 ollama run 。使用 export LLAMA_CACHE="folder" 来强制 llama.cpp 保存到指定位置。请记住模型的最大上下文长度只有 200K。

按照以下方式进入 思考 模式:

复制

复制代码
export LLAMA_CACHE="unsloth/Qwen3.5-397B-A17B-GGUF"
./llama.cpp/llama-cli \
    -hf unsloth/Qwen3.5-397B-A17B-GGUF:MXFP4_MOE \
    --ctx-size 16384 \
    --temp 0.6 \
    --top-p 0.95 \
    --top-k 20 \
    --min-p 0.00

按照以下方式进入 非思考 模式:

复制

复制代码
export LLAMA_CACHE="unsloth/Qwen3.5-397B-A17B-GGUF"
./llama.cpp/llama-cli \
    -hf unsloth/Qwen3.5-397B-A17B-GGUF:MXFP4_MOE \
    --ctx-size 16384 \
    --temp 0.7 \
    --top-p 0.8 \
    --top-k 20 \
    --min-p 0.00 \
    --chat-template-kwargs "{\"enable_thinking\": false}"

通过以下方式下载模型(在安装之后) pip install huggingface_hub hf_transfer )。你可以选择 MXFP4_MOE (动态 4bit)或其他量化版本例如 UD-Q4_K_XL 。我们建议至少使用 2-bit 动态量化 UD-Q2_K_XL 以平衡大小与精度。如果下载卡住,请查看: Hugging Face Hub、XET 调试

复制

复制代码
hf download unsloth/Qwen3.5-397B-A17B-GGUF \
    --local-dir unsloth/Qwen3.5-397B-A17B-GGUF \
    --include "*MXFP4_MOE*" # 对于 Dynamic 2bit 使用 "*UD-Q2_K_XL*"

你可以编辑 --threads 32 来设置 CPU 线程数, --ctx-size 16384 来设置上下文长度, --n-gpu-layers 2 来设置要进行 GPU 卸载的层数。如果你的 GPU 内存不足可以调整该值;如果仅使用 CPU 推理则移除该选项。

复制

复制代码
./llama.cpp/llama-cli \
    --model unsloth/Qwen3.5-397B-A17B-GGUF/MXFP4_MOE/Qwen3.5-397B-A17B-MXFP4_MOE-00001-of-00006.gguf \
    --ctx-size 16384 \
    --temp 0.6 \
    --top-p 0.95 \
    --top-k 20 \
    --min-p 0.00
    --seed 3407

要禁用思考/推理功能,请使用 --chat-template-kwargs "{\"enable_thinking\": false}"

🦙 Llama-server 服务 & OpenAI 的 completion 库

为了将 Qwen3.5-397B-A17B 部署到生产环境,我们使用 llama-server 在一个新终端(例如使用 tmux)中,通过以下命令部署模型:

复制

复制代码
./llama.cpp/llama-server \
    --model unsloth/Qwen3.5-397B-A17B-GGUF/MXFP4_MOE/Qwen3.5-397B-A17B-MXFP4_MOE-00001-of-00006.gguf \
    --alias "unsloth/Qwen3.5-397B-A17B" \
    --temp 0.6 \
    --top-p 0.95 \
    --ctx-size 16384 \
    --top-k 20 \
    --min-p 0.00 \
    --port 8001

然后在另一个新终端,在执行 pip install openai之后,执行:

复制

复制代码
from openai import OpenAI
import json
openai_client = OpenAI(
    base_url = "http://127.0.0.1:8001/v1",
    api_key = "sk-no-key-required",
)
completion = openai_client.chat.completions.create(
    model = "unsloth/Qwen3.5-397B-A17B",
    messages = [{"role": "user", "content": "Create a Snake game."},],
)
print(completion.choices[0].message.content)

要禁用思考/推理功能,请使用 --chat-template-kwargs "{\"enable_thinking\": false}"

🔨与 Qwen3.5 的工具调用

参见 Tool Calling Guide 以获取有关如何进行工具调用的更多细节。在新终端中(若使用 tmux,使用 CTRL+B+D),我们创建一些工具,例如相加两个数字、执行 Python 代码、执行 Linux 功能等:

复制

复制代码
import json, subprocess, random
from typing import Any
def add_number(a: float | str, b: float | str) -> float:
    return float(a) + float(b)
def multiply_number(a: float | str, b: float | str) -> float:
    return float(a) * float(b)
def substract_number(a: float | str, b: float | str) -> float:
    return float(a) - float(b)
def write_a_story() -> str:
    return random.choice([
        "很久以前在一个遥远的星系......",
        "有两个喜欢树懒和代码的朋友......",
        "世界正在终结,因为每只树懒都进化出了超人的智慧......",
        "在一个朋友不知道的情况下,另一个朋友意外编写了一个让树懒进化的程序......",
    ])
def terminal(command: str) -> str:
    if "rm" in command or "sudo" in command or "dd" in command or "chmod" in command:
        msg = "无法执行 'rm, sudo, dd, chmod' 命令,因为它们具有危险性"
        print(msg); return msg
    print(f"正在执行终端命令 `{command}`")
    try:
        return str(subprocess.run(command, capture_output = True, text = True, shell = True, check = True).stdout)
    except subprocess.CalledProcessError as e:
        return f"命令失败:{e.stderr}"
def python(code: str) -> str:
    data = {}
    exec(code, data)
    del data["__builtins__"]
    return str(data)
MAP_FN = {
    "add_number": add_number,
    "multiply_number": multiply_number,
    "substract_number": substract_number,
    "write_a_story": write_a_story,
    "terminal": terminal,
    "python": python,
}
tools = [
    {
        "type": "function",
        "function": {
            "name": "add_number",
            "description": "将两个数字相加。",
            "parameters": {
                "type": "object",
                "properties": {
                    "a": {
                        "type": "string",
                        "description": "第一个数字。",
                    },
                    "b": {
                        "type": "string",
                        "description": "第二个数字。",
                    },
                },
                "required": ["a", "b"],
            },
        },
    },
    {
        "type": "function",
        "function": {
            "name": "multiply_number",
            "description": "将两个数字相乘。",
            "parameters": {
                "type": "object",
                "properties": {
                    "a": {
                        "type": "string",
                        "description": "第一个数字。",
                    },
                    "b": {
                        "type": "string",
                        "description": "第二个数字。",
                    },
                },
                "required": ["a", "b"],
            },
        },
    },
    {
        "type": "function",
        "function": {
            "name": "substract_number",
            "description": "将两个数字相减。",
            "parameters": {
                "type": "object",
                "properties": {
                    "a": {
                        "type": "string",
                        "description": "第一个数字。",
                    },
                    "b": {
                        "type": "string",
                        "description": "第二个数字。",
                    },
                },
                "required": ["a", "b"],
            },
        },
    },
    {
        "type": "function",
        "function": {
            "name": "write_a_story",
            "description": "写一则随机故事。",
            "parameters": {
                "type": "object",
                "properties": {},
                "required": [],
            },
        },
    },
    {
        "type": "function",
        "function": {
            "name": "terminal",
            "description": "在终端执行操作。",
            "parameters": {
                "type": "object",
                "properties": {
                    "command": {
                        "type": "string",
                        "description": "你希望执行的命令,例如 `ls`, `rm`, ...",
                    },
                },
                "required": ["command"],
            },
        },
    },
    {
        "type": "function",
        "function": {
            "name": "python",
            "description": "调用 Python 解释器运行一些 Python 代码。",
            "parameters": {
                "type": "object",
                "properties": {
                    "code": {
                        "type": "string",
                        "description": "要运行的 Python 代码",
                    },
                },
                "required": ["code"],
            },
        },
    },
]

显示全部 148 行

然后我们使用下面的函数(复制粘贴并执行),它们将自动解析函数调用并为任何模型调用 OpenAI 端点:

复制

复制代码
from openai import OpenAI
def unsloth_inference(
    messages,
    temperature = 0.6,
    top_p = 0.95,
    top_k = 20,
    min_p = 0.00,
    repetition_penalty = 1.0,
):
    messages = messages.copy()
    openai_client = OpenAI(
        base_url = "http://127.0.0.1:8001/v1",
        api_key = "sk-no-key-required",
    )
    model_name = next(iter(openai_client.models.list())).id
    print(f"使用模型 = {model_name}")
    has_tool_calls = True
    original_messages_len = len(messages)
    while has_tool_calls:
        print(f"当前消息 = {messages}")
        response = openai_client.chat.completions.create(
            model = model_name,
            messages = messages,
            temperature = temperature,
            top_p = top_p,
            tools = tools if tools else None,
            tool_choice = "auto" if tools else None,
            extra_body = {"top_k": top_k, "min_p": min_p, "repetition_penalty" :repetition_penalty,}
        )
        tool_calls = response.choices[0].message.tool_calls or []
        content = response.choices[0].message.content or ""
        tool_calls_dict = [tc.to_dict() for tc in tool_calls] if tool_calls else tool_calls
        messages.append({"role": "assistant", "tool_calls": tool_calls_dict, "content": content,})
        for tool_call in tool_calls:
            fx, args, _id = tool_call.function.name, tool_call.function.arguments, tool_call.id
            out = MAP_FN[fx](**json.loads(args))
            messages.append({"role": "tool", "tool_call_id": _id, "name": fx, "content": str(out),})
        else:
            has_tool_calls = False
    return messages

显示全部 40 行

在通过 llama-server 启动 Qwen3.5 之后, Qwen3.5 像在 Tool Calling Guide 或参见

相关推荐
永无魇足2 小时前
人工神经网络(Artificial Neural Network,ANN)
人工智能
憨憨の大鸭鸭2 小时前
今日机器学习
人工智能·机器学习
宝贝儿好2 小时前
【强化学习】第十章:随机高斯策略
人工智能·python·深度学习·神经网络·机器人·自动驾驶
eihh233332 小时前
山东大学软件学院毕业设计(二)
人工智能·深度学习·机器学习
历程里程碑2 小时前
普通数组---合并区间
java·大数据·数据结构·算法·leetcode·elasticsearch·搜索引擎
KG_LLM图谱增强大模型3 小时前
图谱智能体记忆技术和应用综述:构建AI Agent的“大脑记忆系统“
人工智能
小龙报3 小时前
【Coze-AI智能体平台】Coze 工作流 = 智能体的 “流程管家”?一文解锁自动化落地新玩法
人工智能·语言模型·自然语言处理·性能优化·数据分析·知识图谱·需求分析
两万五千个小时3 小时前
构建mini Claude Code:08 - Fire and Forget:用后台线程解锁 Multi-Agent 并行执行
人工智能·python·架构