Eigene KI
Neuer Ansatz 14.09.2026
Hardware-Kontext¶
- System: CachyOS (Arch Linux)
- CPU: Intel i7-10700K
- RAM: 32 GB DDR4
- GPU: NVIDIA RTX 4080 (16 GB VRAM)
- Ziel: Lokale KI mit Internet-Recherche, dauerhaftem Gedächtnis und Offline-Betrieb.
Ollama¶
Ollama dient als Backend, um KI-Modelle direkt auf der NVIDIA GPU (via CUDA) auszuführen.
Installation:
sudo pacman -S ollama-cuda
Dienst aktivieren:
sudo systemctl enable --now ollama
Test:
Das Modell qwen2.5:14b wurde via CLI getestet. Es ist ideal für die 16 GB VRAM, da es exzellent in Logik, Coding und deutscher Sprache ist.
ollama run qwen2.5:14b
Ich lade außerdem testweise noch folgende Modelle herunter:
qwen2.5:14b- bereits geladenqwen3.8:27bqwen3-vl:4b-instructqwen3-vl:8b-instructqwen2.5-coder:14bdeepseek-coder-v2deepseek-r1:14bnomic-embed-textFLUX.1-schnell- über ComfyUi
ComfyUI für Bildgenerierung¶
Da ich mein System sauber halten will von komischen Abhängigkeiten der Python-Installation verwende ich kein pip, sondern pipx. Mit ComfyUI werde ich nun auch uv testen.
uv installieren:
yay -S uv
ComfyUI installieren:
git clone https://github.com/comfyanonymous/ComfyUI.git
cd ComfyUI
Einrichtung¶
Virtuelle Umgebung mit uv einrichten:
❯ uv venv --python 3.11
Using CPython 3.11.16
Creating virtual environment at: .venv
Activate with: source .venv/bin/activate.fish
~/git/ComfyUI master
❯ source .venv/bin/activate.fish
~/git/ComfyUI master
ComfyUI ❯ uv pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121
Resolved 26 packages in 990ms
Prepared 26 packages in 1m 13s
Installed 26 packages in 86ms
...
Abhängigkeiten installieren:
ComfyUI ❯ uv pip install -r requirements.txt
Resolved 96 packages in 660ms
Prepared 70 packages in 17.98s
Installed 70 packages in 72ms
...
Mit hf (HuggingFace) Modelle suchen:
hf models ls --search "text encoders"
FLUX.1 herunterladen:
ComfyUI ❯ uv pip install "huggingface_hub[cli]"
Checked 1 package in 1ms
ComfyUI ❯ hf download Comfy-Org/flux1-schnell flux1-schnell-fp8.safetensors --local-dir models/checkpoints
flux1-schnell-fp8.safetensors: downloading bytes: ██████████████████████████████████████████████████████████████████████████████████████████████████| 17.2GB, 39.1MB/s
flux1-schnell-fp8.safetensors: reconstructing file: 100%|██████████████████████████████████████████████████████████████████████████████████| 17.2GB / 17.2GB, 148MB/s
✓ Downloaded
path: /home/marquisor/git/ComfyUI/models/checkpoints/flux1-schnell-fp8.safetensors
ComfyUI ❯ hf download comfyanonymous/flux_text_encoders clip_l.safetensors --local-dir models/clip
Warning: You are sending unauthenticated requests to the HF Hub. Please set a HF_TOKEN to enable higher rate limits and faster downloads.
clip_l.safetensors: downloading bytes: █████████████████████████████████████████████████████████████████████████████████████████████████████████████| 246MB, 17.0MB/s
clip_l.safetensors: reconstructing file: 100%|█████████████████████████████████████████████████████████████████████████████████████████████| 246MB / 246MB, 20.2MB/s
✓ Downloaded
path: /home/marquisor/git/ComfyUI/models/clip/clip_l.safetensors
ComfyUI ❯ hf download comfyanonymous/flux_text_encoders t5xxl_fp8_e4m3fn.safetensors --local-dir models/clip
t5xxl_fp8_e4m3fn.safetensors: downloading bytes: ███████████████████████████████████████████████████████████████████████████████████████████████████| 4.77GB, 39.1MB/s
t5xxl_fp8_e4m3fn.safetensors: reconstructing file: 100%|███████████████████████████████████████████████████████████████████████████████████| 4.89GB / 4.89GB, 143MB/s
✓ Downloaded
path: /home/marquisor/git/ComfyUI/models/clip/t5xxl_fp8_e4m3fn.safetensors
ComfyUI ❯ uv pip install --upgrade comfy-kitchen
Resolved 1 package in 52ms
Checked 1 package in 0.03ms
ComfyUI ❯ uv pip install --upgrade -r requirements.txt
Resolved 103 packages in 267ms
Prepared 25 packages in 1m 16s
Uninstalled 6 packages in 97ms
Installed 25 packages in 79ms
...
ComfyUI ❯ uv pip install --upgrade torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121
Resolved 33 packages in 738ms
Checked 33 packages in 0.22ms
ComfyUI ❯ python main.py --listen 127.0.0.1 --enable-cors-header
[INFO] setup plugin alembic.autogenerate.schemas
...
[INFO] Starting server
[INFO] To see the GUI go to: http://127.0.0.1:8188
Arbeiten mit dem Webinterface¶
Auf die Oberfläche im Browser gehen URL: http://127.0.0.1:8188:

Das Fenster schließen oder ESC drücken. Dann landet man auf dem leeren Node-Graph.
Checkpoint unter Models hinzufügen:

Rechtsclick -> Add Node -> model -> loaders -> Load CLIP (Dual):

Folgende Einstellungen machen:
,
Mit der linken Maustaste am Clip-Punkt klicken und gedrückt halten und auf freie Fläche ziehen, CLIP Text Encode (Prompt) auswählen und platzieren:

Einen zweiten CLIP Text Encode (Prompt) erstellen, Rechtsklick auf den bestehenden, Clone und platzieren, sowie mit dem Clip-Knoten des Load CLIP (Dual) verbinden:

Der obere Knoten ist für den Positiv-Prompt, der untere für den Negativ-Prompt (kann bei FLUX leer bleiben)
Doppelklick auf leere Fläche "Empty Latent Image" und platzieren, sowie auf 1024x1024 setzen:

Doppelklick auf leere Fläche "KSampler" platzieren und die Knoten verbinden wie gezeigt:

"VAE Decode" und "Save Image" hinzufügen, Roter Punkt VAE aus Load Checkpoint -> vae am VAE Decode, Rosa Punkt LATENT aus KSampler -> rosa Punkt samples am VAE Decode, blauer Punkt IMAGE aus VAE Decode -> blauer Punkt images am Save Image:

So sieht der fertige Graph aus
Ergebnisse¶
Auf Run drücken und Feuer frei! Bei Erfolg erscheint bei "Save Image" das Bild:

Die Arbeitsfläche kann man abspeichern und exportieren über das "C"omfy Menü.
ComfyUI speichert außerdem in jedem erzeugten Bild im Ordner ComfyUI/output/ automatisch das vollständige Knotensystem als Metadaten. Man kann diese .png Datei dann einfach in den Graph ziehen und das Schema wird übernommen.
Modelle nachinstallieren¶
Genauere Ergebnisse kann man mit FLUX.1-dev erhalten und im KSampler den Wert steps von 4 auf 25 oder 30 stellen. Beim FLUX1-schnell Modell bringen steps >4 nichts, sondern verfälschen das Bild nur.
Die Ausführungszeit verdoppelt sich bei mir dabei ungefähr von 3-4 Sekunden auf ca. 8 Sekunden.
In ComfyUI/models/checkpoints herunterladen:
wget -c https://huggingface.co/Comfy-Org/flux1-dev/resolve/main/flux1-dev-fp8.safetensors
In der WebUI dann bei Models refreshen und F5 drücken. Bei "Load Checkpoint" dann einfach ein neues Modell wählen und die steps auf 25 oder 30 stellen.
Höhere Textpräzision mit FP16 vom T5-XXL Modell in ComfyUI/models/clip herunterladen:
wget -c https://huggingface.co/comfyanonymous/flux_text_encoders/resolve/main/t5xxl_fp16.safetensors
Stile (LoRAs) und vieles mehr kann man auf HuggingFace herunterladen.
Beispiel Wasserfarbe/Aquarell:

AnythingLLM¶
Für Arch Linux habe ich noch kein natives Paket, weder im Repo oder AUR, noch ein Flatpak gefunden, daher nehme ich ein AppImage.
Herunterladen:
curl -L https://cdn.anythingllm.com/latest/AnythingLLMDesktop.AppImage -o AnythingLLM.AppImage
Ausführbar machen:
chmod +x AnythingLLM.AppImage
AnythingLLM starten, verschiedene Abhängigkeiten werden automatisch heruntergeladen:
./AnythingLLM.AppImage
AnythingLLM für lokale Verwendung mit Ollama manuell einrichten, Modell: qwen2.5:14b für meine nvidia GPU:

Embedding Präferenz auf Provider Ollama und Model auf nomic-embed-text:latest setzen:

Änderungen speichern nicht vergessen!
Agentenfähigkeiten -> Live-Websuche und -Browsing einschalten und Suchmaschine auf DuckDuckGo setzen:

Erinnerungen konfigurieren (rechts oben das Einstellungssymbol anklicken):

Bei Global sowas einstellen wie:

Aider¶
Aider-Chat wird der Coding Agent.
Mit pipx¶
Abhängigkeiten installieren:
sudo pacman -S gcc-fortran openblas
Mit pipx aider-chat installieren:
pipx install aider-chat
Weitere Abhängigkeiten installieren:
pipx inject aider-chat pyaudioop
Auszug:
❯ pipx install aider-chat
installed package aider-chat 0.86.2, installed using Python 3.14.7
These apps are now available
- aider
done! ✨ 🌟 ✨
Aider starten mit:
aider --model ollama_chat/qwen2.5-coder:14b
Sollte das nicht funktionieren geht noch der uv-Weg.
Mit uv¶
Da eventuell die abhängigen Bibliotheken von aider-chat noch nicht für Python 3.14.7 gebaut sind bzw. verfügbar sind, nehmen wir eine ältere Umgebung her:
❯ uv tool install --python 3.12 aider-chat
Resolved 108 packages in 138ms
Prepared 21 packages in 1.89s
Installed 108 packages in 121ms
+ aider-chat==0.86.2
...
+ zipp==3.23.0
Installed 1 executable: aider
Testumgebung¶
Aider läuft standardmäßig in dem Verzeichnis in dem es gestartet wird und arbeitet mit Git.
Daher erstelle ich für einen Testzweck ein Verzeichnis:
mkdir -p ~/AI/hardware_test
Ins Verzeichnis wechseln:
cd ~/AI/hardware_test
Git initialisieren:
git init
Aider starten mit:
aider --model ollama_chat/qwen2.5-coder:14b
Prompt: Erstelle eine Datei gpu_specs.md mit einer strukturierte Tabelle zur NVIDIA RTX 4080 Super (16 GB VRAM) im Vergleich zur RTX 4090. Gehe dabei auf Speicherbandbreite, CUDA-Cores, Stromverbrauch (TBP) und Eignung für lokale LLM-Inferenz ein.
Ergebnis gpu_specs.md innerhalb weniger Minuten:
| GPU-Modell | Speicherbandbreite | CUDA-Cores | TBP (Maximaler Typische Betriebsspannungsverbrauch) | Eignung für lokale LLM-Inferenz |
|---|---|---|---|---|
| NVIDIA RTX 4080 Super (16 GB VRAM) | 320 GB/s | 9728 | 340 W | Gut, aber begrenzt durch geringere Anzahl an CUDA-Cores und geringere VRAM |
| NVIDIA RTX 4090 (24 GB VRAM) | 384 GB/s | 16384 | 450 W | Sehr gut, dank höherer Anzahl an CUDA-Cores und mehr VRAM |
Erweitertes Modell¶
Mal mit dem großen 27B-Modell testen, 16-17 GB VRAM soll das belegen samt "minimaler" CPU-Auslagerung.
Ins Verzeichnis wechseln:
cd ~/AI/hardware_test
Aider mit 27B-Modell starten:
aider --model ollama_chat/qwen3.8:27b
Im Prompteingabefenster:
/add gpu_specs.md
Mit der schon erstellten gpu_specs.md kann weiter gearbeitet werden. Die Informationen sind ja bereits erstellt worden.
Prompt: Erweitere gpu_specs.md um eine detaillierte Gegenüberstellung der Chip-Architekturen (AD103 vs. AD102), der L2-Cache-Größen, der Tensor-Cores der 4. Generation und analysiere, warum die RTX 4080 Super für 14B-27B LLMs ein optimales Preis-Leistungs-Verhältnis auf Linux bietet.
OpenCode¶
OpenCode aus dem Arch Linux Repo installieren:
yay -S opencode-beta
Unbedingt diese beta laden, denn alles andere hat hier überhaupt nicht gescheit funktioniert!
Konfiguration von OpenCode v2 siehe z.B. offizielle Dokumentation
In einem Projektordner, z.B. ~/opencode eine opencode.jsonc erstellen:
{
"$schema": "https://opencode.ai/config.json",
"providers": {
"ollama": {
"name": "Ollama (local)",
"settings": {
"baseURL": "http://127.0.0.1:11434/v1",
"apiKey": "{env:OLLAMA_API_KEY}",
},
},
},
}
Dies definiert den Provider, mein lokales Ollama, bereits installiert im Schritt Eigene KI#Ollama
TextToSpeech (TTS)¶
Kokoro¶
Hier spiele ich mal mit Kokoro-TTS rum. Das ist Open-Source.
In meinem ~/Apps Ordner erstelle ich ein Verzeichnis kokoro-tts:
mkdir kokoro-tts && cd kokoro-tts
Python 3.11 Umgebung in diesem Verzeichnis:
uv venv --python 3.11
Umgebung für die momentane Fish-Shell aktivieren:
source .venv/bin/activate.fish
Pakete installieren:
uv pip install kokoro-onnx soundfile
Sprachdateien installieren:
python -c "
from huggingface_hub import hf_hub_download, list_repo_files
import shutil
repos = ['onnx-community/Kokoro-82M-ONNX', 'hexgrad/Kokoro-82M-ONNX']
found_repo, files = None, []
for r in repos:
try:
files = list_repo_files(r)
found_repo = r
break
except Exception:
continue
if not found_repo:
raise RuntimeError('Kein ONNX-Repository gefunden.')
onnx_file = next((f for f in files if f.endswith('.onnx')), None)
voice_file = next((f for f in files if 'voices' in f), None)
print(f'Gefunden in {found_repo}: {onnx_file} & {voice_file}')
p1 = hf_hub_download(repo_id=found_repo, filename=onnx_file)
p2 = hf_hub_download(repo_id=found_repo, filename=voice_file)
shutil.copy(p1, 'kokoro-v0_19.onnx')
shutil.copy(p2, 'voices.bin')
print('Download erfolgreich abgeschlossen!')
"
Korrekte voices.bin nachladen:
wget -O voices.bin https://github.com/thewh1teagle/kokoro-onnx/releases/download/model-files/voices.bin
Das v1.0 Modell laden:
curl -L -o kokoro-v1.0.onnx https://huggingface.co/onnx-community/Kokoro-82M-ONNX/resolve/main/onnx/model.onnx
curl -L -o voices.bin https://github.com/thewh1teagle/kokoro-onnx/releases/download/model-files/voices.bin
curl -L -o config.json https://huggingface.co/hexgrad/Kokoro-82M/raw/main/config.json
Ein Testskript für englische Sprache amerikanisch wie britisch, test_kokoro.py:
import numpy as np
# Workaround: allow_pickle=True für kokoro-onnx aktivieren
_orig_load = np.load
np.load = lambda *args, **kwargs: _orig_load(
*args, **dict(kwargs, allow_pickle=True)
)
from kokoro_onnx import Kokoro
import soundfile as sf
# Modell und Stimmen laden
kokoro = Kokoro("kokoro-v0_19.onnx", "voices.bin")
# Alle verfügbaren Stimmen im Terminal anzeigen lassen
print("Verfügbare Stimmen in deiner voices.bin:")
print(list(kokoro.voices.keys()))
print("-" * 50)
# US-Englisch Sprachausgabe ('af_bella' = American Female)
samples_us, sample_rate = kokoro.create(
"Hello! Kokoro TTS is running incredibly fast on your CachyOS system.",
voice="af_bella",
speed=1.0,
lang="en-us",
)
sf.write("output_us.wav", samples_us, sample_rate)
# UK-Englisch Sprachausgabe ('bm_george' = British Male)
samples_uk, sample_rate = kokoro.create(
"And here is a very fine British voice, testing the system.",
voice="bm_george",
speed=1.0,
lang="en-us", # v0.19 nutzt oft en-us als generelles Routing für Englisch
)
sf.write("output_uk.wav", samples_uk, sample_rate)
print("Audio-Dateien wurden erfolgreich generiert.")
Stimmen abspielen:
US: pw-play output_us.wav
UK: pw-play output_uk.wav
Fazit: Für englische Stimmen US wie UK eine sehr gute Qualität, wie ich finde!
XTTS-v2¶
Installation:
mkdir -p ~/Apps/xtts-v2 && cd ~/Apps/xtts-v2
uv venv --python 3.11
source .venv/bin/activate.fish
# PyTorch mit CUDA (RTX 4080 Super)
uv pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121
# ToS setzen und Coqui TTS installieren
set -gx COQUI_TOS_AGREED 1
uv pip install TTS
Verifizierung:
python -c "import torch, TTS; print('CUDA:', torch.cuda.is_available(), 'TTS OK')"
Ergebnis muss lauten: CUDA: True TTS OK
Transformers 4.36.2 installieren:
uv pip install "transformers==4.36.2"
Testdatei test_xtts.py erstellen:
import torch
from TTS.api import TTS
device = "cuda" if torch.cuda.is_available() else "cpu"
print(f"Lade XTTS v2 auf {device}...")
tts = TTS("tts_models/multilingual/multi-dataset/xtts_v2").to(device)
tts.tts_to_file(
text="Hallo! XTTS läuft lokal auf deiner RTX 4080 Super im Apps-Ordner unter CachyOS.",
speaker="Ana Florence",
language="de",
file_path="xtts_test.wav"
)
print("Fertig! xtts_test.wav wurde generiert.")
Skript ausführen:
python test_xtts.py
Beispiel anhören:
pw-play xtts_test.wav
Piper TTS¶
Unter Arch Linux (CachyOS) aus dem AUR installierbar:
yay -S piper-tts
<- hier fehlen noch die Modelle! ->
Beispiel einer smart_talk.py:
#!/usr/bin/env python3
import sys
import os
import subprocess
import requests
import re
def is_dense_data(text: str) -> bool:
digits_and_symbols = len(re.findall(r'[\d°C/hPa.+-]', text))
ratio = digits_and_symbols / max(len(text), 1)
keywords = ["hPa", "Apparent temp", "Wet Bulb", "Humidex", "W/m²", "kts", "mm/min"]
return ratio > 0.10 or any(k in text for k in keywords)
def clean_data_report(text: str) -> str:
text = re.sub(r'0\.0\s*mm', 'null Millimeter', text, flags=re.IGNORECASE)
text = re.sub(r'0\.0\s*kmh', 'null Kilometer pro Stunde', text, flags=re.IGNORECASE)
text = re.sub(r'0\.0\s*kts', 'null Knoten', text, flags=re.IGNORECASE)
text = re.sub(r'0\.0\s*W/m²', 'null Watt pro Quadratmeter', text, flags=re.IGNORECASE)
text = re.sub(r'\b0\.0\b', 'null Komma null', text)
text = re.sub(r'\b0,8\b|\b0\.8\b', 'null Komma acht', text)
# Standard-Einheiten & Symbole glattziehen
text = re.sub(r'(\d+)\.(\d+)', r'\1,\2', text)
# Uhrzeiten: 06:00 -> 6 Uhr, 06:05 -> 6 Uhr 5
text = re.sub(r'\b0?(\d{1,2}):(\d{2})\b', lambda m: f"{int(m.group(1))} Uhr" if m.group(2) == "00" else f"{int(m.group(1))} Uhr {int(m.group(2))}", text)
text = re.sub(r'([A-Za-zäöüÄÖÜß]+)\s+(\d{1,2})\s+(\d{4})', r'\2. \1 \3', text)
text = re.sub(r'(\d+)\s*[-–]\s*(\d+)', r'\1 bis \2', text)
# Maßeinheiten fixen
text = re.sub(r'°C', ' Grad Celsius', text)
text = re.sub(r'°F', ' Grad Fahrenheit', text)
text = re.sub(r'hPa', ' Hektopascal', text)
text = re.sub(r'W/m²', ' Watt pro Quadratmeter', text)
text = re.sub(r'kmh', ' Kilometer pro Stunde', text, flags=re.IGNORECASE)
text = re.sub(r'kts', ' Knoten', text, flags=re.IGNORECASE)
text = re.sub(r'mm/min', ' Millimeter pro Minute', text, flags=re.IGNORECASE)
text = re.sub(r'mm/stunde|mm/h', ' Millimeter pro Stunde', text, flags=re.IGNORECASE)
text = re.sub(r'mm', ' Millimeter', text)
text = re.sub(r'in\.', ' Zoll', text)
return text
def normalize_for_tts(text: str, force_raw: bool = False) -> str:
text = re.sub(r'(\d+)\s*[-–]\s*(\d+)', r'\1 bis \2', text)
if force_raw or is_dense_data(text):
print("[Bypass] Deterministischer Regex-Modus aktiv (kein LLM).")
return clean_data_report(text)
prompt = f"""Schreibe den folgenden Text für eine Sprachausgabe um.
Wandele ALLE Zahlen in korrekte, vollständig ausgeschriebene deutsche Wörter um. KEINE Erfindungen, keine Einleitungen, kein Chinesisch!
MUSTER FÜR ZAHLEN UND KONTEXT:
1984 (als Jahr) -> neunzehnhundertvierundachtzig
1984€ (als Währung) -> eintausendneunhundertvierundachtzig Euro
2024 -> zweitausendvierundzwanzig
54 -> vierundfünfzig
15.4 -> fünfzehn Komma vier
ca. -> circa
WICHTIG:
- Beachte den Kontext! Währungen/Mengen ab 1000 als "eintausend...", Jahre vor 2000 als "neunzehnhundert...".
- Verschlucke NIEMALS die Zehnerstellen bei Jahreszahlen!
- Behalte den restlichen Text absolut unverändert bei.
Text: {text}
Ausgabe (NUR der bereinigte Text, keine Einleitung):"""
try:
resp = requests.post("http://localhost:11434/api/generate", json={
"model": "qwen2.5:14b",
"prompt": prompt,
"stream": False,
"options": {"temperature": 0.0}
}, timeout=15)
out = resp.json()["response"].strip()
if any(ord(c) > 0x4E00 for c in out):
return clean_data_report(text)
return out
except Exception:
return clean_data_report(text)
def speak(raw_text: str, force_raw: bool = False):
clean = normalize_for_tts(raw_text, force_raw=force_raw)
clean = clean_data_report(clean) # Safety net für Einheiten/Uhrzeiten
print(f"\n[Gereinigter TTS-Text]: {clean}\n")
model_path = os.path.expanduser("~/piper-tts/models/de_DE-thorsten-medium.onnx")
out_path = "/tmp/tts_out.wav"
if not os.path.exists(model_path):
print(f"Fehler: Piper-Modell nicht gefunden unter {model_path}")
return
cmd = ["piper-tts", "--model", model_path, "--output_file", out_path]
subprocess.run(cmd, input=clean.encode("utf-8"), check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
subprocess.run(["pw-play", out_path])
if __name__ == "__main__":
args_raw = [a for a in sys.argv[1:] if a == "--raw"]
force_raw = len(args_raw) > 0
non_flag_args = [a for a in sys.argv[1:] if a != "--raw"]
content = ""
if non_flag_args:
arg = non_flag_args[0]
if os.path.isfile(arg):
with open(arg, "r", encoding="utf-8") as f:
content = f.read()
else:
content = arg
elif not sys.stdin.isatty():
content = sys.stdin.read()
else:
content = "Im Jahr 2026 waren 54 Leute da."
if content.strip():
speak(content, force_raw=force_raw)
else:
print("Kein Text zum Vorlesen gefunden.")
TBC
Was sind Tokens?¶
Tokens sind wie Klemmbausteine für Sprache: Feste Größe, standardisiert, optimale digitale Verarbeitungsmethode.
Einfache Wörter wie "Haus", "Baum", "Apfel" sind ein Klemmbaustein = 1 Token.
Längere Wörter werden zum Beispiel in Silben oder sinnvolle Wortteile zerlegt: "Speicherverwaltung" -> Speicher + ver + waltung = 3 Tokens.
Sonderzeichen, Satzzeichen, Zeilenumbrüche sind einzeln für sich auch jeweils 1 Token.
Warum Tokens?¶
Beim "dummen" PC geht es nicht um Sprache, sondern Verarbeitung von Daten. Daten verbrauchen Speicherplatz, ob auf der Festplatte oder im RAM. Feste Größen, handlich, wiederverwendbar sind blitzschnell zu verarbeiten, es muss nicht mehr auf die Größe oder den Inhalt an sich eingegangen werden, sondern nur noch auf das Token.
Jeder Baustein (Token) wird in eine Zahlen ID übersetzt, diese hat dann 4 Bytes (32-Bit Integer). Damit hat man zum Beispiel schon gegenüber dem Wort "Speicher" - Länge 8 Buchstaben, das wären 8 Bytes, 4 Bytes gespart. Weil "Speicher" nun ein Token ist, eine Zahlen ID mit 4 Bytes Größe.
Außerdem ist die Datenstruktur so optimal, da CPUs (auch GPUs sind CPUs) diese 32-Bit Integerzahlen blitzschnell verarbeiten können.
Aber mehr bildlicher:
Man stelle sich vor man baut ein Klemmbaustein-Haus nur aus 4x2 Steinen, man spart sich die Suche nach den richtigen Bausteingrößen, hat standardisierte Breiten für Türen, Fenster, und Bauhöhe und baut einfach nur Ratzfatz in die Höhe.
Man büßt etwas Flexibilität ein, das wird aber durch reine Geschwindigkeit mehr als kompensiert!
Und darum geht es bei KI, Geschwindigkeit und Effizienz.
Kleines Schema:
flowchart LR
A["Text:<br>'Speicherverwaltung'"] --> B["Tokenizer<br>(Zerlegung)"]
subgraph Tokens ["3 Bausteine (Tokens)"]
C1["'Speicher'"]
C2["'ver'"]
C3["'waltung'"]
end
B --> C1 & C2 & C3
C1 --> D1["ID: 18420"]
C2 --> D2["ID: 512"]
C3 --> D3["ID: 8941"]
subgraph Processing ["KI-Berechnung"]
D1 & D2 & D3 --> E["Blitzschnelle GPU/VRAM-<br>Verarbeitung der Zahlen"]
end
Veränderung der Tokengrenzen in Zusammenhang mit der Geschwindigkeit¶
Braucht man mehr Tokens für ein Modell, um z.B. komplexe Programmieraufgaben zu lösen oder erweiterte Chat-History und mehr Dateien einzubinden, kann man die Modelle neu konfigurieren für mehr Token.
Um ein besseres bzw. höherwertiges Modell für eigentlich mehr Speicherbedarf auch für kleinere Aufgaben nutzen zu können, oder deren Geschwindigkeit zu erhöhen, kann man die Modelle für weniger Token konfigurieren.
Das geht allerdings zu lasten 1. des VRAMs, 2. Auslagerung auf CPU. Letzteres wird - wenn verwendet - die Erstellung deutlich verlangsamen. Es ist mit maximal 5-10% der normalen, reinen GPU-Geschwindigkeit zu rechnen!
Daher kann im Gegenzug bei Verringerung der Token eine gewisse Auslagerung auf die CPU und deren RAM vermieden werden. Grafikspeicher ist im übrigen auch erheblich schneller als DDR4 oder DDR5 Hauptspeicher (Faktor 8-20x):
| RAM-Typ | Bandbreite (theoretisch) |
|---|---|
| DDR4 Dual-Channel | 35-50 GB/s |
| DDR5 Dual-Channel | 60-90 GB/s |
| GDDR6X (VRAM 256-Bit) | ~736 GB/s |
Der KV-Cache¶
Der KV-Cache (Key-Value Cache) ist das temporäre Gedächtnis des Attention-Mechanismus in Transformer-Modellen. Bei der Generierung jedes einzelnen neuen Wortes (Tokens) muss das Modell die Beziehungsdaten aller vorherigen Tokens auswerten.
Um nicht für jedes neue Token den kompletten Textverlauf von vorne durchzurechnen, speichert das Modell die Schlüssel- (Key) und Wert-Vektoren (Value) aller bisherigen Tokens im Speicher ab.
Der Speicherbedarf des KV-Caches berechnet sich nach folgender Formel:
Der KV-Cache wächst strikt linear mit der Anzahl der verarbeiteten Tokens. Je größer der Kontext (z. B. 64k Tokens), desto mehr Grafikspeicher wird ausschließlich für das Aufrechterhalten des Gesprächsverlaufs benötigt.
FlashAttention-Algorithmus¶
Weiterhin sollte man in der Shell die Umgebungsvariable OLLAMA_FLASH_ATTENTION 1 einstellen. Das aktiviert in Ollama die Nutzung des FlashAttention-Algorithmus.
Standardmäßig berechnet ein Transformer die Beziehungen aller Tokens zueinander in einer großen \(N \times N\) Attention-Matrix, die komplett im Grafikspeicher (VRAM) zwischengespeichert werden muss. FlashAttention teilt diese Berechnung in kleinere Blöcke auf (Tiling) und führt sie direkt im extrem schnellen On-Chip-SRAM der GPU aus, anstatt ständig große Datenmengen in den VRAM zu schreiben und wieder herauszulesen.
Vorteile¶
- Massive VRAM-Einsparung im KV-Cache: Der Speicherbedarf für das Kontextfenster sinkt bei langen Texten um 30 % bis 50 %. Das ermöglicht die Nutzung großer Kontextfenster (z. B. 64k Tokens), ohne dass der Grafikspeicher überläuft
- Höhere Geschwindigkeit bei langen Texten: Die Verarbeitung großer Prompts (Prefill-Phase) und die Erzeugung von Antworten beschleunigen sich spürbar, da der Bandbreiten-Flaschenhals des VRAMs umgangen wird
- Keine Qualitätseinbußen: Im Gegensatz zu aggressiverer Quantisierung führt FlashAttention mathematisch zu exakt denselben Ergebnissen wie die Standard-Attention
Nachteile¶
- Strikte Hardware-Voraussetzungen: FlashAttention setzt moderne GPU-Architekturen mit passenden Tensor Cores voraus (bei NVIDIA ab der RTX 30xx / 40xx Serie bzw. Ampere/Ada-Architektur)
- Minimaler Nutzen bei Kurztexten: Bei sehr kurzen Anfragen (< 512 Tokens) ist der Leistungsgewinn kaum messbar, da der VRAM-Verbrauch hier ohnehin vernachlässigbar ist
- Potenzielle Kantenfehler: Bei sehr spezifischen Quantisierungsformaten oder älteren GPU-Treibern kann es vereinzelt zu Instabilitäten kommen
Setzen der Umgebungsvariablen¶
Für Fish-Shell (temporär):
set -gx OLLAMA_FLASH_ATTENTION 1
Permanent: O.a. Zeile in ~/.config/fish/config.fish eintragen.
Für Bash- oder Zsh-Shell:
export OLLAMA_FLASH_ATTENTION 1
Permanent: O.a. Zeile in ~/.bashrc oder ~/.zshrc eintragen. Die ganze Zeile, mit export.
Bestehende Modelle auf mehr (oder weniger) Tokens umstellen¶
Die Anzahl der Tokens wird über num_ctx <Tokens> festgelegt. Die Beispiele hier sind speziell auf meinen Hardwarekontext ausgelegt (nvidia 4080 Super, 16 GB VRAM).
Daher: YMMV! (Your Mileage May Vary! - Eure Erfahrung kann abweichen!)
Tipp
Die Anzahl der Tokens, die ein Modell verwendet kann in Aider bei geladenem Modell mit /tokens überprüft werden.
Weiterhin mit ollama show <Modelname>, Beispiel: ollama show qwen2.5-coder:14b -> context length 32768 für 32k Tokens.
Kontextlänge meiner Modellauswahl (theoretische Maxima):
| Modell | Context Length |
|---|---|
| nomic-embed-text:latest | 2048 |
| deepseek-r1:14b | 131072 |
| deepseek-coder-v2:latest | 163840 |
| qwen3-vl:8b-instruct | 262144 |
| qwen2.5-coder:14b | 32768 |
| qwen2.5:14b | 32768 |
| qwen3.8:27b | 262144 |
64k Token Modell für qwen2.5-coder:14b¶
Modelfile erstellen Modelfile-14b-64k:
FROM qwen2.5-coder:14b
PARAMETER num_ctx 65536
Das Modell bauen:
ollama create qwen2.5-coder:14b-64k -f Modelfile-14b-64k
Metadaten anlegen in ~/.aider.model.metadata.json:
{
"ollama_chat/qwen2.5-coder:14b-64k": {
"max_input_tokens": 65536,
"max_output_tokens": 8192,
"max_tokens": 8192,
"input_cost_per_token": 0.0,
"output_cost_per_token": 0.0,
"litellm_provider": "ollama_chat",
"mode": "chat"
}
}
Aider-Modellverhalten konfigurieren (optional) in ~/.aider.model.settings.yml:
- name: ollama_chat/qwen2.5-coder:14b-64k
edit_format: diff
Aider mit dem neuen Modell qwen2.5-coder:14b-64k starten und verifizieren:
aider --model ollama_chat/qwen2.5-coder:14b-64k
Auszug:
❯ aider --model ollama_chat/qwen2.5-coder:14b-64k
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
You should probably run aider in your project's directory, not your home dir.
Warning: ollama_chat/qwen2.5-coder:14b-64k expects these environment variables
- OLLAMA_API_BASE: Not set
You can skip this check with --no-show-model-warnings
https://aider.chat/docs/llms/warnings.html
Open documentation url for more info? (Y)es/(N)o/(D)on't ask again [Yes]: D
Aider v0.86.2
Model: ollama_chat/qwen2.5-coder:14b-64k with diff edit format
Git repo: none
Repo-map: disabled
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
> /tokens
Approximate context window usage for ollama_chat/qwen2.5-coder:14b-64k, in tokens:
$ 0.0000 1,941 system messages
==================
$ 0.0000 1,941 tokens total
63,595 tokens remaining in context window
65,536 tokens max context window size
Performantes 8k Token Modell für qwen3.8:27b¶
Modelfile erstellen Modelfile-27b-8k:
FROM qwen3.8:27b
PARAMETER num_ctx 8192
Das Modell bauen:
ollama create qwen3.8:27b-8k -f Modelfile-27b-8k
Metadaten anlegen/erweitern in ~/.aider.model.metadata.json:
{
"ollama_chat/qwen2.5-coder:14b-64k": {
"max_input_tokens": 65536,
"max_output_tokens": 8192,
"max_tokens": 8192,
"input_cost_per_token": 0.0,
"output_cost_per_token": 0.0,
"litellm_provider": "ollama_chat",
"mode": "chat"
},
"ollama_chat/qwen3.8:27b-8k": {
"max_input_tokens": 8192,
"max_output_tokens": 8192,
"max_tokens": 8192,
"input_cost_per_token": 0.0,
"output_cost_per_token": 0.0,
"litellm_provider": "ollama_chat",
"mode": "chat"
}
}
Aider-Modellverhalten konfigurieren/erweitern (optional) in ~/.aider.model.settings.yml:
- name: ollama_chat/qwen2.5-coder:14b-64k
edit_format: diff
- name: ollama_chat/qwen3.8:27b-8k
edit_format: diff
Aider mit dem neuen Modell qwen3.8:27b-8k starten und verifizieren:
aider --model ollama_chat/qwen3.8:27b-8k
Auszug:
❯ aider --model ollama_chat/qwen3.8:27b-8k
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
You should probably run aider in your project's directory, not your home dir.
Warning: ollama_chat/qwen3.8:27b-8k expects these environment variables
- OLLAMA_API_BASE: Not set
You can skip this check with --no-show-model-warnings
https://aider.chat/docs/llms/warnings.html
Open documentation url for more info? (Y)es/(N)o/(D)on't ask again [Yes]: D
Aider v0.86.2
Model: ollama_chat/qwen3.8:27b-8k with diff edit format
Git repo: none
Repo-map: disabled
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
> /tokens
Approximate context window usage for ollama_chat/qwen3.8:27b-8k, in tokens:
$ 0.0000 1,941 system messages
==================
$ 0.0000 1,941 tokens total
6,251 tokens remaining in context window
8,192 tokens max context window size
Aktuelle Ausgabe von ollama list:
❯ ollama list
NAME ID SIZE MODIFIED
qwen3.8:27b-8k 999690a38387 17 GB 4 minutes ago
qwen2.5-coder:14b-64k 4828733707be 9.0 GB 21 minutes ago
nomic-embed-text:latest 0a109f422b47 274 MB 8 hours ago
deepseek-r1:14b c333b7232bdb 9.0 GB 20 hours ago
deepseek-coder-v2:latest 63fb193b3a9b 8.9 GB 20 hours ago
qwen3-vl:8b-instruct 0533d74300e4 6.1 GB 20 hours ago
qwen2.5-coder:14b 9ec8897f747e 9.0 GB 20 hours ago
qwen2.5:14b 7cdf5a0187d5 9.0 GB 20 hours ago
qwen3.8:27b 22130167c4c2 17 GB 20 hours ago
Speichertabelle für qwen3.8:27b bei Reduzierung auf 8k Tokens¶
Speicheraufschlüsselung bei 8k Token für das qwen3.8:27b-Modell auf nvidia RTX 4080 Super:
Kontextgröße (num_ctx) |
KV-Cache Bedarf | Gesamter Speicherbedarf | Verhalten auf RTX 4080 Super |
|---|---|---|---|
| 4.096 Tokens (4k) | ~0,5 GB | ~16,0 GB | Vollständig im VRAM (maximale Geschwindigkeit) |
| 8.192 Tokens (8k) | ~1,2 GB | ~16,7 GB | Leichte CPU-Auslagerung ~1-2 Schichten im RAM |
| 16.384 Tokens (16k) | ~2,5 GB | ~18,0 GB | Spürbare CPU-Auslagerung (langsamer) |
| 32.768 Tokens (32k) | ~5,0 GB | ~20,5 GB | Hohe CPU-Auslagerung (sehr langsam) |
Speichertabelle für qwen3.8:27b bei Erhöhung auf 64k Tokens¶
Beim Qwen3.8 27B Modell (geladen als 4-Bit-Quantisierung Q4_K_M) belegen die reinen Modellgewichte bereits knapp 15 GB des Grafikspeichers.
Bei einer Erhöhung auf 64k Tokens und Setzen der Umgebungsvariablen OLLAMA_FLASH_ATTENTION=1 ergibt sich folgende Speicheraufschlüsselung:
Kontextgröße (num_ctx) |
Modellgewichte (Q4) | KV-Cache Bedarf | Gesamter Speicherbedarf | Verhalten auf RTX 4080 Super |
|---|---|---|---|---|
| 4.096 Tokens (4k) | ~15,0 GB | ~0,6 GB | ~15,6 GB | 100 % im VRAM (Maximale Geschwindigkeit) |
| 8.192 Tokens (8k) | ~15,0 GB | ~1,2 GB | ~16,2 GB | Minimaler Überlauf im System-RAM (~0,2 GB) |
| 16.384 Tokens (16k) | ~15,0 GB | ~2,5 GB | ~17,5 GB | ~1,5 GB Auslagerung in System-RAM (DDR4) |
| 32.768 Tokens (32k) | ~15,0 GB | ~5,0 GB | ~20,0 GB | ~4,0 GB Auslagerung in System-RAM (DDR4) |
| 65.536 Tokens (64k) | ~15,0 GB | ~10,0 GB | ~25,0 GB | ~9,0 GB Auslagerung in System-RAM (Flaschenhals) |
Was passiert bei 64k Tokens mit dem 27B-Modell auf dem PC?¶
- Massiver RAM-Überlauf: Von den benötigten 25 GB Gesamtspeicher passen nur 16 GB in den VRAM der RTX 4080 Super. Rund 9 GB des KV-Caches müssen in den Hauptspeicher (DDR4 RAM) verschoben werden
- Geschwindigkeitseinbruch: Die Speicherbandbreite des DDR4-RAMs (~40–50 GB/s) ist um ein Vielfaches langsamer als die des VRAMs der RTX 4080 Super (736 GB/s). Die Antwortgenerierung bricht von flüssigen ~30 Tokens/Sekunde auf zähe 1 bis 3 Tokens/Sekunde ein
- Empfehlung: Für 64k Tokens Kontextfenster ist auf einer 16-GB-GPU das
qwen2.5-coder:14bdas optimale Modell, da dessen Modellgewichte nur ~9 GB VRAM belegen und der 64k KV-Cache vollständig im verbleibenden VRAM Platz findet
Notiz- und Ideensammlung¶
root42: "Für das LLM Coding kann ich Claude-CLI empfehlen. Das kann verschiedenste Modelle benutzen. Und es ist halt ein Agent auf dem Rechner, und kann direkt den Code editieren. Kann man einfach im Terminal neben seinen Lieblingseditor -- also Emacs -- aufmachen."
