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