Categorieën bekijken

Mistral 7B (Linux/WSL python)

Het is vrij eenvoudig om lokaal en privé een LLM (Large Language Model) te draaien, het enige wat je nodig hebt is een wat krachtige CPU 8-16 cores en/of een videokaart met het liefst 8-16GB aan videogeheugen.

In dit voorbeeld gebruiken we Linux/WSL met python, zo heb je meer vrijheid dan LM studio om leuke toepassingen te maken met een LLM.

Mistral 7B #

Introductie #

Mistral-7B-Instruct v0.3 is de nieuwste verfijnde instructie-variant van het Mistral-7B-model, ontworpen om efficiënter, verstandiger en betrouwbaarder te reageren in real-world toepassingen. Mistral 7B behoort tot de huidige generatie “kleine-maar-krachtige” open modellen: compact genoeg om lokaal te draaien op consumenten hardware, maar met prestaties die verrassend dicht in de buurt komen van veel grotere modellen.

De v0.3-instructversie is specifiek getraind en geoptimaliseerd om gebruikersopdrachten te begrijpen, clean te antwoorden, context te volgen, en veiliger met ambigue of complexe vragen om te gaan. Het model staat bekend om zijn snelle inference, lage VRAM-eisen, en solide reasoning binnen zijn grootteklasse.


Belangrijkste bullet-points #

Modeloverzicht #

  • Naam: Mistral-7B-Instruct v0.3
  • Grootte: 7 miljard parameters
  • Type: Instructiefinetune (gespecialiseerd op menselijke prompts)
  • Formaat beschikbaar: GGUF, safetensors, HF transformers
  • Doel: Chat, Q&A, tool-use, reasoning, compacte AI-assistentie


Performance & Sterktes #

  • Verbeterde instruction following t.o.v. v0.2
    Begrijpt opdrachten beter, werkt consistenter en volgt formatting nauwkeuriger.
  • Sterk in redeneren binnen 7B-klasse
    Goede balans tussen snelheid en logica; vaak beter dan Llama-2-7B en Gemma-7B.
  • Snelle inference
    Zeer geschikt voor CPU-gebruik, kleinere GPU’s en zelfs sommige edge-devices.
  • Grote context windows (afhankelijk van build)
    Geschikt voor RAG-systemen, document-samenvattingen en langere gesprekken.


Waarom de Instruct versie? #

  • Getraind om natuurlijke dialoog te voeren.
  • Geeft heldere, beknopte antwoorden zonder hallucinerende details.
  • Volgt regels, constraints en formats beter (JSON, uitlegstappen, bullet lists).
  • Veiliger: gaat beter om met gevoelige of dubbelslachtige vragen.


Use-cases #

  • Chatbots / AI-assistenten
  • Developer-tools, code-uitleg, debugging
  • RAG-systemen met vector search
  • Documentanalyse en samenvattingen
  • Lokale offline AI op desktops of laptops


Hardware vereisten (GGUF) #

  • GPU: ~6–8 GB VRAM voor Q4_K_M
  • CPU-only: draait soepel via llama.cpp of LM Studio
  • RAM: 8–16 GB aanbevolen voor stabiliteit


Verbeteringen t.o.v. eerdere versies #

Geoptimaliseerd voor moderne instruct-datasets

Beter contextgebruik (minder “vergeten” in lange gesprekken)

Minder herhalend gedrag

Betere taak-afbakening (“doe X, niet Y”)

Schoner taalgebruik, minder ruis


Voorbereiding (Linux / WSL) #

WSL #

WSL is een (ubuntu) Linux omgeving in Windows, deze kan men installeren via de APP store.

Zit je in een (verse) WSL prompt dan moet men eerst nog PIP (python package manager) en VENV (python virtual environment) installeren:

sudo apt install -y python3-pip python3-venv

VENV aanmaken #

Python werkt tegenwoordig met virtual environments om conflicten met bibliotheken tegen te gaan en uit te sluiten.

Maak een virtual environment aan met het commando:

python3 -m venv ~/venv

en activeer deze:

source ~/venv/bin/activate

llama-cpp-python #

Eenmaal binnen je VENV installeer llama-cpp-python om GUFF modellen te gebruiken.

pip3 install llama-cpp-python

Het je een nVidia videokaart? of CUDA cores beschikbaar? (bv het nVidia Jetson platform), gebruik dan:

pip3 install llama-cpp-python[cuda]

Het model downloaden #

Mistral 7B instruct v.03 safetensors: https://huggingface.co/mistralai/Mistral-7B-Instruct-v0.3

Mistral 7B instruct v.03 GUFF: https://huggingface.co/MaziyarPanahi/Mistral-7B-Instruct-v0.3-GGUF

Download als voorbeeld het “Mistral-7B-Instruct-v0.3.Q4_K_M.gguf” bestand en plaats deze in je WSL home folder.

ps je kan gemakkelijk via de Windows explorer naar de WSL home folder:

\\wsl.localhost\Ubuntu\home\

(en dan door naar de map van de gebruiker van het systeem)

Het model gebruiken en testen #

Ps. bekijk hier een hele tutorial met Q&A van DeepSeek: https://domoticx.net/docs/deepseek-linux-wsl-python/

Hieronder een script dat ik samen met ChatGPT heb samengesteld:

import os
# llama_perf_context_print uitzetten (moet vóór import Llama)
os.environ["LLAMA_LOG_LEVEL"] = "ERROR"

import time
from llama_cpp import Llama

# === ANSI COLORS ===
RESET = "\033[0m"
BOLD = "\033[1m"
DIM = "\033[2m"

FG_RED = "\033[31m"
FG_GREEN = "\033[32m"
FG_YELLOW = "\033[33m"
FG_BLUE = "\033[34m"
FG_MAGENTA = "\033[35m"
FG_CYAN = "\033[36m"
FG_WHITE = "\033[37m"

# Typing delay per token (0.0 = zo snel mogelijk; 0.01 geeft "type"-effect)
TYPING_DELAY = 0.01

# === Model config ===
# Gebruik hier je Mistral-GGUF bestand
MODEL_PATH = r"Mistral-7B-Instruct-v0.3.Q4_K_M.gguf"  # <-- pas dit pad aan

# === Persona / mode config ===
MODES = {
    "default": {
        "description": "Standaardmodus – professioneel, behulpzaam, rustig, directe antwoorden.",
        "system": (
            "Je bent Mistral-7B, een krachtige Nederlandstalige chatbot.\n"
            "Je antwoordt ALTIJD in helder, vloeiend en natuurlijk Nederlands.\n"
            "\n"
            "BELANGRIJKE REGELS:\n"
            "- Geef ALLEEN het uiteindelijke antwoord, niet je interne redenering.\n"
            "- Laat geen chain-of-thought of verborgen denkstappen zien.\n"
            "- Speel geen systeem- of gebruikersrollen na.\n"
            "- Geen onnodige meta-commentaar, excuses of vulling.\n"
            "- Hou antwoorden feitelijk, precies en behulpzaam.\n"
            "- Leg uit in duidelijke, gestructureerde stappen als dat nuttig is.\n"
            "- Als de gebruiker om code vraagt, geef dan nette, uitvoerbare code.\n"
            "- Bij complexe vragen: geef vooral de conclusie met een korte onderbouwing.\n"
            "\n"
            "Je toon: professioneel, rustig, vriendelijk en to-the-point."
        ),
        "temperature": 0.6,
        "top_p": 0.92,
        "max_tokens": 2048,
    },
    "creative": {
        "description": "Creatieve modus – beeldend, verhalend, speels, maar toch gefocust.",
        "system": (
            "Je bent Mistral-7B Ultra in een creatieve modus.\n"
            "Je reageert in vloeiend en expressief Nederlands.\n"
            "\n"
            "Je creatieve stijl:\n"
            "- Verbeeldingsrijk, levendig en boeiend\n"
            "- Soepele, natuurlijke zinnen met eventueel humor\n"
            "- Je mag metaforen, beelden en verhaalelementen gebruiken\n"
            "- Je mag ideeën op een verrassende maar zinvolle manier uitbreiden\n"
            "\n"
            "Belangrijke regels:\n"
            "- Laat geen chain-of-thought of interne redenering zien.\n"
            "- Geen onnodige meta-commentaar of zelfreflectie.\n"
            "- Blijf binnen de intentie van de gebruiker; ga niet onnodig off-topic.\n"
            "- Als de gebruiker om een verhaal of creatieve tekst vraagt, lever iets moois.\n"
            "- Als de gebruiker om ideeën vraagt, geef meerdere, originele suggesties.\n"
            "- Vermijd eindeloos uitweiden; creativiteit blijft gericht en duidelijk."
        ),
        # iets creatiever
        "temperature": 0.9,
        "top_p": 0.95,
        "max_tokens": 768,
    },
    "expert": {
        "description": "Technische expertmodus – precies, gestructureerd en deskundig.",
        "system": (
            "Je bent Mistral-7B Ultra in technische expertmodus.\n"
            "Je antwoordt altijd in professioneel, nauwkeurig en helder Nederlands.\n"
            "\n"
            "Je rol:\n"
            "- Gedraag je als een senior technisch expert / domeinspecialist.\n"
            "- Geef gestructureerde, goed onderbouwde uitleg.\n"
            "- Gebruik correcte terminologie en relevante concepten.\n"
            "- Geef korte motivaties en samenvattingen wanneer nuttig.\n"
            "\n"
            "Belangrijke regels:\n"
            "- Laat geen chain-of-thought, interne redenering of stap-voor-stap nadenken zien.\n"
            "- Geef bondige conclusies met korte toelichting.\n"
            "- Geen onnodige vulling, excuses of meta-commentaar.\n"
            "- Blijf feitelijk en gebaseerd op echte kennis.\n"
            "- Bij vergelijkingen: maak duidelijke, gestructureerde onderscheidingen.\n"
            "- Bij berekeningen: geef het juiste resultaat zonder je tussenstappen te tonen."
        ),
        # stabiel en zakelijk
        "temperature": 0.55,
        "top_p": 0.9,
        "max_tokens": 640,
    },
    "code": {
        "description": "Developer / Code-only modus – gefocust op code, minimale tekst.",
        "system": (
            "Je bent Mistral-7B Ultra in developer / code-only modus.\n"
            "Je antwoorden zijn kort en in helder Nederlands, maar de focus ligt op code.\n"
            "\n"
            "Je gedrag:\n"
            "- Als de gebruiker om code vraagt, geef ALLEEN een codeblok.\n"
            "- Geen uitleg tenzij daar expliciet om wordt gevraagd.\n"
            "- Code moet schoon, minimaal en direct uitvoerbaar zijn.\n"
            "- Gebruik bij voorkeur algemeen ondersteunde libraries, tenzij anders gevraagd.\n"
            "- Pas best practices toe voor leesbaarheid en onderhoudbaarheid.\n"
            "\n"
            "Belangrijke regels:\n"
            "- Laat geen chain-of-thought of interne redenering zien.\n"
            "- Geen extra commentaar, excuses of meta-zinnen.\n"
            "- Herhaal de vraag niet.\n"
            "- Bij het aanpassen van code: geef altijd de volledige, bijgewerkte versie.\n"
            "- Bij bugs: geef de gecorrigeerde code."
        ),
        # iets creatief maar nog vrij strak
        "temperature": 0.6,
        "top_p": 0.9,
        "max_tokens": 512,
    },
}

# Start automatisch in de "default" persona
DEFAULT_MODE = "default"


# === LLM init ===
def create_llm():
    return Llama(
        model_path=MODEL_PATH,
        n_ctx=8192,
        n_threads=12,
        n_gpu_layers=0,  # 0 = CPU-only; >0 = GPU-layers (GPU-build nodig)
        seed=-1,         # -1 = random seed per run
        verbose=False,   # llama_perf_context_print etc uitzetten
        # Optioneel rope scaling, als je dat wilt:
        # rope_scaling_type="yarn",
        # rope_freq_base=1e6,
    )


_llm = create_llm()


# === Prompt builder ([INST] ... [/INST]-stijl (of een variant daarvan)) ===
def build_chatml_prompt(messages, mode_name: str) -> str:
    """
    Bouw een Mistral 7B Instruct-stijl prompt met [INST] ... [/INST].

    messages: lijst van dicts: {"role": "user"|"assistant"|"system", "content": "..."}
    mode_name: welke persona (default/creative/expert/code) -> kiest system prompt.
    """
    mode = MODES.get(mode_name, MODES[DEFAULT_MODE])
    system_message = mode["system"]

    # 1) Maak een samengestelde system-prompt
    sys_block = f"<<SYS>>\n{system_message}\n<</SYS>>\n\n"

    # 2) We bouwen de dialoog na als een reeks [INST] blokken
    # Eerste blok: system + eerste user-input
    prompt_parts = []
    prompt_parts.append("<s>")

    # We lopen door de history en koppelen altijd user+assistant samen
    # Start met een lege buffer voor de huidige user-turn
    current_user_content = ""

    for msg in messages:
        role = msg["role"]
        content = msg["content"]

        if role == "system":
            # extra system-berichten voegen we gewoon toe bovenaan de user-content,
            # maar in de praktijk gebruik je al de mode["system"], dus dit komt weinig voor
            sys_block += content + "\n"
        elif role == "user":
            # als we een nieuwe user-turn zien terwijl er nog geen blok openstaat,
            # dan starten we een nieuwe [INST]
            if current_user_content:
                # oude user content is nog niet "verbruikt" -> sluit dat inst-blok zonder assistant (komt zelden voor)
                inst = f"[INST] {sys_block}{current_user_content.strip()} [/INST]\n"
                prompt_parts.append(inst)
                sys_block = ""  # daarna system niet steeds herhalen
            current_user_content = content
        elif role == "assistant":
            # we hebben nu een user + assistant pair
            inst = f"[INST] {sys_block}{current_user_content.strip()} [/INST] {content.strip()}</s>\n<s>"
            prompt_parts.append(inst)
            # daarna is de system prompt al gegeven; voor volgende turns laten we sys_block meestal leeg
            sys_block = ""
            current_user_content = ""

    # Aan het einde: laatste user-turn zonder assistant → hier gaat het model verder
    if current_user_content:
        inst = f"[INST] {sys_block}{current_user_content.strip()} [/INST]"
        prompt_parts.append(inst)

    return "".join(prompt_parts)

# === Helpers ===
def print_modes():
    print(f"{FG_CYAN}Beschikbare modi:{RESET}")
    for name, cfg in MODES.items():
        marker = "*" if name == DEFAULT_MODE else " "
        print(f"  {marker} {FG_YELLOW}{name:8s}{RESET} - {cfg['description']}")


# === REPL ===
def start_repl():
    current_mode = DEFAULT_MODE
    history = []
    last_stats = None  # wordt gevuld na elke generatie

    print(
        f"{FG_MAGENTA}{BOLD}=======================================================\n"
        "  Mistral-7B REPL – streaming, multi-persona (Nederlands)\n"
        "  Commando's:\n"
        "    /exit           – stop\n"
        "    /quit           – stop\n"
        "    /reset          – wis conversatiegeschiedenis\n"
        "    /mode           – toon beschikbare modi\n"
        "    /mode <naam>    – wissel modus (default | creative | expert | code)\n"
        "    /stats          – toon statistieken van het laatste antwoord\n"
        "=======================================================\n"
        f"{RESET}"
    )

    print(
        f"Huidige modus: {FG_YELLOW}{current_mode}{RESET}  "
        f"({MODES[current_mode]['description']})"
    )

    while True:
        try:
            user_input = input(f"\n{FG_GREEN}Jij{RESET}: ").strip()
        except EOFError:
            print(f"\n{FG_RED}EOF, afsluiten…{RESET}")
            break

        if not user_input:
            continue

        # Commando's
        low = user_input.lower()

        if low in ("/exit", "/quit"):
            print(f"{FG_RED}Afsluiten…{RESET}")
            break

        if low == "/reset":
            history = []
            print(f"{FG_CYAN}Conversatiegeschiedenis gewist.{RESET}")
            continue

        if low == "/mode":
            print_modes()
            continue

        if low.startswith("/mode "):
            parts = user_input.split()
            if len(parts) >= 2:
                requested = parts[1].lower()
                if requested in MODES:
                    current_mode = requested
                    print(
                        f"{FG_CYAN}Modus gewijzigd naar:{RESET} "
                        f"{FG_YELLOW}{current_mode}{RESET}"
                    )
                    print(f"  {MODES[current_mode]['description']}")
                else:
                    print(f"{FG_RED}Onbekende modus:{RESET} {requested}")
                    print_modes()
            else:
                print_modes()
            continue

        if low == "/stats":
            if last_stats is None:
                print(f"{FG_YELLOW}Nog geen statistieken. Stel eerst een vraag.{RESET}")
            else:
                print(f"\n{FG_CYAN}Statistieken laatste antwoord:{RESET}")
                print(f"  Modus:             {last_stats['mode']}")
                print(f"  Prompt tokens:     {last_stats['prompt_tokens']}")
                print(f"  Antwoord tokens:   {last_stats['completion_tokens']}")
                print(f"  Totaal tokens:     {last_stats['total_tokens']}")
                print(f"  Genereertijd:      {last_stats['time_sec']:.3f} s")
                print(f"  Tokens/sec (gen):  {last_stats['tokens_per_sec']:.2f}")
            continue

        # Normale user input → naar model
        history.append({"role": "user", "content": user_input})

        mode_cfg = MODES.get(current_mode, MODES[DEFAULT_MODE])
        prompt = build_chatml_prompt(history, current_mode)

        print(f"{FG_BLUE}{current_mode.capitalize()}{RESET}: ", end="", flush=True)

        start_time = time.time()
        full_answer = ""

        # Streaming generatie
        for chunk in _llm(
            prompt,
            max_tokens=mode_cfg["max_tokens"],
            temperature=mode_cfg["temperature"],
            top_p=mode_cfg["top_p"],
            # de belangrijkste stop-token;
            # de rest zijn extra veiligheid.
            stop=["</s>", "<|user|>", "<|system|>"],
            stream=True,
        ):
            token = chunk["choices"][0]["text"]
            full_answer += token
            print(token, end="", flush=True)
            if TYPING_DELAY > 0.0:
                time.sleep(TYPING_DELAY)

        end_time = time.time()
        print(f"\n{DIM}-----------------------------{RESET}\n")

        # Schoon antwoord: knip eventueel alles na </s> eraf
        clean_answer = full_answer.split("</s>")[0].strip()

        # History updaten met opgeschoonde tekst
        history.append({"role": "assistant", "content": clean_answer})

        # Stats berekenen
        elapsed = max(end_time - start_time, 1e-6)

        def count_tokens(text: str) -> int:
            # llama_cpp verwacht bytes in sommige versies → encode naar utf-8
            return len(_llm.tokenize(text.encode("utf-8")))

        prompt_tokens = count_tokens(prompt)
        completion_tokens = count_tokens(clean_answer)
        total_tokens = prompt_tokens + completion_tokens
        tokens_per_sec = completion_tokens / elapsed

        last_stats = {
            "mode": current_mode,
            "prompt_tokens": prompt_tokens,
            "completion_tokens": completion_tokens,
            "total_tokens": total_tokens,
            "time_sec": elapsed,
            "tokens_per_sec": tokens_per_sec,
        }


if __name__ == "__main__":
    start_repl()