From a6e02aea44b78760bfa265331be89c71d053abc2 Mon Sep 17 00:00:00 2001 From: schakal00783 Date: Thu, 16 Jul 2026 20:41:08 +0200 Subject: [PATCH] LLM -> Qwen3-4B-Instruct-2507 (A/B-Sieger, Whole-Pass bis 32k) - Selbst konvertiert (w8a8_g128, ctx 16384, RTX 3090 ~35 min, Kit wiederverwendet) - A/B vs Gemma: 3/3 Prompts besser; 21.8k-Whole-Pass fehlerfrei gewichtet, wo Gemma englisch abdriftete bzw. nur das Ende zusammenfasste - rkllm_server.cpp: ChatML-Template; llm.js: WHOLE_MAX_CHARS 32000, READY-Timeout 60s->180s (Qwen3 laedt ~96s) - E2E validiert: #70 Whole-Pass 15.4 min, #68 Kurzclip sauber - Rollback: .bak-gemmatmpl + Drop-in auf gemma3-Modell Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_0113vDKdSNtD3Dwcw4QMu9yV --- app/llm.js | 24 +++++++++---------- app/rkllm_server.cpp | 2 +- docs/05-llm-analyse.md | 24 ++++++++++--------- docs/08-changelog.md | 20 ++++++++++++++++ .../system/callrec.service.d/gemma-model.conf | 2 +- 5 files changed, 47 insertions(+), 25 deletions(-) diff --git a/app/llm.js b/app/llm.js index df0ad74..57ec984 100644 --- a/app/llm.js +++ b/app/llm.js @@ -32,7 +32,9 @@ function spawnServer() { proc.stderr.on('data', d => process.stderr.write('[rkllm] ' + d)); ready = new Promise((resolve, reject) => { settleReject = reject; - const to = setTimeout(() => { if (!done) { done = true; reject(new Error('rkllm READY timeout')); } }, 60000); + // 180s: Qwen3-4B (5.3GB) needs ~96s to load on the capped clock — the old + // 60s killed it mid-load in an endless retry loop (Gemma was ~72s, barely fit). + const to = setTimeout(() => { if (!done) { done = true; reject(new Error('rkllm READY timeout')); } }, 180000); rl = readline.createInterface({ input: proc.stdout }); rl.on('line', line => { if (line === 'READY') { if (!done) { done = true; clearTimeout(to); resolve(); } return; } @@ -323,17 +325,15 @@ const estTokens = (s) => Math.ceil((s || '').length / 3.3); // ~German chars p // and keeps proper nouns (Com4Tec/René/Tobi+Niklas verified on #70, 2026-07-16). const CHUNK_CHARS = 4200; const SINGLE_MAX_CHARS = 4800; // below this: single combined call (full cleaning) -// The 16K-converted Gemma model is RUN at a 12288 runtime context (rkllm_server -// max_context_len) — but 20000 chars stays the whole-pass cutoff: it is a MODEL -// limit, not a context limit. Verified on #70 (22.6k chars, fits in 12288 ctx): -// above ~6k input tokens Gemma-3-4B ignores the instruction (English chat), -// fixates on the END of the call (recency bias: summarized the closing car -// smalltalk), and its JSON decays (curly quotes, repeated keys, repetition -// loops). At ≤20k chars whole-pass quality is validated good (#69 era). Longer -// recordings use the chunked map-reduce below, whose REDUCE prompt weights -// topics by share across sections. Do NOT raise without re-validating content -// quality — a bigger window does not make the model read evenly. -const WHOLE_MAX_CHARS = 20000; +// Whole-pass cutoff. Model-dependent, NOT just a context question: +// - Gemma-3-4B fell apart above ~6k input tokens (English chat, recency bias, +// JSON decay) → its limit was 20000 chars even at 12288 runtime ctx. +// - Qwen3-4B-Instruct-2507 (2026-07-16 A/B on #70) nailed the SAME 21.8k-char +// whole-pass in one call: correctly weighted German JSON, car smalltalk kept +// out of title/summary, concrete names — the failure mode is gone. +// Budget at 12288 ctx: 12288 − 1536 gen ≈ 10752 tok ≈ 35k chars; 32000 leaves +// margin. Beyond that the section-conglomerate path below takes over. +const WHOLE_MAX_CHARS = 32000; // Split transcript into <=maxChars pieces on natural boundaries (labeled lines // first, else sentences, else hard char cut) so no chunk splits mid-word. diff --git a/app/rkllm_server.cpp b/app/rkllm_server.cpp index fb6d762..b6b94f0 100644 --- a/app/rkllm_server.cpp +++ b/app/rkllm_server.cpp @@ -74,7 +74,7 @@ int main(int argc, char** argv) { // without this the model gets the raw prompt with no turn markers and just // rambles generic filler. prefix/postfix wrap each user prompt; is // added by the runtime (add_bos_token=True at conversion). - rkllm_set_chat_template(h, "", "user\n", "\nmodel\n"); + rkllm_set_chat_template(h, "", "<|im_start|>user\n", "<|im_end|>\n<|im_start|>assistant\n"); printf("READY\n"); fflush(stdout); fprintf(stderr, "rkllm model loaded\n"); fflush(stderr); diff --git a/docs/05-llm-analyse.md b/docs/05-llm-analyse.md index 846c348..8e2b844 100644 --- a/docs/05-llm-analyse.md +++ b/docs/05-llm-analyse.md @@ -1,15 +1,15 @@ -# 05 — LLM-Analyse (Gemma-3-4B auf der NPU) +# 05 — LLM-Analyse (Qwen3-4B auf der NPU) ## Modell | | | |---|---| -| Modell | **Gemma-3-4B-it**, `gemma3-4b-w8a8g128-16k.rkllm` | -| Runtime | rkllm **1.3.0** (Upgrade von 1.2.1 — API- und Chat-Template-Änderungen) | -| Quantisierung | **grouped w8a8 (g128)** — plain w8a8 hat Gemma kaputt-quantisiert; w4a16 auf RK3588 nicht unterstützt | -| Kontext | 16k konvertiert, Runtime **12288** (seit 2026-07-16, vorher 8192; KV-Cache-RAM passt). ABER: Whole-Pass-Grenze bleibt **20 000 Zeichen** — das ist eine **Modell-**, keine Kontextgrenze (s. u.) | -| Tempo | ~5 tok/s Decode → Kurzclip ~5 min, 71-min-Aufnahme ~20 min Analyse | -| Historie | Vorher Qwen2.5-3B (fest 4096 Kontext). Gemma ≈ Gemini-Qualität, aber ~3× langsamer. gemma3n-E4B zu groß (10,4 GB). | +| Modell | **Qwen3-4B-Instruct-2507** (non-thinking), `qwen3-4b-w8a8g128-16k.rkllm` (5,3 GB), seit 2026-07-16 | +| Runtime | rkllm **1.3.0**; Chat-Template = **ChatML** (`<\|im_start\|>user` / `<\|im_end\|><\|im_start\|>assistant`) in `rkllm_server.cpp` | +| Quantisierung | **grouped w8a8 (g128)** — gleiche Wahl wie bei Gemma (plain w8a8 riskant, w4a16 auf RK3588 nicht unterstützt) | +| Kontext | 16k konvertiert, Runtime **12288** → Whole-Pass bis **32 000 Zeichen** (`WHOLE_MAX_CHARS`) — Qwen3 schafft die Synthese, an der Gemma ab ~6k Token scheiterte | +| Tempo | Laden ~96 s (READY-Timeout deshalb 180 s!), 22k-Zeichen-Whole-Pass ~12-15 min, Kurzclip-Analyse ~7 min (gedrosselt) | +| Historie | Qwen2.5-3B (4096 ctx, Feld-Echos) → Gemma-3-4B-it (gut ≤ 20k, Synthese-blind) → **Qwen3-4B** (A/B 2026-07-16: 3/3 Prompts besser, Whole-Pass 21,8k fehlerfrei gewichtet). Rollback: `rkllm_server{,.cpp}.bak-gemmatmpl` + Drop-in auf gemma3-Modell zurückstellen. gemma3n-E4B zu groß (10,4 GB). | **Hart:** niemals Online-LLM — alles lokal auf der NPU. @@ -19,7 +19,7 @@ flowchart TB T["Transkript"] --> A{"≤ 4 800 Zeichen?"} A -- ja --> SINGLE["analyzeSingle:
EIN Call = bereinigter Dialog + JSON-Analyse
(Fallback: separater JSON-Call bei Echo/Parse-Fehler)"] - A -- nein --> B{"≤ 20 000 Zeichen?"} + A -- nein --> B{"≤ 32 000 Zeichen?"} B -- ja --> WHOLE["analyzeWhole:
GANZES Gespräch in einem Analyse-Call
(kohärente Summary, kleine JSON-Ausgabe)
Instruktion VOR und NACH dem Transkript"] WHOLE --> WCHECK{"JSON parsebar +
Zusammenfassung ≥ 20 Zeichen?"} WCHECK -- "JSON kaputt" --> REPAIR["Repair-Call: NUR der kaputte
Output (klein) → gültiges JSON"] @@ -47,10 +47,12 @@ reproduzierbar: Gewichtungsregeln ignoriert (Auto-Smalltalk blieb im Titel), Nam Synthese liegt jenseits von Gemma-3-4B** — Verstehen einzelner 4k-Abschnitte dagegen funktioniert gut (Com4Tec, René, Tobi+Niklas, 20 Bewerbungen alle korrekt extrahiert). Also: LLM versteht, Code führt zusammen. -### Warum die Whole-Pass-Grenze bei 20 000 Zeichen bleibt (Modellgrenze, 2026-07-16) +### Whole-Pass-Grenze = Modellfrage (Gemma-Geschichte, gelöst durch Qwen3) -Aufnahme #70 (26 min, 22,6k Zeichen ≈ 6 900 Token) lag knapp über der Grenze. Versuch, sie per -Runtime-Kontext 12288 durch `analyzeWhole` zu schicken, scheiterte REPRODUZIERBAR an Gemma-3-4B selbst +Mit **Qwen3-4B** liegt die Grenze bei 32 000 Zeichen (Kontext-Budget: 12288 − 1536 ≈ 10 752 Token) — +der 21,8k-Whole-Pass von #70 kam in EINEM Call korrekt gewichtet zurück (Auto-Smalltalk raus, Namen drin). +Zur Geschichte: mit Gemma-3-4B war die Grenze 20 000 Zeichen und zwar als MODELL-Grenze — Aufnahme #70 +(26 min, 22,6k Zeichen ≈ 6 900 Token) scheiterte REPRODUZIERBAR an Gemma selbst (vier Iterationen, alle Fehlbilder real beobachtet): 1. **Instruktion nur oben** → Modell „vergisst" die Aufgabe, antwortet mit englischem Geplauder diff --git a/docs/08-changelog.md b/docs/08-changelog.md index 349f3c5..0202c47 100644 --- a/docs/08-changelog.md +++ b/docs/08-changelog.md @@ -3,6 +3,26 @@ Chronik der Entwicklungsschritte. **Regel: jede zukünftige Änderung bekommt hier einen Eintrag** (Datum, Was, Warum, betroffene Dateien/Backups). +## 2026-07-16 — ✅ LLM → **Qwen3-4B-Instruct-2507** (Nacht; Gemma-Rollback vorhanden) + +Auf User-Wunsch Qwen3-4B selbst konvertiert und im A/B gegen Gemma getestet — **3/3 Prompts klar besser**, +produktiv geschaltet und E2E validiert. + +- **Konvertierung:** wiederverwendetes Kit (`rkllm-convert:1.3.0`-Docker neu gebaut), HF `Qwen/Qwen3-4B-Instruct-2507` + (7,6 GB bf16) → `w8a8_g128`/grq, max_context 16384, auf RTX 3090 in ~35 min → 5,3 GB `.rkllm`, pscp aufs Board. +- **A/B (gleiche Prompts wie Gemma-Tests):** Chunk 1 (Com4Tec-Story) in 140 s mit ALLEN Nuancen (50 %-Kappa, + kein Body Leasing, Perinko/Icon); Auto-Chunk 91 s inkl. Detail-Korrektur „Michael Wagner, nicht Fabienne"; + **Whole-Pass 21,8k Zeichen in 691 s fehlerfrei gewichtet** — exakt der Fall, in dem Gemma englisch schwafelte + bzw. nur das Ende zusammenfasste. Qwen3 = ChatML-Template statt Gemma-Turns (`rkllm_server.cpp`). +- **Produktiv-Swap:** Drop-in `RKLLM_MODEL=qwen3-4b-w8a8g128-16k.rkllm`, prod `rkllm_server` mit ChatML neu + gebaut (Rollback: `.bak-gemmatmpl` + Drop-in zurückstellen), `WHOLE_MAX_CHARS` 20 000 → **32 000** (bis ~35-min- + Aufnahmen Single-Pass; Sektions-Konglomerat bleibt für längere). **GOTCHA:** Qwen3 lädt ~96 s → llm.js + READY-Timeout 60 s → 180 s (sonst Endlos-Retry-Schleife, real passiert). +- **E2E validiert:** #70 Whole-Pass 15,4 min → „Neue Kontakte und Stellenangebote", Auto raus, Comfort Tech/ + Adem Marlock/Nico-Profil-Todo konkret; #68 Kurzclip (analyzeSingle+ChatML) 22,2 min inkl. Queue → sauber. +- Damit ist die 4B-Synthese-Decke aus dem Sektions-Konglomerat-Eintrag (unten) für ≤ 32k Zeichen GESCHLOSSEN; + das Konglomerat bleibt als >32k-Pfad und Fallback. + ## 2026-07-16 — A/B-Test Qwen2.5-3B vs Gemma-3-4B im neuen Chunk-Pfad: Gemma bleibt Auf User-Wunsch das noch vorhandene `qwen2.5-3b.rkllm` gegen die neuen 4,2k-Chunk-Prompts getestet diff --git a/system/etc/systemd/system/callrec.service.d/gemma-model.conf b/system/etc/systemd/system/callrec.service.d/gemma-model.conf index 757275b..eab3a7e 100644 --- a/system/etc/systemd/system/callrec.service.d/gemma-model.conf +++ b/system/etc/systemd/system/callrec.service.d/gemma-model.conf @@ -1,2 +1,2 @@ [Service] -Environment=RKLLM_MODEL=/opt/rkllm-models/gemma3-4b-w8a8g128-16k.rkllm +Environment=RKLLM_MODEL=/opt/rkllm-models/qwen3-4b-w8a8g128-16k.rkllm