Strojenie Ministral-3 na vLLM

Praktyczny przewodnik po odpowiedziach w postaci zwykłego tekstu i JSON

Jeopardy-Game-Benchmark
Benchmark Gry Jeopardy

Lekcje wyciągnięte podczas tworzenia 12-osobowej symulacji Jeopardy zasilanej przez AI Ministral-3-14B-Instruct-2512.


Wprowadzenie

Uruchomienie Ministral-3 na vLLM Jest zaskakująco potężny. Model jest szybki, kreatywny i potrafi generować wysokiej jakości odpowiedzi nawet pod dużym obciążeniem.

Ale gdy przejdziesz od prostych podpowiedzi w czacie do strukturyzowane dane wyjściowe, automatyzacja lub użycie programistyczneszybko robi się skomplikowane.

W trakcie tworzenia Gra oparta na sztuczeniu maszynowym Jeopardy z 12 jednoczesnymi graczami i setkami wywołań modelunatrafiliśmy na kilka praktycznych problemów:

Ten przewodnik podsumowuje praktyczne wnioski wyciągnięte podczas rozwiązywania tych problemóworaz konkretnymi wzorcami, które możesz ponownie wykorzystać w swoich projektach.


Rzeczywisty scenariusz

Nasz projekt porównawczy symuluje pełną grę Jeopardy, w której:

Pojedynczy przebieg gry może łatwo przekroczyć 800 wywołań API.

To środowisko ujawniło przypadki brzegowe, które rzadko pojawiają się w prostych demonstracjach — co czyni je doskonałym środowiskiem testowym do zrozumienia, jak Ministral zachowuje się w rzeczywistych warunkach produkcyjnych.


1. Uruchamianie Ministral-3 na vLLM

Modele Ministral nie używaj standardowej konfiguracji tokenizera HuggingFace.

Oznacza to, że polecenie uruchamiające musi wyraźnie włączyć Format tokenizera Mistral.

bash
vllm serve mistralai/Ministral-3-14B-Instruct-2512 \
  --tokenizer_mode mistral \
  --config_format mistral \
  --load_format mistral

Jeśli Twoja aplikacja polega na wywoływanie funkcji, dodaj flagi narzędzi:

bash
--enable-auto-tool-choice
--tool-call-parser mistral

Ograniczenie

W przeciwieństwie do niektórych innych modeli, Ministral nie obsługuje chat_template_kwargs.

Jeśli wyślesz żądanie w następujący sposób:

json
{
  "chat_template_kwargs": {
    "enable_thinking": false
  }
}

vLLM zwraca:

Kod
HTTP 400: chat_template is not supported for Mistral tokenizers

Oznacza to funkcje takie jak przełączanie "trybu myślenia" (stosowane z modelami takimi jak Qwen lub DeepSeek) są po prostu niedostępne.

Na szczęście, rzadko jest to potrzebne, ponieważ Ministral domyślnie generuje zwięzłe odpowiedzi.


2. Temperatura: Najważniejszy parametr

Ten oficjalna dokumentacja vLLM konsekwentnie używa następującej wartości z Ministral-3:

Kod
temperature = 0.15

Na pierwszy rzut oka wydaje się to niezwykle niskie. Jednak okazuje się, że tak jest. krytyczne dla zadań strukturalnych.

Co się dzieje w wyższych temperaturach

Korzystając z domyślnych ustawień w stylu OpenAI:

javascript
temperature: 0.7

model staje się nadmiernie kreatywny pod względem struktury.

Proste zapytanie takie jak:

json
{ "expertise": "2-3 topics they know best" }

może zwrócić coś takiego:

json
{
  "expertise": [
    {
      "category": "Gourmet Pizza Alchemy",
      "detail": "Can transform random ingredients into Michelin-star pizza"
    },
    {
      "category": "Sumo Wrestling Physics",
      "detail": "Understands body mechanics and center-of-gravity combat"
    }
  ]
}

Technicznie poprawny JSON, ale... nie to, czego wymaga schemat.

W efekcie:


Dlaczego 0.15 działa lepiej

W niskiej temperaturze model staje się strukturalnie zdyscyplinowany.

javascript
temperature: 0.15

Korzyści:

Nawet generowanie kreatywnych tekstów pozostaje silne — model po prostu przestaje improwizować ze strukturą.

Rekomendacja: Użyj temperature: 0.15 jako domyślne ustawienie dla Ministral-3.


3. Otrzymywanie czystych odpowiedzi JSON

Generowanie JSON czytelny maszynowo Generowanie czytelnego JSON z LLM jest trudniejsze niż się wydaje.

Ministral ma tendencję do interpretacji pól schematu semantycznie zamiast strukturalnie, co prowadzi do głęboko zagnieżdżonej struktury wyjściowej.


Podejście naiwne

Na przykład zapytanie:

Kod
Return JSON with these fields.

często generuje rozbudowane struktury.

Przykładowe zapytanie:

json
{ "expertise": "2-3 topics they know best" }

Typowa odpowiedź:

json
{
  "expertise": [
    {
      "category": "Ancient Roman Engineering",
      "detail": "Knows aqueduct systems in surprising detail"
    },
    {
      "category": "Pizza Dough Chemistry",
      "detail": "Obsessed with yeast fermentation dynamics"
    }
  ]
}

To zużywa trzy razy więcej niż oczekiwane tokeny.


Niezawodne rozwiązanie: dwuwarstwowe podpowiedzi

Najbardziej niezawodnym rozwiązaniem jest połączenie dwie instrukcje.

Warstwa 1 — Instrukcja systemowa

Kod
Respond with ONLY valid JSON.
No markdown, no explanation, no text before or after the JSON.
Keep values as short plain strings — never use nested objects or arrays.

Warstwa 2 — Ograniczenie schematu

Bezpośrednio obok definicji schematu:

Kod
Every value MUST be a short plain string — NO arrays, NO nested objects.

Połączony z 0.15co powoduje przewidywalny, płaski JSON.


Budżetowanie tokenów

Nawet przy ograniczeniach, Ministral ma tendencję do generowania dłuższe wartości niż w przypadku innych modeli.

Przykładowa obserwacja z naszego benchmarku:

Model Liczba tokenów wymagana
GPT-4o ~512
Qwen ~512
Ministral-3 ~1024

Bezpieczna zasada:

Ustal budżet na 1,5–2× więcej tokenów dla danych wyjściowych JSON.


Defensywne parsowanie JSON

Nawet przy idealnych promptach, modele czasem generują nieprawidłowy JSON.

Dobrą strategią jest dodanie warstwy defensywnej analizy składniowej.


1) Wyodrębnij JSON z Markdown

javascript
function extractJSON(raw, shape) {
  var text = raw.replace(/^```(?:json)?\s*/i, '').replace(/\s*```$/i, '').trim();
  if (shape === 'array') {
    var m = text.match(/\[[\s\S]*\]/);
    if (m) text = m[0];
  } else {
    var m = text.match(/\{[\s\S]*\}/);
    if (m) text = m[0];
  }
  return text;
}

2) Napraw niekompletne wyjście

Śledź otwarte nawiasy i automatycznie je zamykaj:

javascript
var stack = [];
var inStr = false, esc = false;

for (var i = 0; i < text.length; i++) {
  var ch = text[i];

  if (esc) { esc = false; continue; }
  if (ch === '\\') { esc = true; continue; }
  if (ch === '"') { inStr = !inStr; continue; }
  if (inStr) continue;

  if (ch === '{') stack.push('}');
  else if (ch === '[') stack.push(']');
  else if (ch === '}' || ch === ']') stack.pop();
}

text = text.replace(/,\s*$/, '');

while (stack.length > 0)
  text += stack.pop();

3) Wypłaszczanie zagnieżdżonych wartości

Jeśli model nadal zwraca zagnieżdżone struktury:

javascript
if (Array.isArray(value)) {
  flat = value.map(function(item) {
    if (typeof item === 'string') return item;
    if (typeof item === 'object') return Object.values(item).join(' — ');
    return String(item);
  }).join(', ');
}

4) Ponów nieudane żądania

Prosta pętla ponownych prób dramatycznie zwiększa niezawodność.

Ponieważ Ministral zachowuje się konsekwentnie w niskiej temperaturzepowtórne próby zwykle się powadzą.

Zalecane:

Kod
2–3 retry attempts

4. Uzyskiwanie czystych odpowiedzi w postaci zwykłego tekstu

Ministral uwielbia formatowanie.

Nawet jeśli poprosisz o zwykły tekst, to ma tendencję do generowania:

  • tekst pogrubiony
  • kursywa
  • nagłówki
  • formatowanie kodu w linii

Dzieje się tak, ponieważ model jest dostarczany z wbudowanym zachęta do formatowania bogatych markdown w poleceniu systemowym.


Dlaczego to ma znaczenie

Wiele potoków opiera się na prostych sprawdzeniach ciągów znaków.

Przykład:

javascript
verdict.toUpperCase().startsWith('CORRECT')

Jeśli model zwróci:

Kod
**CORRECT**

Sprawdzenie się nie powiodło.


Rozwiązanie: Zawsze usuwaj formatowanie Markdown

Najbezpieczniejszym podejściem jest znormalizuj wszystkie wyjścia przed przetwarzaniem.

javascript
function stripMarkdown(text) {
  if (!text) return text;

  var s = text.replace(/\*\*([^*]+)\*\*/g, '$1');
  s = s.replace(/__([^_]+)__/g, '$1');
  s = s.replace(/\*([^*]+)\*/g, '$1');
  s = s.replace(/^#{1,6}\s+/gm, '');
  s = s.replace(/`([^`]+)`/g, '$1');
  s = s.replace(/^```[a-z]*\s*$/gm, '');

  return s.trim();
}

Zastosuj to do każdej odpowiedzi modelu, nie tylko Ministral.

Pozwala to uniknąć rozgałęzień specyficznych dla modelu i utrzymuje spójność potoków.


5. Centralizacja zachowań specyficznych dla modelu

Jeśli Twój system obsługuje wiele rodzin modeli (Mistral, Qwen, DeepSeek, Llama itp.), najbardziej przejrzysty projekt to skoncentruj zachowanie modelu w jednym miejscu.

Przykład:

javascript
function buildModelProfile(modelName) {
  var lower = modelName.toLowerCase();
  var isMistral = lower.includes('mistral') || lower.includes('ministral');

  return {
    family: isMistral ? 'Mistral' : 'Generic',

    jsonSystemInstruction: isMistral
      ? 'Respond with ONLY valid JSON. No markdown. Keep values as short plain strings.'
      : 'You output only valid JSON. No markdown fences, no explanation.',

    jsonSchemaHint: isMistral
      ? ' Every value MUST be a short plain string — NO arrays, NO nested objects.'
      : '',

    jsonTemperature: isMistral ? 0.15 : 0.7,
    defaultTemperature: isMistral ? 0.15 : 0.7,

    plainTextInstruction: ' Do not use markdown formatting.'
  };
}

Pozwala to na zachowanie reszty Twojego systemu. niezależny od modelu.

Dodawanie nowego modelu później staje się trywialne.


Arkusz oszustwa Ministral-3

Ustawienie Zalecana wartość Powód
tokenizer_mode mistral Wymagane dla poprawnego tokenizera
config_format mistral Wymagane
load_format mistral Wymagane
chat_template_kwargs Nie wysyłaj Nieobsługiwane
temperatura 0.15 Zapobiega halucynacjom strukturalnym
Instrukcja JSON Jawne wartości płaskie Unikaj zagnieżdżonych obiektów
max_tokens 1,5–2× typowy Model jest rozwlekły
Usuwanie formatowania Markdown Zawsze Zapobiegaj błędom formatowania
Ponowne próby JSON 2–3 próby Niezawodna regeneracja

Ostateczne przemyślenia

Ministral-3 działa wyjątkowo dobrze po odpowiednim dostrojeniu.

Kiedy już:

  • obniż temperaturę
  • ogranicz strukturę JSON
  • unormuj wyjście markdown
  • dodaj defensywne parsowanie

model staje się zdumiająco przewidywalny i gotowy do produkcji.

W naszym teście porównawczym Jeopardy, ta konfiguracja obsługiwała:

  • 12 współbieżnych uczestników AI
  • ponad 800 wywołań API na sesję
  • Przepustowość 2000+ tokenów/sek.
  • spójna, ustrukturyzowana odpowiedź

Wszystko działa lokalnie na Infrastruktura GPU Trooper.AI.


Rozpocznij

Wypróbuj pełny szablon wdrożenia vLLM tutaj: Serwer kompatybilny z vLLM OpenAI