Phase 8: Lokale KI
Wiki mit semantischer Suche ueber pgvector, Diktat ueber faster-whisper, Berichtsassistent und Belegerfassung — alles gegen selbst betriebene Modelle. Dazu zwei Protokollansichten: was die Maschine getan hat und was Menschen getan haben. Nichts, was ein Modell erzeugt, gelangt ungeprueft in einen Nachweis. Jede Ausgabe ist zuerst ein eigener Datensatz, gekennzeichnet, mit zwei gleich grossen Knoepfen davor. Die Datenbank haelt gegen: Eingabe, Ausgabe und Modell sind unveraenderlich, geloescht wird nicht, entschieden wird einmal. Kein Rueckfall auf einen Anbieter: fehlen die Container, fehlt die Funktion. Dafuer bleibt alles andere ohne Modell vollstaendig benutzbar — geprueft von vierzehn E2E-Tests, die ohne laufendes Modell durchlaufen. Postgres bringt jetzt PostGIS und pgvector mit (eigenes Image), Ollama und faster-whisper stehen im Compose-Profil "ki". 73 neue Unit-Tests, 14 neue E2E-Tests, Version 0.9.0. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
12243f177d
commit
f7ccd9a908
+220
-10
@@ -1,10 +1,9 @@
|
||||
# Anleitung
|
||||
|
||||
Bedienung von FLORIAN, Stand Phase 7 (Version 0.8.0). Die Bilder zeigen die
|
||||
Bedienung von FLORIAN, Stand Phase 8 (Version 0.9.0). Die Bilder zeigen die
|
||||
Beispielwehr aus dem Seed und werden mit `npm run screenshots` neu erzeugt.
|
||||
|
||||
> Die lokale KI und die Kassenverwaltung kommen in den folgenden Phasen dazu.
|
||||
> Siehe [ROADMAP.md](ROADMAP.md).
|
||||
> Die Kassenverwaltung kommt in Phase 9 dazu. Siehe [ROADMAP.md](ROADMAP.md).
|
||||
|
||||
---
|
||||
|
||||
@@ -1502,7 +1501,210 @@ wenn sein Code technisch noch stimmt.
|
||||
|
||||
---
|
||||
|
||||
## 43. Rollen und was sie dürfen
|
||||
## 43. Das Wiki der Wehr
|
||||
|
||||

|
||||
|
||||
Unter **Wissen** steht, was die Wehr nachschlagen soll: Dienstvorschriften,
|
||||
Merkblätter, Ausbildungsunterlagen. Also das, was heute als PDF im Ordner steht
|
||||
und im Einsatz niemand findet.
|
||||
|
||||
Lesen darf jeder. Eine Wehr, in der nur die Führung nachschlagen kann, hat kein
|
||||
Wiki, sondern einen Aktenschrank.
|
||||
|
||||
### Zwei Suchen, und beide werden gebraucht
|
||||
|
||||
| Suche | Findet | Braucht ein Sprachmodell |
|
||||
|---|---|---|
|
||||
| **Wortsuche** (das Feld) | was dasteht — „FwDV 7" findet die Seite mit genau dieser Zeichenfolge | nein |
|
||||
| **Frage** (der Kasten oben) | was gemeint ist — „wie lange hält ein Schlauch" findet den Abschnitt über Ablegereife | ja |
|
||||
|
||||
Die Wortsuche zeigt nicht nur den Seitennamen, sondern die **Textstelle**. So
|
||||
sieht man beim Suchen, ob es die richtige Stelle ist, ohne die Seite zu öffnen.
|
||||
|
||||
### Eine Seite schreiben
|
||||
|
||||
**Seite anlegen**, dann Titel, Bereich und Inhalt. Der Inhalt ist Markdown:
|
||||
|
||||
```
|
||||
## Eine Überschrift
|
||||
|
||||
Ein Absatz.
|
||||
|
||||
- Ein Aufzählungspunkt
|
||||
- Noch einer
|
||||
|
||||
| Spalte | Spalte |
|
||||
|---|---|
|
||||
| Wert | Wert |
|
||||
```
|
||||
|
||||
Die **Überschriften sind wichtiger, als sie aussehen**: an ihnen wird die Seite
|
||||
für die Suche zerlegt, und sie stehen später als Fundstelle unter einer
|
||||
Antwort. Eine Seite ohne Überschriften ist ein einziger großer Block und liefert
|
||||
entsprechend grobe Fundstellen.
|
||||
|
||||
Unter dem Textfeld steht beim Bearbeiten, in wie viele Abschnitte zerlegt wird.
|
||||
|
||||
### Fundstelle angeben
|
||||
|
||||
Das Feld **Fundstelle** ist freiwillig und trotzdem das wichtigste: Es steht
|
||||
unter jeder Antwort, die aus dieser Seite gebaut wird.
|
||||
|
||||
> „Zusammenfassung nach FwDV 7, Ausgabe 2002" sagt einem Leser, woran er ist.
|
||||
> Eine Antwort ohne Herkunft ist eine Behauptung.
|
||||
|
||||
Abschreiben sollten Sie eine Dienstvorschrift übrigens nicht — die sind
|
||||
urheberrechtlich geschützt. Was hier hineingehört, ist die **eigene
|
||||
Zusammenfassung** mit Verweis auf die Fundstelle, so wie ein Merkblatt der Wehr.
|
||||
|
||||
### Entwurf oder veröffentlicht
|
||||
|
||||
Ein **Entwurf** wird nicht durchsucht und nicht für Antworten herangezogen. Das
|
||||
ist der Zustand für eine Seite, an der noch gearbeitet wird — sie steht dann nur
|
||||
für die, die bearbeiten dürfen, in der Liste.
|
||||
|
||||
### Jede Änderung wird aufgehoben
|
||||
|
||||
Beim Speichern wird die vorherige Fassung vollständig weggeschrieben. Über
|
||||
**Verlauf** lässt sich jede frühere Fassung im Wortlaut ansehen.
|
||||
|
||||
Das ist keine Bequemlichkeit: eine Dienstvorschrift, die sich unbemerkt ändert,
|
||||
ist schlimmer als keine. Deshalb lässt sich eine Fassung auch nicht löschen.
|
||||
|
||||
---
|
||||
|
||||
## 44. Fragen stellen
|
||||
|
||||
> **Beantwortet wird ausschließlich aus den hinterlegten Seiten.** Steht die
|
||||
> Antwort dort nicht, sagt FLORIAN genau das — statt etwas Plausibles zu
|
||||
> erfinden.
|
||||
|
||||
Im Kasten oben auf der Wissensseite eine Frage eintippen und **Fragen** drücken.
|
||||
Nach ein paar Sekunden steht die Antwort da — in einem gestrichelten Rahmen mit
|
||||
dem Vermerk **Maschinell erzeugt**.
|
||||
|
||||
Darunter stehen die **Fundstellen**, aus denen die Antwort gebaut wurde. Das
|
||||
sind Verweise: ein Klick führt auf die Seite. Lesen Sie sie, bevor Sie sich auf
|
||||
die Antwort verlassen — sie sind das Belegte, die Antwort ist die
|
||||
Zusammenfassung.
|
||||
|
||||
Findet FLORIAN keine passende Stelle, wird das Modell **gar nicht erst gefragt**.
|
||||
Dann steht dort, dass es in den Unterlagen nicht steht. Das ist die richtige
|
||||
Antwort und keine Fehlermeldung.
|
||||
|
||||
### Wenn kein Modell eingerichtet ist
|
||||
|
||||
Dann steht statt des Kastens ein Hinweis. Die Wortsuche funktioniert weiter,
|
||||
und die ist für „FwDV 7" oder „Ablegereife" ohnehin die bessere.
|
||||
|
||||
### Der Hinweis „Suchstand ist älter als der Text"
|
||||
|
||||
Steht auf einer Seite, die geändert, aber noch nicht neu eingebettet wurde. Bis
|
||||
das nachgeholt ist, findet die Frage-Funktion noch die vorherige Fassung —
|
||||
gelesen wird auf der Seite natürlich die aktuelle.
|
||||
|
||||
Der Hintergrundprozess holt das von selbst nach, spätestens im nächtlichen Lauf.
|
||||
Wer nicht warten will, drückt **Jetzt nachziehen**.
|
||||
|
||||
---
|
||||
|
||||
## 45. Diktieren statt tippen
|
||||
|
||||
Auf der Einsatzseite unter **Bericht** → **Beim Schreiben helfen lassen**.
|
||||
|
||||
Der Weg, den es in jeder Wehr schon gibt: jemand spricht auf der Rückfahrt in
|
||||
sein Telefon, und daraus wird abends ein Bericht. Neu ist nur, dass abends schon
|
||||
ein Text dasteht.
|
||||
|
||||
1. **Diktat aufnehmen** drücken. Der Browser fragt nach dem Mikrofon.
|
||||
2. Sprechen. Der Zähler läuft mit.
|
||||
3. **Fertig** drücken. Die Aufnahme wird übertragen und erkannt — bei einer
|
||||
langen Aufnahme dauert das ein bis zwei Minuten. Die Seite kann offen
|
||||
bleiben, der Text erscheint von selbst.
|
||||
4. Text lesen, dann **In den Bericht übernehmen** oder **Verwerfen**.
|
||||
|
||||
> **Bitte gegenlesen.** Fachbegriffe, Ortsnamen und Fahrzeugbezeichnungen
|
||||
> erkennt kein Modell zuverlässig. Die Aufnahme bleibt abgelegt, bis Sie
|
||||
> entschieden haben — wer dem Text nicht traut, hört nach.
|
||||
|
||||
Das Mikrofon fragt der Browser nur über **HTTPS** oder auf `localhost` ab.
|
||||
Bleibt der Knopf ohne Wirkung, liegt es fast immer daran.
|
||||
|
||||
### Aus Stichpunkten einen Entwurf
|
||||
|
||||
Im selben Kasten. Stichpunkte eintippen — oder das Diktat dorthin übernehmen —
|
||||
und **Entwurf erstellen** drücken.
|
||||
|
||||
```
|
||||
Angriffstrupp unter PA ins EG
|
||||
Brand in der Küche, Fettbrand
|
||||
Löschdecke, kein Wasser
|
||||
Belüftung mit Überdrucklüfter
|
||||
Übergabe an Polizei
|
||||
```
|
||||
|
||||
Daraus wird ein Fließtext für die Akte. Was das Modell **nicht** bekommt:
|
||||
Zeitstempel, Einsatznummer und Fahrzeuge. Die stehen schon als Felder im Bericht
|
||||
und stünden sonst zweimal da — beim zweiten Mal womöglich falsch.
|
||||
|
||||
Der Entwurf landet **nicht** im Berichtsfeld. Er steht daneben, gekennzeichnet,
|
||||
mit zwei gleich großen Knöpfen. Erst **Übernehmen** schreibt ihn ins Feld — und
|
||||
auch dann ist er nur ein Textvorschlag im Formular. Gespeichert wird er, wenn
|
||||
Sie **Bericht speichern** drücken.
|
||||
|
||||
> Verantwortlich ist, wer freigibt. Ein Einsatzbericht ist ein Nachweis, und im
|
||||
> Zweifel liest ihn ein Gericht.
|
||||
|
||||
---
|
||||
|
||||
## 46. Belege erfassen
|
||||
|
||||

|
||||
|
||||
Unter **Belege**: **Beleg fotografieren** drücken. Auf dem Handy öffnet sich
|
||||
direkt die Kamera.
|
||||
|
||||
Was das Modell erkennt, steht danach in **änderbaren Feldern**. Was es nicht
|
||||
erkannt hat, bleibt leer und ist **gelb hinterlegt** — das ist eine
|
||||
Aufforderung. Ein geratener Betrag wäre eine falsche Buchung, die niemand
|
||||
bemerkt.
|
||||
|
||||
Prüfen Sie gegen das Foto, das links daneben steht. Dann **Gegenzeichnen**.
|
||||
|
||||
> Mit dem Gegenzeichnen übernehmen Sie, was **dort steht** — nicht, was die
|
||||
> Maschine gelesen hat. Wenn Sie den Betrag berichtigen, ist der berichtigte
|
||||
> Betrag der Ihre. Beides bleibt nachvollziehbar: das KI-Protokoll hält fest,
|
||||
> was ursprünglich erkannt wurde.
|
||||
|
||||
Ohne Vision-Modell funktioniert alles bis auf das Auslesen: der Beleg wird
|
||||
fotografiert und abgelegt, die Angaben tragen Sie von Hand ein.
|
||||
|
||||
Die Buchung daraus kommt mit der Kasse in Phase 9. Hier entsteht erst einmal ein
|
||||
sauber erfasster Beleg mit Foto — und der ist schon mehr wert als ein
|
||||
Schuhkarton.
|
||||
|
||||
### Das KI-Protokoll
|
||||
|
||||

|
||||
|
||||
Für Kommandant und Administrator: unter **KI-Protokoll** steht jede Befragung
|
||||
eines Modells — was hineinging, was herauskam, wer entschieden hat.
|
||||
|
||||
Die Zeile **Offen** ist die wichtigste. Sie zählt die Ausgaben, die erzeugt und
|
||||
nie angesehen wurden. Steht dort eine große Zahl, benutzt jemand den Assistenten
|
||||
und liest nicht gegen.
|
||||
|
||||
Eingabe, Ausgabe und Modell lassen sich nachträglich nicht ändern, und löschen
|
||||
kann man einen Vorgang gar nicht. Das ist der Zweck der Liste: sie soll belegen,
|
||||
was war.
|
||||
|
||||
Daneben unter **Protokoll** steht dasselbe für Menschen: jede Änderung an jedem
|
||||
Datensatz, seit dem ersten Tag.
|
||||
|
||||
---
|
||||
|
||||
## 47. Rollen und was sie dürfen
|
||||
|
||||
Neun Rollen, jede mit einem festen Satz Rechte. Die Zuordnung steht zentral in
|
||||
`src/lib/authz.ts` und wird beim Seed in die Datenbank gespiegelt — eine Wehr
|
||||
@@ -1514,10 +1716,10 @@ kann sie also nachträglich zuschneiden, ohne dass jemand Code ändert.
|
||||
| **Kommandant** | alles innerhalb der eigenen Wehr, einschließlich Rollen vergeben und Protokoll einsehen |
|
||||
| **Gerätewart** | Fahrzeuge und Geräte anlegen, ändern, aussondern; Prüfungen planen und durchführen |
|
||||
| **Atemschutzgerätewart** | zusätzlich Atemschutzgeräte, Träger und die Überwachung nach FwDV 7 |
|
||||
| **Kassier** | Buchungen und Kassenabschluss (Modul noch nicht geplant, siehe Roadmap) |
|
||||
| **Kassier** | Buchungen und Kassenabschluss (Modul in Phase 9), Belege erfassen und gegenzeichnen |
|
||||
| **Schriftführer** | Mitglieder pflegen, Einsätze und Berichte erfassen, den Terminkalender führen |
|
||||
| **Ausbilder** | Übungen und Übungstermine planen, Teilnahme, Anwesenheit und Qualifikationen erfassen |
|
||||
| **Mitglied** | eigene Daten, Listen lesen, Termine sehen und sich zurückmelden, eigene Benachrichtigungswege einrichten |
|
||||
| **Mitglied** | eigene Daten, Listen lesen, Termine sehen und sich zurückmelden, eigene Benachrichtigungswege einrichten, im Wiki lesen |
|
||||
| **Terminal** | nur anmelden und erfassen — nie verwalten |
|
||||
|
||||
Zwei Dinge sind bewusst so gebaut:
|
||||
@@ -1536,7 +1738,7 @@ sind nur die Textvorlagen, und die hängen an einem eigenen Recht.
|
||||
|
||||
---
|
||||
|
||||
## 44. Anmeldung über authentik einrichten
|
||||
## 48. Anmeldung über authentik einrichten
|
||||
|
||||
authentik läuft **getrennt** von FLORIAN und ist nicht Teil dieses Stacks.
|
||||
|
||||
@@ -1589,7 +1791,7 @@ ganze Wehr aus.
|
||||
|
||||
---
|
||||
|
||||
## 45. PIN und Ausweiskarte verwalten
|
||||
## 49. PIN und Ausweiskarte verwalten
|
||||
|
||||
Wie sich am Terminal angemeldet wird, steht in Kapitel 24. Hier geht es darum,
|
||||
was dahinter liegt.
|
||||
@@ -1612,7 +1814,7 @@ Huber), PIN `471203`.
|
||||
|
||||
---
|
||||
|
||||
## 46. Was protokolliert wird
|
||||
## 50. Was protokolliert wird
|
||||
|
||||
Jede schreibende Änderung erzeugt einen Eintrag mit Zeitpunkt, Person,
|
||||
betroffenem Datensatz und den geänderten Feldern im Vorher-Nachher-Vergleich.
|
||||
@@ -1637,7 +1839,7 @@ jeweiligen Detailseite sichtbar.
|
||||
|
||||
---
|
||||
|
||||
## 47. Wenn etwas nicht geht
|
||||
## 51. Wenn etwas nicht geht
|
||||
|
||||
| Beobachtung | Wahrscheinliche Ursache | Was hilft |
|
||||
|---|---|---|
|
||||
@@ -1677,6 +1879,14 @@ jeweiligen Detailseite sichtbar.
|
||||
| Eine Vorlage lässt sich nicht speichern | Ein Platzhalter gehört nicht zu diesem Anlass | Die Meldung nennt ihn und listet die erlaubten auf |
|
||||
| Ein Termin lässt sich nicht löschen | Es liegen schon Rückmeldungen vor | Absagen statt löschen — dann sehen die Angemeldeten den Grund |
|
||||
| Die Zusage wurde zur Warteliste | Die Plätze sind belegt | Sagt jemand ab, rücken Sie von selbst nach und werden benachrichtigt |
|
||||
| Der Kasten „Frage an die Unterlagen" fehlt | Kein Sprachmodell eingerichtet | `LLM_ENABLED=true` und `LLM_BASE_URL`; Container mit `docker compose --profile ki up -d`. Die Wortsuche geht trotzdem |
|
||||
| Die Frage antwortet „steht nicht in den Unterlagen" | Entweder steht es wirklich nicht drin — oder die Seite ist noch nicht eingebettet | Auf der Seite steht dann „Suchstand ist älter als der Text". **Jetzt nachziehen** drücken |
|
||||
| „Das Modell hat nach 120 Sekunden nicht geantwortet" | Ohne Grafikkarte ist das normal | Kleineres Modell (`qwen2.5:7b`) oder `LLM_TIMEOUT` hochsetzen |
|
||||
| „Das Modell liefert 1024 Dimensionen, die Datenbank erwartet 768" | Ein anderes Einbettungsmodell eingetragen | Entweder `nomic-embed-text` benutzen oder die Spalte per Migration ändern — siehe README, Phase 8 |
|
||||
| Der Diktat-Knopf bewirkt nichts | Der Browser fragt das Mikrofon nur über HTTPS oder auf localhost ab | Über die richtige Adresse aufrufen und die Freigabe erteilen |
|
||||
| Das Diktat bleibt bei „Wird erkannt …" stehen | Der Hintergrundprozess läuft nicht, oder Whisper ist nicht erreichbar | `docker compose ps`, dann `docker compose --profile ki up -d whisper`. Die Aufnahme bleibt erhalten |
|
||||
| Beim Beleg bleiben alle Felder leer | Kein Vision-Modell, oder das Foto ist unscharf | Angaben von Hand eintragen. Für die Erkennung `LLM_VISION_MODELL` setzen und das Modell holen |
|
||||
| Die Migration bricht mit „type vector does not exist" ab | Das Postgres-Image bringt pgvector noch nicht mit | `docker compose build postgres`, dann `docker compose up -d postgres` und erneut migrieren |
|
||||
|
||||
Zustand aller Randsysteme auf einen Blick:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user