Wir entwickeln bei AINA lokale KI-Lösungen — Systeme, bei denen Daten das Haus nicht verlassen. Keine Cloud-API, keine laufenden Token-Kosten, volle Kontrolle. Klingt gut in der Theorie. Aber wie weit kann man das treiben?

Um das herauszufinden, habe ich unsere RAG-Demo — einen KI-Assistenten für eine fiktive Gemeindeverwaltung — auf einen Hetzner VPS deployed. Die Rahmenbedingungen: 2 ARM-CPU-Kerne, 4 GB RAM, keine GPU. Monatliche Kosten: 3,29 Euro.

Das Ergebnis nach einer Nacht Optimierung: Antwortzeit von 80 Sekunden auf 7 Sekunden — ein 11-facher Speedup auf identischer Hardware. Nur durch Software-Entscheidungen.

Hier ist der Weg dorthin.

Phase 1: Der naive Ansatz — Ollama

Der erste Instinkt bei LLM-Deployment ist oft: Ollama installieren, Modell laden, fertig. Genau das habe ich gemacht. Qwen2.5-1.5B als Modell, Ollama als Runtime.

Das Ergebnis war ernüchternd: 80 bis 120 Sekunden pro Anfrage. Auf einem Server mit 2 Kernen und 4 GB RAM ist das nicht überraschend — aber es ist auch nicht vorzeigbar. Kein Demo-Besucher wartet zwei Minuten auf eine Antwort.

Erschwerend kam hinzu, dass das LLM frei halluzinierte. Eine Frage nach Müllgebühren lieferte „3,58 Euro" — eine komplett erfundene Zahl. Das ist ein bekanntes Problem von Sprachmodellen ohne Kontext-Anbindung, aber in einer Demo für Verwaltungsmitarbeiter ein K.O.-Kriterium.

Phase 2: Streaming und Prompt Engineering

Der nächste Schritt: Streaming aktivieren. Statt den Nutzer auf die komplette Antwort warten zu lassen, erscheint der erste Token bereits nach etwa 2 Sekunden. Das verändert die wahrgenommene Geschwindigkeit dramatisch — der Besucher sieht sofort, dass etwas passiert.

Parallel dazu habe ich die Prompts verschärft und Token-Limits reduziert. Die RAG-Spalte der Demo liefert jetzt belegbare Antworten: „264,00 Euro — Quelle: Abfallgebühren-Satzung". Halluzination vs. belegte Fakten, direkt nebeneinander — das ist als Demo-Effekt sogar wirkungsvoller als ein Modell, das nie halluziniert.

Trotzdem: Die Gesamtantwortzeit lag weiterhin bei über 60 Sekunden.

Phase 3: Der Durchbruch — llama.cpp statt Ollama

An diesem Punkt habe ich Ollama durch llama.cpp ersetzt — direkt kompiliert, ohne Abstraktionsschicht. Weniger Overhead, volle Kontrolle über Context-Window, Batch-Size und Threading.

Der Unterschied war sofort messbar: llama.cpp war etwa 4× schneller als Ollama auf identischer Hardware.

Aber es ging noch weiter. Zwei zusätzliche Optimierungen brachten den entscheidenden Unterschied:

Sequentiell statt parallel. Auf einem Server mit nur 2 CPU-Kernen ist parallele Verarbeitung kontraproduktiv. Der Overhead für Context-Switching frisst den theoretischen Gewinn auf. Im sequentiellen Modus — erst das LLM, dann der RAG-Retrieval — sank die Antwortzeit um weitere 65 %.

Ergebnis: 7 Sekunden End-to-End. Auf einem ARM-Server für 3,29 Euro im Monat.

Validierung auf x86: AVX2 und der versteckte Schalter

Um die Ergebnisse zu validieren, habe ich einen zweiten VPS aufgesetzt — diesmal x86 (AMD EPYC, 4 Kerne, 8 GB RAM) für 5,49 Euro monatlich.

Erste Messung: 14 Sekunden. Langsamer als erwartet für die doppelte Kernzahl. Die Ursache: Das AVX2-Compiler-Flag war deaktiviert. GGML_AVX2 stand auf OFF — die SIMD-Vektorisierung, die x86-Prozessoren ihren Geschwindigkeitsvorteil bei Matrix-Operationen gibt, wurde schlicht nicht genutzt.

Nach dem Recompile mit aktiviertem AVX2 und angepasstem Threading (4 statt 3 Threads bei 4 Kernen im sequentiellen Betrieb): 11,5 Sekunden.

Der x86-Server ist damit trotz doppelter Kernzahl langsamer als der ARM-Server. Das liegt an der Architektur: ARM Ampere-Kerne haben bei LLM-Inference eine überraschend gute Performance-pro-Watt-Ratio, und bei nur 2 Kernen ist der Scheduling-Overhead minimal.

Was ich gelernt habe

1. Das Framework ist nicht die Lösung. Ollama ist großartig für lokale Entwicklung. Für Deployment auf Minimal-Hardware ist der Overhead zu groß. llama.cpp direkt zu kompilieren brachte 4× Speedup — ohne jede Hardware-Änderung.

2. Weniger Kerne, weniger Overhead. Auf Systemen mit ≤2 Kernen ist sequentielle Verarbeitung schneller als parallele. Das widerspricht der Intuition, ist aber messbar: −65 % Antwortzeit.

3. Compiler-Flags prüfen. AVX2 brachte auf x86 einen Unterschied von +53 % Token/s. Ein einzelnes Compiler-Flag, das standardmäßig deaktiviert war.

4. Context-Size matters für RAG. 512 Tokens sind zu wenig, wenn der RAG-Kontext sinnvoll in den Prompt eingebettet werden soll. 1024 ist das Minimum — auch auf Minimal-Hardware machbar.

5. Halluzination ist ein Feature, kein Bug — zumindest in der Demo. Die Gegenüberstellung von halluzinierter LLM-Antwort und belegter RAG-Antwort macht den Mehrwert von Retrieval Augmented Generation greifbarer als jede Folie.

Die Architektur

Die Demo läuft strikt sequentiell: Das LLM streamt seine Antwort (die RAG-Spalte bleibt leer), nach einer kurzen Pause startet der RAG-Retrieval, und die RAG-Spalte streamt ihre belegte Antwort.

Der Tech-Stack:

  • Modell: Qwen2.5-1.5B (Q4_K_M quantisiert, ~1 GB)
  • Embeddings: Nomic Embed Text v1.5 (Q8, für RAG-Retrieval)
  • Inference: llama.cpp (direkt kompiliert)
  • RAG: ChromaDB mit lokalen Embeddings
  • Frontend: Gradio (Python)
  • Hosting: Hetzner Cloud VPS (ARM, 2 vCPU, 4 GB RAM)

Alles als systemd-Services, automatisch startend. Gesamtkosten: 3,29 Euro pro Monat, keine weiteren laufenden Kosten.

Für wen ist das relevant?

Nicht jede Organisation braucht GPT-4-Niveau. Viele Anwendungsfälle — FAQ-Bots, interne Wissensassistenten, Formular-Hilfen — funktionieren hervorragend mit kleinen, spezialisierten Modellen. Und manchmal ist „alle Daten bleiben im Haus" nicht optional, sondern Pflicht.

Lokale KI heißt nicht „Modell starten und fertig". Es heißt: jeden Layer verstehen — vom Compiler-Flag bis zum Kernel-Scheduler. Aber das Ergebnis ist ein System, das auf einem Server für 3,29 Euro im Monat läuft, keine GPU braucht, keine Cloud-API, keine laufenden Token-Kosten. Und das in 7 Sekunden antwortet.

Live-Demo: local-ai.aina.technology