12 KiB
Neutralisierungs-System
Überblick
Neutralisierung ersetzt mandantensensible Inhalte durch stabile Platzhalter, bevor Text an externe KI-Modelle geht oder dauerhaft im RAG landet. Rohdaten aus Tools werden nicht separat „nachgefiltert“: der Schutz liegt am Content-Einstieg (Indexierung, Extraktion, zentrales AI-Gate) und an vererbten Flags auf FileItem / Session / Feature-Konfiguration.
Architektur: Einzige zentrale Stelle
Für jeden AI-Modell-Call gilt: Prompt, RAG-Kontext und Chat-Nachrichten werden ausschließlich in mainServiceAi.py geprüft und neutralisiert (_shouldNeutralize, _neutralizeRequest). Kein anderes Modul entscheidet parallel „stattdessen“ für dieselbe Modell-Schnittstelle.
Orthogonal dazu (kein Ersatz des Gates, sondern Data-at-rest bzw. Workflow):
- RAG:
mainServiceKnowledge.indexFile()neutralisiert Chunks beiFileItem.neutralize=True. Seit RAG Consent & Control (2026-05):DataSource.neutralizeist die Single Source of Truth für Neutralisierung bei RAG-Indexierung. Das frühereUserConnection.knowledgePreferences.neutralizeBeforeEmbedexistiert nicht mehr — Walker lesenneutralizeausschließlich aus derDataSource-Row, und seit 2026-05 (Cascade-Inherit) folgt der Wert via Path-Traversal dem nächsten expliziten Vorfahren (null= vererbt). Effective-Resolution:serviceKnowledge/_inheritFlags.getEffectiveFlag(). - Agent: DataSource- und FeatureDataSource-Flags setzen bzw. vererben Neutralisierungspflicht; Sub-Agent-Calls können
requireNeutralization=Truetragen. - Workflows: Aktion
neutralizeDataneutralisiert explizit extrahierte ContentParts (prüft u. a.NeutralizationConfig.enabled, nicht das Session-Flag).
Neutralisierungs-Trigger (Prioritätsreihenfolge)
Am AI-Gate (_shouldNeutralize) (Auswertung in der dokumentierten Reihenfolge):
- Feature-Konfiguration:
NeutralizationConfig.enabled(DataNeutraliser / Feature-Instanz). - Chat-/Session-Level:
ServiceCenterContext.requireNeutralization(z. B. Workspace, Automation). - Pro Request:
AiCallRequest.requireNeutralization— expliziter Override auf dem Call.
Quellen gesamt (OR-Logik): Ist irgendwo Neutralisierung erforderlich (Feature-Instanz, Workflow/Session, konkretes Objekt mit FileItem.neutralize / FileFolder.neutralize / DataSource.neutralize / FeatureDataSource.neutralize / FeatureDataSource.neutralizeFields), wird entsprechend verarbeitet. Dokumentierte Produktregel: Kein False von einer Quelle hebt ein True einer anderen auf.
Was neutralisiert wird
Im zentralen AI-Gate (_neutralizeRequest):
request.promptrequest.context(RAG-Kontext)request.messages(OpenAI-artige Nachrichten)
Engine (NeutralizationService): processText, processFile, processBinaryBytes (synchron/asynchron); Rückübersetzung über resolveText / Mappings. Modell-Familien u. a. NEUTRALIZATION_TEXT / NEUTRALIZATION_IMAGE (Private-LLM-Plugin; externe Provider ohne diese Ratings werden für Neutralisierung nicht gewählt).
Nicht erneut neutralisieren: Bereits neutral indexierte RAG-Chunks; Web-Suchergebnisse ohne Mandantendaten (laut Spezifikation).
Bekannter Ist-Stand Medien: Bild-Binary wird in der Engine derzeit weitgehend durchgereicht bzw. übersprungen — dedizierter Bild-Neutralisierungspfad ist in der Fachdoku als Lücke / Soll beschrieben, nicht als abgeschlossene Implementierung ausgewiesen.
Failsafe-Kette
- Toggle
FileItem.neutralize: Index und Chunks werden synchron gelöscht; Re-Index im Hintergrund verhindert Leaks bei Fehlern in der Nachindexierung. - Toggle
FileFolder.neutralize: Propagiertneutralizerekursiv auf alle Dateien im Ordner; Index/Chunks werden pro Datei synchron gelöscht, Re-Index im Hintergrund. Neue/verschobene Dateien erben das Flag des Ziel-Ordners. - Indexierung:
indexFile()neutralisiert Text-Chunks beiFileItem.neutralize=True→ Data-at-rest abgesichert. - DataSource-Download / RAG-Indexierung:
DataSource.neutralizeist die einzige Quelle für die Neutralisierungs-Policy pro Tree-Element. Seit 2026-05 (Cascade-Inherit):neutralize,ragIndexEnabledundscopesind 3-wertig (null= vererbt,True/Falsebzw. ein Scope-String = explizit).nullin der Datenbank bedeutet: Wert vom nächsten Vorfahren-DataSource im Path-Tree übernehmen (longest-prefix wins, gleicherconnectionId+sourceType). Beim Setzen eines expliziten Werts auf einem Parent kaskadiert das Backend (cascadeResetDescendants) durch alle Descendants und setzt deren explizite Werte für dieses eine Flag aufnullzurück — Descendants die bereits geerbt haben, werden nicht angefasst. Walker konsumieren pre-resolved Werte:_loadRagEnabledDataSources(insubConnectorIngestConsumer.py) berechnet pro DS via_inheritFlags.getEffectiveFlag()den effektivenragIndexEnabled/neutralize/scopeund schreibt das in das Walker-Input-Dict — die Sync-Walker (subConnectorSync*.py) bleiben dadurch unverändert und lesen weiter direktds.get("neutralize", False). Die Flag-Werte werden zusätzlich auf erzeugteFileItems übernommen. Der früheresubPolicyResolverist auf einen Backward-Compat-Shim aufgetEffectiveFlagreduziert. Seit 2026-05-23 (Polymorphic UDB Refactor):FeatureDataSourcetraegtneutralize,ragIndexEnabledundneutralizeFields(keinscope, keinuserId, keinworkspaceInstanceIdmehr — FDS ist feature-owned, RBAC-gated). Vererbung und Cascade-Reset laufen ueber_INHERITABLE_FDS_FLAGS = {"neutralize", "ragIndexEnabled"}mit Coordinate(featureInstanceId, tableName). Flag-Toggles laufen ausschliesslich ueberPOST /api/udb/node/{key}/flag/{flag}(sieheb-reference/platform/unified-data-bar.md); die altenPATCH /api/datasources/{id}/...-Routen sind entfallen. - FeatureDataSource (Tabellen-Level):
_queryFeatureInstance()setzt beineutralize=Trueu. a.requireNeutralization=Truefür Sub-Agent-AI-Calls. - FeatureDataSource (Feld-Level):
neutralizeFieldsdefiniert Spalten, deren Werte inFeatureDataProvidermit deterministischen Platzhaltern[NEUT.<field>.<hash>]ersetzt werden, bevor Daten den Sub-Agent erreichen. Gleichheits-Vergleiche bleiben möglich (stabiler Hash). - AI-Call: Bei erzwungener Neutralisierung (
requireNeutralization=True) schlägt fehlgeschlagene Neutralisierung hart fehl (RuntimeError/ Blockierung bzw. Entfernen betroffener Message-Teile — kein Durchleiten im Rohzustand). _rehydrateResponse(): Als Hilfsmethode vorhanden; automatische Rückübersetzung der Modellantwort ist nicht mehr der Standardpfad.
Engine-Failsafe (Spezifikation): Neutralisierung verlangt, Engine nicht verfügbar → nicht weiterverarbeiten; Teilfehler → Teil entfernen, nicht roh weitergeben; fehlendes internes Medienmodell → Medien-Part entfernen.
Unified Data Bar — Tree-Endpoint und Flag-Toggling
Vollstaendige Doku: b-reference/platform/unified-data-bar.md. Hier nur Bezug zur Neutralisierung:
- Tree-Endpoint:
POST /api/udb/tree/childrenmit Body{parents: [null, "<key1>", ...]}. Response:{nodesByParent}mit pre-computedeffectiveNeutralize | "mixed"pro Node. - Flag-Toggle:
POST /api/udb/node/{key}/flag/neutralizemit Body{value: true|false|null}. Polymorph: dieselbe Route bedient DataSource, FeatureDataSource und das virtuelle FdsField (Persistenz dort inFeatureDataSource.neutralizeFields). - Builder:
platform-core/modules/serviceCenter/services/serviceKnowledge/_buildTree.pyorchestriert alle Kinds; effektive Werte und mixed-Aggregation kommen polymorph aus denUdbNode-Subklassen (udbNodes.py). - RBAC: DataSource → Owner-of-record; FDS und FdsField → Feature-Admin (
Role.roleLabel.endswith('-admin')auf der Feature-Instanz). SysAdmin/PlatformAdmin haben keinen automatischen Edit-Bypass auf UDB-Flags. - Frontend:
SourcesTab.tsxrendert ausschliesslich die Backend-Antwort, keine Vererbungslogik. Toggle = POST → Refetch → Render mit Spinner ueber dem Flag-Button waehrend des Round-Trips. Mixed-SymbolU+25E9einheitlich fuer alle drei Flags. - Klick-Semantik bei mixed: Klick auf das mixed-Symbol setzt explizit
false(gilt fuer alle drei Flags). Begruendung: der visuelle Toggle behaelt zwei klare End-Zustaende (true/false); Reset aufnull/inherit erfolgt ausschliesslich durch Parent-Toggle (siehe cascade-inherit). Das ist die einzige Geschaeftslogik im Frontend — alles andere kommt vom Backend.
NeutralizationPanel (Frontend)
ui-nyla/src/pages/views/workspace/NeutralizationPanel.tsx — Übersicht zu Dateien mit neutralize-Flag, Status und Platzhalter-Mappings. Datenbasis u. a. GET /api/workspace/{instanceId}/files → { files: [...] }.
Schlüssel-Dateien
| Bereich | Pfad |
|---|---|
| Zentrales AI-Gate | serviceCenter/services/serviceAi/mainServiceAi.py |
| Neutralisierungs-Engine | features/neutralization/serviceNeutralization/mainServiceNeutralization.py (+ subProcess*.py) |
| Feature-Config / DB | features/neutralization/datamodelFeatureNeutralizer.py, interfaceFeatureNeutralizer.py |
| RAG-Index | serviceCenter/services/serviceKnowledge/mainServiceKnowledge.py |
| Agent / Quellen | serviceCenter/services/serviceAgent/mainServiceAgent.py (_resolveDataSource, _downloadFromDataSource, _queryFeatureInstance) |
| Feld-Neutralisierung (DB) | serviceCenter/services/serviceAgent/featureDataProvider.py — _neutralizeRowFields, _applyFieldNeutralization |
| Datei-Toggle / Index | routes/routeDataFiles.py — PATCH /{fileId}/neutralize |
| Ordner-Toggle / Index | routes/routeDataFiles.py — PATCH /folders/{folderId}/neutralize (rekursive Propagierung) |
| Workflow-Aktion | workflows/methods/methodContext/actions/neutralizeData.py |
| Kontext | serviceCenter/context.py — requireNeutralization |
| Flags (Modelle) | datamodels/datamodelFiles.py (FileItem.neutralize), datamodelFileFolder.py (FileFolder.neutralize), datamodelDataSource.py, datamodelFeatureDataSource.py (neutralize, neutralizeFields, ragIndexEnabled — kein scope/userId/workspaceInstanceId seit 2026-05-23) |
| Effective-Flag-Resolution | serviceCenter/services/serviceKnowledge/_inheritFlags.py (getEffectiveFlag, getEffectiveFlagFds, resolveEffectiveForPath, resolveEffectiveForFds, cascadeResetDescendants) |
| UDB polymorphe Node-Klassen | serviceCenter/services/serviceKnowledge/udbNodes.py (UdbNode-Hierarchie, canEdit / getEffectiveFlag / setFlag / getLogicalChildren) |
| UDB Tree-Builder | serviceCenter/services/serviceKnowledge/_buildTree.py (getChildrenForParents -> POST /api/udb/tree/children) |
| UDB Generic Router | routes/routeUdb.py (POST /api/udb/tree/children, POST /api/udb/node/{key}/flag/{flag}) |
| AI-Operationen / Enums | datamodels/datamodelAi.py, aicore/aicorePluginPrivateLlm.py |
Regeln / Invarianten
- Ein Gate pro Modell-Call: Sämtlicher Text in Richtung externes LLM für diesen Call läuft über
mainServiceAi— keine parallele „zweite“ Neutralisierungstelle für dieselbe Schnittstelle. - Hard-Mode:
requireNeutralization=Trueund Neutralisierung scheitert → Call blockieren bzw. Inhalt nicht im Klartext an das Modell geben. - Kein stilles Weiterreichen sensibler Inhalte bei Pflicht zur Neutralisierung.
- Platzhalter: Mapping
[typ.uuid]in der DB;resolveTextfür kontrollierte Rückübersetzung. - Detaillierte Compliance- und Prozessbeschreibung:
wiki/e-compliance/neutralisierung-detail.md.