# Devstral 2 - 如何运行指南

Devstral 2 是 Mistral 面向软件工程的新代码与智能体型 LLM，提供 [24B](#devstral-small-2-24b) 和 [123B](#devstral-2-123b) 两种规模。123B 模型在 SWE-bench、代码、工具调用和智能体用例上达到了 SOTA。24B 模型可放入 25GB RAM/VRAM，而 123B 可放入 128GB。

{% hint style="success" %}
**2025年12月13日更新**

**我们已修复 Devstral 聊天模板中的问题，效果应该会显著提升。24B 和 123B 已更新。另外请安装截至 2025年12月13日的最新 llama.cpp！**
{% endhint %}

Devstral 2 支持视觉能力、256k 上下文窗口，并采用与 [Ministral 3](/docs/zh/mo-xing/tutorials/ministral-3.md)相同的架构。你现在可以本地运行并 **微调** 这两个模型，使用 Unsloth。

所有 Devstral 2 上传都使用我们的 Unsloth [Dynamic 2.0](/docs/zh/ji-chu/unsloth-dynamic-2.0-ggufs.md) 方法，在 [Aider Polyglot](/docs/zh/ji-chu/unsloth-dynamic-2.0-ggufs/unsloth-dynamic-ggufs-on-aider-polyglot.md) 和 5-shot MMLU 基准上提供最佳性能。

<a href="#devstral-small-2-24b" class="button primary">Devstral-Small-2-24B</a><a href="#devstral-2-123b" class="button primary">Devstral-2-123B</a>

#### **Devstral 2 - Unsloth Dynamic** GGUF：

| Devstral-Small-2-24B-Instruct-2512                                                                                    | Devstral-2-123B-Instruct-2512                                                                               |
| --------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| [Devstral-Small-2-**24B**-Instruct-2512-GGUF](https://huggingface.co/unsloth/Devstral-Small-2-24B-Instruct-2512-GGUF) | [Devstral-2-**123B**-Instruct-2512-GGUF](https://huggingface.co/unsloth/Devstral-2-123B-Instruct-2512-GGUF) |

## 🖥️ **运行 Devstral 2**

查看我们关于运行 [Devstral 24B](#devstral-small-2-24b) 以及大型 [Devstral 123B](#devstral-2-123b) 模型的分步指南。两个模型都支持视觉能力，但目前 **不支持视觉** 在 llama.cpp 中

### :gear: 使用指南

以下是推理的推荐设置：

* <mark style="background-color:blue;">**温度 \~0.15**</mark>
* Min\_P 设为 0.01（可选，但 0.01 效果很好，llama.cpp 默认是 0.1）
* **使用 `--jinja` 来启用系统提示词。**
* 最大上下文长度 = 262,144
* 建议最小上下文：16,384
* 安装最新的 llama.cpp，因为一个 [2025年12月13日的拉取请求](https://github.com/ggml-org/llama.cpp/pull/17945) 修复了问题。

### :tophat:Devstral-Small-2-24B

完整精度（Q8）的 Devstral-Small-2-24B GGUF 可放入 25GB RAM/VRAM。目前仅支持文本。

#### ✨ 在 llama.cpp 中运行 Devstral-Small-2-24B-Instruct-2512

1. 获取最新的 `llama.cpp` 在 [GitHub 这里](https://github.com/ggml-org/llama.cpp)。你也可以按照下面的构建说明操作。将 `-DGGML_CUDA=ON` 改为 `-DGGML_CUDA=OFF` 如果你没有 GPU，或者只想进行 CPU 推理。 **对于 Apple Mac / Metal 设备**，设置 `-DGGML_CUDA=OFF` 然后照常继续——Metal 支持默认开启。

{% code overflow="wrap" %}

```bash
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 -DLLAMA_CURL=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
```

{% endcode %}

2. 如果你想使用 `llama.cpp` 直接加载模型，你可以执行下面的命令：（:`Q4_K_XL`）是量化类型。你也可以直接从 Hugging Face 拉取：

```bash
./llama.cpp/llama-cli \
    -hf unsloth/Devstral-Small-2-24B-Instruct-2512-GGUF:UD-Q4_K_XL \
    --jinja -ngl 99 --ctx-size 16384 \
    --temp 0.15
```

3. 通过以下方式下载模型（在安装 `pip install huggingface_hub hf_transfer` 之后）。你可以选择 `UD_Q4_K_XL` 或其他量化版本。

```python
# !pip install huggingface_hub hf_transfer
import os
os.environ["HF_HUB_ENABLE_HF_TRANSFER"] = "1"
from huggingface_hub import snapshot_download
snapshot_download(
    repo_id = "unsloth/Devstral-Small-2-24B-Instruct-2512-GGUF",
    local_dir = "unsloth/Devstral-Small-2-24B-Instruct-2512-GGUF",
    allow_patterns = ["*UD-Q4_K_XL*", "*mmproj-F16*"], # 用于 Q4_K_XL
)
```

4. 在对话模式下运行模型：

{% code overflow="wrap" %}

```bash
./llama.cpp/llama-cli \
    --model unsloth/Devstral-Small-2-24B-Instruct-2512-GGUF/Devstral-Small-2-24B-Instruct-2512-UD-Q4_K_XL.gguf \
    --mmproj unsloth/Devstral-Small-2-24B-Instruct-2512-GGUF/mmproj-F16.gguf \
    --ctx-size 16384 \
    --n-gpu-layers 99 \
    --seed 3407 \
    --prio 2 \
    --temp 0.15 \
    --jinja
```

{% endcode %}

#### :eyes:Devstral 与视觉

1. 为了体验 Devstral 的图像能力，我们先下载一张像这样的图片 [使用 Unsloth 的 FP8 强化学习](https://unsloth.ai/cgi/image/fp8grpolarge_KharloZxEEaHAY2X97CEX.png?width=3840\&quality=80\&format=auto) 如下：\
   ![](/files/008ae7d8d0dcfe0e5e51c417256dee21a1a8959a)
2. 我们通过以下方式获取图片 `wget https://unsloth.ai/cgi/image/fp8grpolarge_KharloZxEEaHAY2X97CEX.png?width=3840%26quality=80%26format=auto -O unsloth_fp8.png` 这会将图片保存为“unsloth\_fp8.png”
3. 然后通过以下方式加载图片 `/image unsloth_fp8.png` 在模型加载完成后，如下所示：\
   ![](/files/29b6285961e3ce919a589d11c9116bd1bbb082fb)
4. 然后我们向它提问 `描述这张图片` 并得到如下结果：<br>

   <figure><img src="/files/0d56868463897abad96a1f5cde9ea43f2eec759c" alt=""><figcaption></figcaption></figure>

### :truck:Devstral-2-123B

完整精度（Q8）的 Devstral-Small-2-123B GGUF 可放入 128GB RAM/VRAM。目前仅支持文本。

#### :sparkles: 运行 Devstral-2-123B-Instruct-2512 教程

1. 获取最新的 `llama.cpp` 在 [GitHub 这里](https://github.com/ggml-org/llama.cpp)。你也可以按照下面的构建说明操作。将 `-DGGML_CUDA=ON` 改为 `-DGGML_CUDA=OFF` 如果你没有 GPU，或者只想进行 CPU 推理。

{% code overflow="wrap" %}

```bash
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 -DLLAMA_CURL=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
```

{% endcode %}

2. 你可以直接通过 HuggingFace 拉取：

```bash
./llama.cpp/llama-cli \
    -hf unsloth/Devstral-2-123B-Instruct-2512-GGUF:UD-Q2_K_XL \
    --jinja -ngl 99 --ctx-size 16384 \
    --temp 0.15
```

2. 通过以下方式下载模型（在安装 `pip install huggingface_hub hf_transfer` 之后）。你可以选择 `UD_Q4_K_XL` 或其他量化版本。

```python
# !pip install huggingface_hub hf_transfer
import os
os.environ["HF_HUB_ENABLE_HF_TRANSFER"] = "1"
from huggingface_hub import snapshot_download
snapshot_download(
    repo_id = "unsloth/Devstral-2-123B-Instruct-2512-GGUF",
    local_dir = "unsloth/Devstral-2-123B-Instruct-2512-GGUF",
    allow_patterns = ["*UD-Q2_K_XL*", "*mmproj-F16*"],
)
```

3. 在对话模式下运行模型：

{% code overflow="wrap" %}

```bash
./llama.cpp/llama-cli \
    --model unsloth/Devstral-2-123B-Instruct-2512-GGUF/Devstral-2-123B-Instruct-2512-UD-Q2_K_XL.gguf \
    --mmproj unsloth/Devstral-2-123B-Instruct-2512-GGUF/mmproj-F16.gguf \
    --ctx-size 16384 \
    --n-gpu-layers 99 \
    --seed 3407 \
    --prio 2 \
    --temp 0.15 \
    --jinja
```

{% endcode %}

## 🦥 使用 Unsloth 微调 Devstral 2

就像 [Ministral 3](/docs/zh/mo-xing/tutorials/ministral-3.md)一样，Unsloth 支持 Devstral 2 微调。训练速度快 2 倍，显存使用少 70%，并支持长 8 倍的上下文长度。Devstral 2 在 24GB VRAM 的 L4 GPU 上运行得很轻松。

不幸的是，Devstral 2 略微超出了 16GB VRAM 的内存限制，因此目前无法在 Google Colab 上免费微调。不过，你 *可以* 使用我们的 [Kaggle 笔记本](https://www.kaggle.com/notebooks/welcome?src=https://github.com/unslothai/notebooks/blob/main/nb/Kaggle-Magistral_\(24B\)-Reasoning-Conversational.ipynb\&accelerator=nvidiaTeslaT4)免费微调该模型，它支持双 GPU。只需将笔记本中的 Magistral 模型名称改为 `unsloth/Devstral-Small-2-24B-Instruct-2512` 模型。

{% hint style="success" %}
我们为微调 Ministral 3 制作了免费的 Unsloth 笔记本，并且可直接支持 Devstral 2，因为它们共享相同的架构！将名称改为所需模型即可。
{% endhint %}

* Ministral-3B-Instruct [视觉笔记本](https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Ministral_3_VL_\(3B\)_Vision.ipynb) （视觉）（将模型名称改为 Devstral 2）
* Ministral-3B-Instruct [GRPO 笔记本](https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Ministral_3_\(3B\)_Reinforcement_Learning_Sudoku_Game.ipynb) （将模型名称改为 Devstral 2）

{% columns %}
{% column %}
Devstral 视觉微调笔记本

{% embed url="<https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Ministral_3_VL_(3B)_Vision.ipynb>" %}
{% endcolumn %}

{% column %}
Devstral 数独 GRPO RL 笔记本

{% embed url="<https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Ministral_3_(3B)_Reinforcement_Learning_Sudoku_Game.ipynb>" %}
{% endcolumn %}
{% endcolumns %}

### :sunglasses:Llama-server 服务与部署

要将 Devstral 2 部署到生产环境，我们使用 `llama-server` 在一个新终端中，例如通过 tmux，按以下方式部署模型：

{% code overflow="wrap" %}

```bash
./llama.cpp/llama-server \
    --model unsloth/Devstral-Small-2-24B-Instruct-2512-GGUF/Devstral-Small-2-24B-Instruct-2512-UD-Q4_K_XL.gguf \
    --mmproj unsloth/Devstral-Small-2-24B-Instruct-2512-GGUF/mmproj-F16.gguf \
    --alias "unsloth/Devstral-Small-2-24B-Instruct-2512" \
    --n-gpu-layers 999 \
    --prio 3 \
    --min-p 0.01 \
    --ctx-size 16384 \
    --port 8001 \
    --jinja
```

{% endcode %}

当你运行上面的命令时，你会得到：

<figure><img src="/files/007bab08be9f35e0d6394832c3ae39c1cf2c788b" alt=""><figcaption></figcaption></figure>

然后在一个新终端中，在执行 `pip install openai`之后，执行：

{% code overflow="wrap" %}

```python
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/Devstral-Small-2-24B-Instruct-2512",
    messages = [{"role": "user", "content": "2+2 等于多少？"},],
)
print(completion.choices[0].message.content)
```

{% endcode %}

这将直接打印 4。

### :toolbox:Devstral 2 工具调用教程

在完成 [#llama-server-serving-and-deployment](#llama-server-serving-and-deployment "mention") 之后，我们就可以加载一些工具，看看 Devstral 的实际表现！让我们创建一些工具——复制、粘贴并在 Python 中执行它们。

{% code expandable="true" %}

```python
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 subtract_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,
    "subtract_number": subtract_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": "subtract_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"],
            },
        },
    },
]
```

{% endcode %}

然后我们从一组随机可能消息中提出一个简单问题来测试模型：

{% code overflow="wrap" %}

```python
import random
messages = [{
    "role": "user",
    "content": [random.choice([
        {"type": "text", "text": "你能给我写一个故事吗？"},
        {"type": "text", "text": "今天的日期加 3 天是多少？"},
        {"type": "text", "text": "获取当前纳秒时间。"},
        {"type": "text", "text": "用 Python 创建一个斐波那契函数并求 fib(20)。"},
    ])],
}]
```

{% endcode %}

然后我们使用下面的函数（复制并粘贴后执行），它们会自动解析函数调用——Devstral 2 可能会同时发起多个调用！

```python
temperature = 0.15
from openai import OpenAI
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,
        tools = tools if tools else None,
        tool_choice = "auto" if tools else None,
    )
    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
print(json.dumps(messages[original_messages_len:], indent = 2))
```

一分钟后，我们得到：

<figure><img src="/files/48bee5bbce9677b4b0247aa161def8fc96198906" alt=""><figcaption></figcaption></figure>

或者以 JSON 形式：

```json
[
  {
    "role": "assistant",
    "tool_calls": [
      {
        "id": "JviLK0wUveWguuKQHgZdFdYI2adu85jy",
        "function": {
          "arguments": "{}",
          "name": "write_a_story"
        },
        "type": "function"
      }
    ],
    "content": null
  },
  {
    "role": "tool",
    "tool_call_id": "JviLK0wUveWguuKQHgZdFdYI2adu85jy",
    "name": "write_a_story",
    "content": "很久很久以前，在一个遥远的星系里……"
  },
  {
    "role": "assistant",
    "tool_calls": null,
    "content": "在一个遥远的星系里，群星燃烧着超凡脱俗的光芒，有一颗名为埃尔多里亚（Eldoria）的星球。埃尔多里亚是一个鲜明对比的世界——繁华的城市坐落在高耸的群山之间，广袤的沙漠在双重太阳下无尽延伸。埃尔多里亚的人们以操控能量的技艺而闻名遐迩，尤其是他们传奇般的激光光刃。\n\n这些光刃不仅仅是武器；它们是使用者灵魂的延伸，由稀有晶体锻造而成，闪烁着宇宙的本质。每一把光刃都独一无二，其颜色与力量都反映了主人独特的性格与精神。最精湛的战士，被称为“刃生者”（Saberborn），能够以如此精确与优雅的方式挥舞光刃，仿佛他们在与现实本身共舞。\n\n在刃生者之中，有一位名叫凯尔（Kael）的战士。凯尔曾是神圣光刃神殿的守护者之一，但因违抗议会的命令而被放逐，成为了一个被遗弃者。议会试图垄断激光光刃的力量，并用它们控制埃尔多里亚的人民。凯尔相信，光刃应该被那些想要守护的人使用，而不是用来支配他人。\n\n一天，凯尔收到了一条来自沙漠边缘小村庄的求救信号。这个村庄正遭到一伙叛变刃生者的袭击，由一位名叫维克西斯（Vexis）的冷酷军阀率领。维克西斯想要夺取村庄中的古老遗物——一块据说能将任何激光光刃威力放大十倍的晶体。如果维克西斯得逞，他的军队将势不可挡，埃尔多里亚也将坠入黑暗。\n\n凯尔知道自己必须行动。他将那把深蓝色的光刃佩戴在身上——刀锋中低鸣着宇宙的能量——然后穿越沙漠出发。旅途危机四伏，有沙暴，也有维克西斯侦察兵布下的隐秘陷阱。但凯尔没有停下，他被曾经誓言守护的那些人的记忆所驱动，继续前行。\n\n当他抵达村庄时，战斗已进入白热化。维克西斯的战士挥舞着光刃，残酷而高效，轻易击倒防守者。凯尔跃入战团，蓝色光刃化作一道光影，他一个接一个解除并击败敌人。村民们看到救星到来，纷纷在他身后集结，自己的光刃也闪烁起来，共同奋战，夺回家园。\n\n凯尔在村庄广场中央与维克西斯正面对峙。军阀的光刃呈病态的绿色，涌动着黑暗能量。\"你来晚了，凯尔，\"维克西斯冷笑道，\"遗物是我的，有了它，我将统治埃尔多里亚。\"凯尔毫不退缩，举起光刃。\"除非我死，\"他回答。\n\n两位战士猛烈交锋，光刃相接，火花四溅。凯尔感受到遗物的原始力量正沿着维克西斯的刀锋奔涌，但他拒绝后退。他引导出自己的能量，光刃愈发明亮，顶住了维克西斯的攻势。最后，在一次绝望的反击中，凯尔缴了维克西斯的械，令他的光刃铛啷落地。\n\n维克西斯愤怒地低吼着承认失败，但凯尔没有杀他。相反，他给了对方一个选择：\"加入我，守护埃尔多里亚，或者离开，永远不要回来。\"维克西斯心服口服，也看清了凯尔话语中的真相，于是选择与他站在一起。\n\n随着维克西斯一派成为盟友，凯尔和村民们夺回了遗物，并利用它的力量恢复了埃尔多里亚的平衡。光刃神殿得以重建，而激光光刃再次被那些追求守护而非控制的人所执掌。\n\n凯尔的传奇不断传扬，他成为埃尔多里亚人民希望的象征。他的故事提醒他们，即便在最黑暗的时刻，勇气与正义之光也终将胜出。于是，刃生者继续存在，他们的激光光刃成为充满阴影的银河中力量与团结的灯塔。"
  }
]
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://unsloth.ai/docs/zh/mo-xing/tutorials/devstral-2.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
