283 lines
10 KiB
Markdown
283 lines
10 KiB
Markdown
|
|
# Bedienungsanleitung — llamacppctl
|
|||
|
|
|
|||
|
|
`llamacppctl` startet, prüft, stoppt, wechselt und promptet einen
|
|||
|
|
**llama.cpp-Server, der als Docker-Container** läuft — über **eine** CLI statt
|
|||
|
|
mehrerer Shell-Skripte. Die OpenAI-kompatible API des Servers ist standardmäßig
|
|||
|
|
nur auf `127.0.0.1` erreichbar.
|
|||
|
|
|
|||
|
|
Diese Anleitung deckt **Installation** und **Benutzung** vollständig ab. Zur
|
|||
|
|
Installation aus dem fertigen Archiv siehe zusätzlich
|
|||
|
|
[`INSTALL_FROM_ARCHIVE.md`](INSTALL_FROM_ARCHIVE.md). Details zum Sicherheits-
|
|||
|
|
und Betriebsmodell stehen in [`docs/SECURITY_AND_OPERATIONS.md`](docs/SECURITY_AND_OPERATIONS.md);
|
|||
|
|
die vollständige Optionsreferenz in der Manpage (`man/llamacppctl.1`).
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 1. Voraussetzungen
|
|||
|
|
|
|||
|
|
- **Docker** (Daemon läuft, Nutzer in der `docker`-Gruppe): `docker version` muss funktionieren.
|
|||
|
|
- **NVIDIA-GPU** + Treiber + **NVIDIA Container Toolkit** (damit `--gpus` im Container greift): `nvidia-smi` funktioniert, und `docker run --rm --gpus all <cuda-image> nvidia-smi` zeigt die GPU.
|
|||
|
|
- **Python ≥ 3.10**.
|
|||
|
|
- Ein **GGUF-Modell** auf dem Host, das read-only in den Container gemountet wird.
|
|||
|
|
- Das Docker-Image, z. B. `ghcr.io/ggml-org/llama.cpp:server-cuda` (wird beim ersten Start automatisch gezogen).
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 2. Installation
|
|||
|
|
|
|||
|
|
### 2.1 Aus dem Quellcode (empfohlen für Entwicklung)
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
cd llamacppctl
|
|||
|
|
python3 -m venv .venv
|
|||
|
|
source .venv/bin/activate
|
|||
|
|
pip install -e . # editable: CLI == Quellcode in src/
|
|||
|
|
# optional für Tests:
|
|||
|
|
pip install -e ".[dev]"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
> **Wichtig:** Bei `pip install .` (ohne `-e`) läuft das CLI aus einer Kopie in
|
|||
|
|
> `site-packages` — Änderungen an `src/` wirken dann NICHT. Für Entwicklung immer
|
|||
|
|
> `-e` verwenden. Kontrolle: `python -c "import llamacppctl.actions as a; print(a.__file__)"`
|
|||
|
|
> muss auf `src/` zeigen.
|
|||
|
|
|
|||
|
|
### 2.2 Aus dem Archiv
|
|||
|
|
|
|||
|
|
Siehe [`INSTALL_FROM_ARCHIVE.md`](INSTALL_FROM_ARCHIVE.md).
|
|||
|
|
|
|||
|
|
### 2.3 Installation prüfen (ohne Docker)
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
llamacppctl --help
|
|||
|
|
llamacppctl --print-effective-config --config llama.cpp.config --start --dry-run
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 3. Konfiguration
|
|||
|
|
|
|||
|
|
### 3.1 Datei anlegen
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
cp llama.cpp.config.example llama.cpp.config
|
|||
|
|
# llama.cpp.config an den Host anpassen (Pfade, GPU, Ports)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Der Standard-Pfad ist `llama.cpp.config` im aktuellen Verzeichnis, überschreibbar
|
|||
|
|
mit `--config <pfad>`.
|
|||
|
|
|
|||
|
|
### 3.2 Aufbau (INI)
|
|||
|
|
|
|||
|
|
- `[default]` — globale Werte, überschreiben die eingebauten Defaults.
|
|||
|
|
- `[model.<name>]` — ein Modell-Profil, gewählt mit `--profile <name>`.
|
|||
|
|
- `[prompt.<name>]` — ein *Fallback*-Systemprompt, gewählt mit `--system-prompt-profile <name>` (ein explizites `-s`/`--system-file`/`--system-url` gewinnt immer).
|
|||
|
|
|
|||
|
|
**Auflösungsreihenfolge** (niedrig → hoch): eingebaute Defaults → `[default]` →
|
|||
|
|
`[model.<profile>]` → CLI-Overrides.
|
|||
|
|
|
|||
|
|
### 3.3 Wichtige Felder
|
|||
|
|
|
|||
|
|
| Feld / CLI | Bedeutung |
|
|||
|
|
|---|---|
|
|||
|
|
| `image` / `--image` | Docker-Image (z. B. `ghcr.io/ggml-org/llama.cpp:server-cuda`) |
|
|||
|
|
| `hf_home` / `--hf-home` | Host-Verzeichnis, read-only gemountet. **Env-Variablen erlaubt**, z. B. `hf_home = ${HF_HOME}` |
|
|||
|
|
| `model_path` / `--model-path` | Modellpfad, relativ zu `hf_home` (oder absolut) |
|
|||
|
|
| `container_name` / `--container-name` | **Pflicht**, eindeutig pro parallel laufendem Profil (Identitätsanker für Naming/Lock/Stop/Check) |
|
|||
|
|
| `host_port` / `--host-port` | Host-Port (1–65535) |
|
|||
|
|
| `container_port` / `--container-port` | Container-interner Port |
|
|||
|
|
| `model_alias` / `--model-alias` | Modell-Alias in der API |
|
|||
|
|
| `gpu_device` / `--gpu` | GPU-Device für `--gpus device=<id>` (auch mehrere: `1,2`) |
|
|||
|
|
| `ctx_size` / `--ctx-size` | Kontextfenster (KV-Cache wächst damit) |
|
|||
|
|
| `n_predict` / `--n-predict` | Server-seitige max. Vorhersage-Tokens |
|
|||
|
|
| `temp`, `top_p`, `top_k`, `min_p`, `repeat_penalty` | Sampling (beim Start in den Container gebacken) |
|
|||
|
|
| `expose` / `--expose` / `--no-expose` | Port auf allen Interfaces (LAN) statt nur `127.0.0.1`. **Default: aus** |
|
|||
|
|
| `api_key` / `--api-key` | API-Key: Server verlangt ihn, Tool sendet ihn als Bearer-Token |
|
|||
|
|
| `max_tokens` / `--max-tokens` | Antwortbudget für `--chat`/`--start`-Antwort (Default 2048) |
|
|||
|
|
| `chat_temperature` / `--chat-temp` | Temperatur des Chat-Requests (leer = Server-`temp`) |
|
|||
|
|
| `timeout` / `--timeout` | Readiness-Timeout in Sekunden |
|
|||
|
|
| `reasoning` / `--reasoning {on,off}` | Reasoning-Modus |
|
|||
|
|
|
|||
|
|
Boolean-Schalter mit Paar-Form: `--jinja/--no-jinja`, `--fa/--no-fa`,
|
|||
|
|
`--kv-unified/--no-kv-unified`, `--cont-batching/--no-cont-batching`,
|
|||
|
|
`--no-context-shift/--context-shift`.
|
|||
|
|
|
|||
|
|
### 3.4 Beispiel
|
|||
|
|
|
|||
|
|
```ini
|
|||
|
|
[default]
|
|||
|
|
image = ghcr.io/ggml-org/llama.cpp:server-cuda
|
|||
|
|
hf_home = ${HF_HOME}
|
|||
|
|
model_path = models/qwen3/mein-modell-Q4_K_M.gguf
|
|||
|
|
model_alias = mein_llm
|
|||
|
|
container_name = llama_cpp_server
|
|||
|
|
host_port = 8001
|
|||
|
|
gpu_device = 1
|
|||
|
|
ctx_size = 65536
|
|||
|
|
max_tokens = 8192
|
|||
|
|
|
|||
|
|
[prompt.knapp]
|
|||
|
|
system_prompt = Antworte kurz, präzise und technisch.
|
|||
|
|
|
|||
|
|
[model.zweitmodell]
|
|||
|
|
model_path = models/qwen3/anderes-modell.gguf
|
|||
|
|
model_alias = zweit_llm
|
|||
|
|
container_name = llama_cpp_zweit
|
|||
|
|
host_port = 8002
|
|||
|
|
gpu_device = 2
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 4. Die fünf Aktionen
|
|||
|
|
|
|||
|
|
Genau **eine** Aktion pro Aufruf ist erforderlich.
|
|||
|
|
|
|||
|
|
### `--start` — Server starten
|
|||
|
|
Entfernt einen vorhandenen Container gleichen Namens, startet neu, wartet per
|
|||
|
|
echter Chat-Completion auf Readiness (nicht nur offener Port). Läuft unter Lock.
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
llamacppctl --start
|
|||
|
|
llamacppctl --start --profile zweitmodell
|
|||
|
|
llamacppctl --start -p "Sag kurz Hallo." # + Testantwort nach dem Start
|
|||
|
|
llamacppctl --start --dry-run # nur das docker-run-Kommando zeigen
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### `--check` — Status prüfen
|
|||
|
|
Zeigt Container-/HTTP-Status; mit User-Prompt zusätzlich eine echte Chat-Probe.
|
|||
|
|
**Scriptbar:** Exit 0 = läuft & erreichbar, sonst 5.
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
llamacppctl --check
|
|||
|
|
llamacppctl --check -p "ping" && echo "OK" || echo "nicht bereit"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### `--stop` — Container stoppen und entfernen
|
|||
|
|
```bash
|
|||
|
|
llamacppctl --stop
|
|||
|
|
llamacppctl --stop --force # auch bei Inkonsistenzen entfernen
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### `--change` — Modell/Config wechseln
|
|||
|
|
Stoppt, konfiguriert neu, startet — unter Lock. **Validiert den Modellpfad,
|
|||
|
|
bevor** der laufende Container entfernt wird (kein unnötiger Ausfall).
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
llamacppctl --change --profile zweitmodell
|
|||
|
|
llamacppctl --change --model-path models/qwen3/neues-modell.gguf --ctx-size 32768
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### `--chat` — an laufenden Server prompten
|
|||
|
|
```bash
|
|||
|
|
llamacppctl --chat -p "Was ist 2+2?"
|
|||
|
|
llamacppctl --chat -s "Du antwortest nur mit Zahlen." -p "Wie viel ist 6*7?"
|
|||
|
|
llamacppctl --chat --stream -p "Schreibe eine kurze Geschichte."
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 5. Prompts (System & User)
|
|||
|
|
|
|||
|
|
Pro Feld genau **eine** Quelle:
|
|||
|
|
|
|||
|
|
| System-Prompt | User-Prompt | Quelle |
|
|||
|
|
|---|---|---|
|
|||
|
|
| `-s`, `--system` | `-p`, `--prompt` | Literaler Text |
|
|||
|
|
| `--system-file` | `--prompt-file` | Lokale Datei (nur reguläre Dateien, UTF-8, Größenlimit) |
|
|||
|
|
| `--system-url` | `--prompt-url` | HTTPS-URL (SSRF-gehärtet) |
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
llamacppctl --chat --system-file ./prompts/reviewer.md --prompt-file ./frage.txt
|
|||
|
|
llamacppctl --start --system-prompt-profile knapp -p "Erkläre Backups."
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
URL-Quellen sind standardmäßig HTTPS-only, blockieren private/loopback/metadata-
|
|||
|
|
IPs und Redirects; Details in `docs/SECURITY_AND_OPERATIONS.md`.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 6. Antwortsteuerung & Reasoning
|
|||
|
|
|
|||
|
|
- `--max-tokens N` — Budget der Antwort (Default 2048).
|
|||
|
|
- `--chat-temp F` — Temperatur des Requests (sonst gilt die Server-`temp`).
|
|||
|
|
- `--stream` — Tokens live auf stdout, während sie generiert werden.
|
|||
|
|
|
|||
|
|
> **Reasoning-Modelle** (z. B. Qwen3.x mit `reasoning = on`) „denken" erst
|
|||
|
|
> intern (oft mehrere tausend Tokens) und geben **dann** die eigentliche
|
|||
|
|
> Antwort. Ist `--max-tokens` zu klein, endet die Generierung im Denken und der
|
|||
|
|
> sichtbare `content` bleibt **leer** — das Tool warnt dann
|
|||
|
|
> (`finish_reason=length`). Für lange Texte großzügig setzen (z. B. `--max-tokens 8000`).
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 7. Netzwerk & Sicherheit (Kurz)
|
|||
|
|
|
|||
|
|
- Der Port wird per Default nur auf **`127.0.0.1`** veröffentlicht — die
|
|||
|
|
unauthentifizierte API ist **nicht** im LAN erreichbar.
|
|||
|
|
- Für LAN-Zugriff: `--expose`/`expose = true`. **Dann unbedingt** `--api-key`/`api_key` setzen.
|
|||
|
|
- Mit Key schützt llama.cpp `/v1/chat/completions` (ohne Key → 401); `/health`
|
|||
|
|
und `/v1/models` bleiben bewusst offen (für Health-Checks).
|
|||
|
|
- `hf_home` wird **read-only** gemountet; alle Docker-Aufrufe nutzen
|
|||
|
|
Argumentlisten (keine Shell-Injection).
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 8. GPU & Kontextgröße
|
|||
|
|
|
|||
|
|
- Ein Modell muss samt KV-Cache in den GPU-Speicher passen. Der KV-Cache wächst
|
|||
|
|
linear mit `ctx_size` und wird beim Laden komplett allokiert.
|
|||
|
|
- Mehrere GPUs: `gpu_device = 1,2` (bzw. `--gpu 1,2`) verteilt das Modell.
|
|||
|
|
- Vor einer Änderung mit `--dry-run` das Kommando prüfen, mit
|
|||
|
|
`--print-effective-config` die aufgelösten Werte kontrollieren.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 9. Exit-Codes
|
|||
|
|
|
|||
|
|
| Code | Bedeutung |
|
|||
|
|
|---|---|
|
|||
|
|
| 0 | Erfolg (bzw. `--check`: läuft & erreichbar) |
|
|||
|
|
| 1 | Allgemeiner Fehler / leere Antwort trotz Budget |
|
|||
|
|
| 2 | CLI-/Argument-Fehler |
|
|||
|
|
| 3 | Modelldatei nicht gefunden |
|
|||
|
|
| 4 | Docker-Fehler |
|
|||
|
|
| 5 | HTTP/Readiness nicht bereit (auch `--check`, wenn nicht erreichbar) |
|
|||
|
|
| 6 | Lock belegt (anderer `--start`/`--change` läuft; ggf. `--force`) |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 10. Fehlersuche
|
|||
|
|
|
|||
|
|
| Symptom | Ursache | Lösung |
|
|||
|
|
|---|---|---|
|
|||
|
|
| `docker is not available on PATH` | Docker-CLI/-Daemon fehlt | Docker installieren/starten, Nutzer in `docker`-Gruppe |
|
|||
|
|
| `model file not found: …` | `model_path`/`hf_home` falsch | Pfade prüfen; `${HF_HOME}` gesetzt? |
|
|||
|
|
| `Server did not become ready within <n>s` | Modell lädt langsam / OOM | `--timeout` erhöhen, `--logs` für Log-Tail, ggf. `ctx_size` senken |
|
|||
|
|
| Leere Antwort + Hinweis `finish_reason=length` | Reasoning verbraucht das Budget | `--max-tokens` erhöhen |
|
|||
|
|
| `HTTP 401: Invalid API Key` | Server verlangt Key | `--api-key`/`api_key` passend setzen |
|
|||
|
|
| `lock busy: …` | Paralleler `--start`/`--change` | warten oder `--force` (hängender Lock) |
|
|||
|
|
| `--check` zeigt `health=unhealthy`, Server antwortet aber | bildeigener Docker-Healthcheck ≠ HTTP-Probe | unkritisch: `HTTP: OK` + Exit 0 zählt |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 11. Tests
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
pip install -e ".[dev]"
|
|||
|
|
pytest -q # Unit-Tests (kein Docker nötig)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
End-to-End gegen einen echten Server (opt-in, braucht Docker + GPU + Modell):
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
SMOKE_GPU=1 scripts/smoke.sh
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 12. Weiterführend
|
|||
|
|
|
|||
|
|
- `man/llamacppctl.1` — vollständige Optionsreferenz (`man ./man/llamacppctl.1`).
|
|||
|
|
- `docs/SECURITY_AND_OPERATIONS.md` — Architektur, Sicherheits- und Betriebsmodell.
|
|||
|
|
- `README.md` — Kurzüberblick.
|
|||
|
|
- `INSTALL_FROM_ARCHIVE.md` — Installation aus dem `.tar.gz`-Archiv.
|