39 KiB
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 und Subscription-State-Machine.md. - Feature-Instanzen pro Mandant; Zugriff über
FeatureAccessund 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: 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) |
| 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)
- Root-Mandant nur technisch: Nur System-/Betriebskonten (sysadmin-Benutzer). Keine Endkunden-Datenhaltung, keine Feature-Instanzen für Endkunden im Root.
- Jede Selbstregistrierung erzeugt ein eigenes Mandat (Personal oder Company), inkl. Mandats-Admin-Rolle und passender initialer Subscription (Trial vs. Standard).
- Feature Store mit explizitem Mandanten-Kontext und Orphan Control (siehe Abschnitt unten).
- Zwei Einstiege von der Homepage/Landing:
- „Kostenlos testen" → Personal-Mandat + Trial-Plan (
TRIAL_7D). - „Für Unternehmen" → Company-Mandat +
STANDARD_MONTHLY(inkl. Firmenname).
- „Kostenlos testen" → Personal-Mandat + Trial-Plan (
- Unified Data Layer (Variante B): Zentrale mandantenweite Datenschicht mit RAG als Plattformkern; Features als spezialisierte Workflow-Oberflächen.
- Neutralisierung als Kerndisziplin: Flag-basiert pro Datenquelle und pro AI-Call, mit Trusted-Model-Pipeline.
Datenmodell-Erweiterung
Auf Mandate (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 hatisSystem=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)
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:
- Mandat anlegen (mit kopierten System-Rollen, wie bei
createMandate). UserMandate+ Mandats-Admin-Rolle.MandateSubscriptionmit gewähltemplanKey.- Für Features mit
autoCreateInstance: Instanzen im neuen Mandat erzeugen +FeatureAccessmit 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.
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ältfeatureCode+mandateId. Prüft: User ist Admin im Ziel-Mandat, Subscription-Kapazität (maxFeatureInstances). ErstelltFeatureInstance+FeatureAccess+ Admin-Instanzrolle.POST /api/store/deactivate: Body enthältfeatureCode+mandateId. Entfernt eigenenFeatureAccess. Prüft Orphan-Bedingung; löscht Instanz wenn letzer Zugriff entfernt.
Frontend: Texte in 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=personalbzw./register?type=company. - Register-Formular: bei
companyzusätzlich Firmenname; API-Body umregistrationType/companyNameerweitern. - Einladungen: unverändert InvitePage-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(odergateway/modules/migration/migrateRootUsers.py) - Aufruf: Einmalig im Bootstrap (
interfaceBootstrap.py) nachinitRootMandateFeatureseingehä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 jedeFeatureInstanceeinem 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
neutralizeon/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:
- 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).
- 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.
- Scope-Tagging pro Datenquelle: Kein Wettbewerber bietet granulares Tagging (personal / instanz / mandant / global) mit visueller Inline-Steuerung.
- Neutralisierung als Inline-Flag: Einzigartig — keine vergleichbare Plattform bietet Daten-Neutralisierung als pro-Quelle-Toggle.
- 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
- 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). - 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). - 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:
- 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. - 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.
Neutralisierungs-Pipeline
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:
- Container auflösen: Dokumente (PDF, DOCX, XLSX, ...) werden in ihre Bestandteile zerlegt — Text, Bilder, Medien. Dies entspricht dem bestehenden Extraction-Pattern im Code.
- Media droppen: Audio-/Video-Inhalte werden nicht an AI weitergegeben (Datenschutz-Default).
- Bilder extrahieren: Modell mit
opType=neutralizationextrahiert Bildinhalte als neutralen beschreibenden Text (keine Gesichter, keine erkennbaren Merkmale). - Text neutralisieren: Modell mit
opType=neutralizationersetzt personenbezogene und sensible Daten (Namen, Adressen, Kontonummern, etc.) durch Platzhalter (z. B.[PERSON_1],[ADRESSE_2],[KONTO_3]). - 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.
- RAG-Index: Bei neutralisierten Datenquellen landen ausschließlich die neutralisierten Versionen im RAG — die Originale werden nicht indiziert.
- AI-Verarbeitung: Externe Modelle arbeiten ausschließlich mit neutralisierten Texten.
- 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).
Verwandte Dokumente
- Mandanten-Subscription-Konzept.md — Lizenz & Kapazität pro Mandant
- Subscription-State-Machine.md — Statusübergänge
- Billing-Konzept.md — Verbrauch & Prepaid
- 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 v3 — Korrekturen: Neutralisierung via Operation Type (keine speziellen LLMs); nur neutralisierte Dokumente im RAG; Platzhalter-Mapping als Datenbanktabelle (kein Vault); ChatWorkflow-Transparenz; explizites Datenquellen-Tagging mit Scope und Neutralisierungs-Flag; Marktbeurteilung in separates Dokument ausgelagert.