Local LLM mit Tiefgang - volle Transparenz statt Blackbox

Local LLM mit Tiefgang - volle Transparenz statt Blackbox
By Matthias Petermann / on 18.11.2025

Einführung

In meinen vorherigen Artikeln Local LLM – KI-Sprachmodelle ohne Cloud und Lokale KI in der Praxis – Redmine-Ticketanalyse mit Ollama habe ich gezeigt, wie unkompliziert lokale KI heute ist. Einfach Modell ziehen, starten, nutzen – fertig.

Aber während ich diese Leichtigkeit durchaus genieße, bleibt ein Gedanke: Wie viel davon verstehe ich wirklich?

Denn die Komfortschicht von Ollama überdeckt eine komplexe Infrastruktur. Und genau da beginnt der spannende Teil: Wir steigen unter die Oberfläche.

ℹ️ Warum dieser Artikel?
Viele Teams starten mit Ollama – und bleiben dort hängen. Die meisten wissen gar nicht, welche Schichten, Libraries und Build-Prozesse im Hintergrund laufen. Dieser Artikel hilft, diese Lücke zu schließen.
📝 Abgrenzung
Dieser Artikel behandelt keine Trainingsprozesse. Training wird ein eigenes, ausführliches Thema.

Der Stack unter Ollama – was passiert da eigentlich?

Oft wird Ollama als „Runtime“ wahrgenommen. In Wahrheit ist es ein komfortabler Orchestrator, der auf mehreren anderen Projekten aufbaut – insbesondere auf llama.cpp.

Was Ollama wirklich übernimmt

  • Scheduler & Ressourcenverwaltung
  • Modell-Bundles und Registry
  • Prompt-/Template-Handling
  • Daemon + HTTP-API
  • Backend-Auswahl (CUDA/Metal/ggml)
  • Cache-Management
ℹ️ Fun Fact: Ollama ist kein Fork von llama.cpp
Viele glauben, Ollama enthalte eine eigene Inferenz-Engine. Tatsächlich nutzt es weitgehend unverändert das llama.cpp-Backend – nur verpackt in eine komfortable Daemon-Struktur.

Was llama.cpp macht

llama.cpp ist bewusst minimalistisch:

  • reine C/C++-Engine
  • offene GGUF-Modelle
  • keine Bundles, keine Registry
  • vollständige Kontrolle über Quantisierung, Tokenizer, Build-Flags

Ollama erweitert, abstrahiert und verpackt – llama.cpp führt aus.


Warum der manuelle Weg wichtig ist

Wer KI in sensiblen oder produktiven Bereichen betreibt, braucht Transparenz:

  • Welche Komponenten werden ausgeführt?
  • Welche Compiler wurden genutzt?
  • Welche quantisierte Variante habe ich wirklich geladen?
  • Welche Abhängigkeiten existieren?
  • Wie vertrauenswürdig ist die Supply Chain?
ℹ️ Transparenz als Sicherheitsfaktor
Je weniger Blackbox, desto geringer das Risiko unbemerkter Manipulation – sei es im Build-Prozess, im Modell oder in der Laufzeitumgebung.

Schritt 1: llama.cpp selbst bauen

Um vollständige Transparenz über die Inferenz-Engine zu erhalten, bauen wir llama.cpp direkt aus dem Quellcode. Der Prozess besteht aus drei klar abgegrenzten Teilschritten:


1.1 Repository klonen

Wir holen den vollständigen, unveränderten Quellcode von GitHub:

git clone https://github.com/ggml-org/llama.cpp.git
cd llama.cpp

Damit steht exakt die Version bereit, die auch auf GitHub veröffentlicht wurde – inklusive Commit-Hashes und Build-Skripten.


1.2 Abhängigkeiten installieren

Am Beispiel von Ubuntu lässt sich die für llama.cpp notwendige Build-Umgebung mit wenigen Paketen einrichten. Der Vorteil: Die gesamte Toolchain bleibt extrem schlank und transparent.

sudo apt update
sudo apt install -y \
    build-essential \
    cmake \
    libcurl4-openssl-dev

Was steckt dahinter?

  • build-essential Installiert die grundlegende Compiler-Toolchain (gcc/g++, make).

  • cmake Wird von llama.cpp als Build-System verwendet.

  • libcurl4-openssl-dev Kleine, unkritische Systembibliothek – nötig für HTTP-Funktionalität einzelner llama.cpp-Tools.

Damit ist die Ubuntu-Umgebung bereits vollständig vorbereitet, um llama.cpp aus dem Quellcode zu kompilieren.


1.3 Build erzeugen

Wir erzeugen eine reproduzierbare Release-Build-Variante:

cmake -B build
cmake --build build --config Release

Optional: Binary global verfügbar machen:

export PATH=$PATH:$(pwd)/build/bin

ℹ️ Tipp: Reproduzierbare Builds

Bei jedem Build sollte man dokumentieren:

  • Commit-Hash
  • CMake-Version
  • Compiler-Version (gcc/clang)
  • verwendete Flags

So entsteht eine vollständig überprüfbare und reproduzierbare Inferenzumgebung, die langfristig auditier- und dokumentierbar bleibt.


Schritt 2: Modelle ohne Ollama laden

Ollama nutzt eigene, komplexe Bundle-Formate. Für einen vollständig transparenten Stack laden wir stattdessen reine GGUF-Modelle – ohne Zusatzschichten, ohne versteckte Metadaten und ohne proprietäre Struktur.

Der Prozess besteht aus drei klaren Einzelschritten:


2.1 Download-Tool bauen

Wir verwenden ein kleines, schlankes CLI („hfdownloader“), das ausschließlich die tatsächlichen Artefakte von HuggingFace lädt – rekursiv, reproduzierbar und ohne Abhängigkeit auf Python-Ökosysteme.

git clone https://github.com/bodaay/HuggingFaceModelDownloader.git
cd HuggingFaceModelDownloader

# Binary bauen
make

# Binary systemweit verfügbar machen
cp output/hfdownloader_linux_amd64_2.0.0 ~/bin/hfdownloader
ℹ️ Warum nicht einfach 'huggingface-cli'?
Das HuggingFace-Python-CLI ist funktional, aber bringt viele Abhängigkeiten mit. hfdownloader ist ein einzelnes statisches Binary → ideal für reproduzierbare Systeme und Minimalcontainer.

2.2 Zugriffs-Token setzen

Private oder große Modelle benötigen ein gültiges HuggingFace-Access-Token:

export HF_TOKEN=hf_xxxxxxxxx

2.3 GGUF-Modell herunterladen

Jetzt holen wir das Modell genau so, wie es auf HuggingFace liegt – ohne Umwandlung oder Zusatzdaten:

hfdownloader download Qwen/Qwen2.5-3B-Instruct-GGUF -o ./models

Die Ordnerstruktur entspricht exakt dem Repository. Wichtig: Hier wird nichts entpackt, zusammengeführt oder verändert.

ℹ️ Warum GGUF?
GGUF ist ein offenes, dokumentiertes Austauschformat – vollständig transparent, ohne Blackbox-Metadaten und optimiert für lokale Inferenz. Damit ist es ideal für reproduzierbare, überprüfbare LLM-Pipelines.

Schritt 3: Eine eigene API – mit llama-server

Nachdem Modell und Engine bereitstehen, brauchen wir nur noch eine leichtgewichtige, lokale API, über die Anwendungen mit dem Modell kommunizieren können. Genau diese Lücke schließt llama-server.

llama-server ist die offizielle HTTP-Server-Komponente von llama.cpp und bietet:

  • eine OpenAI-kompatible REST-API
  • ohne proprietäre Layers oder Bundles
  • direkt ausführbar als einzelne Binary

Damit lässt sich jedes GGUF-Modell sofort per Curl, Skript oder Client-Bibliothek ansprechen.

llama-server   -m ./models/Qwen/Qwen2.5-3B-Instruct-GGUF/qwen2.5-3b-instruct-q5_k_m.gguf   --host 0.0.0.0   --port 8080

Schritt 4: API testen

Sobald llama-server läuft, stellt er eine OpenAI-kompatible REST-API bereit. Das bedeutet: vorhandene Tools, Skripte und Clients funktionieren ohne Anpassungen – nur der Host ändert sich.

Die wichtigsten Endpunkte für erste Tests sind:


4.1 Chat Completion (mehrere Nachrichten, Rollen)

Der Chat-Endpunkt bildet das klassische ChatGPT-Verhalten ab:

curl http://localhost:8080/v1/chat/completions \
  -d '{
        "model": "qwen2.5-3b",
        "messages": [
          { "role": "user", "content": "Erkläre lokale LLMs." }
        ]
      }'

4.2 Text Completion (einfacher Prompt → Antwort)

Für klassische Prompt-in/Text-out-Fälle:

curl http://localhost:8080/v1/completions \
  -d '{
        "model": "qwen2.5-3b",
        "prompt": "Schreibe ein Gedicht."
      }'

Dieser Endpunkt verhält sich wie das ursprüngliche GPT-3 API-Modell: Ein reiner Prompt ohne Rollen – ideal für kurze Texte, Code, Extraktionen und strukturierte Ausgaben.

ℹ️ Hinweis: API-Kompatibilität
Viele Tools, die OpenAI-APIs unterstützen (LangChain, LlamaIndex, N8N, Flowise usw.), funktionieren mit llama-server direkt – oft ohne Änderungen.

Schritt 5: Bonus – die neue Web-GUI von llama-server

llama-server bringt eine leichtgewichtige UI gleich mit:

LLama WebUI

Features:

  • browserbasierte Chat-Oberfläche
  • mehrere Sessions, History, Parametersteuerung
  • vollständig lokal
  • Dokumentverarbeitung (PDF, TXT, MD, JSON)
ℹ️ Performance-Tipp
Die Web-UI nutzt dieselbe Engine wie die API. Wenn maximale Performance erforderlich ist, sollten im UI große Kontextfenster oder komplexe Systemprompts deaktiviert werden.

Fazit

Wir haben den Ollama-Stack Schicht für Schicht geöffnet und einen vollständig transparenten lokalen LLM-Workflow aufgebaut:

  • die Inferenz-Engine selbst kompiliert
  • Modelle direkt und nachvollziehbar geladen
  • Quantisierung bewusst kontrolliert
  • eine eigene, minimalistische OpenAI-kompatible API bereitgestellt
  • und mit der Web-UI einen lokalen Bedienkomfort ergänzt

Ergebnis: Statt einer abstrahierten Blackbox steht nun ein klarer, auditierbarer und reproduzierbarer KI-Stack, der sich in jeder Ebene verstehen und anpassen lässt.


ℹ️ Woran ich als Nächstes arbeiten möchte

Im nächsten Schritt möchte ich mich einmal in Ruhe mit den inneren Mechanismen eines Modells beschäftigen. Geplant ist ein Blick auf:

  • die Erstellung eines eigenen Tokenizers
  • die Definition einer kleinen, nachvollziehbaren Modellarchitektur
  • erste Experimente zu Training & Feintuning
  • die Konvertierung nach GGUF
  • einfache Evaluations- und Testverfahren

Ziel ist kein vollwertiger Leitfaden, sondern ein verständlicher Mini-LLM-Stack als Lernprojekt, an dem sich die zentralen Bausteine eines Modells erkunden lassen.