Finjustering af Ministral-3 på vLLM

En Praktisk Guide til Almindelig Tekst og JSON-Svar

Jeopardy-Game-Benchmark
Jeopardy-Game-Benchmark

Læringer fra opbygningen af en 12-deltager AI Jeopardy-simulation drevet af Ministral-3-14B-Instruct-2512.


Introduktion

Kører Ministral-3 på vLLM er overraskende kraftfuld. Modellen er hurtig, kreativ og i stand til at producere svar af høj kvalitet, selv under tunge arbejdsbelastninger.

Men så snart du bevæger dig fra simple chat-prompts til strukturerede output, automatisering eller programmeret brugting bliver hurtigt komplicerede.

Under udviklingen af en AI-drevet Jeopardy-spil med 12 samtidige spillere og hundredvis af modelkald, vi stødte på flere praktiske problemer:

Denne guide opsummerer praktiske erfaringer opnået ved at løse disse problemer, sammen med konkrete mønstre, du kan genbruge i dine egne projekter.


Det virkelige scenarie

Vores benchmark-projekt simulerer et fuldt Jeopardy-spil, hvor:

En enkelt spilrunde kan let overstige 800 API-kald.

Dette miljø afslørede kanttilfælde, der sjældent opstår i simple demoer – hvilket gør det til et fantastisk testmiljø for at forstå hvordan Ministral opfører sig under virkelige produktionsbelastninger.


1. Betjening af Ministral-3 på vLLM

Ministral-modeller brug ikke standard HuggingFace tokenizer konfigurationen.

Dette betyder, at startkommandoen eksplicit skal aktivere Mistral tokenizer format.

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

Hvis din applikation er afhængig af funktionskald, tilføj flagene:

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

Vigtig begrænsning

I modsætning til nogle andre modeller, Ministral understøtter ikke chat_template_kwargs.

Hvis du sender en anmodning som denne:

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

vLLM returnerer:

Kode
HTTP 400: chat_template is not supported for Mistral tokenizers

Det betyder funktioner som eksplicit "tænkende tilstand"-skift (brugt med modeller som Qwen eller DeepSeek) er simpelthen ikke tilgængelig.

Heldigvis er dette sjældent nødvendigt, da Ministral allerede producerer koncise output som standard.


2. Temperatur: Den vigtigste parameter

The officiel vLLM-dokumentation bruger konsekvent følgende værdi med Ministral-3:

Kode
temperature = 0.15

Ved første øjekast virker dette ekstremt lavt. Dog viser det sig at være afgørende for strukturerede opgaver.

Hvad sker der ved højere temperaturer

Ved brug af OpenAI-standardindstillingen:

javascript
temperature: 0.7

modellen bliver overdrevent kreativ med struktur.

En simpel forespørgsel som:

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

kan returnere noget i stil med:

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"
    }
  ]
}

Selvom det teknisk set er gyldig JSON, er det det ikke. ikke hvad skemaet bad om.

Resultatet:


Hvorfor 0.15 fungerer bedre

Ved lav temperatur bliver modellen strukturelt disciplineret.

javascript
temperature: 0.15

Fordele:

Selv kreativ tekstgenerering forbliver stærk — modellen holder simpelthen op med at improvisere med strukturen.

Anbefaling: Brug temperature: 0.15 som din standardindstilling for Ministral-3.


3. Getting Clean JSON Responses

Producerer maskinlæsbar JSON fra LLM'er er sværere end det lyder.

Ministral har tendens til at fortolke skemafelter semantisk i stedet for struktureltsom fører til dybt indlejret output.


Den naive tilgang

En prompt som:

Kode
Return JSON with these fields.

producerer ofte verbøse strukturer.

Eksempel på anmodning:

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

Typisk svar:

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

Dette forbruger tre gange de forventede tokens.


Den pålidelige løsning: Promptering i to lag

Den mest pålidelige løsning kombinerer to instruktioner.

Lag 1 — Systeminstruktion

Kode
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.

Lag 2 — Skemabegrænsning

Lige ved siden af skema definitionen:

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

Kombineret med temperatur 0,15hvilket producerer forudsigelig flad JSON.


Tokenbudgettering

Selv når den er begrænset, har Ministral tendens til at producere længere værdier end andre modeller.

Eksempel på observation fra vores benchmark:

Model Nødvendige tokens
GPT-4o ~512
Qwen ~512
Ministral-3 ~1024

En sikker regel:

Budgetér 1,5–2× antallet af tokens til JSON-output.


Forsvarende JSON-analyse

Selv med perfekte prompter genererer modeller af og til misdannet JSON.

En god strategi er at tilføje defensive parsing lag.


1) Udtræk JSON fra 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) Reparer afkortet output

Spor åbne parenteser og luk dem automatisk:

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) Udflad indlejrede værdier

Hvis modellen stadig returnerer indlejrede strukturer:

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) Gentag mislykkede anmodninger

En simpel retry-løkke øger pålideligheden dramatisk.

Fordi Ministral opfører sig konsekvent ved lav temperatur, forsøg lykkes normalt.

Anbefalet:

Kode
2–3 retry attempts

4. Getting Clean Plain-Text Responses

Ministral elsker formatering.

Selv når du beder om klar tekst, har den tendens til at producere:

  • fed tekst
  • kursiv fremhævelse
  • overskrifter
  • formatering af inline-kode

Dette sker, fordi modellen leveres med en indbygget system prompt der tilskynder til rig markdown formatering.


Hvorfor dette er vigtigt

Mange pipelines er afhængige af simple strengtjek.

Eksempel:

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

Men hvis modellen returnerer:

Kode
**CORRECT**

kontrollen mislykkes.


Løsning: Fjern altid markdown

Den sikreste tilgang er at normaliser alle output før behandling.

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();
}

Anvend dette til hvert modelrespons, ikke kun Ministral.

Det undgår modelspecifik forgrening og holder pipelines konsistente.


5. Centralisering af Model-Specifik Adfærd

Hvis dit system understøtter flere modelfamilier (Mistral, Qwen, DeepSeek, Llama osv.), er det mest vedligeholdelsesvenlige design at centraliser modeladfærd på ét sted.

Eksempel:

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.'
  };
}

Dette giver resten af dit system mulighed for at forblive model-uafhængig.

Det bliver trivielt at tilføje en ny model senere.


Ministral-3 Snyd ark

Indstilling Anbefalet værdi Årsag
tokenizer_mode mistral Påkrævet for korrekt tokenizer
config_format mistral Påkrævet
load_format mistral Påkrævet
chat_template_kwargs Send ikke Ikke understøttet
temperatur 0.15 Forhindrer strukturel hallucination
JSON-instruktion Eksplicitte flade værdier Undgå indlejrede objekter
max_tokens 1,5–2× typisk Modellen er ordrig
Fjernelse af Markdown Altid Forhindre formateringsfejl
JSON-genforsøg 2–3 forsøg Pålidelig gendannelse

Afsluttende tanker

Ministral-3 yder ekstremt godt, når det er korrekt indstillet.

Når du:

  • sænk temperaturen
  • begræns JSON-strukturer
  • normaliser markdown output
  • tilføj defensiv parsing

modellen bliver bemærkelsesværdigt forudsigelig og produktionsklar.

I vores Jeopardy-benchmark understøttede denne opsætning:

  • 12 samtidige AI-deltagere
  • 800+ API-kald pr. session
  • 2.000+ tokens/sek gennemstrømning
  • konsistent struktureret output

Kører lokalt på Trooper.AI GPU-infrastruktur.


Kom i gang

Prøv den fulde vLLM-implementeringsskabelon her: vLLM OpenAI-kompatibel server