Files
Aime/docs/08-changelog.md
T
schakal00783andClaude Fable 5 7391b0ded7 Mikro->Mix gegatet: Doppelweg-Hall der Gegenseite beseitigt
Cepstral-Beweis auf #71: stabiler 80ms-Peak nur auf Gegenseiten-Segmenten
= Muschel-Bleed durchs offene Mikro zusaetzlich zum digitalen Pfad.
cr_mic2mix-Loopback entfernt, stattdessen pw-link micgate.out -> callmix
(Fallback ohne Gate bleibt). Verschmierte Embeddings (verschmolzene
Remote-Sprecher) sollten damit ebenfalls weg sein - echter Call offen.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0113vDKdSNtD3Dwcw4QMu9yV
2026-07-17 00:11:58 +02:00

181 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 08 — Changelog
Chronik der Entwicklungsschritte. **Regel: jede zukünftige Änderung bekommt hier einen Eintrag** (Datum,
Was, Warum, betroffene Dateien/Backups).
## 2026-07-17 — Hall auf der Gegenseite + verschmolzene Sprecher: Mikro→Mix jetzt GEGATET (`.bak-gatedmix`)
User: Gegenseite klingt „wie in einem schallenden Raum", Diarization macht aus zwei entfernten Personen
eine. **Diagnose per Cepstral-Analyse** auf #71: Gegenseiten-Segment hat einen felsenstabilen Peak bei
**79–80 ms** (22/26 Frames) = deterministische verzögerte Kopie; User-Segment sauber. Ursache: der direkte
`cr_mic2mix`-Loopback (immer offen) legte die Gegenseite doppelt in den Mix — digital (`cr_pc2mix`) UND als
Kopfhörermuschel-Bleed durchs Mikro (~80 ms Latenz). Der Hall verschmiert zusätzlich die TitaNet-Embeddings
→ zwei Remote-Stimmen (gleicher Kanal + gleicher „Raum") rücken über die 0,5-Kosinus-Schwelle zusammen.
- **Fix** (`aime-usb-audio-setup.sh`): direkter `cr_mic2mix`-Loopback ENTFERNT; stattdessen
`pw-link micgate.out → callmix` — dieselbe Gate-Auskopplung wie beim Teams-Selbstecho-/iPhone-Uplink-Fix.
In Sprechpausen des Nutzers ist das Mikro komplett aus der Aufnahme. Fallback ohne Gate = alter Loopback.
- Verifiziert strukturell (`pw-link -l`: callmix ← cr_pc2mix + micgate.out, kein HSMIC-Direktpfad) und
funktional (4 s callrec_mix bei Stille = RMS 0/Peak 0). **Echter Call steht noch aus.**
- **Nebenbei entdeckt:** OOM-Ereignis 17:47 (die fünf 60-s-READY-Timeout-Retries beim ersten Qwen3-Start
luden je 5,3 GB, NPU-Speicher der gekillten Loads wurde nicht schnell genug frei) riss mariadb, callrec
UND pipewire weg — Audio-Topologie war seitdem TOT. Mit dem 180-s-Timeout (Changelog unten) behoben;
Topologie neu aufgebaut. Lehre: nach OOM immer `pw-link -l` prüfen.
## 2026-07-16 — Glossar: Einträge bearbeitbar (`.bak-gedit`)
User-Wunsch: gespeicherte Glossar-Begriffe nachträglich ändern. Neu: `PUT /api/glossary/:id`
(`db.updateGlossary`), GUI-Stift-Button je Eintrag lädt Begriff+Soundalikes ins Formular
(Titel „… bearbeiten", Speichern/Abbrechen), Update statt Neuanlage. Bundle `index-NRK6cGUI.js`.
API-verifiziert (POST→PUT→DELETE). Dateien: `server.js`, `db.js`, `web/src/main.js`.
## 2026-07-16 — rkllm vor Diar/Identify freigeben (TitaNet-OOM) + #71 validiert
- **Bug:** residenter rkllm (5,9 GB) ließ den TitaNet-spk-embed-Helper OOM-sterben („spk beendet" pro
Cluster) → Konsolidierung bekam NULL Centroids → #71 bekam still **48 Roh-Sprecher**. Fix (`server.js`):
`llm.stopServer()` am Anfang von `rediarize()` UND `analyzeRecording()` (Reload ~96 s, egal bei 15-min-Analysen).
- **#71 E2E danach:** Diar 48 → **2 Sprecher** (5 schwache gefoldet), Qwen3-Whole-Pass auf 31,3k Zeichen →
„Steuergestaltung für wachsendes Unternehmen" mit konkreten Zahlen (30 MA, 3 Mio Umsatz, 15-20 %-Ziel,
Kick-off Hamburg, Termin 23.) — alter Titel war generisch, alte Sprecherzahl 28.
- **OFFEN:** `identify … spk beendet` schlägt auch MIT freiem RAM fehl → Voice-Auto-Naming (Voiceprints)
aktuell tot; Teilnehmer erscheinen als „Sprecher N". Separater Bug, noch zu untersuchen (spk.js/Helper).
## 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
(Test-Binary mit ctx 4096 in /tmp, direkt übers Stdin-Protokoll — kein App-Umbau). Ergebnis: **3 von 3
Outputs unbrauchbar** (Feldbeschreibungs-Echo ohne Inhalt; Abbruch nach ~100 Zeichen mitten im Satz;
Struktur-Müll mit der Feldbeschreibung als JSON-Key) — Gemma lieferte im selben Job 8/8 verwertbare
Sektionen. Dazu prinzipiell: Qwen2.5 hat fix 4096 Kontext → der validierte Whole-Pass (≤ 20k Zeichen)
wäre unmöglich. Testartefakte entfernt. Offene Alternative (ungetestet, ~½ Tag): Qwen3-4B neu konvertieren.
## 2026-07-16 — Sektions-Konglomerat statt LLM-Reduce (Abend, `.bak-sectionsum`)
Auftrag: Lang-Transkript-Summary (> 20k Zeichen) inhaltlich retten. Prompt-Lab auf #70 mit temporärem
`/api/debug/ask`-Endpoint (nach Abschluss wieder entfernt), ~15 Reduce-/Chunk-Varianten in je ~2 min
statt 9-min-Vollanalysen.
- **Befund 1: Chunk-Größe ist der Fidelity-Hebel.** 8k-Chunks → generisches namenloses Blabla; 4k-Chunks →
geerdete Summaries mit echten Eigennamen (Com4Tec, René, Tobi+Niklas, 20 Bewerbungen). `CHUNK_CHARS` 8000 → **4200**.
- **Befund 2: Cross-Section-Synthese unmöglich auf Gemma-3-4B.** Jede LLM-Reduce-Variante (Gewichtungsregeln,
Abschnitts-Labels, Themen-Tabellen) → Regeln ignoriert, Namen durch Rollen ersetzt, Dubletten, Loops.
**LLM-Reduce abgeschafft.**
- **Neue `analyzeLong`-Pipeline** (`llm.js`): 4,2k-Chunks sequenziell mit **Rolling Context** (1-Satz-
Kurzfassungen der Vorgänger → erkennt geänderte Entscheidungen: „jetzt X, vorher Y") + **eigennamen-
Extraktionsfeld**; Echo-Guard (Feldbeschreibungs-Echo ist INHALTS-abhängig reproduzierbar → bis 3 Versuche
mit variiertem Prompt: ohne Kontext, ohne Hint) + Feld-Salvage per Regex + Prosa-Rettung (≥ 60 Zeichen);
**Titel-Mikro-Call** ohne letzten Abschnitt (Smalltalk-Filter); **Konglomerat baut Code**: „Gesprächsverlauf
in n Teilen — genannte Namen: …" + „Teil i/n: …" chronologisch. Kap. 05 (Mermaid + Marotten aktualisiert).
- **E2E #70 (3 Läufe):** Titel ohne Auto-Smalltalk ✅, alle Abschnitte mit echten Namen ✅, Auto nur noch in
Teil 7 ✅, 12-14 min für 26-min-Aufnahme. Restgrenze: Abschnitts-Summaries variieren pro Lauf in Schärfe
(Sampling), keine thematische Verdichtung ÜBER Abschnitte (4B-Decke). ≤ 20k Zeichen weiter Whole-Pass.
## 2026-07-16 — #70-Debug: Phantom-Sprecher, Summary-Fehlgewichtung, Thermal-Leck (Nachmittag)
Auslöser: Aufnahme #70 (26 min, 4 Personen) — 7 Sprecher erkannt, Titel/Summary aus Randthemen
(Auto-Smalltalk der letzten 3 min statt Recruiting-Hauptteil). 9 Analyse-Iterationen auf #70.
- **Diarization Stufe 3 „Weak-Fold"** (`diar.js`, `.bak-weakfold`): finale Sprecher < `DIAR_MIN_TOTAL_SEC`
(20 s, Segment-Zeiten!) werden in den akustisch nächsten starken Sprecher gefaltet — ein einzelner
≥ 3-s-Streu-Cluster konnte vorher einen Phantom-Anchor eröffnen (#70: 3 Phantome mit 2,5–4,8 s).
10 s reichte NICHT (9,2-s-Phantom kam durch). **Verifiziert: #70 konstant 50 Roh-Cluster → exakt 4.** Kap. 04.
- **Whole-Pass-Grenze = Modellgrenze bewiesen** (Runden 2–5): rkllm-Runtime-Kontext 8192 → **12288**
(`rkllm_server.cpp`, `.bak-ctx12k`, Rebuild) und `WHOLE_MAX_CHARS` testweise 32k → Gemma-3-4B kippt
> ~6k Input-Token reproduzierbar: englisches Geplauder statt Analyse, Recency-Bias (fasst nur das
Gesprächsende zusammen), JSON-Zerfall (typografische Quotes, Wiederholungsschleifen). `WHOLE_MAX_CHARS`
zurück auf **20 000** (dort validiert gut); ctx 12288 bleibt (Headroom). Kap. 05 neuer Abschnitt.
- **Robustheit `llm.js`** (`.bak-ctx12k`): End-Instruktion nach dem Transkript (ohne Platzhalter-Skelett —
Gemma kopiert `...` wörtlich!); JSON-Repair-Call (repariert kaputten Output OHNE das große Transkript);
Sanity-Check (Zusammenfassung ≥ 20 Zeichen) → sonst Chunk-Fallback statt Hard-Error; `extractJson()`
repariert typografische Anführungszeichen + trailing commas; **1 Retry pro Chunk** (Parse = Sampling-
Münzwurf: 3/3 ok vs 1/3 im nächsten Lauf; Retry live bestätigt); Reduce-Input mit „Abschnitt i von n"-
Labels + Gewichtungsregeln (Hauptthema = mehrere Abschnitte; Nur-letzter-Abschnitt = Smalltalk).
- **Regression bestätigt: Chunk-Prompt MUSS minimal bleiben** — eine zusätzliche Anweisungszeile
(Namen/Firmen/Zusagen) kippte 2/3 Chunks in Prosa (Runde 6). Zurückgebaut.
- **Thermal-Leck geschlossen** (`server.js`, `.bak-anacap`): `analyzeRecording()` lief als einziger
schwerer Pfad OHNE A76-Takt-Cap → 95–97 °C bei langer Gemma-Generierung (lüfterlos!). Jetzt Cap wie
`transcribeWav`/`rediarize`. Kap. 03/05.
- **Ehrliches Ergebnis #70:** Sprecher exakt 4 ✅, Analyse crashfrei in ~9 min ✅, Summary führt jetzt mit
Recruiting, ABER Auto-Smalltalk bleibt in Titel/Kernpunkten und konkrete Namen (Com4Tec, Kandidaten)
fehlen — **Qualitätsdecke des 4B-Modells im Chunk-Pfad (> 20k Zeichen)**. Aufnahmen ≤ ~23 min laufen
über den validierten Whole-Pass.
## 2026-07-16
- **Codebase ins Repo aufgenommen:** `app/` = Spiegel von `/opt/callrec` (Backend, Frontend-Quellen, Inferenz-Python, Skripte, `rkllm_server.cpp`), `system/` = systemd-Units + WirePlumber/PipeWire-Drop-ins + sudoers. Modelle/Binaries/Aufnahmen bewusst außen vor (`.gitignore`).
- **Projekt umbenannt in „Aime“** (vormals CallRec, wie der BT-Name des Boards): Gitea-Repo → `sascha/Aime`,
Web-GUI-Titel/Header → „Aime“ (Bundle `index-CuWeUh1j.js`, Backups `index.html.bak-aime`/`main.js.bak-aime`),
Doku rebranded. Technische Bezeichner auf dem Board bleiben `callrec*` (Stabilität).
- **Doku-Repo angelegt** (dieses Repo). Ab jetzt verbindliche Ablage inkl. Diagrammen.
## 2026-07-15/16 — Strukturierte Analyse + Teilnehmer-Fix (`.bak-structsum`)
- Teilnehmer kommen aus der **Diarization** statt vom LLM (das listete alle *erwähnten* Namen → 11 statt 3 bei #69).
- Summary strukturiert: Überblick + „Kernpunkte“ + „Entscheidungen“ als Bullets (`composeSummary`, CSS `pre-line`).
- Chunk-Salvage-JSON-Leak gefixt; Reduce-Input gekappt + Retry; `cleanList` filtert `null`/`[object Object]`/Objekt-Todos.
- Referenz #69 (71,6 min): Analyse ~20 min, Ergebnis sauber. Bundle `index-B2dSaBty.js`.
## 2026-07-15 — LLM-Wechsel auf Gemma-3-4B + Marker-Fix
- Qwen2.5-3B → **Gemma-3-4B-it** (`gemma3-4b-w8a8g128-16k.rkllm`, grouped w8a8; plain w8a8 kaputt, w4a16 nicht unterstützt).
- rkllm-Runtime 1.2.1 → **1.3.0**. `analyzeWhole` (≤ 20k Zeichen) ersetzt Map-Reduce im Mittelbereich.
- Qualität ≈ Gemini, ~3× langsamer. Wort-Highlight-Marker lief ~1 Wort voraus → SYNC_MS=250 (`index-CLero27-.js`).
## 2026-07-14 — Kurzclip-Beschleunigung + Analyse-Timer
- Clips ≤ 30 s über warmen CPU-Whisper-Daemon ohne rkllm-Swap: 10-s-Clip 120 s → **53 s** (`.bak-shortdaemon`).
- GUI zeigt Analysezeit live (`analysis_ms`/`analysis_started_at`, `.bak-anatimer`).
## 2026-07-13 — Diarization stabil + NPU-Meilenstein 2 + iPhone-Repair
- **Greedy-Anchor-Merge** ersetzt Union-Find: derselbe Call ergab vorher 2/3/7 Sprecher, jetzt konstant 27→3 (`.bak-anchor`).
- **Meilenstein 2:** Whisper-turbo-Encoder auf NPU produktiv — RTF 1,51 → 0,61, Temp 90 °C → 59 °C; batched (batch=8) → RTF 0,389.
- iPhone neu gekoppelt, `Trusted=yes` gesetzt (fehlte → keine HFP-Nodes → #58 ohne Gegenseite).
## 2026-07-09 — Meilenstein-1-Snapshot, Auto-Diarization, Glossar, Whisper-Kalibrierung
- Snapshot `/opt/callrec/milestones/m1-20260709/` (Rollback-Punkt).
- Auto-Diarization: 2-stufige Konsolidierung (17→2 verifiziert), `reanalyze` re-diarisiert immer (0=auto / >0=gepinnt).
- **Glossar Phase 1:** deterministische Ganzwort-Ersetzung + LLM-Hinweis, GUI-Tab, „Aime“ geseedet.
- Whisper-Kalibrierung: medium beam1 optimal (11,3 % WER vs large-v3); `--initial-prompt` mit Termliste = Echo-Loop (tabu).
- 46-min-E2E über Nacht sauber (Long-Rec-Fixes produktionsvalidiert).
## 2026-07-08 — Echo-Fix, Thermal-Crashes, Whisper medium, Sprecher-Lernen, Long-Rec-Analyse
- **Selbst-Echo der Gegenseite gelöst:** LADSPA-Noise-Gate `micgate` im Mikro→PC-Pfad (Thr −45 dB, Mic-HW niedrig); AEC verworfen. Reboot-verifiziert.
- **Thermal-Schutzabschaltungen gefixt:** A76-Cap 1,6 GHz über die ganze `transcribeWav` (Whisper+Diar), SIGSTOP-Guard >90 °C; crash-sicheres asynchrones Speichern (`stopAndSave` persistiert vor Transkription); `/retranscribe` für hängende Zeilen.
- Whisper small → **medium** (Hörfehler).
- **Sprecher-Umbenennen + Stimm-Lernen** (Voiceprint-Zentroide) + Leertaste Play/Pause.
- Lange Aufnahmen (> 20 min) analysierbar: Chunked Map-Reduce gegen 4096-Token-Limit; Diar-Timeout skaliert mit Dauer; Lösch-Confirm.
## 2026-07-07 — MEILENSTEIN 1: USB-Headset-Pfad komplett
- Logi H390 ersetzt toten ES8323-Mikrofonpfad; callmix + 4 Loopbacks + `callrec_mix`-Remap; Volume-Bridge; Inline-Tasten.
- Kaltstart-verifiziert. Clip-sicheres Gain-Staging (PC→Mix 60 %) — Übersteuerung hatte Diarization zerstört (14 Sprecher, #41).
- Re-Diarization mit fester Sprecherzahl (`numSpeakers`), #41 14→2 verifiziert.
- LLM-Fixes: Phantom-Todo (Prompt-Beispiel-Echo) entfernt, KV-Cache-Clear, leere Todos → `[]`.
## 2026-07-06
- ES8323-3,5mm-Mikro als defekt diagnostiziert (Verstärkung fest −42,14 dB) → Entscheidung für USB-Headset.
## 2026-07-05 — „Helikopter“-Debugging (Referenz)
- ES8323 S16-Modus = Treiberbug (S24 nutzen); PipeWire/alsaloop-Capture des ES8323 injizierte ~13 Klicks/s,
arecord nicht; Windows-Shared-Mode-AGC = separates 20-Hz-Pumpen (exklusiv sauber).
- Sauberer Fallback damals: dual native-clock arecord. Durch Headset-Pfad obsolet.
## Früher
- iPhone-BT-HFP-Pfad aufgebaut (Rollen-Drop-in, frischer Pair, Echo-Fix Uplink←micgate, Route-Watcher) — Details Kap. 06.
- BT-Buds No-Bonding-Quirk: LinkKey manuell persistiert (Referenz, Dongle entfernt).