Zum Inhalt

llama-swap

Das einzige LLM-Backend dieser Maschine. Ein Container trägt alle Modell-Rollen auf der iGPU und lädt sie bei Bedarf um. GitHub

Eckdaten

Image ghcr.io/mostlygeek/llama-swap:unified-vulkan, per Digest gepinnt
Inhalt llama-swap + llama-server + whisper.cpp
Endpunkt http://10.10.10.7:9292/v1 (OpenAI-kompatibel)
Web-UI http://10.10.10.7:9292/ui
Weitere Pfade /health, /logs, /running, /metrics
Konfiguration /opt/aiserver/llama-swap/config.yaml — zugleich die Rollenkarte
Netz ai (Container-intern lauscht llama-swap auf 8080)

Modelle kommen aus zwei read-only gemounteten Quellen:

Mount Quelle Zweck
/models /opt/ai/models/ollama/models/blobs Ollama-Blob-Store — llama.cpp liest dieselben GGUF-Dateien, kein doppelter Download
/models-hf /opt/ai/models/hf Offizielle HF-GGUFs, wo der Ollama-Blob nicht taugt

Gruppen

Die Rollen sind in drei Gruppen aufgeteilt. Das ist der Kern des Speicherkonzepts: 96 GB GTT reichen nicht für alle Modelle gleichzeitig, aber der Sprachpfad darf nie warten.

Gruppe Verhalten Mitglieder
resident persistent: true, kein Swap voice, embed — dauerhaft geladen, vor Verdrängung geschützt
docs kein Swap untereinander ocr, json — die Paperless-Kette bleibt zusammen geladen
heavy swappt, eins aktiv brain, coder, translator, vision, allround, cleanup

Die docs-Gruppe ist kein Detail: Ohne sie würde ein Dokumentenstapel pro Dokument zweimal swappen (ocrjsonocr …).

Ein Chat gegen ein heavy-Modell verdrängt die Residenten nicht.

Pflicht-Flags

Diese Flags gelten für alle Modelle. Ohne sie gibt es kein Fehlerbild, sondern schlechtes Verhalten.

Flag Gilt für Ohne es
--load-mode none alle ~30 min Seitentabellenaufbau beim Laden (mmap-Falle auf gfx1151 — und mmap ist der llama.cpp-Default)
-ngl 999 alle Teile des Modells bleiben auf der CPU
--flash-attn on alle 1–9 % Tempo verschenkt, nie nachteilig
--jinja alle Die im GGUF eingebetteten Chat-Templates greifen nicht
--ctx-size explizit alle Der Kontext steht sonst nicht fest
--spec-type draft-mtp brain (Head im GGUF), translator (zusätzlich --spec-draft-model) 28–63 statt 79–89 Tokens/s
--image-min-tokens 1024 ocr, vision (Qwen-VL-Familie) A4-Scans werden so klein skaliert, dass OCR nach zwei Zeilen abbricht
--reasoning-budget 0 cleanup (laguna) Thinking frisst das Token-Budget, content bleibt leer

Speculative Decoding ist verteilungserhaltend — es verändert die Ausgabe nicht, nur das Tempo.

Residenten-Preload

hooks:
  on_startup:
    preload:
      - voice-llamacpp
      - embed-llamacpp

Nicht entfernen

Residenten laden sonst lazy — erst beim ersten Request. Der Sprachpfad wäre nach jedem Neustart einen Kaltstart lang taub. Mit Preload steht er nach 2,6 Sekunden.

GPU-Zugriff im Container

devices: [/dev/kfd, /dev/dri]
group_add: ["991", "44"]    # render, video
security_opt: [seccomp:unconfined]

Die GIDs sind hostspezifisch und gehören gegengeprüft:

getent group render video

Digest-Pflege

Die -m-Pfade in config.yaml zeigen bei Ollama-Blobs auf sha256-…-Dateien. Nach einem ollama pull desselben Tags ändert sich der Digest, und llama-swap findet die Datei nicht mehr.

docker exec ollama cat /root/.ollama/models/manifests/registry.ollama.ai/library/<modell>/<tag> \
  | jq -r '.layers[] | select(.mediaType | contains("model")) | .digest'

Danach den Pfad in config.yaml nachziehen und docker compose restart llama-swap.

ollama rm kann llama-swap-Modelle zerstören

Vier Rollen lesen ihre Gewichte direkt aus dem Ollama-Blob-Store: coder, voice, cleanup und json. Vor jedem ollama rm die Digests gegen config.yaml prüfen — die Blobs sind Dateien, keine Ollama-Verwaltungsobjekte, und Ollama weiß nichts von ihrem zweiten Nutzer.

Betrieb

# Zustand
curl -s localhost:9292/health                       # erwartet OK
curl -s localhost:9292/v1/models | jq '.data[].id'  # Rollenliste mit Ladezustand

# Residenz prüfen (löst zugleich den Lazy-Load aus)
curl -s localhost:9292/v1/chat/completions -H 'Content-Type: application/json' \
  -d '{"model":"voice-llamacpp","max_tokens":8,"messages":[{"role":"user","content":"OK?"}]}'

# Neustart nach Config-Änderung
docker compose -f /opt/aiserver/llama-swap/docker-compose.yaml restart

Der erste Aufruf eines kalten heavy-Modells dauert 1,5–24 Sekunden — das ist der Swap, kein Fehler. brain und coder liegen am unteren Ende, translator und allround am oberen.

Warum nicht Ollama

Ollama war das ursprüngliche Backend und ist seit dem 31.07.2026 gestoppt. Die Gründe, verkürzt:

  • Tempo: brain läuft mit MTP-Speculative-Decoding auf llama-swap mit 89 t/s statt 64 t/s auf Ollama
  • Kontext-Falle: Ollama lud german-ocr mit 262k Kontext und belegte 12 statt 4 GB. Auf llama-swap steht der Kontext immer in der Config
  • Fehlerbilder: qwen3-vl:4b lieferte über Ollama sporadisch leere Antworten (Bug), über das HF-GGUF nicht
  • Architektur-Inkompatibilitäten: Einige Ollama-Blobs tragen Ollama-eigene Architekturkennungen, die llama.cpp nicht lädt — dafür gibt es /models-hf

Der Ollama-Container bleibt reaktivierbar (ollama/, archiviert), etwa um Modelle zu ziehen. Sein Blob-Store ist weiterhin aktive Dateiquelle.

Details der Umstellung: docs/testrunde-2/z2-migration.md im aiserver-Repo.