666 lines
44 KiB
Markdown
666 lines
44 KiB
Markdown
# Multi-Mandate Onboarding, Root-Mandant und Feature-Store — Konzept
|
||
|
||
## Zweck
|
||
|
||
Dieses Dokument beschreibt die **Ziel-Architektur** für Marktzugang und Mandantenmodell:
|
||
|
||
- **Ein globales Benutzerkonto** (`User`) mit **mehreren Mandats-Zugehörigkeiten** (`UserMandate`) — unverändertes Multi-Mandate-Modell.
|
||
- **Subscriptions und Kapazität** bleiben **pro Mandant** gebunden (`MandateSubscription`); siehe [Mandanten-Subscription-Konzept.md](./Mandanten-Subscription-Konzept.md) und [Subscription-State-Machine.md](./Subscription-State-Machine.md).
|
||
- **Feature-Instanzen** pro Mandant; Zugriff über `FeatureAccess` und Rollen.
|
||
- **Unified Data Layer** als zentrale, mandantenweite Datenschicht mit integriertem RAG und Neutralisierung.
|
||
|
||
**Abgrenzung:** Konkrete Implementierungsschritte (PRs, Skripte) werden hier nur skizziert; der Fokus liegt auf **fachlicher Einordnung** und **skalierbarer Trennung** von System-Mandant, Personal-Mandant und Firmen-Mandant.
|
||
|
||
---
|
||
|
||
## Kundenbedürfnisse (zwei Profile)
|
||
|
||
### Profil A — Firma (Team-Mandant)
|
||
|
||
- Ein Mandant repräsentiert die **Organisation**.
|
||
- **Eine Subscription auf Mandanten-Stufe** (z. B. Standard monatlich/jährlich).
|
||
- **Mehrere Benutzer** arbeiten im gleichen Mandanten; weitere Personen per **Einladung**.
|
||
- **Eigene Feature-Instanzen** im Firmen-Mandanten (kein „Gemeinschafts-Pool" mit Fremddaten).
|
||
|
||
### Profil B — Einzelperson (Solo-Mandant)
|
||
|
||
- Registrierung ohne sofortige Firmenstruktur.
|
||
- **Eigenes Mandat** (logisch „Personal"), damit Subscription, Limits und Daten **klar diesem Mandanten** zugeordnet sind.
|
||
- Späterer Ausbau: **Team einladen**, Mandanten-Label/Name anpassen — wird fachlich zum **Firmen-Mandanten** (Profil A), ohne neues Benutzerkonto.
|
||
|
||
---
|
||
|
||
## IST-Zustand (kurz, Stand Codebase-Analyse)
|
||
|
||
| Thema | Aktuell |
|
||
|--------|---------|
|
||
| Identität | `User` ohne `mandateId`; Kontext pro Request über `X-Mandate-Id` / `X-Instance-Id` |
|
||
| Registrierung | `POST /api/local/register` erstellt **nur** den User; Mandats-Zuweisung folgt über Admin, Einladung oder Folgeaktionen |
|
||
| Root-Mandant | Bootstrap: `name=root`, `isSystem=true`, interne Subscription `ROOT`, Feature-Instanzen mit `autoCreateInstance` |
|
||
| Feature Store | **Shared-Instance-Pattern** in [routeStore.py](../../gateway/modules/routes/routeStore.py): eine gemeinsame `FeatureInstance` pro Store-Feature im **Root-Mandanten**; Aktivierung = `FeatureAccess` + User-Rolle; Datenisolation über `read="m"` / `_createdBy` |
|
||
| Dashboard | Keine Instanzen → Redirect auf `/store` ([Dashboard.tsx](../../frontend_nyla/src/pages/Dashboard.tsx)) |
|
||
| Datensilos | Jede Feature-Instanz hat eigene Daten (z. B. CommCoach eigene Voice-Definitionen, Workspace eigene Chats); Parallelstrukturen und Duplikation |
|
||
|
||
**Kernproblem:** Endnutzer landen faktisch im **Root-Mandanten** über den Store. Subscription/Billing pro Kunde ist nicht sauber modellierbar; Upgrade-Pfad „Einzelperson → Firma" fehlt. Datensilos pro Feature verhindern Cross-Feature-Workflows und duplizieren Logik (Voice, Kontext, Dokumente).
|
||
|
||
---
|
||
|
||
## Ziel-Architektur (Entscheidungen)
|
||
|
||
1. **Root-Mandant nur technisch:** Nur System-/Betriebskonten (sysadmin-Benutzer). Keine Endkunden-Datenhaltung, **keine Feature-Instanzen** für Endkunden im Root.
|
||
2. **Jede Selbstregistrierung erzeugt ein eigenes Mandat** (Personal oder Company), inkl. Mandats-Admin-Rolle und passender initialer Subscription (Trial vs. Standard).
|
||
3. **Feature Store** mit explizitem Mandanten-Kontext und Orphan Control (siehe Abschnitt unten).
|
||
4. **Zwei Einstiege von der Homepage/Landing:**
|
||
- **„Kostenlos testen"** → Personal-Mandat + Trial-Plan (`TRIAL_7D`).
|
||
- **„Für Unternehmen"** → Company-Mandat + `STANDARD_MONTHLY` (inkl. Firmenname).
|
||
5. **Unified Data Layer (Variante B):** Zentrale mandantenweite Datenschicht mit RAG als Plattformkern; Features als spezialisierte Workflow-Oberflächen.
|
||
6. **Neutralisierung als Kerndisziplin:** Flag-basiert pro Datenquelle und pro AI-Call, mit Trusted-Model-Pipeline.
|
||
|
||
---
|
||
|
||
## Datenmodell-Erweiterung
|
||
|
||
Auf `Mandate` ([datamodelUam.py](../../gateway/modules/datamodels/datamodelUam.py)) ein fachlicher Typ:
|
||
|
||
| Wert | Bedeutung |
|
||
|------|-----------|
|
||
| `system` | Root / nicht löschbar, nur Betrieb (sysadmin-User) |
|
||
| `personal` | Solo-Mandat (ein primärer „Owner"-Admin) |
|
||
| `company` | Organisations-Mandat (Team, Einladungen) |
|
||
|
||
**Trennung `isSystem` vs. `mandateType`:** Beide Felder haben unterschiedliche Aufgaben und bleiben bestehen:
|
||
|
||
- **`isSystem`** = operativer Schutz (Delete-Schutz, Bootstrap-Erkennung). Nur der Root-Mandant hat `isSystem=true`.
|
||
- **`mandateType`** = fachliche Semantik (Markt, Onboarding, UI, Reporting). Steuert keine Geschäftslogik oder Feature-Gates.
|
||
|
||
**`mandateType` ist mutabel und rein informativ.** Ein Personal-Mandat kann zu `company` geändert werden, wenn der User sein Team erweitert (Einladungen, Name ändern). Es darf **keine Geschäftslogik** hart an `personal` vs. `company` gebunden sein — keine Feature-Gates, keine unterschiedliche Kapazitätsbehandlung auf Basis von `mandateType`. Der Typ dient der UI-Darstellung, dem Reporting und der Onboarding-Steuerung.
|
||
|
||
**Default für bestehende Mandanten (Migration):** `company`, Root explizit `system`.
|
||
|
||
---
|
||
|
||
## Onboarding-Flows (Zielbild)
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
subgraph entry [Homepage]
|
||
TrialCTA[Kostenlos testen]
|
||
CompanyCTA[Fuer Unternehmen]
|
||
end
|
||
|
||
subgraph personalFlow [Personal]
|
||
TrialCTA --> RegP["Register type=personal"]
|
||
RegP --> MP[Mandate personal]
|
||
MP --> SubT[Subscription TRIAL_7D]
|
||
SubT --> ProvP[Feature-Instanzen im Mandat]
|
||
end
|
||
|
||
subgraph companyFlow [Company]
|
||
CompanyCTA --> RegC["Register type=company"]
|
||
RegC --> MC[Mandate company + Firmenname]
|
||
MC --> SubS[Subscription STANDARD]
|
||
SubS --> ProvC[Feature-Instanzen im Mandat]
|
||
end
|
||
|
||
subgraph invite [Parallel: Einladung]
|
||
Inv[Einladung annehmen]
|
||
Inv --> UM[UserMandate / FeatureAccess im Zielmandat]
|
||
end
|
||
```
|
||
|
||
### OAuth-Registrierung (Google, MSAL)
|
||
|
||
Bei OAuth-Registrierung gibt es kein Formular mit „Personal vs. Company". Lösung: **Onboarding-Wizard nach erstem OAuth-Login**, der den User durch die Mandant-Typ-Auswahl führt. Der Wizard erscheint nur beim allerersten Login (kein Mandate vorhanden) und erstellt das Mandate mit derselben Provisionierungslogik wie die Formular-Registrierung.
|
||
|
||
### Invitation-Flow
|
||
|
||
Ein User, der sich via Einladung registriert, erhält **beides**: Zugang zum einladenden Mandanten (via `UserMandate` + `FeatureAccess`) **und** bei Bedarf ein eigenes Personal-Mandate. Der User soll auch ausserhalb des einladenden Mandanten arbeiten können. Das eigene Mandate wird nicht zwingend bei Einladungs-Annahme erstellt, sondern bei Bedarf (z. B. erste Store-Aktivierung ohne Admin-Mandate — siehe Feature Store).
|
||
|
||
### Subscription-Timing
|
||
|
||
Die Trial-Subscription startet nicht ab Registrierung, sondern ab **erstem Login**. Bei Magic-Link- oder OAuth-Flows vergehen zwischen Registrierung und erstem Login Stunden. Daher: `MandateSubscription` wird bei Registrierung mit Status `PENDING` erstellt. Bei erstem Login wird der Status auf `ACTIVE` gesetzt und die Trial-Periode beginnt. So verliert kein User Trial-Tage durch Registrierungs-Delays.
|
||
|
||
### Backend-Orchestrierung
|
||
|
||
Zentrale interne Provisionierung nach `createUser`:
|
||
|
||
1. Mandat anlegen (mit kopierten System-Rollen, wie bei `createMandate`).
|
||
2. `UserMandate` + Mandats-Admin-Rolle.
|
||
3. `MandateSubscription` mit gewähltem `planKey`.
|
||
4. Für Features mit `autoCreateInstance`: Instanzen **im neuen Mandat** erzeugen + `FeatureAccess` mit Admin-Instanzrolle für den Owner.
|
||
|
||
**RBAC-Hinweis:** `createMandate` prüft heute Berechtigungen; für öffentliche Registrierung ist eine Root-privilegierte interne Routine nötig (kein Zirkelschluss „User braucht Rechte, die er noch nicht hat").
|
||
|
||
---
|
||
|
||
## Feature Store: Own Instance Pattern
|
||
|
||
### Prinzip: Expliziter Mandanten-Kontext — nie implizit
|
||
|
||
Ein User ist Mitglied in **1..n Mandanten**. Daher darf die Store-Aktivierung **nie** ein Mandat erraten oder implizit wählen. Der Kontext muss **immer explizit** definiert sein:
|
||
|
||
- **UI:** Der Store zeigt pro Feature die **Mandanten des Users** als Aktivierungsziel. Die Aktivierung erfolgt nicht einfach mit „Aktivieren", sondern mit dem **Namen des Ziel-Mandanten** (z. B. Dropdown oder Kachel-Auswahl bei Multi-Mandat-Usern; bei Solo-Mandant-Usern ist die Auswahl trivial, da nur ein Mandat existiert).
|
||
- **API:** Der Activate-Endpoint erhält immer ein **explizites `mandateId`** — keine Heuristik, kein „primäres Mandat".
|
||
|
||
### Mehrere Instanzen desselben Features
|
||
|
||
Es ist erlaubt, **mehrere Instanzen desselben Features** in einem Mandanten zu haben. Beispiel: Zwei separate Workspace-Instanzen für unterschiedliche Projekte oder Teams. Jede Aktivierung im Store erstellt eine neue `FeatureInstance` — auch wenn bereits eine Instanz desselben Feature-Typs existiert. Die Subscription-Kapazität (`maxFeatureInstances`) begrenzt die Gesamtzahl, nicht pro Feature-Typ.
|
||
|
||
### Store-Zugang: Für alle User (Upselling)
|
||
|
||
Der Store ist für **jeden User sichtbar** — nicht nur für Admins. Der Store ist ein zentrales Upselling-Instrument.
|
||
|
||
**User mit Admin-Mandate:** Aktiviert Features direkt im gewählten Mandanten.
|
||
|
||
**User ohne Admin-Mandate** (z. B. nur als Mitglied in fremden Mandanten eingeladen): Wenn dieser User ein Feature im Store aktiviert, wird **automatisch ein eigenes Personal-Mandate erstellt** (inkl. Subscription), und die Feature-Instanz wird in diesem neuen Mandanten angelegt. Damit kann jeder User über den Store zu einem eigenständigen Mandanten-Admin werden — ohne separaten Registrierungsprozess.
|
||
|
||
### Berechtigungen
|
||
|
||
Nur **Mandanten-Admins** können im Feature Store neue Features für ihren Mandanten aktivieren. Bei Aktivierung erhält der Admin die jeweilige **Admin-Instanzrolle** auf der neu erstellten `FeatureInstance`. Ein User ohne Admin-Mandate löst die Auto-Mandate-Erstellung aus (siehe oben) und wird automatisch Admin des neuen Mandanten.
|
||
|
||
### Deaktivierung und Orphan Control
|
||
|
||
„Deaktivieren" im Store bedeutet: Der User entfernt seinen eigenen `FeatureAccess` auf die Instanz. **Wenn der letzte User eine Feature-Instanz deaktiviert** (d. h. kein `FeatureAccess`-Record mehr verbleibt), wird die **Feature-Instanz gelöscht** (Orphan Control). Dies verhindert verwaiste Instanzen, die Subscription-Kapazität belegen.
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
UserDeactivate["User deaktiviert Feature"] --> RemoveAccess["FeatureAccess entfernen"]
|
||
RemoveAccess --> CheckOrphan{"Noch FeatureAccess-Records auf dieser Instanz?"}
|
||
CheckOrphan -->|Ja| Done["Instanz bleibt bestehen"]
|
||
CheckOrphan -->|Nein| DeleteInstance["FeatureInstance loeschen + Stripe-Quantity sync"]
|
||
```
|
||
|
||
### Technische Umsetzung
|
||
|
||
- `POST /api/store/activate`: Body enthält `featureCode` + `mandateId`. Prüft: User ist Admin im Ziel-Mandat, Subscription-Kapazität (`maxFeatureInstances`). Erstellt `FeatureInstance` + `FeatureAccess` + Admin-Instanzrolle.
|
||
- `POST /api/store/deactivate`: Body enthält `featureCode` + `mandateId`. Entfernt eigenen `FeatureAccess`. Prüft Orphan-Bedingung; löscht Instanz wenn letzer Zugriff entfernt.
|
||
|
||
Frontend: Texte in [Store.tsx](../../frontend_nyla/src/pages/Store.tsx) anpassen auf „Feature für deinen Mandanten aktivieren" mit expliziter Mandanten-Auswahl.
|
||
|
||
---
|
||
|
||
## Homepage / Frontend
|
||
|
||
- **Zwei CTAs** (auf Login- oder Landing-Route): Links nach `/register?type=personal` bzw. `/register?type=company`.
|
||
- **Register-Formular:** bei `company` zusätzlich **Firmenname**; API-Body um `registrationType` / `companyName` erweitern.
|
||
- **Einladungen:** unverändert [InvitePage](../../frontend_nyla/src/pages/InvitePage.tsx)-Flow; nach Annahme kann der User mehrere Mandate haben (bestehendes Modell).
|
||
|
||
---
|
||
|
||
## Migration: Root-Mandant bereinigen
|
||
|
||
### Logik (klar definiert)
|
||
|
||
Die Migration läuft als **separates Modul/Script**, das im Bootstrap aufgerufen wird und später wieder entfernt werden kann.
|
||
|
||
**Algorithmus:**
|
||
|
||
```
|
||
SCHRITT 1 — Feature-Daten migrieren (ALLE User, unabhängig von isSysAdmin):
|
||
|
||
FÜR JEDEN User MIT FeatureAccess auf Root-Mandant-Instanzen:
|
||
|
||
1. Hat der User bereits ein eigenes Mandat (nicht Root)?
|
||
→ JA: Ziel-Mandat = bestehendes Mandat
|
||
→ NEIN: Neues Personal-Mandat erstellen (mandateType=personal, name=username)
|
||
System-Rollen kopieren (copySystemRolesToMandate)
|
||
UserMandate mit admin-Rolle im neuen Mandat erstellen
|
||
MandateSubscription erstellen (TRIAL_7D oder passender Plan)
|
||
2. FÜR JEDE FeatureAccess des Users auf Root-Shared-Instanzen:
|
||
a. Neue FeatureInstance im Ziel-Mandat erstellen (falls nicht schon vorhanden)
|
||
b. Daten migrieren: Alle Records mit featureInstanceId=alte_Root_Instanz
|
||
UND _createdBy=userId → featureInstanceId auf neue Instanz umschreiben
|
||
c. FeatureAccess + Rolle auf neue Instanz erstellen
|
||
d. Alten FeatureAccess auf Root-Instanz entfernen
|
||
|
||
SCHRITT 2 — Root-Mandant bereinigen:
|
||
|
||
Alle Feature-Instanzen im Root-Mandanten entfernen (keine Endkunden-Instanzen mehr)
|
||
FÜR JEDEN User MIT UserMandate zum Root-Mandanten:
|
||
Hat der User isSysAdmin=true?
|
||
→ JA: UserMandate bleibt (Systembetrieb)
|
||
→ NEIN: UserMandate zum Root entfernen
|
||
```
|
||
|
||
**Wichtig:** Das `isSysAdmin`-Flag ist irrelevant für die Feature-Migration (Schritt 1). Es steuert System-Zugriff, nicht Daten-Zugriff. Ein sysadmin mit FeatureAccess auf Root-Instanzen bekommt seine Daten genauso migriert. Das Flag ist nur in Schritt 2 relevant: sysadmin-User behalten ihre Root-Mandant-Mitgliedschaft (für Systembetrieb), alle anderen verlieren sie.
|
||
|
||
### Implementierung
|
||
|
||
- **Ort:** `gateway/scripts/script_migrate_root_users.py` (oder `gateway/modules/migration/migrateRootUsers.py`)
|
||
- **Aufruf:** Einmalig im Bootstrap (`interfaceBootstrap.py`) nach `initRootMandateFeatures` eingehängt; nach erfolgreicher Migration wird ein Flag gesetzt (z. B. DB-Record), damit der Code nur einmal läuft.
|
||
- **Entfernung:** Nach Verifikation in Produktion kann das Modul und der Bootstrap-Aufruf entfernt werden.
|
||
- **Sicherheit:** Dry-Run-Modus als Parameter; Logging aller Aktionen; kein Hard-Delete ohne Verifikation.
|
||
|
||
---
|
||
|
||
## Unified Data Layer (Entscheidung: Variante B)
|
||
|
||
### Entscheidung
|
||
|
||
**Variante B wird gewählt.** Die Feature-Datensilos (z. B. CommCoach mit eigenen Voice-Definitionen parallel zum AI Workspace, doppelte Sprachdefinitionen) zeigen bereits heute, dass die instanz-zentrierte Datenhaltung zu Duplikation und Fragmentierung führt.
|
||
|
||
### Prinzip
|
||
|
||
Der **Unified Data Layer** ist die zentrale mandantenweite Datenschicht. Features (Workspace, Automation, CommCoach, Trustee, ...) sind **spezialisierte Oberflächen/Workflows**, die auf denselben Datenpool zugreifen.
|
||
|
||
```
|
||
Mandate
|
||
└── Unified Data Layer (RAG Repository)
|
||
├── Dokumente, Extraktionen, Wissensbasis, Voice-Definitionen
|
||
├── Neutralisierungskonfiguration (zentral pro Mandat)
|
||
│
|
||
├── Workspace nutzt: Chat, Recherche, Dokumentenkontext
|
||
├── Automation nutzt: Workflows mit Zugriff auf Mandantendaten
|
||
├── CommCoach nutzt: Coaching mit geteiltem Kontext + Voice
|
||
└── Trustee nutzt: Fachlogik auf Mandantendaten
|
||
```
|
||
|
||
### Datenreferenzierung (bleibt erhalten)
|
||
|
||
Daten behalten ihre bestehenden Referenzen:
|
||
|
||
- **`featureInstanceId`:** Zeigt an, **welches Feature** die Daten erzeugt hat (Quellenschutz). Bleibt als Herkunftsreferenz erhalten.
|
||
- **`_createdBy`:** Zeigt an, **welcher User** die Daten erzeugt hat. Bleibt für RBAC und Zuordnung.
|
||
- **`mandateId`:** Implizit enthalten, da jede `FeatureInstance` einem Mandanten zugeordnet ist. Muss nicht redundant auf Daten-Records gespeichert werden.
|
||
|
||
### Datenquellen-Tagging: Explizite Klassifikation durch den User
|
||
|
||
Jede Datenquelle, die in den Unified Data Layer eingebunden wird, muss vom User **explizit getaggt** werden. Es gibt keine implizite Zuordnung. Der User entscheidet beim Einbinden über zwei Dimensionen:
|
||
|
||
**1. Sichtbarkeits-Scope (wer darf diese Daten sehen/nutzen):**
|
||
|
||
| Scope | Bedeutung | Wer hat Zugriff |
|
||
|-------|-----------|-----------------|
|
||
| `personal` | Private Daten des Users | Nur der erstellende User, in allen Features |
|
||
| `featureInstance` | Daten für eine spezifische Feature-Instanz | Alle User mit FeatureAccess auf diese Instanz |
|
||
| `mandate` | Mandantenweite Daten | Alle User im Mandanten, in allen Features |
|
||
| `global` | Plattformweite Daten | Alle User plattformweit (read-only) |
|
||
|
||
**Einschränkung `scope=global`:** Dieser Scope ist mächtig — eine falsch getaggte Datenquelle wäre plattformweit sichtbar. Daher ist `scope=global` **ausschliesslich durch sysadmin-User setzbar** und RBAC-geschützt. Reguläre User und Mandanten-Admins können maximal `scope=mandate` setzen.
|
||
|
||
**2. Neutralisierungs-Flag (`neutralize: true/false`):**
|
||
|
||
Ob die Daten vor AI-Zugriff neutralisiert werden sollen (siehe Abschnitt Neutralisierung).
|
||
|
||
**Tagging-Zeitpunkt:** Das Tagging erfolgt **beim Einbinden** der Datenquelle (Upload, Verlinkung, Import). Der Scope und das Neutralisierungs-Flag können jederzeit nachträglich geändert werden — Änderungen lösen eine Re-Indizierung im RAG aus.
|
||
|
||
**UI-Konzept:** Einfach und unkompliziert direkt in der UDB gelöst — kein separater Dialog nötig.
|
||
|
||
**Files-Tab** — Jede Datei zeigt Scope- und Neutralisierungs-Symbole inline:
|
||
|
||
```
|
||
FILES
|
||
─────────────────────────────────────
|
||
📄 Jahresabschluss_2025.pdf 👤 🔒
|
||
📄 Firmenrichtlinien.docx 🏢
|
||
📄 Meine_Notizen.txt 👤
|
||
📄 Branchenreport.pdf 🏢 🔒
|
||
─────────────────────────────────────
|
||
👤 = personal 👥 = instanz 🏢 = mandant 🔒 = neutralisiert
|
||
```
|
||
|
||
**Sources-Tab** — Die Datenquellen selbst (Mailkonten, Datenbanken, Konnektoren) haben keine Scope-/Neutralisierungs-Icons. Die Icons erscheinen erst auf den **aktiv eingebundenen Elementen** (Active Sources), weil der Scope pro eingebundenem Element definiert wird, nicht pro Quelle:
|
||
|
||
```
|
||
SOURCES
|
||
─────────────────────────────────────
|
||
ACTIVE PERSONAL SOURCES
|
||
■ p.motsch@valueon.ch – Patrick Mo... x 👤 🔒
|
||
|
||
ACTIVE FEATURE SOURCES
|
||
■ PowerOn Prod – TrusteeDataJournal... x 👥
|
||
■ PowerOn Prod – TrusteeDataJournal... x 👥 🔒
|
||
|
||
BROWSE SOURCES
|
||
● p.motsch@valueon.ch
|
||
● patrick.motsch@pamocreate.com
|
||
● ag112559@gmail.com
|
||
|
||
FEATURE DATA
|
||
▸ Root
|
||
PowerOn Teams 4 tables
|
||
PowerOn Test 10 tables
|
||
▸ VO-Team
|
||
CEO Com-Coach 10 tables
|
||
PowerOn Prod 10 tables
|
||
─────────────────────────────────────
|
||
```
|
||
|
||
**Logik:** Die Datenquellen unter "Browse Sources" und "Feature Data" sind **Katalog-Einträge** — sie zeigen, was verfügbar ist. Erst wenn der User eine Quelle **aktiviert** (in die Active-Liste zieht), erscheinen die Scope- und Neutralisierungs-Symbole, weil erst dann die Entscheidung getroffen wird, wie die Daten im RAG integriert werden.
|
||
|
||
- **Klick auf das Scope-Symbol** (bei Active-Elementen): Wechselt den Scope zyklisch (personal → instanz → mandant) oder zeigt ein kleines Popover.
|
||
- **Klick auf das Neutralisierungs-Symbol:** Toggled `neutralize` on/off (🔒 / kein Symbol).
|
||
- **Default bei Aktivierung:** `personal` + `neutralize=false` — der User passt bei Bedarf mit einem Klick an.
|
||
- Die Symbole sind sofort sichtbar und verständlich, ohne dass der User einen Einstellungsdialog öffnen muss.
|
||
|
||
### Datenmodell: Unified Data Layer
|
||
|
||
Das RAG-System existiert bereits in der Plattform und kann ausgebaut werden. Die Erweiterung betrifft primär die Scope- und Neutralisierungs-Metadaten auf bestehenden Entitäten:
|
||
|
||
```
|
||
DataSource (Erweiterung bestehender Modelle)
|
||
├── id -- existiert
|
||
├── mandateId -- existiert (via FeatureInstance)
|
||
├── featureInstanceId -- existiert (Quellenschutz)
|
||
├── _createdBy -- existiert (User-Zuordnung)
|
||
├── scope -- NEU: "personal" | "featureInstance" | "mandate" | "global"
|
||
├── neutralize -- NEU: boolean (Flag pro Datenquelle)
|
||
└── neutralizationStatus -- NEU: "pending" | "completed" | "failed" | "not_required"
|
||
|
||
NeutralizerMapping (existiert als DataNeutralizerAttributes, wird erweitert)
|
||
├── id -- Platzhalter-ID (z.B. [name.uuid])
|
||
├── mandateId -- Mandanten-Zuordnung
|
||
├── featureInstanceId -- Quellenschutz
|
||
├── userId -- Ersteller
|
||
├── originalText -- Originalwert
|
||
├── patternType -- Typ (email, phone, name, address, iban, ...)
|
||
└── fileId -- Zugehörige Datei
|
||
|
||
RAG-Index (Vektorspeicher, bestehende Infrastruktur)
|
||
├── Dokument-Embedding -- existiert
|
||
├── mandateId -- NEU als Filter-Metadatum
|
||
├── scope -- NEU als Filter-Metadatum
|
||
├── featureInstanceId -- NEU als Filter-Metadatum
|
||
├── userId -- NEU als Filter-Metadatum (für scope=personal)
|
||
└── isNeutralized -- NEU: nur neutralisierte Versionen wenn Flag gesetzt
|
||
```
|
||
|
||
Die bestehenden Datenmodelle (`ContentExtracted`, `ContentPart`, Konnektoren) bleiben unverändert. Die Scope- und Neutralisierungs-Felder werden als Erweiterung auf die Datenquellen-Ebene aufgesetzt. Der RAG-Index erhält Scope-Metadaten als Filterkriterien, um die Union-Query über die vier Scopes effizient auszuführen.
|
||
|
||
### RAG als zentrales Repository — der Plattformkern
|
||
|
||
Das RAG-System ist das Repository, das die Plattform ausmacht — ein klarer USP. Alle getaggten Datenquellen werden im RAG indiziert und stehen den Features als Kontextquelle zur Verfügung.
|
||
|
||
Bei jeder RAG-Query wird der **effektive Scope** aus der Kombination der Sichtbarkeiten des anfragenden Users aufgebaut:
|
||
|
||
```
|
||
RAG-Query-Context = personal(userId)
|
||
∪ featureInstance(instanceId) -- falls im Feature-Kontext
|
||
∪ mandate(mandateId)
|
||
∪ global()
|
||
```
|
||
|
||
Das bedeutet: Ein User im Workspace-Feature sieht bei einer RAG-Suche seine persönlichen Daten, die Daten der Workspace-Instanz, die Mandantendaten und globale Daten — automatisch zusammengeführt, entsprechend der Tags der Datenquellen.
|
||
|
||
### UI-Komponente: Unified Data Bar (UDB)
|
||
|
||
Der Unified Data Layer braucht eine **einheitliche UI-Komponente**, die in **allen Feature-Instanzen** verfügbar ist — vergleichbar mit dem Folder-/File-Tree, der heute im Workspace existiert. Diese Komponente heisst **Unified Data Bar (UDB)**.
|
||
|
||
**Herkunft:** Die bestehende Sidebar im AI Workspace mit den Tabs **Chats**, **Files**, **Sources** ist der Ausgangspunkt. Diese Komponente wird aus dem Workspace extrahiert und als **plattformweite, wiederverwendbare UI-Komponente** bereitgestellt.
|
||
|
||
**Tabs der UDB:**
|
||
|
||
| Tab | Inhalt | Scope-Filter |
|
||
|-----|--------|-------------|
|
||
| **Chats** | Chat-Verläufe als Baumstruktur, gruppiert nach Feature-Instanzen | personal + featureInstance |
|
||
| **Files** | Dokumente, Uploads, Extraktionen | Alle Scopes (personal / featureInstance / mandate / global) |
|
||
| **Sources** | Eingebundene Datenquellen mit Scope- und Neutralisierungs-Tags | Alle Scopes |
|
||
|
||
**Chats-Tab: Baumstruktur nach Feature-Instanzen**
|
||
|
||
Der Chats-Tab rendert alle Chats des Users als **hierarchischer Baum**. Top-Level sind die Feature-Instanzen, darunter die Chats in dieser Instanz, ggf. mit feature-spezifischen Substrukturen:
|
||
|
||
```
|
||
CHATS
|
||
─────────────────────────────────────
|
||
▾ AI Workspace – Mein Workspace
|
||
💬 Recherche Datenschutzgesetz
|
||
💬 Report Q1 Entwurf
|
||
💬 Brainstorming Produktstrategie
|
||
▾ CommCoach – CEO Coaching
|
||
▾ Coaching-Modul
|
||
💬 Verhandlungstechnik Session 3
|
||
💬 Feedback-Gespräch Vorbereitung
|
||
▾ Dossier
|
||
💬 Analyse Kommunikationsstil
|
||
▾ Trustee – Jahresabschluss 2025
|
||
💬 Bilanzprüfung Rückfragen
|
||
💬 Steueroptimierung Szenarien
|
||
▸ Automation – Workflow Tests
|
||
─────────────────────────────────────
|
||
```
|
||
|
||
Diese Struktur gibt dem User eine **featureübergreifende Übersicht** aller seiner AI-Interaktionen an einem Ort. Per Klick navigiert er direkt in den Chat — die UDB wechselt dabei ggf. den Feature-Kontext. Die aktuelle Feature-Instanz ist hervorgehoben und expandiert; andere Instanzen sind zugeklappt, aber zugänglich.
|
||
|
||
**Skalierung:** Bei vielen Feature-Instanzen und Hunderten von Chats kann der Baum unübersichtlich werden. Daher bietet der Chats-Tab zwei Zusatzfunktionen:
|
||
|
||
- **Suchfunktion:** Volltextsuche über Chat-Titel und -Inhalte, filtert den Baum auf Treffer.
|
||
- **Flat Mode:** Optionale chronologische Liste (alle Chats ohne Hierarchie, sortiert nach letzter Aktivität) als Alternative zur Baumansicht. Umschaltbar per Toggle im Tab-Header.
|
||
|
||
**Drag-and-Drop: Chats als Kontext im Prompt**
|
||
|
||
Chats aus dem Chat-Baum können per **Drag-and-Drop in den Prompt-Bereich gezogen** werden. Dabei wird **nicht der Chat-Inhalt selbst** übergeben, sondern die **RAG-Daten, die zu diesem Chat gehören** (Dokumente, Extraktionen, Quellen). Das RAG wird nach den dem Chat zugeordneten Daten abgefragt und diese als Kontext für den aktuellen AI-Call bereitgestellt. Use Cases:
|
||
|
||
- **Auswertung:** Mehrere Coaching-Sessions aus CommCoach in den Workspace ziehen → „Fasse die Fortschritte der letzten 5 Sessions zusammen."
|
||
- **Cross-Feature-Analyse:** Trustee-Chat + Workspace-Recherche zusammen auswerten → „Vergleiche die steuerlichen Szenarien mit den Recherche-Ergebnissen."
|
||
- **Reporting:** Chats aus verschiedenen Instanzen zusammenziehen → „Erstelle einen Wochenbericht aus diesen Interaktionen."
|
||
- **Coaching-Review:** CommCoach-Dossier-Chats als Input für eine Meta-Analyse → „Welche Kommunikationsmuster zeigen sich über die Sessions hinweg?"
|
||
|
||
Dasselbe Prinzip gilt auch für **Files** und **Sources** aus den anderen Tabs — alle UDB-Elemente sind als Kontext in Prompts nutzbar. Die UDB wird damit zur zentralen **Kontext-Steuerung** für alle AI-Interaktionen.
|
||
|
||
**Einbettung in Features:**
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────┐
|
||
│ Feature-Instanz (z.B. Trustee, CommCoach, Workspace)│
|
||
│ │
|
||
│ ┌──────────┐ ┌──────────────────────────────────┐ │
|
||
│ │ UDB │ │ │ │
|
||
│ │ │ │ Feature-spezifischer Content │ │
|
||
│ │ [Chats] │ │ (Workflows, Formulare, etc.) │ │
|
||
│ │ [Files] │ │ │ │
|
||
│ │ [Sources]│ │ │ │
|
||
│ │ │ │ │ │
|
||
│ └──────────┘ └──────────────────────────────────┘ │
|
||
└─────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
**Verhalten:**
|
||
|
||
- Die UDB zeigt immer die Daten **im Kontext der aktuellen Feature-Instanz** an, ergänzt um personal- und mandate-scope Daten.
|
||
- Der Chats-Tab zeigt den **gesamten Chat-Baum** über alle Feature-Instanzen des Users.
|
||
- Der User kann in der UDB Datenquellen einbinden (Upload, Scope/Neutralisierung taggen) — direkt aus dem Feature-Kontext heraus.
|
||
- Die UDB ist eine **gemeinsame Komponente** (`frontend_nyla/src/components/`), nicht pro Feature dupliziert.
|
||
|
||
**Implikation:** Die bestehende Sidebar-Logik im AI Workspace (Chats, Files, Sources) muss in Phase 2 aus dem Workspace **herausgelöst** und als eigenständige, wiederverwendbare Komponente bereitgestellt werden. Features binden die UDB ein und erhalten automatisch Zugriff auf den Unified Data Layer.
|
||
|
||
### Wettbewerbseinordnung: UDB als Differenzierungsmerkmal
|
||
|
||
Eine Analyse bestehender AI-Plattformen (Stand März 2026) zeigt, dass **keine** eine vergleichbare featureübergreifende Daten- und Chat-Baumnavigation bietet:
|
||
|
||
| Plattform | Chat-Organisation | Datenquellen | Cross-Feature-Navigation |
|
||
|-----------|-------------------|-------------|-------------------------|
|
||
| **ChatGPT (OpenAI)** | Flach: Ordner + Projects, ein Feature (Chat) | Dateien pro Chat oder pro Projekt | Kein Feature-Konzept; alles ist Chat |
|
||
| **Langdock** | "Projects" gruppieren Chats flach; Agents als separate Kategorie | Knowledge Folders (bis 1000 Files), 55+ Integrations | Chats und Agents getrennt; keine Baumstruktur über Feature-Grenzen |
|
||
| **PageSpace** | Alles ist eine "Page" in einem einheitlichen Baum (Docs, Channels, Agents) | Dateien als Pages im Baum; Kontext durch Platzierung | Nächster Vergleich: einheitlicher Baum, aber kein Mandanten-Modell, kein RAG-Scope-Tagging, keine Neutralisierung |
|
||
| **Microsoft Copilot** | Pro App (Word, Teams, etc.); keine übergreifende Chat-Historie | Microsoft Graph (SharePoint, OneDrive) | Kein übergreifender Chat-Baum; jede App hat eigene AI-Interaktion |
|
||
| **Google Gemini** | Flach: Chat-Liste in Gemini; separate AI in Docs/Sheets | Google Drive | Keine Feature-übergreifende Struktur |
|
||
|
||
**Was PowerON mit der UDB anders macht:**
|
||
|
||
1. **Feature-Instanz-Hierarchie im Chat-Baum:** Kein anderes Tool zeigt Chats gruppiert nach fachlichen Modulen (Treuhand, Coaching, Workspace, Automation) in einem navigierbaren Baum mit feature-spezifischen Substrukturen (z. B. CommCoach → Coaching-Modul → Sessions).
|
||
2. **Drei Dimensionen in einer Sidebar (Chats + Files + Sources):** Langdock und ChatGPT trennen Chat-Liste, Knowledge-Ordner und Integrationen in separate Bereiche. Die UDB vereint sie in einer Komponente mit durchgängigem Scope-Modell.
|
||
3. **Scope-Tagging pro Datenquelle:** Kein Wettbewerber bietet granulares Tagging (personal / instanz / mandant / global) mit visueller Inline-Steuerung.
|
||
4. **Neutralisierung als Inline-Flag:** Einzigartig — keine vergleichbare Plattform bietet Daten-Neutralisierung als pro-Quelle-Toggle.
|
||
5. **Multi-Tenant mit Mandanten-Isolation:** PageSpace hat den ähnlichsten Baum-Ansatz, aber ohne Mandantenmodell, ohne RBAC-Scopes und ohne Neutralisierung.
|
||
|
||
Die UDB mit hierarchischem Chat-Baum, Scope-Tagging und Neutralisierung ist nach aktuellem Stand ein **Alleinstellungsmerkmal**.
|
||
|
||
### Phasenplan
|
||
|
||
1. **Phase 1 (jetzt):** Mandate-per-Registration und Own-Instance-Pattern umsetzen — schafft die richtige Mandanten-Grenze als Voraussetzung. Daten bleiben instanz-zentriert. `FeatureInstance` = **Datengrenze** (Instanz besitzt ihre Daten exklusiv).
|
||
2. **Phase 2:** UDB-Komponente aus dem Workspace extrahieren und als wiederverwendbare Plattformkomponente bereitstellen. Shared Data Scope pro Mandant einführen. Dateien, Extraktionen und Wissensbasis mandantenweit verfügbar machen. Doppelte Strukturen (z. B. Voice in CommCoach vs. Workspace) konsolidieren. `FeatureInstance` = **Datengrenze + mandantenweiter Zugriff** (Daten sind instanz-zugeordnet, aber mandantenweit les-/nutzbar via Scope).
|
||
3. **Phase 3 (Ziel):** Features sind reine Workflow-Oberflächen mit eingebetteter UDB. Der Unified Data Layer (inkl. Neutralisierung, RAG, Vektorspeicher) ist die zentrale Plattformkomponente. RAG-Scopes (personal/instance/mandate/global) sind vollständig implementiert. `FeatureInstance` = **UI-Scope** (die Instanz steuert nur noch, welche Workflow-Oberfläche der User sieht, nicht mehr die Datengrenze).
|
||
|
||
**Semantische Verschiebung von `FeatureInstance`:** Die Bedeutung von `FeatureInstance` wandelt sich fundamental über die Phasen — von der Datengrenze (Phase 1) zum UI-Scope (Phase 3). In Phase 1 besitzt eine Instanz ihre Daten exklusiv. In Phase 3 ist der Unified Data Layer die Datengrenze, und die Instanz definiert nur noch den Workflow-Kontext. Dieser Wandel ist beabsichtigt und muss bei der Implementierung der Phasenübergänge berücksichtigt werden: Daten-Ownership verschiebt sich von der Instanz zum Mandanten.
|
||
|
||
---
|
||
|
||
## Daten-Neutralisierung als Kerndisziplin
|
||
|
||
### Prinzip: Flag-basierte Steuerung
|
||
|
||
Die Neutralisierung wird über **zwei unabhängige Flags** gesteuert:
|
||
|
||
1. **Flag auf der Datenquelle** (`neutralize: true/false`): Beim Einbinden einer Datenquelle in den Unified Data Layer legt der User fest, ob diese Daten neutralisiert werden sollen. Ist das Flag gesetzt, landen **ausschließlich neutralisierte Dokumente** im RAG — nie die Originale.
|
||
2. **Flag pro AI-Call** (`requireNeutralization: true/false`): Unabhängig von der Datenquelle kann ein Feature oder der User verlangen, dass ein spezifischer AI-Call nur mit neutralisierten Daten arbeitet (z. B. für User-Prompts, die sensible Daten enthalten).
|
||
|
||
**Regel:** Wenn **mindestens eines** der beiden Flags gesetzt ist, werden die Daten **vor dem Senden an eine AI-Instanz** neutralisiert.
|
||
|
||
**Fail-Safe-Regel:** Wenn die Neutralisierung **fehlschlägt** (Modell nicht erreichbar, Timeout, unvollständige Neutralisierung), wird das Dokument **nicht weitergegeben** — weder an das externe AI-Modell noch in den RAG-Index. Der AI-Call wird ohne dieses Dokument ausgeführt und der User erhält einen Hinweis, dass ein Dokument aus Datenschutzgründen nicht einbezogen werden konnte. Kein Fallback auf Originaldaten.
|
||
|
||
### Zugelassene Modelle: Operation Type `neutralization`
|
||
|
||
Es gibt **keine speziellen LLMs** für Neutralisierung. Die zugelassenen AI-Modelle in der Plattform haben einen **Operation Type**. Modelle mit dem Operation Type `neutralization` dürfen für Neutralisierungsaufgaben eingesetzt werden. Dies sind Modelle, denen die Plattform vertraut (z. B. lokal gehostet oder vertraglich abgesichert). Externe LLMs ohne diesen Operation Type sehen **nur neutralisierte Daten**.
|
||
|
||
Die Operation-Type-Konfiguration wird zentral verwaltet und ist nicht vom User änderbar.
|
||
|
||
### Bestehende Code-Basis (Ausbau, kein Neubau)
|
||
|
||
Die Neutralisierung ist in der Plattform bereits implementiert und kann ausgebaut werden:
|
||
|
||
- **`StringParser`** ([subParseString.py](../../gateway/modules/features/neutralization/serviceNeutralization/subParseString.py)): Regex-basierte Erkennung und Ersetzung von Emails, Telefonnummern, Adressen, IBANs, Daten, Policy-IDs und Namen. Platzhalter im Format `[type.uuid]` (z. B. `[name.a1b2c3d4-...]`). Mapping `originalText → Platzhalter` wird pro Durchlauf aufgebaut und ist persistent nutzbar.
|
||
- **`DataNeutralizerAttributes`** ([datamodelFeatureNeutralizer.py](../../gateway/modules/features/neutralization/datamodelFeatureNeutralizer.py)): Bestehende Datenbanktabelle für Platzhalter-Mappings mit `originalText`, `patternType`, `mandateId`, `featureInstanceId`, `userId`, `fileId`. Entspricht der im Konzept beschriebenen Platzhalter-Mapping-Tabelle.
|
||
- **`neutralizeData`-Action** ([neutralizeData.py](../../gateway/modules/workflows/methods/methodContext/actions/neutralizeData.py)): Bestehende Workflow-Action, die Dokumente (`ContentExtracted` / `ContentPart`) durch die Neutralisierung führt und als `ActionDocument` mit Validierungs-Metadaten zurückgibt.
|
||
- **`DataNeutraliserConfig`**: Pro Feature-Instanz konfigurierbar mit `enabled`-Flag und `namesToParse`-Liste.
|
||
|
||
Die bestehende Architektur (Config → StringParser → Mapping-Tabelle → neutralisierter Output) entspricht dem Zielbild. Die Erweiterung betrifft: Integration der Scope-Flags, Anbindung an den RAG-Index, Fail-Safe-Logik (Dokument nicht weitergeben bei Fehler), und Re-Hydrierung der AI-Responses.
|
||
|
||
### Neutralisierungs-Pipeline
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
Input["Eingabedaten (Dokument / Datenquelle)"] --> Resolve["Container aufloesen"]
|
||
Resolve --> TypeCheck{"Datentyp?"}
|
||
|
||
TypeCheck -->|Text| NeutText["Modell mit opType=neutralization: Text neutralisieren"]
|
||
TypeCheck -->|Bild| NeutImage["Modell mit opType=neutralization: Bild → neutraler Text"]
|
||
TypeCheck -->|Media| Drop["Media droppen (kein AI-Zugriff)"]
|
||
|
||
NeutText --> MappingTable["Platzhalter-Mapping-Tabelle: Original ↔ Platzhalter"]
|
||
NeutImage --> MappingTable
|
||
|
||
MappingTable --> RAGIndex["Nur neutralisierte Dokumente → RAG-Index"]
|
||
MappingTable --> ChatWorkflow["Neutralisierte Dokumente im ChatWorkflow sichtbar"]
|
||
|
||
RAGIndex --> AICall["AI-Call mit neutralisierten Daten"]
|
||
AICall --> AIResponse["AI-Response (mit Platzhaltern)"]
|
||
AIResponse --> Rehydrate["Platzhalter rueckwandeln via Mapping-Tabelle"]
|
||
Rehydrate --> UserOutput["Endergebnis fuer User"]
|
||
```
|
||
|
||
**Schritte im Detail:**
|
||
|
||
1. **Container auflösen:** Dokumente (PDF, DOCX, XLSX, ...) werden in ihre Bestandteile zerlegt — Text, Bilder, Medien. Dies entspricht dem bestehenden Extraction-Pattern im Code.
|
||
2. **Media droppen:** Audio-/Video-Inhalte werden nicht an AI weitergegeben (Datenschutz-Default).
|
||
3. **Bilder extrahieren:** Modell mit `opType=neutralization` extrahiert Bildinhalte als neutralen beschreibenden Text (keine Gesichter, keine erkennbaren Merkmale).
|
||
4. **Text neutralisieren:** Modell mit `opType=neutralization` ersetzt personenbezogene und sensible Daten (Namen, Adressen, Kontonummern, etc.) durch **Platzhalter** (z. B. `[PERSON_1]`, `[ADRESSE_2]`, `[KONTO_3]`).
|
||
5. **Platzhalter-Mapping-Tabelle:** Die Zuordnung Original → Platzhalter wird in einer **Datenbanktabelle** pro Mandant gespeichert. Diese Tabelle ist die Grundlage für Rückwandlung und User-Einsicht.
|
||
6. **RAG-Index:** Bei neutralisierten Datenquellen landen **ausschließlich die neutralisierten Versionen** im RAG — die Originale werden nicht indiziert.
|
||
7. **AI-Verarbeitung:** Externe Modelle arbeiten ausschließlich mit neutralisierten Texten.
|
||
8. **Rückwandlung:** Die AI-Response wird über die Mapping-Tabelle re-hydriert — Platzhalter werden durch Originaldaten ersetzt, bevor der User das Ergebnis sieht.
|
||
|
||
### Transparenz im ChatWorkflow
|
||
|
||
Die neutralisierten Dokumente werden im **ChatWorkflow-Objekt als Dokumente mitgegeben**. Der User sieht damit bei jedem AI-Call, welche Daten tatsächlich an das externe Modell gesendet werden. Dies schafft Vertrauen und Nachvollziehbarkeit:
|
||
|
||
- Im Chat-UI: Aufklappbarer Bereich „Gesendete Daten (neutralisiert)" mit den neutralisierten Dokumenten.
|
||
- Der User kann prüfen, ob die Neutralisierung korrekt und vollständig ist.
|
||
- Bei Problemen kann der User die Neutralisierung der Datenquelle anpassen oder deaktivieren.
|
||
|
||
### User-Kontrolle
|
||
|
||
- Der User kann seine **neutralisierten Daten einsehen** (Transparenz: was wurde wie neutralisiert).
|
||
- Der User kann Platzhalter-Mappings **löschen** (Einträge in der Mapping-Tabelle entfernen).
|
||
- Pro Datenquelle ist der Neutralisierungs-Status sichtbar (Badge/Icon im UI).
|
||
|
||
### Automatisierung
|
||
|
||
Die Neutralisierung läuft **vollautomatisch** in der Pipeline:
|
||
|
||
- Beim **Einbinden** einer Datenquelle mit `neutralize=true`: Daten werden sofort neutralisiert und die Mapping-Tabelle befüllt. **Nur die neutralisierte Version** wird im RAG-Index gespeichert.
|
||
- Bei jedem **AI-Call** mit `requireNeutralization=true`: Die Eingabedaten des Calls durchlaufen die Pipeline vor dem Senden.
|
||
- **Batch-Re-Neutralisierung:** Bei Änderung des Neutralisierungs-Flags einer Datenquelle werden bestehende RAG-Einträge re-neutralisiert oder de-neutralisiert (Mapping-Tabelle und RAG-Index aktualisieren).
|
||
|
||
---
|
||
|
||
## Subscription: Datenvolumen als Parameter
|
||
|
||
Das RAG und der Unified Data Layer sind kein limitierender Faktor — die Plattform verfügt über ausreichend Datenbank- und Speicherkapazität, und der Fokus liegt darauf, Daten **breit verfügbar** zu machen. Dennoch sollte das **Datenvolumen als Parameter in der Subscription** integriert werden:
|
||
|
||
- `maxDataVolumeMB` (oder GB) pro Mandant als Subscription-Parameter in `MandateSubscription`.
|
||
- Kein hartes Billing auf Datenvolumen, aber ein Soft-Limit, das dem Mandanten-Admin angezeigt wird.
|
||
- Bei Annäherung ans Limit: Hinweis im UI (z. B. „80% des Datenvolumens genutzt"), kein Schreib-Block.
|
||
- Upgrade-Pfad: Höherer Plan = mehr Volumen.
|
||
|
||
Dies dient der Kapazitätsplanung und dem Upselling, nicht der Einschränkung.
|
||
|
||
---
|
||
|
||
## Onboarding-Assistant
|
||
|
||
Der **Onboarding-Assistant** ist ein zentrales Element der User Experience. Nach Registrierung (oder nach erstem OAuth-Login) führt ein interaktiver Assistent den neuen User durch die ersten Schritte:
|
||
|
||
1. **Mandant-Typ bestätigen** (bei OAuth-Flow: Personal oder Company wählen).
|
||
2. **Erste Feature-Instanz aktivieren** — der Assistant empfiehlt basierend auf dem Kundenprofil (z. B. Workspace als Startpunkt).
|
||
3. **Erste Datenquelle einbinden** — Upload oder Konnektor aktivieren, Scope und Neutralisierung erklären.
|
||
4. **Erster AI-Call** — der User erlebt sofort den Mehrwert.
|
||
|
||
Der Onboarding-Assistant ist kein einmaliger Wizard, sondern ein kontextsensitiver Begleiter, der bei neuen Features, nach Pausen oder bei leeren Zuständen (kein Chat, keine Daten) wieder erscheint. Er reduziert Time-to-Value und ist entscheidend für Trial-Conversion.
|
||
|
||
---
|
||
|
||
## Mandanten-Löschung: Kaskade
|
||
|
||
Die Löschung eines Mandanten erfordert eine vollständige Kaskade über alle abhängigen Entitäten. Diese muss atomar und nachvollziehbar ablaufen:
|
||
|
||
```
|
||
Mandate löschen
|
||
├── Alle FeatureInstances im Mandanten
|
||
│ ├── Alle FeatureAccess-Records pro Instanz
|
||
│ ├── Alle Daten pro Instanz (Chats, Dokumente, Extraktionen)
|
||
│ └── Alle Neutralisierungs-Mappings (DataNeutralizerAttributes) pro Instanz
|
||
├── Alle DataSources im Mandanten
|
||
│ └── RAG-Index-Einträge mit mandateId entfernen
|
||
├── Alle UserMandate-Zuordnungen zum Mandanten
|
||
│ (User selbst bleiben bestehen — sie haben ggf. andere Mandate)
|
||
├── MandateSubscription
|
||
│ └── Stripe-Subscription kündigen / Stripe-Quantity auf 0
|
||
├── Mandate-spezifische Konfigurationen (Rollen, Settings)
|
||
└── Mandate-Record selbst (soft-delete mit Retention oder hard-delete)
|
||
```
|
||
|
||
**Schutzmechanismen:**
|
||
|
||
- `isSystem=true`-Mandate (Root) sind nicht löschbar.
|
||
- Löschung nur durch Mandanten-Admin oder sysadmin.
|
||
- Bestätigung mit expliziter Eingabe des Mandantennamens (wie bei GitHub Repo-Löschung).
|
||
- Soft-Delete mit 30-Tage-Retention als Default; endgültige Löschung per Batch-Job.
|
||
|
||
---
|
||
|
||
## Verwandte Dokumente
|
||
|
||
- [Mandanten-Subscription-Konzept.md](./Mandanten-Subscription-Konzept.md) — Lizenz & Kapazität pro Mandant
|
||
- [Subscription-State-Machine.md](./Subscription-State-Machine.md) — Statusübergänge
|
||
- [Billing-Konzept.md](./Billing-Konzept.md) — Verbrauch & Prepaid
|
||
- [Navigation-API-Konzept.md](./Navigation-API-Konzept.md) — Mandantenbaum in der UI
|
||
|
||
---
|
||
|
||
## Referenz: zentrale Code-Stellen
|
||
|
||
| Bereich | Pfad |
|
||
|---------|------|
|
||
| Registrierung | `gateway/modules/routes/routeSecurityLocal.py` |
|
||
| Store (IST) | `gateway/modules/routes/routeStore.py` |
|
||
| Bootstrap / Root | `gateway/modules/interfaces/interfaceBootstrap.py` |
|
||
| Mandat anlegen | `gateway/modules/interfaces/interfaceDbApp.py` (`createMandate`, `createUserMandate`) |
|
||
| Feature-Instanz | `gateway/modules/interfaces/interfaceFeatures.py` (`createFeatureInstance`) |
|
||
| Pläne | `gateway/modules/datamodels/datamodelSubscription.py` (`BUILTIN_PLANS`) |
|
||
| Dashboard / Store UI | `frontend_nyla/src/pages/Dashboard.tsx`, `frontend_nyla/src/pages/Store.tsx` |
|
||
| Migration (neu) | `gateway/scripts/script_migrate_root_users.py` (oder `gateway/modules/migration/`) |
|
||
|
||
---
|
||
|
||
*Dokument-Version: 2026-03-23 v4 — Review-Integration: mandateType mutabel/informativ, isSystem-Trennung, OAuth-Wizard, Subscription-Timing (PENDING), Store für alle User mit Auto-Mandate, mehrere Instanzen pro Feature erlaubt, scope=global RBAC-geschützt, UDL-Datenmodell definiert (bestehende Infrastruktur), FeatureInstance-Semantikverschiebung über Phasen, Chat-Baum-Skalierung (Suche+Flat), Drag-and-Drop via RAG-Daten, bestehender Neutralisierungs-Code referenziert, Datenvolumen als Subscription-Parameter, Onboarding-Assistant, Mandanten-Löschung-Kaskade.*
|