Ministral-3 afstemmen op vLLM

Een praktische handleiding voor platte tekst en JSON-antwoorden

Jeopardy-Game-Benchmark
Jeopardy-Game-Benchmark

Lessons geleerd tijdens het bouwen van een 12-speler AI Jeopardy simulatie aangedreven door Ministral-3-14B-Instruct-2512.


Inleiding

Uitvoeren Ministral-3 op vLLM is verrassend krachtig. Het model is snel, creatief en in staat om hoogwaardige antwoorden te produceren, zelfs onder zware belasting.

Maar zodra je overgaat van eenvoudige chatprompts naar gestructureerde output, automatisering of programmatisch gebruikdingen snel ingewikkeld worden.

Tijdens de ontwikkeling van een AI-gestuurd Jeopardy-spel met 12 gelijktijdige spelers en honderden modeloproepen, we kwamen verschillende praktische problemen tegen:

Deze handleiding vat samen praktische lessen geleerd tijdens het oplossen van deze problemensamen met concrete patronen die je in je eigen projecten kunt hergebruiken.


Het Real-World Scenario

Ons benchmarkproject simuleert een volledig Jeopardy-spel waarin:

Een enkele spelronde kan gemakkelijk 800 API-aanroepen overschrijden 800 API-oproepen.

Deze omgeving bracht randgevallen aan het licht die zelden voorkomen in eenvoudige demo's — waardoor het een geweldige testomgeving is om te begrijpen hoe Ministral presteert onder echte productiebelastingen.


1. Ministral-3 serveren op vLLM

Ministral-modellen Gebruik niet de standaard HuggingFace tokenizer configuratie.

Dit betekent dat het opstartcommando expliciet de Mistral tokenizer-formaat.

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

Als uw applicatie hiervan afhankelijk is functie aanroepen, voeg de tool-vlaggen toe:

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

Belangrijke beperking

In tegenstelling tot sommige andere modellen, Ministral ondersteunt dit niet chat_template_kwargs.

Als u een verzoek als dit verzendt:

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

vLLM retourneert:

Code
HTTP 400: chat_template is not supported for Mistral tokenizers

Dat betekent functies zoals expliciete "denkmodus" schakeling (gebruikt met modellen zoals Qwen of DeepSeek) zijn eenvoudigweg niet beschikbaar.

Gelukkig is dit zelden nodig omdat Ministral standaard al beknopte uitvoer produceert.


2. Temperatuur: De belangrijkste parameter

De officiële vLLM documentatie gebruikt consequent de volgende waarde met Ministral-3:

Code
temperature = 0.15

Op het eerste gezicht lijkt dit erg laag. Echter, blijkt dit toch het geval te zijn. cruciaal voor gestructureerde taken.

Wat gebeurt er bij hogere temperaturen

Met de standaardinstellingen van OpenAI:

javascript
temperature: 0.7

het model wordt overdreven creatief met structuur.

Een eenvoudige vraag zoals:

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

kan zoiets retourneren:

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

Technisch gezien geldige JSON, maar het is niet wat het schema vroeg.

Het resultaat:


Waarom 0.15 werkt beter

Bij lage temperatuur wordt het model structureel gedisciplineerd.

javascript
temperature: 0.15

Voordelen:

Zelfs creatieve tekstgeneratie blijft sterk — het model stopt simpelweg met het improviseren met structuur.

Aanbeveling: Gebruik temperature: 0.15 als uw standaardwaarde voor Ministral-3.


3. Schone JSON-antwoorden verkrijgen

Produceren machine-leesbare JSON van LLM's is lastiger dan het klinkt.

Ministral interpreteert schema-velden vaak semantisch semantisch in plaats van structureel, wat resulteert in een diep geneste uitvoer.


De naïeve aanpak

Een prompt zoals:

Code
Return JSON with these fields.

produceert vaak uitvoerige structuren.

Voorbeeld aanvraag:

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

Typische reactie:

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

Dit verbruikt drie keer het verwachte aantal tokens.


De betrouwbare oplossing: Tweelaagse prompting

De meest betrouwbare oplossing combineert twee instructies.

Laag 1 — Systeeminstructie

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

Laag 2 — Schema-beperking

Direct naast de schema definitie:

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

Gecombineerd met temperatuur 0.15dit produceert voorspelbare platte JSON.


Tokenbudgettering

Zelfs wanneer beperkt, heeft Mistral de neiging om te produceren langere waarden dan andere modellen.

Voorbeeld observatie uit onze benchmark:

Model Benodigde tokens
GPT-4o ~512
Qwen ~512
Ministral-3 ~1024

Een veilige regel:

Houd rekening met 1,5–2× het aantal tokens voor JSON-uitvoer.


Defensieve JSON-parsing

Zelfs met perfecte prompts genereren modellen af en toe ongeldige JSON.

Een goede strategie is om toe te voegen defensieve parsing lagen.


1) Haal JSON uit 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) Repareer afgeknotte uitvoer

Open en sluit haakjes automatisch:

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) Geneste waarden afvlakken

Als het model nog steeds geneste structuren retourneert:

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) Herhaal mislukte verzoeken

Een eenvoudige herhaallus verhoogt de betrouwbaarheid aanzienlijk.

Omdat Ministral gedraagt zich consistent bij lage temperaturenherhalingen lukken meestal.

Aanbevolen:

Code
2–3 retry attempts

4. Schone platte tekst responses krijgen

Ministral houdt van opmaak.

Zelfs wanneer je om platte tekst vraagt, heeft het de neiging om te produceren:

  • vetgedrukte tekst
  • cursief benadrukken
  • kopjes
  • inline code opmaak

Dit gebeurt omdat het model wordt geleverd met een ingebouwde systeemprompt die rijke markdown-opmaak aanmoedigt.


Waarom dit belangrijk is

Veel pipelines vertrouwen op eenvoudige stringcontroles.

Voorbeeld:

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

Maar als het model teruggeeft:

Code
**CORRECT**

de controle mislukt.


Oplossing: verwijder altijd de markdown

De veiligste aanpak is om normaliseer alle uitvoer voor verwerking.

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

Pas dit toe voor elk modelantwoord, niet alleen Ministral.

Het vermijdt model-specifieke vertakkingen en houdt pipelines consistent.


5. Centraliseren van Modelfamilie-Specifiek Gedrag

Als uw systeem meerdere modelfamilies ondersteunt (Mistral, Qwen, DeepSeek, Llama, enz.), is het meest onderhoudbare ontwerp om centraliseer modelgedrag op één plek.

Voorbeeld:

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

Hierdoor kan de rest van uw systeem behouden blijven. model-onafhankelijk.

Het toevoegen van een nieuw model wordt later triviaal.


Ministral-3 Cheat Sheet

Instelling Aanbevolen Waarde Reden
tokenizer_mode mistral Vereist voor de juiste tokenizer
config_format mistral Vereist
load_format mistral Vereist
chat_template_kwargs Niet verzenden Niet ondersteund
temperatuur 0.15 Voorkomt structurele hallucinaties
JSON-instructie Expliciete platte waarden Vermijd geneste objecten
max_tokens 1,5–2× gebruikelijk Het model is uitvoerig
Markdown verwijdering Altijd Voorkom formateringsfouten
JSON-pogingen 2–3 pogingen Betrouwbare herstel

Eindgedachten

Ministral-3 presteert uitzonderlijk goed wanneer het correct is afgestemd.

Zodra je:

  • verlaag de temperatuur
  • JSON-structuren beperken
  • markdown uitvoer normaliseren
  • defensieve JSON-parsing toevoegen

het model wordt opmerkelijk voorspelbaar en productieklaar.

In onze Jeopardy benchmark ondersteunde deze setup:

  • 12 gelijktijdige AI-deelnemers
  • 800+ API-aanroepen per sessie
  • 2.000+ tokens/sec doorvoer
  • consistente gestructureerde uitvoer

Alles draait lokaal op Trooper.AI GPU-infrastructuur.


Aan de slag

Probeer hier de volledige vLLM-implementatiesjabloon: vLLM OpenAI-Compatibele Server