Der sherpa-Diarisierungslauf brauchte RAM quadratisch zur Gesprächslänge
(gemessen 349 MB bei 10 min, 1389 MB bei 20 min, 2643 MB bei 30 min). Die
54-min-Aufnahme #99 riss 5,4 GB, wurde OOM-getötet und nahm wegen
OOMPolicy=stop die ganze App mit — in der GUI "Transkription fehlgeschlagen"
und "failed to fetch".
Die v2-Pipeline verwirft sherpas Labels und nutzt nur die Turn-Grenzen, also
laeuft der Pass jetzt in 600-s-Abschnitten (je +-5 s Kontext) und die Grenzen
werden mit Zeit-Offset zusammengesetzt. Zusätzlich OOMPolicy=continue, damit
ein OOM-getöteter Helfer die App nicht mehr stoppt.
Verifiziert an #99: sherpa-Spitze 363 MB, 726 Turns, 8 Sprecher, Analyse
vollstaendig (done/done).
Aufgefallen beim Nachsehen, warum die GUI bei 29:32 Audio 2h34min Analysezeit zeigte.
1. Ein systemctl restart SIGKILLt alle Kindprozesse, die DB-Zeile blieb aber auf
analysis_status=pending mit gesetztem analysis_started_at. Die GUI zaehlte dadurch
ewig hoch fuer Arbeit, die es nicht mehr gab. db.failStaleJobs() setzt beim Start
pending/transcribing auf error - zum Startzeitpunkt kann kein Job legitim laufen.
2. /api/search/answer lud rkllm und gab es nie frei: 1h48min lang 82,6% RAM belegt,
nur 237 MB verfuegbar. Genau der Zustand, den analyzeRecording und rediarize mit
llm.stopServer() bewusst vermeiden, weil der TitaNet-Helfer daneben an OOM stirbt
(auf #71 seinerzeit 48 Rohsprecher). stopServer() jetzt im finally der Route.
Die Section-Zusammenfassung kommt als "Titel:\nFliesstext"-Bloecke zurueck, wurde aber
als flache Textwand gerendert. renderSummary() setzt Titelzeilen jetzt fett.
Im Renderer statt in der DB, damit es sofort fuer alle vorhandenen Zusammenfassungen
gilt, ohne 22 Aufnahmen je 20 Minuten neu zu analysieren.
Regel /^[^.!?]{3,90}:$/ = kurze, alleinstehende Zeile mit Doppelpunkt am Ende. Gegen
die echten Zusammenfassungen #85/#79/#74 geprueft: 37 Titel erkannt, keine
Fliesstextzeile falsch fett, keine Doppelpunkt-Zeile uebersehen.
Drei vom Nutzer angeforderte Funktionen.
Titel umbenennen: PATCH /api/recordings/:id/title. Neue Spalte recordings.title_locked,
damit eine spaetere KI-Analyse einen von Hand gesetzten Titel nicht wieder ueberschreibt.
Papierkorb: DELETE setzt nur noch deleted_at, nichts wird angefasst. Wiederherstellen,
endgueltig loeschen, Aufraeumlauf beim Boot und taeglich (Boot-Lauf, weil das Board oft
tagelang aus ist). Behebt nebenbei einen Altfehler: das bisherige Loeschen liess die
words-Zeilen verwaist in der DB und die WAV/MP3-Dateien auf der Karte zurueck.
Inhaltssuche: neue Tabelle passages (1689 Zeilen aus 22 Aufnahmen) mit Sprecher und
Zeitfenster je Passage. Trefferliste kommt aus SQL in Millisekunden, die formulierte
Antwort mit Belegen [1][2] wird nachgereicht (Qwen3-4B, ~140 s) - deshalb zweistufig.
Hybrid: MariaDB FULLTEXT + Passage-Embedding + Dokument-Embedding aus Titel/Zusammenfassung
(multilingual-e5-small, ONNX/CPU, taskset A55). Ohne Modell degradiert die Suche sauber
auf Volltext statt auszufallen.
Gemessene Grenzen sind dokumentiert statt geschoent: e5-small trennt auf verrauschtem
ASR-Text schwach (Kosinus-Spanne aller Passagen nur 0,8085-0,8560; eine irrelevante
Passage schlug die richtige Antwort). Dokument-Ebene und IDF-Wertung heben die richtige
Passage auf Rang 11 von 296, aber nicht in die Top-8, die das LLM liest. Naechster Hebel
waere multilingual-e5-base.
Relative Datumsangaben werden deterministisch aufgeloest, nicht vom LLM - ein falsches
Kalenderfenster waere schlimmer als gar keins.
Neu: app/search.js, app/emb.js, app/inference/embed_text.py, docs/09-suche-und-verwaltung.md
Board-Backups: .bak-search
#79 verlor einen von vier Sprechern und lieferte eine 1082-Zeichen-Zusammenfassung.
Diarisierung: ein zweiter, falsch verankerter Marker verschmolz Saschas Cluster
in Merles (Kosinus -0,727). must-link prueft jetzt die akustische Aehnlichkeit,
Anker werden nach Trefferqualitaet gewichtet, und enrolled Stimmen wirken als
cannot-link -- damit ist der Box-Besitzer immer ein eigener Sprecher.
LLM: der Whole-Pass komprimierte das erste Thema weg. Sektions-Pfad jetzt ab
9000 Zeichen, kleinere Chunks, woertlicher Kontext aus dem Vorgaenger-Chunk und
ein gezielter Pruef-Aufruf gegen ueberdehnte Einzelaussagen.
#79: 4 Sprecher, Zusammenfassung ~5300 Zeichen, 7 Themenbloecke.
Regression: #70 -> 4, #68 -> 2, #74 -> 20 Sprecher unveraendert.