Drei weitere Kandidaten geprueft, alle drei abgelehnt

Ministral-3-3B: Architektur mistral3 steht nicht in der Liste des
rkllm-Toolkits, dazu FP8-Gewichte -- nie konvertiert. Config auf Llama
umbiegen waere moeglich gewesen, aber ein falsch eingelesenes Modell
haette plausiblen Unsinn produziert.

Phi-4-mini: konvertiert (5,06 GB), laedt aber nicht auf der NPU.
partial_rotary_factor 0.75 ergibt 48 LongRoPE-Faktoren, die Runtime
erwartet starr head_dim/2 = 64. Rockchips "Phi3" meint Phi-3-mini.

SmolLM3-3B: laeuft, in 2 von 3 Faellen schneller als Qwen3, scheitert
aber inhaltlich -- 13 statt 61 Todos, JSON-Schluessel als
Listeneintraege, Englisch-Drift im deutschen Text.

rkllm_server.cpp: Chat-Template kommt jetzt aus der Umgebung
(RKLLM_TMPL_SYSTEM / _USER_PRE / _USER_POST), Defaults = bisheriges
ChatML-Verhalten, mit Qwen3 gegengeprueft. Damit ist der naechste
Kandidat eine Drop-in-Zeile statt einer Neuuebersetzung.

Doku: Changelog 2026-09-29 (4) mit Messwerten und allen Fallstricken,
Kapitel 05 um Architektur-Vorpruefung und Template-Konfiguration
erweitert. Messstrecke: Zeitlimit 45 -> 90 Minuten.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017A91sWU5aWrqZ2DWNTLVph
This commit is contained in:
2026-09-29 19:59:04 +02:00
co-authored by Claude Opus 5
parent 9e9958fb01
commit 3a2e8144f7
4 changed files with 176 additions and 11 deletions
+29
View File
@@ -391,6 +391,29 @@ flowchart LR
Die Konvertierung eines neuen Modells passiert **nicht** auf dem Board (kein CUDA), sondern auf dem PC
mit `tools/rkllm-qwen35/` — rkllm-toolkit-Version muss exakt zur Runtime auf dem Board passen.
### Bevor ein Kandidat geladen wird: Architektur prüfen
Das rkllm-Toolkit 1.3.0 kennt nur diese Architekturen (aus `loader.so`/`common.so` extrahiert):
```
Baichuan, ChatGLM, Deepseek, DeepseekOCR, Gemma/2/3/3n/4, InternLM2, Llama,
MiniCPM/3, Phi/Phi3, Qwen2/2VL/2.5VL/3/3VL/3.5, RWKV7, SmolLM3, TeleChat/2
```
`curl -sL https://huggingface.co/<repo>/raw/main/config.json` und `architectures` dagegenhalten —
Sekunden statt Gigabyte. **Gelistet heißt aber nicht lauffähig:** Phi-4-mini trägt `Phi3ForCausalLM`,
scheitert aber an der Geometrie (partielle Rotary Embeddings, 48 statt 64 LongRoPE-Faktoren), weil
Rockchips Phi3-Pfad auf Phi-3-mini zugeschnitten ist. Ebenso zu prüfen: `quantization_config` —
FP8-Gewichte (z. B. Ministral 3) kann das Toolkit nicht lesen.
### Chat-Template aus der Umgebung
rkllm wendet das HF-Template **nicht** selbst an. Ohne passende Marker bekommt das Modell den nackten
Prompt und faselt generisches Füllmaterial. Seit 2026-09-29 kommen die Marker aus der Umgebung:
`RKLLM_TMPL_SYSTEM`, `RKLLM_TMPL_USER_PRE`, `RKLLM_TMPL_USER_POST` (Defaults = ChatML, also unverändert
für Qwen und Gemma3). `\n`/`\t` werden im Server dekodiert, weil systemd-`Environment=` keine echten
Umbrüche kann — und aus demselben Grund beim Schreiben des Drop-ins **zitiertes Heredoc statt `printf`**.
### Denk-Modelle (Qwen3.5 und jünger)
Modelle mit Denk-Modus liefern sonst Gedankengang statt JSON. `RKLLMInput.enable_thinking = false` genügt
@@ -405,3 +428,9 @@ langsamer**, Zusammenfassungen auf ein Drittel bis Viertel gekürzt, und von 61
hält die JSON-Vorgabe nicht durch (13 Parse-Fehler mit Wiederholungen). Die Prosa selbst war nicht
schlechter. **Qwen3-4B bleibt produktiv.** Lehre: bei diesem Aufbau entscheidet nicht der Benchmark,
sondern ob ein Modell über lange deutsche Transkripte hinweg sauberes JSON liefert.
Am selben Tag zusätzlich geprüft: **Ministral-3-3B** (Architektur `mistral3` nicht unterstützt, dazu
FP8-Gewichte — nie konvertiert), **Phi-4-mini** (konvertiert, aber `rkllm_init failed` wegen partieller
Rotary Embeddings) und **SmolLM3-3B** (läuft, stellenweise schneller als Qwen3, scheitert aber inhaltlich:
13 statt 61 Todos, JSON-Schlüssel als Listeneinträge, Englisch-Drift). Drei Kandidaten, drei Absagen —
Details im Changelog 2026-09-29 (4).
+114
View File
@@ -3,6 +3,120 @@
Chronik der Entwicklungsschritte. **Regel: jede zukünftige Änderung bekommt hier einen Eintrag** (Datum,
Was, Warum, betroffene Dateien/Backups).
## 2026-09-29 (4) — Zwei weitere Kandidaten geprüft: Ministral, Phi-4-mini, SmolLM3
Nach dem Qwen3.5-Ergebnis systematisch auf Hugging Face gesucht. Drei Kandidaten, **alle drei gescheitert**
— zwei davon, bevor überhaupt gemessen werden konnte.
### Vorab: welche Architekturen das Toolkit überhaupt kennt
Aus `rkllm_toolkit-1.3.0` (`loader.so`/`common.so`) extrahiert — **vor** dem nächsten Download prüfen:
```
Baichuan, ChatGLM, Deepseek, DeepseekOCR, Gemma/2/3/3n/4, InternLM2, Llama,
MiniCPM/3, Phi/Phi3, Qwen2/2VL/2.5VL/3/3VL/3.5, RWKV7, SmolLM3, TeleChat/2
```
Prüfbefehl: `architectures` aus der `config.json` des HF-Repos gegen diese Liste halten
(`curl -sL https://huggingface.co/<repo>/raw/main/config.json`). Das kostet Sekunden statt Gigabyte.
### Ministral-3-3B-Instruct-2512 — gar nicht erst konvertierbar
`Mistral3ForConditionalGeneration` / `model_type: mistral3` steht **nicht** in der Liste; der einzige
Mistral-Treffer im Toolkit ist ein Tokenizer-Detail namens `fix_mistral_regex`. Dazu sind die
veröffentlichten Gewichte **FP8-quantisiert** (`quant_method: fp8`), das Toolkit erwartet bf16/fp16.
Die `config.json` auf `LlamaForCausalLM` umzubiegen wäre möglich gewesen — bewusst **nicht** gemacht:
Mistral 3 hat Sliding-Window-Attention und einen multimodalen Wrapper, ein falsch eingelesenes Modell
hätte plausiblen Unsinn produziert und den A/B wertlos gemacht.
### Phi-4-mini-instruct — konvertiert, lädt aber nicht auf der NPU
Konvertierung gelang erst im dritten Anlauf (5,06 GB, 244 s):
1. `cannot import name 'LossKwargs' from 'transformers.utils'` — der mitgelieferte `modeling_phi3.py` ist
gegen ein älteres transformers geschrieben, das Toolkit verlangt 5.8.0.
2. `auto_map` auf die native Implementierung umgebogen → `Not found AutoModelForCausalLM in the auto_map`.
3. `auto_map` **komplett** entfernt (nur `architectures: Phi3ForCausalLM` bleibt) → Konvertierung sauber.
Auf dem Board dann:
```
E rkllm: Tensor rope_factors_long.weight has wrong shape, expected 64, got 48
rkllm_init failed
```
**Ursache:** Phi-4-mini nutzt **partielle** Rotary Embeddings (`partial_rotary_factor: 0.75`). Bei
`head_dim` 128 werden nur 96 Dimensionen rotiert → **48** LongRoPE-Faktoren. Das Modell ist stimmig;
die rkllm-Runtime rechnet in ihrem Phi3-Pfad starr mit `head_dim/2 = 64` und kennt nur die Geometrie von
Phi-3-mini. **Rockchips „Phi3" meint Phi-3, nicht Phi-4-mini.** Die LongRoPE-Skalierung auszubauen hätte
den Kontext auf die Basislänge zurückgeworfen — wir brauchen 12288. Abgebrochen.
### SmolLM3-3B — gemessen und verloren
Konvertierung sauber (3,74 GB, 181 s; Warnung „pad token has two ids 128012/128004" stammt aus dem Modell,
ohne Folgen für Einzelgenerierung). Template: ChatML **plus** `/no_think` in der Systemzeile **plus**
leerer Denkblock — SmolLM3 denkt sonst per Default.
| Aufnahme | Zeichen | Qwen3-4B | SmolLM3-3B | Tempo | Summary | Todos |
|---|---|---|---|---|---|---|
| #86 | 8 390 | 10,5 min | **8,8 min** | −17 % | 2 896 → 1 167 | 6 → 2 |
| #123 | 19 275 | 24,5 min | 27,6 min | +13 % | 6 071 → 1 988 | 18 → 5 |
| #114 | 34 843 | 41,8 min | **38,1 min** | −9 % | 9 288 → 2 386 | 37 → 6 |
**SmolLM3 ist stellenweise schneller** (2 von 3 Fällen) — erwartbar bei 3B gegen 4B. Es scheitert am Inhalt:
- **Todos strukturell kaputt:** `["task","Melanie trifft sich …","user","Philipp","status"]` — das Modell
schreibt die JSON-**Schlüssel** als Listeneinträge. Über alle drei Gespräche 13 statt 61 Einträge, und
die vorhandenen sind teils Schrott.
- **Englisch-Drift:** „Their Plan ist nun: am 28., Freitag …" mitten im deutschen Fließtext.
- **Platzhalter-Artefakte:** `[...]` als Auslassungszeichen im Ergebnistext.
- Zeilenumbrüche landen als literales `\n` bzw. `\\n` im Text (wie schon bei Qwen3.5).
### Fazit nach drei Kandidaten an einem Tag
**Qwen3-4B-Instruct-2507 bleibt, und der Abstand ist größer als gedacht.** Die eigentliche Erkenntnis ist
nicht „Modell X ist schlechter", sondern: **die Menge der Modelle, die auf dieser NPU überhaupt laufen, ist
deutlich kleiner als Rockchips Unterstützungsliste suggeriert** — eine Architektur kann gelistet sein und
trotzdem an der Geometrie scheitern (Phi-4-mini). Und von denen, die laufen, scheitert bisher jedes an
derselben Stelle: **sauberes JSON über lange deutsche Transkripte hinweg**, nicht am Sprachverständnis.
### Code-Änderung, die bleibt: Chat-Template aus der Umgebung
`rkllm_server.cpp` liest das Template jetzt aus der Umgebung, weil rkllm das HF-Template **nicht** selbst
anwendet (ohne passende Marker faselt das Modell generisches Füllmaterial):
| Variable | Default (= bisheriges Verhalten) |
|---|---|
| `RKLLM_TMPL_SYSTEM` | leer |
| `RKLLM_TMPL_USER_PRE` | `<\|im_start\|>user\n` |
| `RKLLM_TMPL_USER_POST` | `<\|im_end\|>\n<\|im_start\|>assistant\n` |
| `RKLLM_SUPPRESS_THINK` | aus; `1` hängt `<think>\n\n</think>\n\n` an USER_POST |
`\n` und `\t` werden im Server dekodiert, weil systemd-`Environment=`-Zeilen keine echten Umbrüche
enthalten können. Ohne die Variablen ist das Verhalten unverändert — mit Qwen3 gegengeprüft (97,9 s,
`chat template: sys=0 user_pre=17 user_post=33 bytes`). Backups `rkllm_server{,.cpp}.bak-tmplenv`.
Der nächste Kandidat ist damit eine Drop-in-Zeile statt einer Neuübersetzung.
**GOTCHA:** `printf` mit `\n` schreibt **echte** Zeilenumbrüche in die Unit-Datei und zerlegt die
`Environment=`-Zeile. Zitiertes Heredoc nehmen, damit das literale `\n` stehen bleibt:
```bash
cat > /etc/systemd/system/callrec.service.d/gemma-model.conf <<'EOF'
[Service]
Environment="RKLLM_TMPL_SYSTEM=<|im_start|>system\n/no_think<|im_end|>\n"
EOF
```
### Aufgeräumt
Beide Testmodelle vom Board gelöscht, Drop-in zurück auf Qwen3, Analysen von #86/#114/#123 per `restore`
wiederhergestellt (19,4 min/7 Todos, 58,5 min/32, 38,4 min/16).
**#112 brauchte den Umweg über das Backup:** der Rauchtest-Schnappschuss hatte den *Fehlerzustand* des
Phi-Versuchs eingefangen, `restore` hätte den zurückgeschrieben. Stattdessen `db-callrec.sql.gz` aus der
Sicherung in eine Hilfsdatenbank `callrec_restore` geladen, die eine Zeile per JOIN zurückkopiert,
Hilfsdatenbank gelöscht — #112 steht wieder auf 124,0 s / 118 Zeichen. **Lehre: `snapshot` nie auf einer
Aufnahme laufen lassen, die gerade einen fehlgeschlagenen Lauf hinter sich hat.**
## 2026-09-29 (3) — A/B-Ergebnis: Qwen3.5-4B verliert, Qwen3-4B bleibt produktiv
**Durchgeführt:** Qwen3.5-4B selbst konvertiert (RTX 3090, rkllm-toolkit 1.3.0 im Container,