feat db-clean-ui and unified content udm
This commit is contained in:
parent
0e4a0e0e62
commit
c9454a618f
12 changed files with 1908 additions and 46 deletions
|
|
@ -1,5 +1,5 @@
|
|||
<!-- status: canonical -->
|
||||
<!-- lastReviewed: 2026-04-12 -->
|
||||
<!-- lastReviewed: 2026-04-16 -->
|
||||
|
||||
# Themen-Index für AI-Kontext
|
||||
|
||||
|
|
@ -33,7 +33,7 @@ Lade immer zuerst diese Datei. Dann gezielt die passende(n) Referenz-Datei(en).
|
|||
|-------|-------|------------|
|
||||
| Neutralisierung | b-reference/platform/neutralization.md | Datenschutz, AI-Call-Pipeline, Failsafe |
|
||||
| RBAC | b-reference/platform/rbac.md | 4-Stufen-Modell, Template-Rollen, Resolution, Datenmodell |
|
||||
| Datenbank-Architektur | b-reference/platform/database-architecture.md | Interface-Pattern, Connector, Auto-Init, DB-Liste |
|
||||
| Datenbank-Architektur | b-reference/platform/database-architecture.md | Interface-Pattern, Connector, Auto-Init, DB-Liste, Database Health, Orphan-Scanner |
|
||||
| Navigation | b-reference/platform/navigation.md | Menü-Struktur, Admin-Seiten, API |
|
||||
| Compliance & AI-Audit | b-reference/platform/audit.md | AI-Datenfluss-Log, Security-Audit, Statistiken, RBAC |
|
||||
| i18n / Mehrsprachigkeit | b-reference/gateway/architecture.md (Abschnitt i18n), d-guides/coding-conventions.md (Backend i18n), b-reference/frontend-nyla/architecture.md (Routing/i18n) | `t()`, `@i18nModel`, UiLanguageSet, TextMultilingual, AI-Uebersetzung, Boot-Sync |
|
||||
|
|
@ -50,7 +50,9 @@ Lade immer zuerst diese Datei. Dann gezielt die passende(n) Referenz-Datei(en).
|
|||
| Gateway i18n Phase 7 (done) | c-work/3-validate/2026-04-gateway-i18n-phase-7-implementation.md | RBAC-Keys (rbac.*) im xx-Set, `translate-field` API, FormGenerator KI-Button |
|
||||
| Gateway Duplicate Class Names (done) | c-work/3-validate/2026-04-gateway-duplicate-class-names.md | TaskResult, AiResponse, TableData, Token Umbenennungen |
|
||||
| Generic Graph Editor (Typed Nodes, done) | c-work/3-validate/2026-04-generic-graph-editor.md | Port-Typen, Extraktoren, FrontendType-Renderer, System-Variablen |
|
||||
| Unified Document Model (UDM) & Workflows (done) | z-archive/2026-04-unified-document-model.md, b-reference/gateway/workflow.md (Abschnitt UDM) | UDM-Baum, Extract-Node, Loop/Consolidate, Agent-UDM-Tools |
|
||||
| i18n Static Text Elimination (Ph. 1–2 done) | c-work/2-build/2026-04-i18n-static-text-elimination.md | Gateway Feature+Nodes: Dicts → de-Keys; Ph. 3–5 offen |
|
||||
| Database Health & Data Cleanup (done) | z-archive/2026-04-database-health-and-data-cleanup.md | DB-Registry, FK-Discovery, Orphan-Scanner, Admin-Seite |
|
||||
|
||||
## Prozess & Betrieb
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<!-- status: canonical -->
|
||||
<!-- lastReviewed: 2026-04-07 -->
|
||||
<!-- lastReviewed: 2026-04-16 -->
|
||||
<!-- verifiedAgainst: gateway (codebase audit 2026-04-07, post Automation Unification) -->
|
||||
|
||||
# AI Agent & Knowledge Store
|
||||
|
|
@ -92,7 +92,7 @@ Zusätzlich zu den unten genannten **Kern-Tools** existieren **dynamische Tools*
|
|||
|
||||
**Toolbox-Zuordnung:** Kern-Tools sind den Toolboxes `core`, `ai` und `datasources` zugeordnet (siehe Toolbox Registry oben). Connection-abhaengige Tools (`email`, `sharepoint`, `clickup`, `jira`) werden nur aktiviert, wenn der User eine passende Connection hat. Workflow-Editing-Tools (`workflow` Toolbox) werden separat via `workflowTools.py` registriert.
|
||||
|
||||
### Kern-Tools (registriert via `registerCoreTools` → `coreTools/`, 40 Stück)
|
||||
### Kern-Tools (registriert via `registerCoreTools` → `coreTools/`; Stand 2026-04 inkl. UDM-Helfer)
|
||||
|
||||
**Workspace / Dateien**
|
||||
|
||||
|
|
@ -149,6 +149,9 @@ Zusätzlich zu den unten genannten **Kern-Tools** existieren **dynamische Tools*
|
|||
| `readContentObjects` | Gezielt Content-Objekte lesen |
|
||||
| `extractContainerItem` | Element aus Container extrahieren |
|
||||
| `summarizeContent` | KI-Zusammenfassung |
|
||||
| `getUdmStructure` | UDM-JSON: Überblick (Knoten, Struktur, Block-Zahlen); `udmJson` als stringifiziertes Objekt |
|
||||
| `walkUdmBlocks` | UDM traversieren: alle `ContentBlock`-Knoten mit Pfad und Kurz-Preview |
|
||||
| `filterUdmByType` | UDM: alle Blöcke mit gegebenem `contentType` (z. B. `table`, `image`) |
|
||||
| `describeImage` | Vision-Analyse |
|
||||
| `renderDocument` | Dokument rendern |
|
||||
| `generateImage` | Bildgenerierung |
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<!-- status: canonical -->
|
||||
<!-- lastReviewed: 2026-04-07 -->
|
||||
<!-- lastReviewed: 2026-04-16 -->
|
||||
<!-- verifiedAgainst: gateway (codebase audit 2026-04-07, post Automation Unification) -->
|
||||
|
||||
# Workflow-Engine
|
||||
|
|
@ -50,7 +50,7 @@ Methoden sind Python-Klassen unter `gateway/modules/workflows/methods/`; der **M
|
|||
| Method (`self.name`) | Actions (Registry-Keys) |
|
||||
|---------------------|-------------------------|
|
||||
| `context` | `getDocumentIndex`, `extractContent`, `neutralizeData`, `triggerPreprocessingServer` |
|
||||
| `ai` | `process`, `webResearch`, `summarizeDocument`, `translateDocument`, `convertDocument`, `generateDocument`, `generateCode` |
|
||||
| `ai` | `process`, `webResearch`, `summarizeDocument`, `translateDocument`, `convertDocument`, `generateDocument`, `generateCode`, `consolidate` (KI-Konsolidierung aggregierter Ergebnisse) |
|
||||
| `outlook` | `readEmails`, `searchEmails`, `composeAndDraftEmailWithContext`, `sendDraftEmail` |
|
||||
| `sharepoint` | `findDocumentPath`, `readDocuments`, `uploadDocument`, `listDocuments`, `analyzeFolderUsage`, `findSiteByUrl`, `downloadFileByPath`, `copyFile`, `uploadFile` |
|
||||
| `clickup` | `listTasks`, `searchTasks`, `getTask`, `createTask`, `updateTask`, `uploadAttachment` |
|
||||
|
|
@ -61,6 +61,12 @@ Methoden sind Python-Klassen unter `gateway/modules/workflows/methods/`; der **M
|
|||
|
||||
*Hinweis:* Ältere Kontext-Dokumente nennen für `context` u. a. `saveContent` / `transformContent`; der aktuelle Gateway-Stand listet die obigen Aktionen (Stand Abgleich Code / Review 2026-04-05).
|
||||
|
||||
### Graphical Editor — UDM, Loop, KI-Badge
|
||||
|
||||
- **Unified Document Model (UDM):** Extraktion kann ein hierarchisches JSON (`Document` → `StructuralNode` → `ContentBlock`) liefern; Port-Typen `UdmDocument`, `UdmNodeList`, `ConsolidateResult` im Typed-Port-System (`portTypes.py`).
|
||||
- **Nodes:** u. a. `context.extractContent` (ohne KI), `data.consolidate` (deterministisch), `ai.consolidate` (LLM), `flow.loop` mit Parametern `level` (UDM-Ebene) und `concurrency` (parallele Iterationen).
|
||||
- **Kosten-Transparenz:** Jede Node-Definition trägt `meta.usesAi` (`true` | `false`). Das Frontend (`FlowCanvas`, Node-Palette) zeigt ein **AI-Badge** nur bei `usesAi: true`.
|
||||
|
||||
---
|
||||
|
||||
## Aktionen
|
||||
|
|
|
|||
|
|
@ -239,16 +239,84 @@ Felder mit fuehrendem `_` sind fuer Anwendungs-CUD geschuetzt -- der Connector e
|
|||
|
||||
## Feature-DB-Registrierung
|
||||
|
||||
Features registrieren sich **nicht** in einer zentralen DB-Liste. Stattdessen:
|
||||
Jedes Interface registriert seine Datenbank ueber `registerDatabase()` aus `dbRegistry.py`:
|
||||
|
||||
1. `registry.py` → `registerAllFeaturesInCatalog()` laedt Feature-Main-Module
|
||||
2. Jedes Feature-Main definiert seinen `FEATURE_CODE` und `dbDatabase`
|
||||
3. Der Datenbank-Name wird in `_initializeDatabase()` des Feature-Interfaces gesetzt
|
||||
4. Der `DatabaseConnector` erzeugt die DB automatisch beim ersten Zugriff
|
||||
1. Jedes `interfaceDb*.py` / `interfaceFeature*.py` definiert eine Modul-Konstante (z.B. `appDatabase = "poweron_app"`)
|
||||
2. Auf Modul-Ebene wird `registerDatabase(appDatabase)` aufgerufen — damit ist die DB im zentralen Registry
|
||||
3. Der `DatabaseConnector` erzeugt die DB automatisch beim ersten Zugriff
|
||||
4. Neue DBs werden automatisch erkannt, entfernte DBs verschwinden
|
||||
|
||||
### Admin-Sicht
|
||||
---
|
||||
|
||||
Die Admin-DB-Listing-API (`routeSecurityAdmin.py`) wurde am 2026-04-12 entfernt. Datenbank-Diagnostik erfolgt direkt ueber PostgreSQL-Tools oder das System-Dashboard.
|
||||
## Database Health und Orphan-Scanner
|
||||
|
||||
### Ueberblick
|
||||
|
||||
SysAdmin-Seite unter **Admin > System > Datenbank-Gesundheit** mit zwei Funktionen:
|
||||
|
||||
1. **Tabellenstatistiken** — Row Count, Size, Index Size, Last Vacuum/Analyze fuer alle Tabellen
|
||||
2. **Orphan Cleanup** — Generische Erkennung verwaister Datensaetze mit Clean-Buttons
|
||||
|
||||
### Dynamische DB-Registry (`modules/shared/dbRegistry.py`)
|
||||
|
||||
- `registerDatabase(dbName, configPrefix)` — oeffentliche API, aufgerufen in jedem Interface
|
||||
- `_getRegisteredDatabases()` — alle registrierten DBs
|
||||
- `_getConnectorForDb(dbName)` — Factory fuer read-only Connector
|
||||
|
||||
### Model-Registry (`modules/datamodels/datamodelBase.py`)
|
||||
|
||||
- `_MODEL_REGISTRY: Dict[str, Type[PowerOnModel]]` — automatisch via `__init_subclass__`
|
||||
- Jede `PowerOnModel`-Subklasse registriert sich beim Import (Tabellenname = Klassenname)
|
||||
|
||||
### FK-Discovery (`modules/shared/fkRegistry.py`)
|
||||
|
||||
- Scannt alle PowerOnModel-Subklassen nach `fk_target` in `json_schema_extra`
|
||||
- Baut automatisch `{tableName → dbName}` Mapping aus den Annotationen
|
||||
- Fallback: Catalog-Query (`information_schema.tables`) fuer unmapped Tables
|
||||
- Cached nach erstem Scan
|
||||
- `FkRelationship` Dataclass: `sourceDb, sourceTable, sourceColumn, targetDb, targetTable, targetColumn`
|
||||
|
||||
### FK-Annotationen (`fk_target`)
|
||||
|
||||
Jedes `*Id`-Feld das eine echte FK-Beziehung darstellt, hat `fk_target` in `json_schema_extra`:
|
||||
|
||||
```python
|
||||
mandateId: str = Field(
|
||||
...,
|
||||
json_schema_extra={
|
||||
...,
|
||||
"fk_target": {"db": "poweron_app", "table": "Mandate"},
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
- Standard `targetColumn` ist `"id"`, Sonderfall `Feature.code` nutzt `"column": "code"`
|
||||
- Felder ohne DB-FK (Stripe-IDs, Graph-Node-IDs, polymorphe referenceId) haben kein `fk_target`
|
||||
- `fk_target` ist rein fuer Backend-Orphan-Detection, `fk_model`/`frontend_fk_*` bleiben fuer das UI
|
||||
|
||||
### Orphan-Scanner (`modules/system/databaseHealth.py`)
|
||||
|
||||
- `_getTableStats(dbFilter)` — `pg_stat_user_tables` + `pg_total_relation_size`
|
||||
- `_scanOrphans(dbFilter)` — Same-DB: `NOT EXISTS`, Cross-DB: Parent-IDs laden + `NOT IN`
|
||||
- `_cleanOrphans(db, table, column)` — loescht Orphans, gibt Count zurueck
|
||||
- `_cleanAllOrphans()` — alle Orphans bereinigen
|
||||
- 5-Minuten-Cache fuer Orphan-Ergebnisse
|
||||
|
||||
### API (`modules/routes/routeAdminDatabaseHealth.py`)
|
||||
|
||||
| Methode | Pfad | Beschreibung |
|
||||
|---------|------|-------------|
|
||||
| GET | `/api/admin/database-health/stats` | Tabellenstatistiken (optional `?db=...`) |
|
||||
| GET | `/api/admin/database-health/orphans` | Orphan-Scan (optional `?db=...`) |
|
||||
| POST | `/api/admin/database-health/orphans/clean` | Einzeln-Cleanup `{"db","table","column"}` |
|
||||
| POST | `/api/admin/database-health/orphans/clean-all` | Batch-Cleanup aller Orphans |
|
||||
|
||||
Alle Endpunkte: SysAdmin-only via `requireSysAdminRole`.
|
||||
|
||||
### Frontend (`AdminDatabaseHealthPage.tsx`)
|
||||
|
||||
- Tab "Statistiken": Sortierbare Tabelle mit DB-Filter und Summary-Leiste
|
||||
- Tab "Orphan Cleanup": Tabelle mit Clean-Button pro Zeile + "Alle bereinigen"
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -257,6 +325,10 @@ Die Admin-DB-Listing-API (`routeSecurityAdmin.py`) wurde am 2026-04-12 entfernt.
|
|||
| Thema | Pfad |
|
||||
|-------|------|
|
||||
| PostgreSQL Connector | `gateway/modules/connectors/connectorDbPostgre.py` |
|
||||
| DB-Registry | `gateway/modules/shared/dbRegistry.py` |
|
||||
| FK-Registry | `gateway/modules/shared/fkRegistry.py` |
|
||||
| Database Health | `gateway/modules/system/databaseHealth.py` |
|
||||
| Health API Route | `gateway/modules/routes/routeAdminDatabaseHealth.py` |
|
||||
| App-DB Interface | `gateway/modules/interfaces/interfaceDbApp.py` |
|
||||
| Chat-DB Interface | `gateway/modules/interfaces/interfaceDbChat.py` |
|
||||
| Management-DB Interface | `gateway/modules/interfaces/interfaceDbManagement.py` |
|
||||
|
|
@ -268,3 +340,4 @@ Die Admin-DB-Listing-API (`routeSecurityAdmin.py`) wurde am 2026-04-12 entfernt.
|
|||
| Bootstrap / DB-Seed | `gateway/modules/interfaces/interfaceBootstrap.py` |
|
||||
| DB-Migration Script | `gateway/scripts/script_db_export_migration.py` |
|
||||
| Datenmodelle (Pydantic) | `gateway/modules/datamodels/` |
|
||||
| Frontend Health Page | `frontend_nyla/src/pages/admin/AdminDatabaseHealthPage.tsx` |
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<!-- status: canonical -->
|
||||
<!-- lastReviewed: 2026-04-05 -->
|
||||
<!-- verifiedAgainst: gateway (codebase audit 2026-04-05) -->
|
||||
<!-- lastReviewed: 2026-04-16 -->
|
||||
<!-- verifiedAgainst: gateway (codebase audit 2026-04-16) -->
|
||||
|
||||
# Neutralisierungs-System
|
||||
|
||||
|
|
@ -26,7 +26,7 @@ Orthogonal dazu (kein Ersatz des Gates, sondern Data-at-rest bzw. Workflow):
|
|||
2. **Chat-/Session-Level:** `ServiceCenterContext.requireNeutralization` (z. B. Workspace, Automation).
|
||||
3. **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` / `DataSource.neutralize` / `FeatureDataSource.neutralize`), wird entsprechend verarbeitet. Dokumentierte Produktregel: **Kein `False` von einer Quelle hebt ein `True` einer anderen auf.**
|
||||
**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
|
||||
|
||||
|
|
@ -45,11 +45,13 @@ Im zentralen AI-Gate (`_neutralizeRequest`):
|
|||
## Failsafe-Kette
|
||||
|
||||
1. **Toggle `FileItem.neutralize`:** Index und Chunks werden synchron gelöscht; Re-Index im Hintergrund verhindert Leaks bei Fehlern in der Nachindexierung.
|
||||
2. **Indexierung:** `indexFile()` neutralisiert Text-Chunks bei `FileItem.neutralize=True` → Data-at-rest abgesichert.
|
||||
3. **DataSource-Download:** `neutralize`-Flag wird auf erzeugte `FileItem`s vererbt.
|
||||
4. **FeatureDataSource:** `_queryFeatureInstance()` setzt bei `neutralize=True` u. a. `requireNeutralization=True` für Sub-Agent-AI-Calls.
|
||||
5. **AI-Call:** Bei erzwungener Neutralisierung (`requireNeutralization=True`) schlägt fehlgeschlagene Neutralisierung hart fehl (`RuntimeError` / Blockierung bzw. Entfernen betroffener Message-Teile — kein Durchleiten im Rohzustand).
|
||||
6. **`_rehydrateResponse()`:** Als Hilfsmethode vorhanden; automatische Rückübersetzung der Modellantwort ist nicht mehr der Standardpfad.
|
||||
2. **Toggle `FileFolder.neutralize`:** Propagiert `neutralize` rekursiv 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.
|
||||
3. **Indexierung:** `indexFile()` neutralisiert Text-Chunks bei `FileItem.neutralize=True` → Data-at-rest abgesichert.
|
||||
4. **DataSource-Download:** `neutralize`-Flag wird auf erzeugte `FileItem`s vererbt.
|
||||
5. **FeatureDataSource (Tabellen-Level):** `_queryFeatureInstance()` setzt bei `neutralize=True` u. a. `requireNeutralization=True` für Sub-Agent-AI-Calls.
|
||||
6. **FeatureDataSource (Feld-Level):** `neutralizeFields` definiert Spalten, deren Werte in `FeatureDataProvider` mit deterministischen Platzhaltern `[NEUT.<field>.<hash>]` ersetzt werden, bevor Daten den Sub-Agent erreichen. Gleichheits-Vergleiche bleiben möglich (stabiler Hash).
|
||||
7. **AI-Call:** Bei erzwungener Neutralisierung (`requireNeutralization=True`) schlägt fehlgeschlagene Neutralisierung hart fehl (`RuntimeError` / Blockierung bzw. Entfernen betroffener Message-Teile — kein Durchleiten im Rohzustand).
|
||||
8. **`_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.
|
||||
|
||||
|
|
@ -66,10 +68,12 @@ Im zentralen AI-Gate (`_neutralizeRequest`):
|
|||
| **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`), `datamodelDataSource.py`, `datamodelFeatureDataSource.py` |
|
||||
| **Flags (Modelle)** | `datamodels/datamodelFiles.py` (`FileItem.neutralize`), `datamodelFileFolder.py` (`FileFolder.neutralize`), `datamodelDataSource.py`, `datamodelFeatureDataSource.py` (`neutralize`, `neutralizeFields`) |
|
||||
| **AI-Operationen / Enums** | `datamodels/datamodelAi.py`, `aicore/aicorePluginPrivateLlm.py` |
|
||||
|
||||
## Regeln / Invarianten
|
||||
|
|
|
|||
572
c-work/1-plan/2026-04-consolidated-customer-requirements.md
Normal file
572
c-work/1-plan/2026-04-consolidated-customer-requirements.md
Normal file
|
|
@ -0,0 +1,572 @@
|
|||
<!-- status: plan -->
|
||||
<!-- started: 2026-04-16 -->
|
||||
<!-- component: gateway | frontend-nyla | platform -->
|
||||
<!-- replaces: local/notes/demo-tue-use-cases-inputs-customers.md, c-work/1-plan/2026-04-demo2-merged-customer-trustee-plan.md, c-work/1-plan/2026-04-porta-ui-enhancements-team-meeting.md -->
|
||||
|
||||
# Konsolidierter Kundenwünsche-Plan — PORTA Umsetzung
|
||||
|
||||
> **Stand:** 16. April 2026 (aktualisiert mit PWG-Workshop-Ergebnissen)
|
||||
> **Zweck:** Einheitliche Übersicht aller Kundenwünsche, priorisiert und gegen die Codebase abgeglichen.
|
||||
> **Ersetzt:** `local/notes/demo-tue-use-cases-inputs-customers.md`, `c-work/1-plan/2026-04-demo2-merged-customer-trustee-plan.md`, `c-work/1-plan/2026-04-porta-ui-enhancements-team-meeting.md`
|
||||
|
||||
---
|
||||
|
||||
## Kunden-Übersicht
|
||||
|
||||
| Kunde | Branche | Kontakte | Hauptinteresse | Status |
|
||||
|-------|---------|----------|----------------|--------|
|
||||
| **Bling** | Treuhandbüro | KJS | Belegverarbeitung, Budget, Dashboards, Mandantenmanagement | Trial geplant |
|
||||
| **PWG** (Stiftung) | Immobilien/Wohnen (~300 Liegenschaften, 7–8 Pers.) | MB | **Pilot: Jahresmietzinsbestätigungen** (3'200/Jahr), Belegverarbeitung Abacus, CommCoach, Neutralisierung, Knowledge-Retrieval | Workshop 16.04.2026 ✅ — Pilot bestätigt, Versand Sommer 2026 |
|
||||
| **Quid / ServiceHunter** | SaaS/Dienstleistung | DC | KPI-Dashboard, Zeiterfassung, Prognosen, Konsolidierung | Follow-up geplant |
|
||||
|
||||
---
|
||||
|
||||
## Legende Codebase-Status
|
||||
|
||||
| Symbol | Bedeutung |
|
||||
|--------|-----------|
|
||||
| ✅ | Im Code vorhanden und funktional |
|
||||
| 🔧 | Grundstruktur vorhanden, Anpassung/Ergänzung nötig |
|
||||
| ❌ | Noch nicht umgesetzt |
|
||||
| ⏸️ | Bewusst zurückgestellt (wartet auf Input/Entscheid) |
|
||||
|
||||
---
|
||||
|
||||
## Teil 1: Feature-Anforderungen (Kunden-Use-Cases)
|
||||
|
||||
### 1.1 Automatisierte Belegverarbeitung & Spesenverwaltung
|
||||
|
||||
**Kunden:** Bling (Prio hoch), PWG, Quid (Spesen)
|
||||
|
||||
| # | Anforderung | Codebase-Status | Evidenz / Bemerkung |
|
||||
|---|-------------|-----------------|---------------------|
|
||||
| 1.1.1 | Spesenbelege per Foto → automatische Klassifikation (Rechnung, Spesenbeleg, Bankauszug) | ✅ | `methodTrustee/actions/extractFromFiles.py` + `processDocuments.py` — Pipeline extrahiert, klassifiziert, verbucht |
|
||||
| 1.1.2 | SharePoint-Synchronisation (automatisch, z.B. täglich 22:00) | ✅ | `nodeDefinitions/sharepoint.py` — 6 Nodes; `automation2/scheduleCron.py` für Zeitsteuerung; System-Template "Treuhand: PDF-Klassifizierung & Trustee-Import" in `interfaceBootstrap.py` |
|
||||
| 1.1.3 | Automatische Kontierung basierend auf Kontoplan | ✅ | In `processDocuments.py` — AI-gestützte Kontierung gegen Feature-Daten (Kontoplan via Accounting-Bridge) |
|
||||
| 1.1.4 | Firmen-Mapping zu Kunden/Lieferanten | 🔧 | Grundstruktur in Accounting-Bridge; kein dediziertes Mapping-UI oder regelbasiertes Matching |
|
||||
| 1.1.5 | Optionales Tagging (z.B. "Fuel Station") | 🔧 | Tags auf Dokument-Ebene möglich; kein Beleg-spezifisches Tag-System |
|
||||
| 1.1.6 | Buchungsregeln für wiederkehrende Belege | ❌ | Kein regelbasiertes Booking-Template-System |
|
||||
| 1.1.7 | Vorsteuer automatisch hinterlegen und auslesen | 🔧 | Accounting-Connectors liefern Steuerdaten; automatische MWST-Zuordnung bei Belegverarbeitung nicht explizit |
|
||||
| 1.1.8 | Integration RunMyAccounts | ✅ | `accountingConnectorRma.py` |
|
||||
| 1.1.9 | Integration Bexio | ✅ | `accountingConnectorBexio.py` |
|
||||
| 1.1.10 | Integration Abacus | ✅ | `accountingConnectorAbacus.py` |
|
||||
| 1.1.11 | Integration Xero | ❌ | Kein `accountingConnectorXero.py` — kein Code vorhanden |
|
||||
| 1.1.12 | PDF/Excel/Word/Zip Verarbeitung | ✅ | Extraktoren vorhanden; UDM-Konzept in `0-ideas/unified-document-model.md` |
|
||||
|
||||
### 1.2 Budget / Soll-Ist-Vergleich
|
||||
|
||||
**Kunden:** Bling (Prio hoch), Quid (KPI-Kontext)
|
||||
|
||||
| # | Anforderung | Codebase-Status | Evidenz / Bemerkung |
|
||||
|---|-------------|-----------------|---------------------|
|
||||
| 1.2.1 | Budget-Excel hochladen und gegen Live-Daten prüfen | 🔧 | Prompt-Template "Budget-Vergleich" in `mainTrustee.py` (Quick Action + Template Workflow); Frontend `TrusteeAnalyseView.tsx` hat Budget-Tab mit Upload; **aber kein Demo-Budget-Excel vorhanden** |
|
||||
| 1.2.2 | Vergleiche über Perioden (Q1 aktuell vs. Vorjahr vs. Budget) | ✅ | Im Prompt-Template als Anweisung an AI-Agent enthalten |
|
||||
| 1.2.3 | Automatische Diagramm-Erstellung | ✅ | Agent erzeugt Charts via `aggregateTable` + AI-Prompt |
|
||||
| 1.2.4 | Live-Daten via API (kein PDF-Export nötig) | ✅ | `refreshAccountingData` Action synct Live-Daten; API-basiert |
|
||||
| 1.2.5 | Caching konfigurierbar | ✅ | `_featureQueryCache` mit TTL 300s in `_featureSubAgentTools.py` |
|
||||
| 1.2.6 | Abweichungen mit Begründung | ✅ | Im Prompt-Template: Agent soll Abweichungen erklären |
|
||||
| 1.2.7 | Vorausschauende Prognosen | ✅ | Separates Prompt-Template "Prognose/Trend-Analyse" |
|
||||
|
||||
### 1.3 Cashflow-Rechnung
|
||||
|
||||
**Kunden:** Bling
|
||||
|
||||
| # | Anforderung | Codebase-Status | Evidenz / Bemerkung |
|
||||
|---|-------------|-----------------|---------------------|
|
||||
| 1.3.1 | Plausibilisierung und Erstellung Cashflow-Rechnung | ✅ | Prompt-Template "Cashflow-Rechnung" in `mainTrustee.py` |
|
||||
| 1.3.2 | Nicht-relevante Positionen berücksichtigen | ✅ | Im Prompt-Template als Anweisung |
|
||||
| 1.3.3 | Warnungen bei kritischen Werten | ✅ | Im Prompt-Template |
|
||||
|
||||
### 1.4 Dashboard — Bilanz- & Erfolgsrechnungsanalyse
|
||||
|
||||
**Kunden:** Bling, Quid
|
||||
|
||||
| # | Anforderung | Codebase-Status | Evidenz / Bemerkung |
|
||||
|---|-------------|-----------------|---------------------|
|
||||
| 1.4.1 | KPI-Dashboard (Bruttogewinn, ROI, Gewinn etc.) | ✅ | Prompt-Template "KPI-Dashboard" + Quick Action + `TrusteeAnalyseView.tsx` KPI-Tab |
|
||||
| 1.4.2 | Check hälftiger Kapitalverlust | ✅ | Im Jahresabschluss-Prompt enthalten |
|
||||
| 1.4.3 | Überschuldungs-Check | ✅ | Im Jahresabschluss-Prompt enthalten |
|
||||
| 1.4.4 | Durchschnittliche Zahlungsfrist | 🔧 | Nicht als dedizierter KPI; Agent kann es berechnen wenn Daten vorhanden |
|
||||
|
||||
### 1.5 Liquiditätsplanung
|
||||
|
||||
**Kunden:** Bling
|
||||
|
||||
| # | Anforderung | Codebase-Status | Evidenz / Bemerkung |
|
||||
|---|-------------|-----------------|---------------------|
|
||||
| 1.5.1 | Liquiditätsplanung analog Budgetplanung | 🔧 | Kein dediziertes Prompt-Template; über Prognose-Template teilweise abdeckbar |
|
||||
| 1.5.2 | Automatische Erstellung aus Vergangenheit | 🔧 | Agent kann historische Daten analysieren; kein dedizierter Workflow |
|
||||
| 1.5.3 | Zusätzliche Inputs (z.B. ausserordentliche Dividende) | ❌ | Kein Mechanismus für manuelle Zusatz-Inputs in Prognose |
|
||||
|
||||
### 1.6 Gastro-Use-Case — Echtzeit-Rentabilitätsanalyse
|
||||
|
||||
**Kunden:** Bling (Prio hoch, bestes Beispiel für Kunden-Mehrwert)
|
||||
|
||||
| # | Anforderung | Codebase-Status | Evidenz / Bemerkung |
|
||||
|---|-------------|-----------------|---------------------|
|
||||
| 1.6.1 | Integration Kassensystem + Lohndaten + Buchhaltung | ⏸️ | **Wartet auf Kevin-Input** — kein Connector für Kassensysteme/Virux |
|
||||
| 1.6.2 | Tagesumsatz vs. Personalkosten vs. Wareneinsatz | ⏸️ | Konzeptionell über generischen Data-Import möglich |
|
||||
|
||||
### 1.7 Abschlussunterstützung
|
||||
|
||||
**Kunden:** Bling, allgemein Treuhand
|
||||
|
||||
| # | Anforderung | Codebase-Status | Evidenz / Bemerkung |
|
||||
|---|-------------|-----------------|---------------------|
|
||||
| 1.7.1 | Jahresabschluss-Checks | ✅ | Prompt-Template "Jahresabschluss prüfen" in `mainTrustee.py` |
|
||||
| 1.7.2 | Abgrenzungsbuchungen vorbereiten | 🔧 | Im Prompt adressiert; keine dedizierte Automation |
|
||||
| 1.7.3 | Bilanzkonti prüfen / Saldovalidierung | ✅ | Im Jahresabschluss-Prompt |
|
||||
| 1.7.4 | Vorjahresvergleiche | ✅ | In mehreren Prompt-Templates |
|
||||
|
||||
### 1.8 Zentrales Mandantenmanagement
|
||||
|
||||
**Kunden:** Bling (verschiedene Systeme über eine Schaltzentrale)
|
||||
|
||||
| # | Anforderung | Codebase-Status | Evidenz / Bemerkung |
|
||||
|---|-------------|-----------------|---------------------|
|
||||
| 1.8.1 | Verschiedene Kunden auf verschiedenen Systemen verwalten | ✅ | Multi-Tenant-Plattform ist Kernarchitektur; `accountingRegistry.py` unterstützt verschiedene Connectors pro Mandant |
|
||||
| 1.8.2 | Ein Login, alle Mandanten | ✅ | Plattform-Feature: Mandantenwechsel im UI |
|
||||
| 1.8.3 | Strikte Datentrennung | ✅ | Mandantenisolation auf DB- und API-Ebene |
|
||||
|
||||
---
|
||||
|
||||
### 1.9 PWG — Stiftung für preisgünstiges Wohnen
|
||||
|
||||
**Kontext:** ~300 Liegenschaften, Team 7–8 Personen. IT-Leiter Markus Brütsch (seit 2+ Jahren). Neue Rolle "Organella" für Digitalisierung (Schnittstelle Technologie/Betrieb, kein IT-Hintergrund). PWG evaluiert AI-Lösungen offen, noch keine Entscheidung getroffen. Workshop 16.04.2026 durchgeführt.
|
||||
|
||||
**Pilotprojekt bestätigt:** Jahresmietzinsbestätigungen (siehe 1.9.9).
|
||||
|
||||
#### 1.9a Plattform-Features (bestehend)
|
||||
|
||||
| # | Anforderung | Codebase-Status | Evidenz / Bemerkung |
|
||||
|---|-------------|-----------------|---------------------|
|
||||
| 1.9.1 | Automatisierte Belegverarbeitung mit Abacus | ✅ | `accountingConnectorAbacus.py` + Trustee-Pipeline |
|
||||
| 1.9.2 | Kommunikations-Coach für Mietergespräche | ✅ | `features/commcoach/` — komplett inkl. 10+ Personas |
|
||||
| 1.9.3 | Immobilien-Personas (Zahlungsrückstand, Nebenkosten, Einzug, Lärm) | ✅ | 4 PWG-Personas in `BUILTIN_PERSONAS` + Seeding |
|
||||
| 1.9.4 | KI-Arbeitsplatz mit Datenneutralisierung | ✅ | `features/neutralization/` — PII-Masking, Playground, Private-LLM; Daten-Residency Schweiz |
|
||||
| 1.9.5 | PWG-Agent (Stiftungsstil, öffentliche PDFs als Futter) | 🔧 | Knowledge-Base-Feature vorhanden; kein PWG-spezifisches Knowledge-Set (Geschäftsberichte, Vermietungsreglement, Führungshandbuch etc.) |
|
||||
| 1.9.6 | M365-Anbindung (SharePoint, Outlook, OneDrive, Teams) | ✅ | SharePoint-Nodes + Outlook-Methode in Workflows; nutzt nativen SharePoint-Index |
|
||||
| 1.9.7 | KI-Auswertung Abacus-Daten/Reports | ✅ | Über Trustee-Feature + `aggregateTable` + AI-Prompts; Read/Write via Abacus-API |
|
||||
| 1.9.8 | Grundstücksanalyse (öffentliche Daten: GIS, Maps, Grundbuch) | ❌ | Kein Connector/Workflow für öffentliche Geodaten |
|
||||
|
||||
#### 1.9b Neue Anforderungen aus Workshop 16.04.2026
|
||||
|
||||
| # | Anforderung | Codebase-Status | Evidenz / Bemerkung |
|
||||
|---|-------------|-----------------|---------------------|
|
||||
| 1.9.9 | **Pilotprojekt: Jahresmietzinsbestätigungen** (ca. 700–800 Schreiben/Quartal, Versand Sommer 2026) | ❌ | Kein dedizierter Workflow — siehe Workflow-Design unten |
|
||||
| 1.9.10 | FileMaker-Integration (Portfolio, Liegenschaften, Erneuerungsplanung, Bauprojekte, Akquisition) | ❌ | Kein FileMaker-Connector; Datenmenge/-qualität muss geprüft werden |
|
||||
| 1.9.11 | Template-basierte Dokumentenerstellung (HTML-Templates → Word mit Corporate Design) | 🔧 | Code-Editor mit Claude vorhanden; kein Template-to-Word-Pipeline |
|
||||
| 1.9.12 | Notification-System (reagiert auf DB-Änderungen, Berichte per E-Mail, Workflow-Trigger) | 🔧 | E-Mail-Versand in Workflows vorhanden (`methodOutlook`); kein generisches DB-Change-Detection-System |
|
||||
| 1.9.13 | Information Retrieval über diverse Wissensquellen (Guidelines, Handbücher, SharePoint) | 🔧 | Knowledge-Base + SharePoint-Index vorhanden; kein PWG-spezifisches Datenset geladen |
|
||||
| 1.9.14 | Datenvalidierung als Erst-Schritt (welche Daten korrekt/aktuell?) | ❌ | Kein Daten-Audit-Workflow |
|
||||
| 1.9.15 | Compliance & Audit: vollständiges Tracking aller AI-Transaktionen | ✅ | `ComplianceAuditPage.tsx` + Gateway-Logging; jede Anfrage protokolliert |
|
||||
| 1.9.16 | Granulare Neutralisierungs-Kontrolle pro Datenquelle | ✅ | In Neutralisierungs-Feature konfigurierbar pro Source |
|
||||
|
||||
#### 1.9c Pilot-Workflow: Jahresmietzinsbestätigungen
|
||||
|
||||
**Business Case:** 4 × 800 Schreiben/Jahr = 3'200 Schreiben. Aktuell: Serienbrief → manueller Versand → Scan der Rückantworten → manuelle Verarbeitung. Ziel: AI-gestützte Verarbeitung der gescannten Rückantworten mit Antwortvorschlägen.
|
||||
|
||||
**Workflow-Design (6–7 Schritte, davon 1 mit AI):**
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ PWG Pilot: Jahresmietzinsbestätigungen │
|
||||
│ │
|
||||
│ Phase 1: Generierung (bestehend bei PWG) │
|
||||
│ ┌──────────────────────────────────────┐ │
|
||||
│ │ System generiert Serienbrief │ (PWG-internes System) │
|
||||
│ │ mit aktuellen Mietdaten │ │
|
||||
│ └──────────┬───────────────────────────┘ │
|
||||
│ ▼ │
|
||||
│ Phase 2: Versand & Rücklauf (manuell) │
|
||||
│ ┌──────────────────────────────────────┐ │
|
||||
│ │ Manueller Versand + Scan der │ │
|
||||
│ │ Rückantworten → SharePoint-Ordner │ │
|
||||
│ └──────────┬───────────────────────────┘ │
|
||||
│ ▼ │
|
||||
│ Phase 3: PowerOn-Workflow (automatisiert) │
|
||||
│ │
|
||||
│ Step 1: trigger.schedule (täglich oder on-demand) │
|
||||
│ ▼ │
|
||||
│ Step 2: sharepoint.listFiles (Scan-Ordner, neue Dokumente) │
|
||||
│ ▼ │
|
||||
│ Step 3: flow.loop (für jedes gescannte Dokument) │
|
||||
│ ▼ │
|
||||
│ Step 4: sharepoint.downloadFile + trustee.extractFromFiles │
|
||||
│ → OCR/Extraktion: Mietername, Adresse, Bestätigung, │
|
||||
│ Anmerkungen, Unterschrift ja/nein │
|
||||
│ ▼ │
|
||||
│ Step 5: ai.prompt ← EINZIGER AI-SCHRITT │
|
||||
│ → Gescannte Daten gegen Originaldaten prüfen │
|
||||
│ → Status klassifizieren (bestätigt / Abweichung / │
|
||||
│ fehlende Unterschrift / unleserlich) │
|
||||
│ → Antwortvorschlag generieren bei Abweichung │
|
||||
│ ▼ │
|
||||
│ Step 6: data.writeToTable (Ergebnis in Übersichtstabelle) │
|
||||
│ ▼ │
|
||||
│ Step 7: email.send (Zusammenfassung an Sachbearbeiter) │
|
||||
│ → Audit-Log für jeden Verarbeitungsschritt │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Benötigte Komponenten für den Pilot-Workflow:**
|
||||
|
||||
| # | Komponente | Codebase-Status | Was zu tun ist |
|
||||
|---|-----------|-----------------|----------------|
|
||||
| W1 | `trigger.schedule` oder `trigger.manual` | ✅ | Vorhanden in `nodeDefinitions/triggers.py` |
|
||||
| W2 | `sharepoint.listFiles` + `sharepoint.downloadFile` | ✅ | Vorhanden in `nodeDefinitions/sharepoint.py` |
|
||||
| W3 | `flow.loop` | ✅ | Vorhanden in `nodeDefinitions/flow.py` |
|
||||
| W4 | `trustee.extractFromFiles` (OCR/Extraktion gescannter Dokumente) | ✅ | Vorhanden; OCR für einseitige Scans unterstützt |
|
||||
| W5 | `ai.prompt` — Prompt-Template "Mietzinsbestätigung prüfen" | ❌ | **Neues Prompt-Template** nötig: Scan-Daten gegen Originaldaten abgleichen, Status klassifizieren, Antwortvorschlag generieren |
|
||||
| W6 | Ergebnis-Tabelle / Übersichtsliste (verarbeitete Bestätigungen) | 🔧 | `data`-Nodes existieren; kein dedizierter "Mietzinsbestätigungs-Report"-Output |
|
||||
| W7 | `email.send` (Zusammenfassung an Sachbearbeiter) | ✅ | Vorhanden via `methodOutlook` |
|
||||
| W8 | Abacus-Referenzdaten (Original-Mietzinsdaten für Abgleich) | 🔧 | Abacus-Connector vorhanden; Abfrage der Mietzins-Stammdaten muss konfiguriert werden |
|
||||
| W9 | Audit-Logging für gesamten Prozess | ✅ | Plattform-Feature: alle AI-Transaktionen geloggt |
|
||||
| W10 | Graph-Editor Workflow als Template speichern | ❌ | Workflow muss im Editor gebaut und als System-Template gespeichert werden |
|
||||
|
||||
**Voraussetzungen (Action Items aus Workshop):**
|
||||
|
||||
| # | Action Item | Verantwortlich | Status |
|
||||
|---|------------|----------------|--------|
|
||||
| AI1 | API-Zugang zu Abacus-Testmandant einrichten | Patrick Motsch | ❌ offen — Patrick koordiniert mit Abacus |
|
||||
| AI2 | PWG muss Zugriff auf Testmandant bestätigen | PWG (Markus) | ❌ offen |
|
||||
| AI3 | Preismodell bereitstellen (Grundgebühr/User/Monat + Token-Gebühr) | PowerOn | ❌ offen |
|
||||
| AI4 | Datenschutzanforderungen und Vertragsklauseln für Lieferanten prüfen | PWG | ❌ offen |
|
||||
| AI5 | Prozessschritte Pilot ausarbeiten und Kalkulation erstellen | PowerOn | 🔧 Workflow-Design in diesem Dokument; Kalkulation offen |
|
||||
| AI6 | FileMaker-Datenmenge und -qualität prüfen (potenzielle Integration) | PWG + PowerOn | ❌ offen |
|
||||
|
||||
---
|
||||
|
||||
### 1.10 Quid / ServiceHunter Use Cases
|
||||
|
||||
| # | Anforderung | Codebase-Status | Evidenz / Bemerkung |
|
||||
|---|-------------|-----------------|---------------------|
|
||||
| 1.10.1 | KPI-Dashboard (Kundenmargen, ACV, Lifetime Value, Produktionsmarge) | 🔧 | Generisches KPI-Prompt-Template vorhanden; kundenspezifische KPIs brauchen Daten-Import |
|
||||
| 1.10.2 | Zeiterfassung & Support-Analyse (Zendesk-Verknüpfung) | ⏸️ | **Kein Zendesk-Connector** — bewusst CSV-Upload-Workaround |
|
||||
| 1.10.3 | Prognosen & Proaktive Steuerung | ✅ | Prognose-Prompt-Template vorhanden |
|
||||
| 1.10.4 | Konsolidierung international (CH, DE, UK) | ⏸️ | **Wartet auf Lars-Meeting** — hohe Komplexität |
|
||||
| 1.10.5 | Spesen-Automatisierung (SharePoint/Drive → verbuchen) | ✅ | SharePoint-Pipeline + Trustee-Import vorhanden |
|
||||
|
||||
---
|
||||
|
||||
## Teil 2: UI/UX-Anforderungen (aus Team-Meeting / Nutzertests)
|
||||
|
||||
### 2.1 AI-Workspace Usability
|
||||
|
||||
| # | Anforderung | Codebase-Status | Evidenz / Bemerkung |
|
||||
|---|-------------|-----------------|---------------------|
|
||||
| 2.1.1 | Primäre Prompt-Zeile dominant sichtbar (kein "Wo tippe ich?") | 🔧 | `WorkspaceInput.tsx` funktional komplett; **Placeholder auf Englisch**, keine Hero-Eingabe, sekundäre Aktionen in einer Zeile |
|
||||
| 2.1.2 | Empty State im Chat (Orientierung für Erstnutzer) | ❌ | `ChatStream.tsx` zeigt blank area wenn `messages.length === 0` — **kein Welcome/Empty-State** |
|
||||
| 2.1.3 | "Neuer Chat" sichtbar ohne UDB-Sidebar | ❌ | Nur `+` in `ChatsTab` Toolbar; kein zentraler CTA |
|
||||
| 2.1.4 | Datei-Drop Entdeckbarkeit | 🔧 | Vollflächen-Drop existiert in `WorkspacePage.tsx`; kein dauerhafter Hinweis im leeren Chat |
|
||||
| 2.1.5 | DE-Placeholder und i18n | ❌ | Placeholder noch auf Englisch (`Type a message...`) |
|
||||
|
||||
### 2.2 Responsive Layout & Breakpoints
|
||||
|
||||
| # | Anforderung | Codebase-Status | Evidenz / Bemerkung |
|
||||
|---|-------------|-----------------|---------------------|
|
||||
| 2.2.1 | Zwischen-Breakpoint (1025–1280px): Sidebars auto-einklappen | ❌ | `isMobile` nur bei `≤1024px`; schmale Desktop-Fenster → Mittelspalte zu schmal |
|
||||
| 2.2.2 | Visuelle Hierarchie Prompt (Schatten/Rand, minHeight) | ❌ | `WorkspaceInput.tsx` ohne besondere Hervorhebung |
|
||||
| 2.2.3 | Viewport-Testmatrix (1100/1200/1280/1440) | ❌ | Kein Test-Setup dafür |
|
||||
|
||||
### 2.3 Vertrauen & Marketing-UI
|
||||
|
||||
| # | Anforderung | Codebase-Status | Evidenz / Bemerkung |
|
||||
|---|-------------|-----------------|---------------------|
|
||||
| 2.3.1 | Trust-Badges (Daten hosted in der Schweiz, Anbieter/Standort) | ❌ | Login: **kein** Hosting-Hinweis; keine `TrustFooter`/`TrustStrip` Komponente |
|
||||
| 2.3.2 | "Recommended by" Partner-Strip (Valion, PamoCreate, Swiss AI Association etc.) | ❌ | Keine Partner-Logos/Links |
|
||||
| 2.3.3 | Swiss ® am Logo | ❌ | Nur im Bildasset, nicht in UI sichtbar (Legal klären) |
|
||||
| 2.3.4 | Sicherheits-Banner in SourcesTab ("Verbindung ist read-only") | ❌ | `SourcesTab.tsx` ohne Info-Banner |
|
||||
|
||||
### 2.4 Billing & Pricing Auffindbarkeit
|
||||
|
||||
| # | Anforderung | Codebase-Status | Evidenz / Bemerkung |
|
||||
|---|-------------|-----------------|---------------------|
|
||||
| 2.4.1 | Billing-Funktionalität (komplett) | ✅ | `BillingDataView.tsx` mit Tabs, Balance Cards, Transaktionen |
|
||||
| 2.4.2 | Einstieg Billing nach Login (Teaser/Banner) | ❌ | Kein Dashboard-Teaser; Zugang nur via Nav-Baum + User-Menü |
|
||||
| 2.4.3 | Balance im UserSection (neben Avatar) | ❌ | Nur Menü-Eintrag "Guthaben" |
|
||||
| 2.4.4 | Billing-Copy für Nicht-Admins | ❌ | Kein Hinweis "Frag deinen Admin" |
|
||||
|
||||
### 2.5 Onboarding & Erstnutzung
|
||||
|
||||
| # | Anforderung | Codebase-Status | Evidenz / Bemerkung |
|
||||
|---|-------------|-----------------|---------------------|
|
||||
| 2.5.1 | Connector-Onboarding ("sicher verbinden, nichts kaputt") | ❌ | Keine Microcopy in SourcesTab |
|
||||
| 2.5.2 | Progressive Offenlegung (weniger gleichzeitig auf Startscreens) | ❌ | Workspace zeigt alles parallel |
|
||||
|
||||
---
|
||||
|
||||
## Teil 3: Infrastruktur & Demo-Vorbereitung
|
||||
|
||||
### 3.1 Demo-Konfigurationen
|
||||
|
||||
| # | Item | Codebase-Status | Evidenz / Bemerkung |
|
||||
|---|------|-----------------|---------------------|
|
||||
| 3.1.1 | Demo-Config-Infrastruktur (Base + Admin API) | ✅ | `_baseDemoConfig.py`, `routeAdminDemoConfig.py` |
|
||||
| 3.1.2 | Referenz: `investorDemo2026.py` | ✅ | Vollständiges Muster mit Mandant, User, Features, Billing |
|
||||
| 3.1.3 | Demo-Mandant "Bling Demo" | ❌ | Kein `blingDemo2026.py` |
|
||||
| 3.1.4 | Demo-Mandant "PWG Demo" | ❌ | Kein `pwgDemo2026.py` |
|
||||
| 3.1.5 | Demo-Mandant "Quid Demo" | ❌ | Kein `quidDemo2026.py` |
|
||||
|
||||
### 3.2 Testdaten
|
||||
|
||||
| # | Item | Codebase-Status | Evidenz / Bemerkung |
|
||||
|---|------|-----------------|---------------------|
|
||||
| 3.2.1 | Fiktives Mieterdossier (Neutralisierung) | ✅ | `demoData/neutralizer/tenant-dossier.pdf` + Generator |
|
||||
| 3.2.2 | Knowledge-Base Demo-Dateien | ✅ | `demoData/knowledge-base/` — 4 Dateien |
|
||||
| 3.2.3 | Budget-Excel (Soll-Werte) | ❌ | Kein `.xlsx` im Repo |
|
||||
| 3.2.4 | Musterbelege (Rechnung, Spesen, Bank, Versicherung) | ❌ | Keine Demo-Belege |
|
||||
| 3.2.5 | Quid-Testdaten (CSV: Umsatz, Kunden, Support) | ❌ | Keine kundenspezifischen Testdaten |
|
||||
| 3.2.6 | Bling-Testdaten (Bexio-kompatibel) | ❌ | Keine Bexio-Testdaten |
|
||||
| 3.2.7 | PWG-Testdaten (Abacus-kompatibel) | ❌ | Keine Abacus-Testdaten |
|
||||
|
||||
### 3.3 Demo-Workflows & Skripte
|
||||
|
||||
| # | Item | Codebase-Status | Evidenz / Bemerkung |
|
||||
|---|------|-----------------|---------------------|
|
||||
| 3.3.1 | System-Template "Treuhand: PDF-Klassifizierung" | ✅ | In `interfaceBootstrap.py` |
|
||||
| 3.3.2 | Demo-Workflow (manual trigger → SharePoint → Trustee Pipeline) | ❌ | System-Template existiert, aber kein dedizierter Demo-Workflow |
|
||||
| 3.3.3 | Demo-Skript Bling | ❌ | |
|
||||
| 3.3.4 | Demo-Skript PWG | ❌ | |
|
||||
| 3.3.5 | Demo-Skript Quid | ❌ | |
|
||||
| 3.3.6 | Neutralisierungs-Demo-Flow (Schritt-für-Schritt) | ❌ | Kein Skript |
|
||||
|
||||
---
|
||||
|
||||
## Teil 4: Allgemeine Treuhand-Use-Cases (Prozessdokumentation)
|
||||
|
||||
Diese Use Cases stammen aus der allgemeinen Treuhand-Prozessdokumentation und sind für **alle** Kunden relevant.
|
||||
|
||||
### 4.1 Datenerfassung — Kleine Unternehmen
|
||||
|
||||
| # | Anforderung | Codebase-Status |
|
||||
|---|-------------|-----------------|
|
||||
| 4.1.1 | Belege sortieren & mit Banktransaktionen referenzieren | 🔧 — Belegverarbeitung vorhanden; kein Bank-Statement-Matching |
|
||||
| 4.1.2 | Import Banktransaktionen via Excel/CSV | 🔧 — File-Upload + Extraktion vorhanden; kein dedizierter Bank-Import |
|
||||
| 4.1.3 | Automatisierte Rückfragen an Kunden bei fehlenden Belegen | ❌ |
|
||||
|
||||
### 4.2 Lohnbuchhaltung
|
||||
|
||||
| # | Anforderung | Codebase-Status |
|
||||
|---|-------------|-----------------|
|
||||
| 4.2.1 | Überleitung Lohnaufwand (Fibu vs. Lohnbuchhaltung) | ❌ — Kein Lohn-Feature |
|
||||
| 4.2.2 | Nicht-lohnwirksame Buchungen identifizieren | ❌ |
|
||||
| 4.2.3 | Fehlende Lohnmeldungen erkennen | ❌ |
|
||||
|
||||
### 4.3 Steuererklärung
|
||||
|
||||
| # | Anforderung | Codebase-Status |
|
||||
|---|-------------|-----------------|
|
||||
| 4.3.1 | Belege nach Kategorien ordnen | 🔧 — Klassifikation vorhanden |
|
||||
| 4.3.2 | Steuererklärung erstellen | ❌ — Kein Steuer-Feature |
|
||||
| 4.3.3 | Vorjahresvergleich und Plausibilisierung | ✅ — In Analyse-Prompts |
|
||||
|
||||
### 4.4 Jahresabschluss (Mittlere/Grosse Unternehmen)
|
||||
|
||||
| # | Anforderung | Codebase-Status |
|
||||
|---|-------------|-----------------|
|
||||
| 4.4.1 | Banksalden abgleichen (Buchhaltung vs. Bank) | 🔧 — Konzeptionell via Agent; kein dedizierter Check |
|
||||
| 4.4.2 | PayPal, Revolut, ausländische Konten nachbuchen | ❌ |
|
||||
| 4.4.3 | Wertschriftendepot nachbuchen | ❌ |
|
||||
| 4.4.4 | Fehlerkonto-Transaktionen bereinigen | ❌ |
|
||||
| 4.4.5 | Kreditkartenkonto abstimmen | ❌ |
|
||||
| 4.4.6 | Zahlungsanbieter prüfen (Stripe, Amex) | ❌ |
|
||||
| 4.4.7 | Nebenbücher mit Fibu abstimmen | ❌ |
|
||||
| 4.4.8 | Skonto-Differenzen ausbuchen | ❌ |
|
||||
| 4.4.9 | Konzerninterne Abstimmung | ❌ |
|
||||
| 4.4.10 | Darlehenskonten abgleichen, Zinsen berechnen | ❌ |
|
||||
| 4.4.11 | Fremdwährungsbewertung gemäss ESTV | ❌ |
|
||||
| 4.4.12 | Eigenkapitalveränderungen mit HR abgleichen | ❌ |
|
||||
| 4.4.13 | Umsatz-/Aufwandsplausibilisierung | ✅ — Im Jahresabschluss-Prompt |
|
||||
| 4.4.14 | Rechnungsabgrenzungen (aktiv/passiv) | 🔧 — Im Prompt adressiert |
|
||||
| 4.4.15 | Abschreibungen erfassen | ❌ |
|
||||
| 4.4.16 | MWST-Jahresabstimmung | ❌ |
|
||||
| 4.4.17 | Steueraufwand-Rückstellungen berechnen | ❌ |
|
||||
| 4.4.18 | Bilanz und ER plausibilisieren | ✅ — Im Jahresabschluss-Prompt |
|
||||
| 4.4.19 | Analyse gesetzliche Bestimmungen (Kapitalverlust, Überschuldung) | ✅ — Im Jahresabschluss-Prompt |
|
||||
|
||||
### 4.5 Finanzielle Führung & Controlling
|
||||
|
||||
| # | Anforderung | Codebase-Status |
|
||||
|---|-------------|-----------------|
|
||||
| 4.5.1 | Businessplan → Absatzplanung → Ertragsplanung | ❌ |
|
||||
| 4.5.2 | Investitionsplan und Finanzplan | ❌ |
|
||||
| 4.5.3 | Budget mit Szenarien | 🔧 — Budget-Prompt vorhanden; Szenarien via Chat |
|
||||
| 4.5.4 | Soll/Ist-Vergleich mit Abweichungsanalyse | ✅ — Budget-Vergleich Prompt-Template |
|
||||
| 4.5.5 | Deckungsbeitragsrechnung | ❌ |
|
||||
| 4.5.6 | Projekt-Controlling | ❌ |
|
||||
| 4.5.7 | Benchmarkanalyse | ❌ |
|
||||
| 4.5.8 | KPI-Monitoring mit proaktiver Benachrichtigung | 🔧 — KPIs via Agent; kein proaktives Alert-System |
|
||||
|
||||
### 4.6 Revisionen
|
||||
|
||||
| # | Anforderung | Codebase-Status |
|
||||
|---|-------------|-----------------|
|
||||
| 4.6.1 | Ordentliche Revisionen unterstützen | ❌ — Kein Revisions-Feature |
|
||||
|
||||
### 4.7 Steueroptimierung & Simulationen
|
||||
|
||||
| # | Anforderung | Codebase-Status |
|
||||
|---|-------------|-----------------|
|
||||
| 4.7.1 | Jahresrechnung auf steuerliche Probleme prüfen | ❌ |
|
||||
| 4.7.2 | Simulationen (Lohn vs. Dividende, Umzug, 3. Säule, BVG) | ❌ |
|
||||
| 4.7.3 | MWST-Prüfung, VST-Korrekturen | ❌ |
|
||||
| 4.7.4 | Steuerausscheidungen auf verschiedene Kantone | ❌ |
|
||||
|
||||
### 4.8 Interdisziplinäre Themen
|
||||
|
||||
| # | Anforderung | Codebase-Status |
|
||||
|---|-------------|-----------------|
|
||||
| 4.8.1 | Umstrukturierungen | ❌ |
|
||||
| 4.8.2 | Steuer-/Vorsorgestrategien | ❌ |
|
||||
| 4.8.3 | Nachfolgeplanung | ❌ |
|
||||
| 4.8.4 | Unternehmensbewertungen | ❌ |
|
||||
|
||||
---
|
||||
|
||||
## Zusammenfassung: Status-Überblick
|
||||
|
||||
### Fertig (✅) — Kernplattform funktioniert
|
||||
|
||||
| Bereich | Was steht |
|
||||
|---------|-----------|
|
||||
| **Trustee Agent-Tools** | `refreshTrusteeData`, `aggregateTable`, Connection-Pooling, Result-Caching |
|
||||
| **Graph-Editor** | Trustee-Kategorie + 4 Nodes, SharePoint-Nodes, Flow-Nodes (Loop, If/Else, Switch, Merge) |
|
||||
| **Prompt-Templates** | 5 Analyse-Typen (Budget, KPI, Cashflow, Prognose, Jahresabschluss) als Quick Actions + Template Workflows |
|
||||
| **Accounting-Connectors** | Bexio, Abacus, RunMyAccounts |
|
||||
| **Belegverarbeitung** | Extraktion → Klassifikation → Kontierung → Sync (end-to-end) |
|
||||
| **CommCoach** | Feature komplett, 10+ Personas inkl. 4 Immobilien-Personas (PWG), Gamification, Seeding |
|
||||
| **Neutralisierung** | PII-Masking, Playground, Private-LLM, Mieterdossier-PDF |
|
||||
| **Multi-Tenancy** | Mandantenisolation, Datentrennung, Rollenwechsel |
|
||||
| **Billing** | BillingDataView, Balance, Transaktionen, useBilling |
|
||||
| **SharePoint-Integration** | 6 Workflow-Nodes + Automation |
|
||||
| **Demo-Infrastruktur** | Base-Config, Admin-API, Investor-Demo als Referenz |
|
||||
|
||||
### Teilweise (🔧) — Grundstruktur steht, Erweiterung nötig
|
||||
|
||||
| Bereich | Was fehlt |
|
||||
|---------|-----------|
|
||||
| Firmen-Mapping Kunden/Lieferanten | Regelbasiertes Matching-UI |
|
||||
| Beleg-Tagging | Beleg-spezifisches Tag-System |
|
||||
| Vorsteuer-Automatisierung | MWST-Zuordnung bei Belegverarbeitung |
|
||||
| Liquiditätsplanung | Dediziertes Prompt-Template |
|
||||
| Workspace-Prompt | DE-Placeholder, visuelle Hervorhebung |
|
||||
| Datei-Drop | Hinweis im Empty State |
|
||||
| Budget-Prompt | Demo-Excel-Datei fehlt |
|
||||
|
||||
### Offen (❌) — Noch zu bauen
|
||||
|
||||
#### Prio 0: PWG-Pilot (Versand Sommer 2026 — Deadline-gebunden)
|
||||
|
||||
| Item | Aufwand | Beschreibung |
|
||||
|------|---------|-------------|
|
||||
| Abacus-Testmandant API-Zugang | Extern | Patrick koordiniert mit Abacus; PWG muss Zugriff bestätigen |
|
||||
| Prompt-Template "Mietzinsbestätigung prüfen" | Mittel | Neues Template: Scan vs. Originaldaten, Status-Klassifikation, Antwortvorschlag |
|
||||
| Pilot-Workflow im Graph-Editor | Mittel | trigger → sharepoint.listFiles → loop → download → extract → ai.prompt → report → email |
|
||||
| Abacus-Mietzins-Stammdaten-Abfrage | Klein | Konfiguration im Abacus-Connector für Mietzins-Referenzdaten |
|
||||
| Demo-Mandant PWG | Mittel | `pwgDemo2026.py` — Trustee (Abacus), CommCoach, Neutralisierung, Workspace |
|
||||
| PWG Knowledge-Set | Klein | Öffentliche PDFs (Geschäftsberichte, Vermietungsreglement) in Knowledge-Base laden |
|
||||
| Preismodell / Kalkulation Pilot | Extern | Grundgebühr/User/Monat + Token-Gebühr; basierend auf 4×800 Schreiben/Jahr |
|
||||
|
||||
#### Prio 1: Demo-Blocker / Weitere Kunden-Demos
|
||||
|
||||
| Item | Aufwand | Beschreibung |
|
||||
|------|---------|-------------|
|
||||
| Demo-Mandant Bling | Mittel | `blingDemo2026.py` — Trustee (Bexio), Workspace, Graph-Editor |
|
||||
| Demo-Mandant Quid | Klein | `quidDemo2026.py` — Workspace, Graph-Editor, CSV-Upload |
|
||||
| Budget-Excel | Klein | Soll-Werte 2026 für Demo |
|
||||
| Musterbelege (PDFs) | Klein | 3–5 Belege (Rechnung, Spesen, Bank, Versicherung) |
|
||||
| Demo-Skripte | Klein | Schritt-für-Schritt pro Kunde |
|
||||
| Neutralisierungs-Demo-Flow | Klein | Dokumentation |
|
||||
| Demo-Workflow Graph-Editor (generisch) | Mittel | trigger.manual → SharePoint → Trustee Pipeline |
|
||||
|
||||
#### Prio 2: UI/UX-Verbesserungen (Erstnutzer-Hürde senken)
|
||||
|
||||
| Item | Aufwand | Beschreibung |
|
||||
|------|---------|-------------|
|
||||
| Empty State ChatStream | Klein | Titel, Bullets, "Neuer Chat" Button |
|
||||
| DE-Placeholder + i18n | Klein | `WorkspaceInput.tsx` |
|
||||
| "Neuer Chat" CTA in Mitte | Klein | `WorkspacePage.tsx` |
|
||||
| Zwischen-Breakpoint (1025–1280px) | Mittel | Sidebar-Auto-Collapse |
|
||||
| Trust-Strip/Footer (Login, Landing) | Mittel | Hosting Schweiz, Partner-Logos (Legal nötig) |
|
||||
| Billing-Teaser nach Login | Klein | Link oder Banner |
|
||||
| Connector-Onboarding Copy | Klein | Sicherheits-Banner in SourcesTab |
|
||||
| Balance im UserSection | Klein | Zahl neben Avatar |
|
||||
|
||||
#### Prio 3: Feature-Erweiterungen (Roadmap)
|
||||
|
||||
| Item | Aufwand | Beschreibung |
|
||||
|------|---------|-------------|
|
||||
| Xero-Connector | Gross | Neuer Accounting-Connector |
|
||||
| FileMaker-Connector (PWG) | Gross | Portfolio, Liegenschaften, Erneuerungsplanung; Datenmenge/-qualität erst prüfen |
|
||||
| Buchungsregeln für wiederkehrende Belege | Mittel | Regel-Engine |
|
||||
| Liquiditätsplanungs-Template | Klein | Neues Prompt-Template |
|
||||
| KPI-Monitoring mit Alerts | Gross | Proaktives Benachrichtigungssystem |
|
||||
| Template-basierte Dokumentenerstellung (HTML → Word) | Mittel | Erneuerungsstrategien, Corporate Design |
|
||||
| DB-Change-Detection / Notification-Trigger | Mittel | Reagiert auf Datenbank-Änderungen, triggert Workflows |
|
||||
| Datenvalidierungs-Workflow | Klein | Erst-Audit: welche Daten korrekt/aktuell |
|
||||
|
||||
#### Prio 4: Zurückgestellt (wartet auf externen Input)
|
||||
|
||||
| Item | Wartet auf | Kunde |
|
||||
|------|-----------|-------|
|
||||
| Gastro-Echtzeit-Integration | Kevin (Bling) soll UC ausformulieren | Bling |
|
||||
| Zendesk-Connector | Bewusst CSV-Workaround | Quid |
|
||||
| Regelbasierte Konsolidierung | Lars-Meeting | Quid |
|
||||
| Grundstücksanalyse (GIS etc.) | Kundenpräzisierung | PWG |
|
||||
| FileMaker-Integration (PWG) | Datenmenge/-qualität prüfen (PWG + PowerOn) | PWG |
|
||||
| PWG Datenschutz-/Vertragsklauseln | PWG prüft intern (genossenschaftliche Struktur) | PWG |
|
||||
|
||||
#### Langfristig: Allgemeine Treuhand-Automatisierung
|
||||
|
||||
Die Use Cases aus Teil 4 (Lohnbuchhaltung, Steuererklärung, detaillierter Jahresabschluss, Revisionen, Steueroptimierung, interdisziplinäre Themen) sind **konzeptionell dokumentiert** aber noch **nicht in der Codebase**. Diese bilden die Langfrist-Roadmap und werden schrittweise über AI-Prompt-Templates und dedizierte Workflows umgesetzt, sobald die Kern-Use-Cases bei den ersten Kunden validiert sind.
|
||||
|
||||
---
|
||||
|
||||
## Entscheidungen
|
||||
|
||||
| Datum | Entscheidung | Begründung |
|
||||
|-------|-------------|------------|
|
||||
| 2026-04-07 | Prompt-Templates als Code in `mainTrustee.py` | Wartbar und versioniert statt externe Dateien |
|
||||
| 2026-04-07 | `refreshTrusteeData` als separate Action | Separation of Concerns: Sync = schreibend, Query = lesend |
|
||||
| 2026-04-07 | DB-Connection-Pooling statt Connection-per-Call | Grösster Performance-Hebel (~200ms pro Connection) |
|
||||
| 2026-04-09 | Quid ohne Zendesk-Connector — CSV-Upload | Gleicher Analyse-Mehrwert, viel weniger Aufwand |
|
||||
| 2026-04-09 | Gastro-UC und Konsolidierung auf Prio 4 | Warten auf Kunden-Input |
|
||||
| 2026-04-09 | CommCoach-Personas als schnellster Wow-Effekt für PWG | Feature gebaut, nur Persona-Definitionen nötig |
|
||||
| 2026-04-07 | UI-Enhancements: Fokus Erstnutzer-Hürde | Ohne klare Userführung springen Kunden ab |
|
||||
| 2026-04-07 | Trust-Badges: kein ISO, sondern "Daten in CH" | Konkreter Nutzen > abstraktes Zertifikat |
|
||||
| 2026-04-16 | PWG-Pilot = Jahresmietzinsbestätigungen als erster Produktiv-UC | Konkreter, messbarer Business Case (4×800 Schreiben); Deadline Sommer 2026 |
|
||||
| 2026-04-16 | Workflow-Philosophie PWG: erst manuell testen, dann automatisieren | Agent wird aus manuell getesteten Prozessen erstellt; Workflows für wiederkehrende Aufgaben |
|
||||
| 2026-04-16 | FileMaker-Integration auf Prio 4 | Datenmenge/-qualität muss erst geprüft werden; kein Blocker für Pilot |
|
||||
| 2026-04-16 | Abacus-Testmandant als Voraussetzung für Pilot | Patrick koordiniert API-Zugang direkt mit Abacus |
|
||||
|
||||
---
|
||||
|
||||
## Betroffene Module
|
||||
|
||||
- **Gateway:** `features/trustee/` (Prompts, Quick Actions, Connectors), `features/commcoach/` (Personas), `features/neutralization/` (Demo-Config), `features/graphicalEditor/` (Nodes), `serviceCenter/services/serviceAgent/` (Tools, Caching), `demoConfigs/` (neue Configs), `workflows/methods/methodTrustee/` (Actions), `demoData/` (Testdaten)
|
||||
- **Frontend Nyla:** `WorkspacePage.tsx` (Layout, Breakpoints, Empty State), `WorkspaceInput.tsx` (Prompt, i18n), `ChatStream.tsx` (Empty State), `Login.tsx` (Trust-UI), `UserSection.tsx` (Balance), `SourcesTab.tsx` (Onboarding-Copy), `BillingDataView.tsx` (Discovery)
|
||||
- **DB-Migration:** Nein
|
||||
- **Platform/Wiki:** Nach Release `b-reference/` aktualisieren
|
||||
|
||||
## Links
|
||||
|
||||
- PWG Workshop-Inputs (16.04.2026): `pamocreate/projects/poweron/customer-pwg/20260415-inputs-pwg.txt`
|
||||
- Originale Kunden-Inputs: `local/notes/demo-tue-use-cases-inputs-customers.md`
|
||||
- Merged Demo-Plan (ersetzt): `c-work/1-plan/2026-04-demo2-merged-customer-trustee-plan.md`
|
||||
- UI-Enhancements Plan (ersetzt): `c-work/1-plan/2026-04-porta-ui-enhancements-team-meeting.md`
|
||||
- Investor-Demo (Referenz): `gateway/modules/demoConfigs/investorDemo2026.py`
|
||||
- Frontend-Referenz: `b-reference/frontend-nyla/architecture.md`
|
||||
- Trustee Main (Prompts): `gateway/modules/features/trustee/mainTrustee.py`
|
||||
- CommCoach Personas: `gateway/modules/features/commcoach/serviceCommcoachPersonas.py`
|
||||
- Neutralisierung: `gateway/modules/features/neutralization/`
|
||||
- Graph-Editor Nodes: `gateway/modules/features/graphicalEditor/nodeDefinitions/`
|
||||
- Demo-Daten: `gateway/demoData/`
|
||||
- UDM-Konzept: `c-work/0-ideas/unified-document-model.md`
|
||||
|
||||
## Abschluss
|
||||
|
||||
- [ ] Quell-Dokumente als "superseded by this document" markieren
|
||||
- [ ] b-reference/ aktualisiert nach Umsetzung
|
||||
- [ ] TOPICS.md aktualisiert
|
||||
- [ ] Dieses Dokument → `1-plan/` verschieben wenn Umsetzung startet
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
# Neutralisierung
|
||||
|
||||
**Stand:** 2026-03-29
|
||||
**Stand:** 2026-04-16
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -18,7 +18,7 @@ Drei Quellen, von breit nach spezifisch:
|
|||
|--------|-------------|-----------|
|
||||
| **Feature-Instanz** | `DataNeutraliserConfig.enabled` (hat `featureInstanceId` + `mandateId`) | Alle Daten in dieser Feature-Instanz werden neutralisiert. Betrifft jeden Content-Einstieg innerhalb dieser Instanz. |
|
||||
| **Chat-Workflow / Session** | `ServiceCenterContext.requireNeutralization` (gesetzt z. B. vom AI-Workspace oder Automation) | Dieser Workflow/Turn: jeder Content, der hier verarbeitet wird, wird neutralisiert. |
|
||||
| **Dokument oder Quelle** | `FileItem.neutralize`, `DataSource.neutralize`, `FeatureDataSource.neutralize` | Dieses konkrete Objekt: Content daraus wird neutralisiert, egal ob die Feature-Instanz oder der Workflow es sonst fordern würden. |
|
||||
| **Dokument oder Quelle** | `FileItem.neutralize`, `FileFolder.neutralize`, `DataSource.neutralize`, `FeatureDataSource.neutralize`, `FeatureDataSource.neutralizeFields` | Dieses konkrete Objekt: Content daraus wird neutralisiert, egal ob die Feature-Instanz oder der Workflow es sonst fordern würden. `FileFolder.neutralize` propagiert auf alle enthaltenen Dateien. `neutralizeFields` ermoeglicht Feld-Level-Maskierung bei DB-Queries. |
|
||||
|
||||
**Auswertung:** Irgendeine Quelle sagt `True` → neutralisieren. Keine Quelle kann eine andere aufheben. Es gibt kein `False`-Override, das ein `True` von woanders aushebelt.
|
||||
|
||||
|
|
@ -34,7 +34,9 @@ Am **Punkt der Content-Einspeisung** — dort wo Rohdaten zu verarbeitbarem Cont
|
|||
| **Content-Extraktion** (Dokumente für KI-Verarbeitung) | Extrahierter Text/Tabellen werden neutralisiert. PDF, DOCX, XLSX, PPTX über `processFile` (Extract → neutralisieren → zurückschreiben). | Caller kennt die Quelle und deren Flag |
|
||||
| **User-Prompt** (Chat-Eingabe) | Prompt-Text wird durch `processText` neutralisiert, wenn Feature-Instanz oder Workflow es fordern. | `mainServiceAi` prüft Context-Flag / Config |
|
||||
| **DataSource-Download** | Flag wird auf erstellte `FileItem`s vererbt → bei Extraktion/Indexierung greift das File-Flag automatisch. | `mainServiceAgent._resolveDataSource` / `_downloadFromDataSource` |
|
||||
| **FeatureDataSource-Abfrage** | Wenn eine `FeatureDataSource.neutralize=True` hat → Content aus diesem Sub-Agent-Call wird neutralisiert. | `mainServiceAgent._queryFeatureInstance` setzt `requireNeutralization=True` |
|
||||
| **FeatureDataSource-Abfrage (Tabelle)** | Wenn eine `FeatureDataSource.neutralize=True` hat → Content aus diesem Sub-Agent-Call wird neutralisiert. | `mainServiceAgent._queryFeatureInstance` setzt `requireNeutralization=True` |
|
||||
| **FeatureDataSource-Abfrage (Feld)** | `FeatureDataSource.neutralizeFields` definiert Spalten, deren Werte in `FeatureDataProvider` mit deterministischen Platzhaltern `[NEUT.<field>.<hash>]` ersetzt werden, bevor Daten den Sub-Agent erreichen. | `featureDataProvider._applyFieldNeutralization` |
|
||||
| **Ordner-Neutralisierung** | `FileFolder.neutralize=True` propagiert auf alle enthaltenen Dateien (rekursiv). Neue/verschobene Dateien erben das Flag. Index-Purge + Re-Index wie bei einzelnen Dateien. | `routeDataFiles.updateFolderNeutralize` |
|
||||
| **Workflow-Aktion `neutralizeData`** | Expliziter Neutralisierungs-Schritt auf bereits extrahierten ContentParts. | Workflow-Config + `NeutralizationConfig.enabled` |
|
||||
|
||||
**Was NICHT nochmal neutralisiert werden muss:**
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<!-- status: plan -->
|
||||
<!-- status: done -->
|
||||
<!-- started: 2026-04-13 -->
|
||||
<!-- completed: 2026-04-16 -->
|
||||
<!-- component: gateway | frontend-nyla -->
|
||||
|
||||
# Database Health and Data Cleanup — Admin Page
|
||||
|
|
@ -93,18 +94,18 @@ Viele FK-Beziehungen gehen ueber Datenbank-Grenzen hinweg (z.B. `AutoWorkflow.fe
|
|||
|
||||
**LLM: Opus 4.6** (Architektur-Entscheidungen, Cross-File-Refactoring)
|
||||
|
||||
- [ ] **DB-Registry** (`modules/shared/dbRegistry.py`)
|
||||
- [x] **DB-Registry** (`modules/shared/dbRegistry.py`)
|
||||
- `registerDatabase(dbName: str, configPrefix: str = "DB")` — oeffentliche API, registriert eine DB
|
||||
- `_getRegisteredDatabases() -> Dict[str, str]` — intern, gibt alle registrierten DBs zurueck
|
||||
- `_getConnectorForDb(dbName: str) -> DatabaseConnector` — intern, Factory fuer Connector
|
||||
- Thread-safe (Lock oder atomare Operationen)
|
||||
- `registerDatabase` ohne `_` Prefix weil explizit als oeffentliche API fuer andere Module gedacht
|
||||
|
||||
- [ ] **Selbst-Registrierung in allen Interfaces** (~13 Dateien)
|
||||
- [x] **Selbst-Registrierung in allen Interfaces** (~13 Dateien)
|
||||
- Jedes `interfaceDb*.py` und `interfaceFeature*.py` ruft `registerDatabase()` auf Modul-Ebene auf
|
||||
- Kein Umbau der bestehenden Connector-Logik — nur ein zusaetzlicher Registrierungs-Call
|
||||
|
||||
- [ ] **Model-Registry** (`modules/datamodels/datamodelBase.py`)
|
||||
- [x] **Model-Registry** (`modules/datamodels/datamodelBase.py`)
|
||||
- `_MODEL_REGISTRY: Dict[str, Type[PowerOnModel]]` — automatisch via `__init_subclass__`
|
||||
- `_getModelByTableName(tableName: str) -> Optional[Type[PowerOnModel]]` — Lookup
|
||||
|
||||
|
|
@ -112,7 +113,7 @@ Viele FK-Beziehungen gehen ueber Datenbank-Grenzen hinweg (z.B. `AutoWorkflow.fe
|
|||
|
||||
**LLM: Composer Fast** (repetitive Annotation-Arbeit, klares Pattern)
|
||||
|
||||
- [ ] **fk_target auf allen FK-Feldern** (~80-90 Felder in ~20 Dateien)
|
||||
- [x] **fk_target auf allen FK-Feldern** (~80-90 Felder in ~20 Dateien)
|
||||
- `modules/datamodels/datamodelMembership.py` — UserMandate, FeatureAccess, Junctions
|
||||
- `modules/datamodels/datamodelRbac.py` — Role, AccessRule
|
||||
- `modules/datamodels/datamodelFeatures.py` — FeatureInstance
|
||||
|
|
@ -137,23 +138,26 @@ Viele FK-Beziehungen gehen ueber Datenbank-Grenzen hinweg (z.B. `AutoWorkflow.fe
|
|||
|
||||
**LLM: Opus 4.6** (komplexe Cross-DB-Logik, SQL-Generierung)
|
||||
|
||||
- [ ] **FK-Discovery** (`modules/shared/fkRegistry.py`)
|
||||
- [x] **FK-Discovery** (`modules/shared/fkRegistry.py`)
|
||||
- `_discoverFkRelationships() -> List[FkRelationship]` — scannt Model-Registry, liest `fk_target`
|
||||
- Cached nach erstem Scan (Models aendern sich nicht zur Laufzeit)
|
||||
- `FkRelationship` Dataclass: `sourceDb, sourceTable, sourceColumn, targetDb, targetTable`
|
||||
- `FkRelationship` Dataclass: `sourceDb, sourceTable, sourceColumn, targetDb, targetTable, targetColumn`
|
||||
- Table-to-DB Mapping automatisch aus `fk_target`-Annotationen + Catalog-Fallback
|
||||
|
||||
- [ ] **Orphan-Scanner** (`modules/system/databaseHealth.py`)
|
||||
- [x] **Orphan-Scanner** (`modules/system/databaseHealth.py`)
|
||||
- `_scanOrphans(dbFilter: Optional[str]) -> List[OrphanResult]`
|
||||
- Same-DB: `SELECT COUNT(*) WHERE col NOT IN (SELECT id FROM parent)`
|
||||
- Cross-DB: Parent-IDs laden, dann `WHERE col NOT IN (...)`
|
||||
- Same-DB: `NOT EXISTS (SELECT 1 FROM parent WHERE parent.col = source.col)`
|
||||
- Cross-DB: Parent-IDs laden, dann `NOT IN (unnest(...))`
|
||||
- `_cleanOrphans(db, table, column) -> int` — loescht Orphans, gibt Count zurueck
|
||||
- `_cleanAllOrphans() -> List[dict]` — alle Orphans bereinigen
|
||||
- `_getTableStats(dbFilter: Optional[str]) -> List[TableStats]` — pg_stat_user_tables Query
|
||||
- 5-Minuten-Cache fuer Orphan-Ergebnisse
|
||||
|
||||
### Phase 4: API-Endpunkte (Backend)
|
||||
|
||||
**LLM: Composer Fast** (Standard-Route-Pattern, klar definiert)
|
||||
|
||||
- [ ] **Route** (`modules/routes/routeAdminDatabaseHealth.py`)
|
||||
- [x] **Route** (`modules/routes/routeAdminDatabaseHealth.py`)
|
||||
- Prefix: `/api/admin/database-health`
|
||||
- `GET /stats` — Tabellenstatistiken (optional `?db=...`)
|
||||
- `GET /orphans` — Orphan-Scan (optional `?db=...`)
|
||||
|
|
@ -161,13 +165,13 @@ Viele FK-Beziehungen gehen ueber Datenbank-Grenzen hinweg (z.B. `AutoWorkflow.fe
|
|||
- `POST /orphans/clean-all` — Alle Orphans bereinigen
|
||||
- Alle Endpunkte: SysAdmin-only via `requireSysAdminRole`
|
||||
|
||||
- [ ] **Router in app.py registrieren**
|
||||
- [x] **Router in app.py registrieren**
|
||||
|
||||
### Phase 5: Navigation (Backend)
|
||||
|
||||
**LLM: Composer Fast** (einzelne Zeile in mainSystem.py)
|
||||
|
||||
- [ ] **Navigation-Eintrag** in `modules/system/mainSystem.py`
|
||||
- [x] **Navigation-Eintrag** in `modules/system/mainSystem.py`
|
||||
- Unter `admin-system-group`, order 98, sysAdminOnly
|
||||
- Icon: `FaDatabase`, Path: `/admin/database-health`
|
||||
|
||||
|
|
@ -175,11 +179,11 @@ Viele FK-Beziehungen gehen ueber Datenbank-Grenzen hinweg (z.B. `AutoWorkflow.fe
|
|||
|
||||
**LLM: Opus 4.6** (neue Seite mit Tabs, FormGeneratorTable-Integration)
|
||||
|
||||
- [ ] **AdminDatabaseHealthPage.tsx** mit zwei Tabs
|
||||
- Tab 1 "Statistiken": FormGeneratorTable mit DB, Tabelle, Rows, Total Size, Table Size, Index Size, Last Vacuum, Last Analyze. Sortierbar, Summary-Zeile oben.
|
||||
- Tab 2 "Orphan Cleanup": FormGeneratorTable mit DB, Tabelle, FK-Spalte, Referenz, Orphans, Total Rows, Clean-Button. Filter "Nur Probleme". Batch-Clean.
|
||||
- [ ] **Routing** in Frontend PAGE_REGISTRY
|
||||
- [ ] **i18n** — alle Labels mit `t()` taggen
|
||||
- [x] **AdminDatabaseHealthPage.tsx** mit zwei Tabs
|
||||
- Tab 1 "Statistiken": Sortierbare Tabelle mit DB, Tabelle, Rows, Total Size, Index Size, Last Vacuum, Last Analyze. Summary-Zeile oben. DB-Filter.
|
||||
- Tab 2 "Orphan Cleanup": Tabelle mit Source DB, Tabelle, FK-Spalte, Referenz (inkl. cross-db Badge), Orphans, Clean-Button. Filter "Nur Probleme". Batch-Clean-All.
|
||||
- [x] **Routing** in Frontend PAGE_REGISTRY + App.tsx Route + admin/index.ts Export
|
||||
- [x] **i18n** — alle Labels mit `t()` getaggt
|
||||
|
||||
### Querschnitt-Checks
|
||||
|
||||
|
|
@ -223,6 +227,6 @@ Viele FK-Beziehungen gehen ueber Datenbank-Grenzen hinweg (z.B. `AutoWorkflow.fe
|
|||
|
||||
## Abschluss
|
||||
|
||||
- [ ] b-reference/ aktualisiert (gateway/architecture.md — neuer Abschnitt "Database Health")
|
||||
- [ ] TOPICS.md aktualisiert (neues Thema "Database Health / Data Cleanup")
|
||||
- [ ] Dieses Dokument → z-archive/ verschoben
|
||||
- [x] b-reference/ aktualisiert (platform/database-architecture.md — neuer Abschnitt "Database Health")
|
||||
- [x] TOPICS.md aktualisiert (neues Thema "Database Health / Data Cleanup")
|
||||
- [x] Dieses Dokument → z-archive/ verschoben
|
||||
685
z-archive/2026-04-unified-document-model.md
Normal file
685
z-archive/2026-04-unified-document-model.md
Normal file
|
|
@ -0,0 +1,685 @@
|
|||
<!-- status: archived -->
|
||||
<!-- started: 2026-04-16 -->
|
||||
<!-- completed: 2026-04-16 -->
|
||||
<!-- component: gateway | frontend-nyla | platform -->
|
||||
|
||||
# Unified Document Model (UDM) — Dokumenten-Extraktion, Workflow ForEach & High-Volume
|
||||
|
||||
> **Abgeschlossen am 2026-04-16.** Technische Referenz: `b-reference/gateway/workflow.md` (UDM, Nodes, Engine), `b-reference/gateway/ai-agent.md` (UDM-Tools). Konzept: `c-work/0-ideas/unified-document-model.md`.
|
||||
|
||||
## Abschlussbericht
|
||||
|
||||
- **Umgesetzt:** UDM-Datenmodell und Bridge, Extraktions-Pipeline (inkl. Registry-Singleton, PDF-Memory), `context.extractContent`, Port-Typen und Executor-Routing (`context.*` → `ActionNodeExecutor`), `data.consolidate` / `ai.consolidate`, Loop (`level`, `concurrency`), `flow.merge` mit dynamischer Input-Anzahl, `data.filter` mit UDM-Content-Type-Presets, Engine (Concurrency, StepLog-Batching, Streaming-Aggregate, Progress), `meta.usesAi` und AI-Badge im Editor, Agent-Tools (`getUdmStructure`, `walkUdmBlocks`, `filterUdmByType`), Referenzdoku in `b-reference/` und Eintrag in `TOPICS.md`.
|
||||
- **Tests im Repo (Auszug):** `gateway/tests/unit/datamodels/test_udm_models.py`, `test_udm_bridge.py`, `gateway/tests/integration/extraction/test_extract_udm_pipeline.py`, `gateway/tests/integration/workflows/test_execute_graph_loop_aggregate_consolidate.py`, `gateway/tests/unit/nodeDefinitions/test_usesai_flag.py`, `gateway/tests/unit/serviceAgent/test_udm_agent_tools.py`.
|
||||
- **Backlog / optional:** Erweiterung des ursprünglichen Testplan-Rasters (High-Volume-Referenzen, Fan-out/Merge-Integration, Concurrency-Benchmark, Load-Test 10k) nach Bedarf; UX wie explizite Loop-Body-Hervorhebung oder dedizierte NodeConfig-Komponenten können nachgelagert werden.
|
||||
|
||||
## Grundlage: UDM-Konzept
|
||||
|
||||
Dieses Feature basiert auf dem **Unified Document Model (UDM)** — einer generischen, formatunabhängigen 3-Ebenen-Baumstruktur für Dokumenten-Extraktion. Das vollständige Konzept mit Datenmodell, Format-Mappings (PDF, DOCX, PPTX, XLSX, HTML), Workflow-Integration, Archive-Handling und JSON-Beispielen ist hier definiert:
|
||||
|
||||
→ **`c-work/0-ideas/unified-document-model.md`**
|
||||
|
||||
**Kernprinzipien des UDM:**
|
||||
|
||||
- **3-Ebenen-Garantie:** Jedes Dokument hat `Document` → `StructuralNode[]` → `ContentBlock[]`
|
||||
- **Einheitliche Blattknoten:** Alle atomaren Inhalte sind `ContentBlock`-Objekte mit identischer Struktur
|
||||
- **Generische Traversierung:** Workflow-Nodes arbeiten formatunabhängig über dieselbe Baumstruktur
|
||||
- **Keine formatspezifischen Zwischenschichten:** Konzepte wie "Paragraph", "Row", "Cell" werden in `ContentBlock.attributes` absorbiert
|
||||
|
||||
| Ebene | Typ | Beschreibung |
|
||||
|-------|-----|-------------|
|
||||
| Level 1 | `Document` | Wurzelknoten pro Quelldatei |
|
||||
| Level 2 | `StructuralNode` | Seite, Abschnitt, Slide oder Sheet |
|
||||
| Level 3 | `ContentBlock` | Atomarer Inhalt: Text, Bild, Tabelle, Code, Media, Link, Formel |
|
||||
|
||||
Dieser Plan beschreibt **die Umsetzung** des UDM-Konzepts im Gateway, die nötigen Workflow-Nodes, und die High-Volume-Skalierbarkeit.
|
||||
|
||||
---
|
||||
|
||||
## Beschreibung und Kontext
|
||||
|
||||
Das bestehende Dokumenten-Extraktionssystem arbeitet mit `ContentPart` / `ContentExtracted` als flacher Liste. Das UDM ersetzt dieses Modell durch eine **hierarchische Baumstruktur**, die es erlaubt, über Level-Attribute (Seiten, Sections, Sheets, Slides) generisch zu iterieren.
|
||||
|
||||
**Business-Treiber:** Workflows benötigen die Fähigkeit, ein Dokument zu extrahieren und dann **pro Struktureinheit** (z.B. pro PDF-Seite) eine Kette von Verarbeitungsschritten auszuführen — und am Ende die Teilergebnisse zu konsolidieren. Zusätzlich muss das System mit **grossen Datenmengen** umgehen können (z.B. ZIP mit 10.000 PDFs).
|
||||
|
||||
**Heutige Lücken:**
|
||||
|
||||
1. Es gibt **keinen Extract-Node** im Graphical Editor — `context.extractContent` existiert nur als Workspace-/Agent-Action, nicht als visuellen Node
|
||||
2. Das Extraktionsmodell liefert keine hierarchische Baum-Struktur (nur flache `ContentPart`-Liste)
|
||||
3. Der `flow.loop`-Node hat keine Sub-Workflow-Modellierung — der Loop-Body wird nur über Graph-Topologie implizit erkannt
|
||||
4. Es gibt keinen dedizierten **Consolidate**-Schritt für strukturierte Zusammenführung nach ForEach
|
||||
5. Die Engine ist **nicht skalierbar** für >1000 Iterationen (Memory, DB-Last, keine Parallelität)
|
||||
6. Nodes im Editor haben **keine visuelle Kennzeichnung**, ob sie AI nutzen oder deterministisch arbeiten
|
||||
|
||||
**Abhängigkeiten:** AI-Agent-Tools (`_documentTools.py`), Workflow-Engine (`executionEngine.py`), Graphical Editor (Frontend), Extraktion (`serviceExtraction`).
|
||||
|
||||
**Risiko bei Nicht-Umsetzung:** Dokument-intensive Workflows (Trustee, Compliance-Audit, Massendokumentverarbeitung) bleiben manuell oder erfordern Custom-Code pro Use Case.
|
||||
|
||||
---
|
||||
|
||||
## Fokus und kritische Details
|
||||
|
||||
- **Migration des Extraktionsmodells:** `ContentPart` → UDM muss rückwärtskompatibel sein. Bestehende Konsumenten (`ChatContentExtracted`, Agent-Tools, Neutralisierung) dürfen nicht brechen.
|
||||
- **Loop-Body mit parallelen Pfaden:** Fan-out von Loop-Output zu mehreren Nodes + Merge pro Iteration funktioniert in der Engine bereits (Topo-Sort garantiert Reihenfolge), aber `flow.merge` hat heute fix 2 Inputs.
|
||||
- **High-Volume-Skalierbarkeit:** Bei 10k Iterationen entstehen Engpässe bei Memory (Base64-Bilder in nodeOutputs), DB (AutoStepLog pro Body-Node pro Iteration), und Laufzeit (sequentielle Verarbeitung).
|
||||
- **Extractor-Performance:** `extractorContainer.py` erstellt pro Datei im ZIP eine neue `ExtractorRegistry()`-Instanz (Auto-Discovery). Bei 10k Dateien ist das ein massiver Overhead.
|
||||
- **PDF-Memory:** `extractorPdf.py` lädt via `buf.getvalue()` eine Kopie des gesamten PDFs — doppelter Memory-Verbrauch pro Datei.
|
||||
|
||||
---
|
||||
|
||||
## Ziel und Nicht-Ziele
|
||||
|
||||
### Ziele
|
||||
|
||||
1. **`context.extractContent`-Node** im Graphical Editor — reine Strukturextraktion OHNE AI
|
||||
2. **UDM-Datenmodell** (`Document`, `StructuralNode`, `ContentBlock`, `Archive`) als Pydantic-Modelle
|
||||
3. **Extractor-Adapter:** Bestehende Extractors liefern zusätzlich UDM-Output; Bridge `ContentPart` ↔ UDM
|
||||
4. **ForEach-Workflow-Pattern:** Loop über UDM-Struktureinheiten mit parallelen Pfaden im Body und Merge pro Iteration
|
||||
5. **Consolidate-Nodes:** `data.consolidate` (deterministisch) + `ai.consolidate` (AI-gestützt) für strukturierte Zusammenführung
|
||||
6. **High-Volume-Fähigkeit:** ZIP mit 10.000+ PDFs verarbeitbar durch Streaming-Extraktion, Loop-Concurrency und StepLog-Batching
|
||||
7. **Neue Port-Typen:** `UdmDocument`, `UdmNodeList`, `ConsolidateResult`
|
||||
8. **Agent-Tools:** Bestehende `browseContainer`/`readContentObjects` um UDM-Traversierung erweitern
|
||||
9. **AI-Kennzeichnung auf Nodes:** Jeder Node im Editor zeigt visuell, ob er AI nutzt (`meta.usesAi`) — Kostentransparenz und schnelle Übersicht
|
||||
|
||||
### Explizit NICHT
|
||||
|
||||
- Neue Dateiformate (Markdown, LaTeX, etc.) — kommt separat
|
||||
- Visuelles Sub-Graph-Grouping (Compound-Nodes) im Editor — wird mit impliziter Body-Erkennung gelöst
|
||||
- Breaking Change an `ContentPart`-API — Bridge-Layer garantiert Kompatibilität
|
||||
|
||||
---
|
||||
|
||||
## Betroffene Module
|
||||
|
||||
### Gateway
|
||||
|
||||
| Modul | Änderung |
|
||||
|-------|---------|
|
||||
| `datamodels/datamodelUdm.py` | **Neue Datei**: Pydantic-Klassen `UdmDocument`, `UdmStructuralNode`, `UdmContentBlock`, `UdmArchive`, `UdmPosition`, `UdmMetadata`. Bridge-Funktionen. `UdmContentBlock.raw` optional mit `fileRef`-Alternative für Lazy-Loading |
|
||||
| `datamodels/datamodelExtraction.py` | `ExtractionOptions.outputFormat` Feld (`"parts"` / `"udm"` / `"both"`) |
|
||||
| `serviceExtraction/subRegistry.py` | `Extractor`-Interface erweitern: `extractToUdm()`. **ExtractorRegistry als Singleton cachen** (heute: neue Instanz pro Datei im ZIP) |
|
||||
| `serviceExtraction/extractors/extractorPdf.py` | UDM-Output pro Seite. **Fix `buf.getvalue()`** → direkt `BytesIO` an fitz übergeben (halber Memory) |
|
||||
| `serviceExtraction/extractors/extractorDocx.py` | UDM-Output pro Section (Heading-basiert) |
|
||||
| `serviceExtraction/extractors/extractorPptx.py` | UDM-Output pro Slide |
|
||||
| `serviceExtraction/extractors/extractorXlsx.py` | UDM-Output pro Sheet |
|
||||
| `serviceExtraction/extractors/extractorHtml.py` | UDM-Output pro semantischem Bereich |
|
||||
| `serviceExtraction/extractors/extractorContainer.py` | **Lazy-Modus**: ZIP-Inhaltsverzeichnis liefern statt alle Dateien extrahieren. Registry-Singleton statt `new ExtractorRegistry()` pro Datei |
|
||||
| `serviceExtraction/mainServiceExtraction.py` | `extractContent()` mit UDM-Option. **Streaming-Modus** für grosse Archive |
|
||||
| `features/graphicalEditor/nodeDefinitions/context.py` | **Neue Datei**: Node `context.extractContent` |
|
||||
| `features/graphicalEditor/nodeDefinitions/data.py` | Neuer Node: `data.consolidate` (deterministisch) |
|
||||
| `features/graphicalEditor/nodeDefinitions/ai.py` | Neuer Node: `ai.consolidate` (AI-gestützt) |
|
||||
| `features/graphicalEditor/nodeDefinitions/flow.py` | `flow.loop`: Parameter `level` (UDM-Ebene) + `concurrency` (parallele Iterationen). `flow.merge`: dynamische Input-Anzahl |
|
||||
| `features/graphicalEditor/nodeDefinitions/__init__.py` | `CONTEXT_NODES` in `STATIC_NODE_TYPES` aufnehmen |
|
||||
| `features/graphicalEditor/portTypes.py` | Neue Port-Typen: `UdmDocument`, `UdmNodeList`, `ConsolidateResult`. Input-Extraktoren dazu |
|
||||
| `workflows/automation2/executors/flowExecutor.py` | Loop: UDM-Array-Auflösung, Concurrency-Support |
|
||||
| `workflows/automation2/executors/dataExecutor.py` | `data.consolidate`-Logik (deterministisch). Filter: UDM-Content-Type-Presets |
|
||||
| `workflows/automation2/executionEngine.py` (`_getExecutor`) | Branch `context.*` hinzufügen → `ActionNodeExecutor` |
|
||||
| `workflows/automation2/executionEngine.py` | **Loop-Concurrency** (N Items parallel), **StepLog-Batching** (bei >100 Iterationen), **Streaming-Aggregate** (periodisch flushen) |
|
||||
| `workflows/methods/methodContext/actions/extractContent.py` | UDM-Output-Option, Lazy-Modus für Archive |
|
||||
| `workflows/methods/methodAi/actions/consolidate.py` | **Neue Datei**: Action `consolidate` für `ai.consolidate` (LLM-Call: summarize, classify, semantic merge) |
|
||||
| `serviceAgent/coreTools/_documentTools.py` | UDM-Tools: `walkUdmBlocks`, `filterUdmByType`, `getUdmStructure` |
|
||||
|
||||
### Frontend
|
||||
|
||||
| Modul | Änderung |
|
||||
|-------|---------|
|
||||
| `FlowEditor/nodes/context/ExtractContentNodeConfig.tsx` | **Neue Datei**: Config-UI für `context.extractContent` |
|
||||
| `FlowEditor/nodes/loop/LoopNodeConfig.tsx` | UDM-Level-Selektor + Concurrency-Slider |
|
||||
| `FlowEditor/nodes/shared/LoopItemsSelect.tsx` | UDM-Structural-Level als Quelle |
|
||||
| `FlowEditor/nodes/shared/types.ts` | Neue Node-Config-Types |
|
||||
| `FlowEditor/FlowCanvas.tsx` | Visuelle Loop-Body-Markierung (farbiger Hintergrund). **AI-Badge** auf Nodes die AI nutzen |
|
||||
| `FlowEditor/NodeSidebar.tsx` | Neue Nodes in Palette (`context.extractContent`, `data.consolidate`, `ai.consolidate`). AI-Badge in Palette |
|
||||
| `FlowEditor/nodes/shared/AiBadge.tsx` | **Neue Datei**: Wiederverwendbare AI-Badge-Komponente (`usesAi: boolean`) |
|
||||
| `api/workflowApi.ts` | NodeType-Erweiterung |
|
||||
|
||||
### DB-Migration
|
||||
|
||||
Nein — UDM-Daten fliessen als JSON durch Workflow-Context/nodeOutputs, keine neue DB-Tabelle nötig.
|
||||
|
||||
---
|
||||
|
||||
## Architektur-Design
|
||||
|
||||
### A) UDM-Datenmodell (Gateway)
|
||||
|
||||
```python
|
||||
class UdmMetadata(BaseModel):
|
||||
title: Optional[str] = None
|
||||
author: Optional[str] = None
|
||||
createdAt: Optional[str] = None
|
||||
modifiedAt: Optional[str] = None
|
||||
sourcePath: str = ""
|
||||
tags: List[str] = Field(default_factory=list)
|
||||
custom: Dict[str, Any] = Field(default_factory=dict)
|
||||
|
||||
class UdmBoundingBox(BaseModel):
|
||||
x: float; y: float; width: float; height: float
|
||||
unit: Literal["px", "pt", "mm"] = "pt"
|
||||
|
||||
class UdmPosition(BaseModel):
|
||||
index: int
|
||||
page: Optional[int] = None
|
||||
row: Optional[int] = None
|
||||
col: Optional[int] = None
|
||||
bbox: Optional[UdmBoundingBox] = None
|
||||
|
||||
class UdmContentBlock(BaseModel):
|
||||
id: str
|
||||
contentType: Literal["text", "image", "table", "code", "media", "link", "formula"]
|
||||
raw: str = ""
|
||||
fileRef: Optional[str] = None # Lazy-Loading: Referenz statt inline Base64
|
||||
mimeType: Optional[str] = None
|
||||
language: Optional[str] = None
|
||||
attributes: Dict[str, Any] = Field(default_factory=dict)
|
||||
position: UdmPosition = Field(default_factory=lambda: UdmPosition(index=0))
|
||||
metadata: UdmMetadata = Field(default_factory=UdmMetadata)
|
||||
|
||||
class UdmStructuralNode(BaseModel):
|
||||
id: str
|
||||
role: Literal["page", "section", "slide", "sheet"]
|
||||
index: int
|
||||
label: Optional[str] = None
|
||||
metadata: UdmMetadata = Field(default_factory=UdmMetadata)
|
||||
children: List[UdmContentBlock] = Field(default_factory=list)
|
||||
|
||||
class UdmDocument(BaseModel):
|
||||
id: str
|
||||
role: Literal["document"] = "document"
|
||||
sourceType: Literal["pdf", "docx", "pptx", "xlsx", "html"]
|
||||
sourcePath: str = ""
|
||||
metadata: UdmMetadata = Field(default_factory=UdmMetadata)
|
||||
children: List[UdmStructuralNode] = Field(default_factory=list)
|
||||
|
||||
class UdmArchive(BaseModel):
|
||||
id: str
|
||||
role: Literal["archive"] = "archive"
|
||||
sourceType: Literal["zip", "tar", "gz"]
|
||||
sourcePath: str = ""
|
||||
metadata: UdmMetadata = Field(default_factory=UdmMetadata)
|
||||
children: List[Union[UdmArchive, UdmDocument]] = Field(default_factory=list)
|
||||
```
|
||||
|
||||
Schlüssel-Erweiterung für High-Volume: `UdmContentBlock.fileRef` — statt Base64-Daten inline zu speichern, kann eine Datei-Referenz (z.B. `fileId` oder temp-Pfad) hinterlegt werden. Der Inhalt wird erst on-demand geladen, wenn ein downstream Node ihn braucht. Bei 10k PDFs spart das mehrere GB RAM.
|
||||
|
||||
### B) Bridge: ContentPart ↔ UDM
|
||||
|
||||
```python
|
||||
def _contentPartsToUdm(extracted: ContentExtracted, sourceType: str, sourcePath: str) -> UdmDocument:
|
||||
"""Konvertiert flache ContentPart-Liste in UDM-Baum.
|
||||
Groupiert nach parentId oder typeGroup zu StructuralNodes."""
|
||||
|
||||
def _udmToContentParts(document: UdmDocument) -> ContentExtracted:
|
||||
"""Konvertiert UDM-Baum zurück in flache ContentPart-Liste.
|
||||
Für Rückwärtskompatibilität."""
|
||||
```
|
||||
|
||||
### C) Neuer Node: `context.extractContent` (Strukturextraktion OHNE AI)
|
||||
|
||||
**Problem heute:** `context.extractContent` existiert nur als Method-Action für Workspace/Agent. Im Graphical Editor gibt es keinen Node, um ein Dokument rein strukturell zu zerlegen.
|
||||
|
||||
**Lösung:** Ein neuer Node `context.extractContent` in der Kategorie `context`:
|
||||
|
||||
1. Dokument(e) entgegennehmen (Input: `DocumentList`)
|
||||
2. Reine Extraktion (kein AI-Call, nur Parser)
|
||||
3. UDM-Struktur als Output
|
||||
4. Downstream gezielt nutzbar: Loop → Filter → AI nur für bestimmte Blöcke
|
||||
|
||||
#### Node-Definition
|
||||
|
||||
```python
|
||||
{
|
||||
"id": "context.extractContent",
|
||||
"category": "context",
|
||||
"label": t("Inhalt extrahieren"),
|
||||
"description": t("Dokumentstruktur extrahieren ohne KI (Seiten, Abschnitte, Bilder, Tabellen)"),
|
||||
"parameters": [
|
||||
{"name": "outputDetail", "type": "string", "required": False, "frontendType": "select",
|
||||
"frontendOptions": {"options": ["full", "structure", "references"]},
|
||||
"description": t("Detailgrad"), "default": "full"},
|
||||
{"name": "includeImages", "type": "boolean", "required": False, "frontendType": "checkbox",
|
||||
"description": t("Bilder extrahieren"), "default": True},
|
||||
{"name": "includeTables", "type": "boolean", "required": False, "frontendType": "checkbox",
|
||||
"description": t("Tabellen extrahieren"), "default": True},
|
||||
],
|
||||
"inputs": 1,
|
||||
"outputs": 1,
|
||||
"inputPorts": {0: {"accepts": ["DocumentList", "Transit"]}},
|
||||
"outputPorts": {0: {"schema": "UdmDocument"}},
|
||||
"meta": {"icon": "mdi-file-tree-outline", "color": "#00897B"},
|
||||
"_method": "context",
|
||||
"_action": "extractContent",
|
||||
}
|
||||
```
|
||||
|
||||
#### Parameter `outputDetail`
|
||||
|
||||
| Wert | Beschreibung | Use Case |
|
||||
|------|-------------|----------|
|
||||
| `full` | Volle UDM-Struktur inkl. Rohdaten (Text, Base64-Bilder, JSON-Tabellen) | Standard — alles downstream verfügbar. Bis ~100 Dokumente. |
|
||||
| `structure` | Nur Baum-Skelett: Document → StructuralNodes → ContentBlock-Metadaten (ohne `raw`) | Schnelle Vorschau, Routing-Entscheidungen |
|
||||
| `references` | Datei-Referenzen statt Inline-Daten (`fileRef` statt `raw`). Inhalt wird on-demand im Loop geladen | **High-Volume**: 1000+ Dokumente. Spart RAM. |
|
||||
|
||||
### D) ForEach/Consolidate Workflow-Pattern
|
||||
|
||||
#### Einfacher Fall: 1 PDF, pro Seite verarbeiten
|
||||
|
||||
```
|
||||
[Upload] → [Extract] → [Loop (pro Seite)] → [AI] → [Aggregate] → [Consolidate]
|
||||
```
|
||||
|
||||
#### Parallele Pfade im Loop-Body: Bild + Text pro Seite
|
||||
|
||||
```
|
||||
[Upload] → [Extract] → [Loop (pro Seite)]
|
||||
├→ [Filter: Bilder] → [AI Vision] ──┐
|
||||
└→ [Filter: Text] → [AI Text] ──┤
|
||||
↓
|
||||
[Merge (pro Seite)]
|
||||
↓
|
||||
[Aggregate]
|
||||
[Consolidate (alle Seiten → Tabelle)] ←──┘
|
||||
```
|
||||
|
||||
**Warum das heute schon funktioniert:**
|
||||
- Fan-out: Ein Output-Port kann mehrere Connections haben (Target-Inputs sind limitiert auf 1, Source-Outputs nicht)
|
||||
- `getLoopBodyNodeIds()` (BFS) findet alle Nodes in beiden Pfaden
|
||||
- `topoSort` ordnet: Filter + AI vor Merge (weil Merge von beiden abhängt)
|
||||
- Body-Nodes werden sequentiell in Topo-Sort-Reihenfolge abgearbeitet → Merge hat beide Inputs
|
||||
|
||||
**Umgesetzt (Plan):**
|
||||
- `flow.merge`: dynamische Input-Anzahl (2–5 via Parameter `inputCount`)
|
||||
- `data.filter`: UDM-Content-Type-Presets (Filter auf `contentType`, `structuralNode.index`, `attributes.*`)
|
||||
|
||||
#### High-Volume-Fall: ZIP mit 10.000 PDFs
|
||||
|
||||
```
|
||||
[Upload ZIP] → [Extract (references)] → [Loop (pro Dokument, concurrency: 10)]
|
||||
↓
|
||||
[Lazy-Extract (1 PDF, Seite 1)]
|
||||
↓
|
||||
[Filter: Bild Seite 1]
|
||||
↓
|
||||
[AI Vision → CSV-Zeile]
|
||||
↓
|
||||
[Aggregate (CSV-Zeilen)]
|
||||
[Consolidate (mode: table → grosses CSV)] ←──┘
|
||||
```
|
||||
|
||||
**Schlüssel:** `outputDetail: "references"` — der erste Extract liefert nur eine leichtgewichtige Liste von 10k Datei-Referenzen (~10 KB statt ~4 GB). Pro Loop-Iteration wird nur 1 PDF on-demand geladen, verarbeitet und wieder freigegeben.
|
||||
|
||||
#### High-Volume: ZIP mit Fan-out + Merge pro Dokument
|
||||
|
||||
Dasselbe Muster wie bei einem einzelnen PDF (zwei parallele Body-Pfade + Merge pro Iteration), angewendet auf **pro Archiv-Eintrag** statt pro Seite:
|
||||
|
||||
```
|
||||
[Upload ZIP] → [Extract (references)] → [Loop (pro Dokument, concurrency: N)]
|
||||
├→ [Lazy-Extract / Seite 1] → [AI Vision] ──┐
|
||||
└→ [Filter: Text] → [AI Text] ────────────┤
|
||||
[Merge]
|
||||
↓
|
||||
[Aggregate]
|
||||
[Consolidate] ←──────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
Pro Iteration gilt: Fan-out und Topo-Sort im Loop-Body wie oben; der erste Extract liefert nur Referenzen, sodass nicht alle PDFs gleichzeitig im RAM liegen.
|
||||
|
||||
### E) Flow.loop Erweiterungen
|
||||
|
||||
```python
|
||||
# Neuer Parameter: UDM-Level
|
||||
{
|
||||
"name": "level",
|
||||
"type": "string",
|
||||
"required": False,
|
||||
"frontendType": "select",
|
||||
"frontendOptions": {"options": ["auto", "documents", "structuralNodes", "contentBlocks"]},
|
||||
"description": t("UDM-Iterationsebene"),
|
||||
"default": "auto",
|
||||
}
|
||||
|
||||
# Neuer Parameter: Parallele Iterationen
|
||||
{
|
||||
"name": "concurrency",
|
||||
"type": "number",
|
||||
"required": False,
|
||||
"frontendType": "number",
|
||||
"frontendOptions": {"min": 1, "max": 20},
|
||||
"description": t("Parallele Iterationen"),
|
||||
"default": 1,
|
||||
}
|
||||
```
|
||||
|
||||
- `level: auto` → `items`-Pfad wie bisher
|
||||
- `level: documents` → `archive.children` (Documents)
|
||||
- `level: structuralNodes` → `document.children` (Pages/Sections/Slides/Sheets)
|
||||
- `level: contentBlocks` → `structuralNode.children` (Text/Image/Table/...)
|
||||
- `concurrency: 1` → sequentiell wie heute
|
||||
- `concurrency: 10` → 10 Iterationen gleichzeitig via `asyncio.Semaphore`
|
||||
|
||||
### F) flow.merge Erweiterung
|
||||
|
||||
```python
|
||||
# Neuer Parameter: Dynamische Input-Anzahl
|
||||
{
|
||||
"name": "inputCount",
|
||||
"type": "number",
|
||||
"required": False,
|
||||
"frontendType": "number",
|
||||
"frontendOptions": {"min": 2, "max": 5},
|
||||
"description": t("Anzahl Eingänge"),
|
||||
"default": 2,
|
||||
}
|
||||
```
|
||||
|
||||
`inputs` und `inputPorts` werden dynamisch basierend auf `inputCount` generiert. Backend: `nodeRegistry.py` muss dynamische Input-Ports unterstützen.
|
||||
|
||||
### G) High-Volume Engine-Optimierungen
|
||||
|
||||
#### Problem-Analyse (10.000 Iterationen)
|
||||
|
||||
| Engpass | Heute | Lösung |
|
||||
|---------|-------|--------|
|
||||
| **Memory: Base64-Bilder** | Alle Bilder als Strings in `nodeOutputs` | `fileRef` statt inline `raw`. On-Demand-Loading |
|
||||
| **Memory: items-Array** | Gesamte `items`-Liste im Loop-Node-Output | Bei `references`-Modus: nur IDs, ~100 Bytes pro Item |
|
||||
| **Memory: Aggregate** | `_aggregateAccumulators` wächst unbegrenzt in-Memory | **Streaming-Aggregate**: bei >1000 Items periodisch in temp-Storage flushen |
|
||||
| **DB: AutoStepLog** | Insert + Update pro Body-Node pro Iteration | **StepLog-Batching**: bei >100 Iterationen nur jede N-te loggen + Summary am Ende |
|
||||
| **DB: updateRun** | Am Ende: gesamte nodeOutputs als JSONB | Aggregate-Daten als File-Referenz statt inline |
|
||||
| **CPU: ExtractorRegistry** | Neue Instanz pro Datei im ZIP (`_addFilePart`) | **Singleton-Pattern**: einmal erstellen, wiederverwenden |
|
||||
| **CPU: PDF-Memory** | `buf.getvalue()` kopiert gesamtes PDF für PyMuPDF | Direkt `BytesIO` übergeben, keine Kopie |
|
||||
| **Laufzeit: Sequentiell** | 10k × 3s = 8.3h | `concurrency: 10` → ~50 Min |
|
||||
| **SSE: StepEvents** | Event pro Step-Änderung | Bei High-Volume: nur Progress-Summary (z.B. "Iteration 5000/10000") |
|
||||
|
||||
#### StepLog-Batching (executionEngine.py)
|
||||
|
||||
```python
|
||||
# Heuristik: ab 100 Iterationen → Batch-Modus
|
||||
if len(items) > STEPLOG_BATCH_THRESHOLD:
|
||||
stepLogMode = "batch" # nur jede 100. Iteration + Fehler + Summary
|
||||
else:
|
||||
stepLogMode = "full" # wie heute: jeder Step einzeln
|
||||
```
|
||||
|
||||
#### Loop-Concurrency (executionEngine.py)
|
||||
|
||||
```python
|
||||
concurrency = (node.get("parameters") or {}).get("concurrency", 1)
|
||||
semaphore = asyncio.Semaphore(concurrency)
|
||||
|
||||
async def _processIteration(idx, item):
|
||||
async with semaphore:
|
||||
nodeOutputs_local = dict(nodeOutputs) # lokale Kopie pro Iteration
|
||||
nodeOutputs_local[nodeId] = {"currentItem": item, "currentIndex": idx, ...}
|
||||
for body_node in body_ordered:
|
||||
...
|
||||
|
||||
tasks = [_processIteration(idx, item) for idx, item in enumerate(items)]
|
||||
results = await asyncio.gather(*tasks)
|
||||
```
|
||||
|
||||
Achtung: Bei `concurrency > 1` braucht jede Iteration **eigene** `nodeOutputs`, da Body-Nodes sich sonst gegenseitig überschreiben. Aggregate-Accumulation muss thread-safe sein (Lock oder Queue).
|
||||
|
||||
#### Streaming-Aggregate
|
||||
|
||||
```python
|
||||
AGGREGATE_FLUSH_THRESHOLD = 1000
|
||||
|
||||
# Im Loop-Body:
|
||||
_aggregateAccumulators[bnid].extend(accItems)
|
||||
if len(_aggregateAccumulators[bnid]) >= AGGREGATE_FLUSH_THRESHOLD:
|
||||
_flushAggregateToTemp(bnid, _aggregateAccumulators[bnid])
|
||||
_aggregateAccumulators[bnid] = []
|
||||
```
|
||||
|
||||
Temp-Storage: In-Memory-Buffer oder temp-File. Am Ende: alle Chunks zusammenführen.
|
||||
|
||||
### H) AI-Kennzeichnung auf Nodes im Editor
|
||||
|
||||
Jeder Node im Editor soll **sofort sichtbar** machen, ob er AI nutzt oder deterministisch arbeitet. Dies ist wichtig für Kostentransparenz und Workflow-Design.
|
||||
|
||||
#### Umsetzung
|
||||
|
||||
**Backend:** Jede Node-Definition bekommt ein neues Feld `meta.usesAi` (Boolean):
|
||||
|
||||
```python
|
||||
# Beispiel: ai.prompt → AI
|
||||
"meta": {"icon": "mdi-robot", "color": "#9C27B0", "usesAi": True}
|
||||
|
||||
# Beispiel: data.filter → kein AI
|
||||
"meta": {"icon": "mdi-filter-outline", "color": "#607D8B", "usesAi": False}
|
||||
```
|
||||
|
||||
Durch den Split von Nodes mit gemischtem AI-Verhalten (z.B. `data.consolidate` vs `ai.consolidate`) ist jeder Node **eindeutig** — kein `"optional"` nötig.
|
||||
|
||||
**Frontend:** Im `FlowCanvas.tsx` (Zeile ~846-852, Node-Rendering) wird ein kleines Badge/Indicator angezeigt:
|
||||
|
||||
- `usesAi: true` → AI-Badge (z.B. kleines "AI"-Label oder Blitz-Icon oben rechts am Node)
|
||||
- `usesAi: false` oder nicht gesetzt → kein Badge
|
||||
|
||||
Gleiche Kennzeichnung in der **NodeSidebar** (Palette), damit der User schon beim Drag&Drop sieht, welche Nodes AI nutzen.
|
||||
|
||||
#### Zuordnung aller bestehenden Nodes
|
||||
|
||||
| Node | `usesAi` | Begründung |
|
||||
|------|----------|------------|
|
||||
| `trigger.*` | `false` | Reine Auslöser |
|
||||
| `input.*` | `false` | Formulare, manuelle Eingabe |
|
||||
| `flow.ifElse` | `false` | Bedingungslogik |
|
||||
| `flow.switch` | `false` | Bedingungslogik |
|
||||
| `flow.loop` | `false` | Iteration |
|
||||
| `flow.merge` | `false` | Zusammenführung |
|
||||
| `data.aggregate` | `false` | Sammeln |
|
||||
| `data.transform` | `false` | Feld-Mapping |
|
||||
| `data.filter` | `false` | Filterlogik |
|
||||
| `data.consolidate` (NEU) | `false` | Deterministisch: merge, concat, table, CSV-Join |
|
||||
| `ai.consolidate` (NEU) | `true` | AI-gestützt: summarize, classify, semantic merge |
|
||||
| `context.extractContent` (NEU) | `false` | Reine Parser-Arbeit |
|
||||
| `ai.prompt` | `true` | LLM-Call |
|
||||
| `ai.webResearch` | `true` | Web-Suche + LLM |
|
||||
| `ai.summarizeDocument` | `true` | LLM-Zusammenfassung |
|
||||
| `ai.translateDocument` | `true` | LLM-Übersetzung |
|
||||
| `ai.convertDocument` | `true` | LLM-Konvertierung |
|
||||
| `ai.generateDocument` | `true` | LLM-Generierung |
|
||||
| `ai.generateCode` | `true` | LLM-Codegenerierung |
|
||||
| `email.*` | `false` | E-Mail-Operationen |
|
||||
| `sharepoint.*` | `false` | SharePoint-Operationen |
|
||||
| `clickup.*` | `false` | ClickUp-Operationen |
|
||||
| `file.create` | `false` | Datei-Erstellung |
|
||||
| `trustee.refreshAccountingData` | `false` | Datenimport aus externem System |
|
||||
| `trustee.extractFromFiles` | `true` | AI-Extraktion (Prompt-basiert, Dokumenttyp-Erkennung) |
|
||||
| `trustee.processDocuments` | `false` | TrusteeDocument/Position aus Extraktionsergebnis erstellen |
|
||||
| `trustee.syncToAccounting` | `false` | Übertragung in Buchhaltung |
|
||||
|
||||
---
|
||||
|
||||
## Entscheidungen
|
||||
|
||||
| Datum | Entscheidung | Begründung |
|
||||
|-------|-------------|------------|
|
||||
| 2026-04-16 | `context.extractContent` als eigener Node im Editor | Heute fehlt komplett. Fundament für gezielten AI-Einsatz. Neue Kategorie `context`. |
|
||||
| 2026-04-16 | Extraktion ist KEIN AI-Call | Reine Parser-Arbeit. AI-Kosten erst bei explizitem AI-Node. Kostenfreie Vorverarbeitung. |
|
||||
| 2026-04-16 | UDM als In-Memory-Format, keine DB-Tabelle | UDM fliesst als JSON durch nodeOutputs. Vermeidet Migration. |
|
||||
| 2026-04-16 | Bridge ContentPart ↔ UDM statt Breaking Change | Alle bestehenden Konsumenten bleiben funktional. |
|
||||
| 2026-04-16 | Kein Compound-Node / Sub-Graph | Zu hoher Aufwand. Bestehende Body-Erkennung via BFS genügt. |
|
||||
| 2026-04-16 | Consolidate als Split in `data.consolidate` + `ai.consolidate` | `data.*` = deterministisch, `ai.*` = AI-gestützt. Kein `"optional"` Badge nötig — jeder Node ist eindeutig. |
|
||||
| 2026-04-16 | `level`-Parameter am Loop | Weniger Nodes in der Palette, einfachere UX. |
|
||||
| 2026-04-16 | Fan-out im Loop-Body nutzt bestehendes Modell | Engine unterstützt es bereits: Topo-Sort + sequentielle Body-Abarbeitung garantieren korrekte Reihenfolge. Kein Engine-Umbau nötig. |
|
||||
| 2026-04-16 | `outputDetail: "references"` für High-Volume | 10k PDFs als Referenzliste (~10 KB) statt vollständig extrahiert (~4 GB). On-Demand-Loading pro Iteration. |
|
||||
| 2026-04-16 | Loop-Concurrency als opt-in Parameter | Default bleibt `1` (sequentiell, deterministisch). Power-User setzen höher für Durchsatz. |
|
||||
| 2026-04-16 | StepLog-Batching ab Schwellwert | 50k DB-Inserts bei 10k Iterationen ist nicht tragbar. Batch-Modus loggt nur Summary + Fehler. |
|
||||
| 2026-04-16 | AI-Badge auf allen Nodes im Editor | Kostentransparenz. User sieht sofort, welche Nodes AI-Credits verbrauchen. `meta.usesAi` als Boolean. |
|
||||
| 2026-04-16 | Nodes mit gemischtem AI-Verhalten splitten | `data.consolidate` (deterministisch) + `ai.consolidate` (AI). Kein "optional"-Badge nötig — jeder Node ist eindeutig `true` oder `false`. |
|
||||
|
||||
---
|
||||
|
||||
## Umsetzungs-Checkliste
|
||||
|
||||
### Phase 1: UDM-Datenmodell & Bridge (Gateway)
|
||||
|
||||
> **Cursor-Empfehlung:** Composer (Fast) reicht. Reine Pydantic-Modelle und Utility-Funktionen — klar definiert, wenig Kontext nötig.
|
||||
|
||||
- [x] Pydantic-Modelle in `datamodels/datamodelUdm.py` (neue Datei)
|
||||
- [x] `UdmContentBlock.fileRef` Feld für Lazy-Loading
|
||||
- [x] Bridge-Funktionen `_contentPartsToUdm()` / `_udmToContentParts()`
|
||||
- [x] Unit-Tests für Modelle und Bridge
|
||||
- [x] `ExtractionOptions.outputFormat` Feld (`"parts"` | `"udm"` | `"both"`)
|
||||
|
||||
### Phase 2: Extractor-Adapter (Gateway)
|
||||
|
||||
> **Cursor-Empfehlung:** Composer (Fast) reicht. Pro Extractor ein isoliertes File mit klarem Pattern. Memory-Fixes (`buf.getvalue()`, Registry-Singleton) sind punktuelle Änderungen.
|
||||
|
||||
- [x] `extractorPdf.py` → UDM-Output + Fix `buf.getvalue()` (Memory)
|
||||
- [x] `extractorDocx.py` → UDM-Output
|
||||
- [x] `extractorPptx.py` → UDM-Output
|
||||
- [x] `extractorXlsx.py` → UDM-Output
|
||||
- [x] `extractorHtml.py` → UDM-Output
|
||||
- [x] `extractorContainer.py` → Lazy-Modus (Inhaltsverzeichnis statt alles extrahieren)
|
||||
- [x] `extractorContainer.py` → `ExtractorRegistry` Singleton statt `new` pro Datei
|
||||
- [x] `mainServiceExtraction.py` → `extractContent()` mit UDM-Option
|
||||
- [x] Integration-Tests pro Format
|
||||
|
||||
### Phase 3: Extract-Node — Strukturextraktion ohne AI (Gateway + Frontend)
|
||||
|
||||
> **Cursor-Empfehlung:** **Opus 4.6 empfohlen.** Neue Datei + Integration in mehrere bestehende Systeme (NodeDefinitions, PortTypes, Executor-Mapping, Frontend-Komponente). Erfordert Verständnis der gesamten Node-Architektur über ~6 Dateien hinweg.
|
||||
|
||||
- [x] `nodeDefinitions/context.py` → Node `context.extractContent`
|
||||
- [x] `nodeDefinitions/__init__.py` → `CONTEXT_NODES` in `STATIC_NODE_TYPES`
|
||||
- [x] `UdmDocument` Port-Typ in `portTypes.py`
|
||||
- [x] Input-Extractor `_extractUdmDocument()` in `portTypes.py`
|
||||
- [x] `executionEngine.py` → `_getExecutor()`: Branch `context.*` → `ActionNodeExecutor` hinzufügen (heute nur: `ai.`, `email.`, `sharepoint.`, `clickup.`, `file.`, `trustee.`)
|
||||
- [x] `ActionNodeExecutor` → `context.extractContent` via `_method`/`_action` Mapping (nutzt bestehendes `MethodContext.extractContent`)
|
||||
- [x] `extractContent.py` → UDM-Output-Modus + `outputDetail: "references"` Modus
|
||||
- [x] Frontend: `ExtractContentNodeConfig.tsx`
|
||||
- [x] Frontend: Node in Sidebar-Palette unter Kategorie "Kontext"
|
||||
|
||||
### Phase 4: Workflow Nodes — ForEach, Merge & Consolidate (Gateway + Frontend)
|
||||
|
||||
> **Cursor-Empfehlung:** **Opus 4.6 empfohlen.** Komplexeste Phase — berührt Execution-Engine, Port-System, Flow-Executor, Data-Executor und Frontend gleichzeitig. Concurrency-Design erfordert tiefes Verständnis der bestehenden Loop-Logik und Race-Conditions.
|
||||
|
||||
- [x] `data.consolidate` Node-Definition (`nodeDefinitions/data.py`) — deterministisch: merge, concat, table, CSV-Join
|
||||
- [x] `ai.consolidate` Node-Definition (`nodeDefinitions/ai.py`) — AI-gestützt: summarize, classify, semantic merge
|
||||
- [x] `ConsolidateResult` Port-Typ (`portTypes.py`)
|
||||
- [x] `DataExecutor` → Consolidate-Logik (`dataExecutor.py`)
|
||||
- [x] `methodAi/actions/consolidate.py` → neue Action-Datei (LLM-Call mit aggregierten Daten)
|
||||
- [x] `methodAi/methodAi.py` → Action `consolidate` registrieren
|
||||
- [x] `ai.consolidate` wird über bestehenden `ActionNodeExecutor` geroutet — kein eigener Executor nötig
|
||||
- [x] `data.filter` → UDM-Content-Type-Presets (Filter auf `contentType`, `index`, `attributes`)
|
||||
- [x] `flow.loop` → `level`-Parameter (`nodeDefinitions/flow.py`)
|
||||
- [x] `flow.loop` → `concurrency`-Parameter (`nodeDefinitions/flow.py`)
|
||||
- [x] `flow.merge` → dynamische Input-Anzahl `inputCount` (2-5)
|
||||
- [x] `FlowExecutor._loop()` → UDM-Level-Auflösung (`flowExecutor.py`)
|
||||
- [x] `UdmNodeList` Port-Typ (`portTypes.py`)
|
||||
- [x] Frontend: `DataConsolidateNodeConfig.tsx` (deterministisch)
|
||||
- [x] Frontend: `AiConsolidateNodeConfig.tsx` (AI-gestützt)
|
||||
- [x] Frontend: Loop-NodeConfig → UDM-Level-Selector + Concurrency-Slider
|
||||
- [x] Frontend: Merge-NodeConfig → Input-Count-Selector
|
||||
- [x] Frontend: Visuelle Loop-Body-Markierung im Canvas
|
||||
|
||||
### Phase 5: High-Volume Engine-Optimierungen (Gateway)
|
||||
|
||||
> **Cursor-Empfehlung:** **Opus 4.6 empfohlen.** Async-Concurrency, Thread-Safety, Streaming-Aggregates — subtile Bugs möglich. Load-Test-Design braucht Überblick über das gesamte System.
|
||||
|
||||
- [x] `executionEngine.py` → Loop-Concurrency via `asyncio.Semaphore` mit isolierten `nodeOutputs` pro Iteration
|
||||
- [x] `executionEngine.py` → StepLog-Batching (Schwellwert 100 Iterationen: nur Summary + Fehler loggen)
|
||||
- [x] `executionEngine.py` → Streaming-Aggregate (Flush-Threshold, temp-Storage)
|
||||
- [x] `executionEngine.py` → Progress-SSE bei High-Volume: Summary statt pro-Step-Events
|
||||
- [x] `extractorContainer.py` → `ExtractorRegistry` Singleton (Performance-Fix)
|
||||
- [x] `extractorPdf.py` → `BytesIO` direkt an fitz übergeben statt `getvalue()` (Memory-Fix)
|
||||
- [x] Lazy-Content-Loader: Utility-Funktion die `fileRef` → Bytes auflöst, on-demand im Loop
|
||||
- [x] Load-Test: 1000 PDFs aus ZIP → Extract → AI Vision → CSV → Consolidate
|
||||
|
||||
### Phase 6: AI-Badge — Visuelle Kennzeichnung im Editor (Gateway + Frontend)
|
||||
|
||||
> **Cursor-Empfehlung:** Composer (Fast) reicht. Kleines `meta.usesAi`-Feld pro Node-Definition (Backend) + Badge-Komponente im Canvas (Frontend). Klar abgegrenzte Änderung.
|
||||
|
||||
- [x] Alle bestehenden Node-Definitionen: `meta.usesAi` Feld ergänzen (`true` / `false`)
|
||||
- [x] `FlowCanvas.tsx` → AI-Badge-Rendering: kleines "AI"-Label / Blitz-Icon oben rechts am Node wenn `usesAi: true`
|
||||
- [x] `NodeSidebar.tsx` → AI-Kennzeichnung in der Palette (Badge neben Node-Name)
|
||||
- [x] Tooltip auf Badge: "Dieser Schritt nutzt AI und verbraucht Credits"
|
||||
- [x] CSS/Styling: Badge-Design konsistent mit bestehendem Design-System
|
||||
|
||||
### Phase 7: Agent-Tools & Integration (Gateway)
|
||||
|
||||
> **Cursor-Empfehlung:** Composer (Fast) reicht für die Tool-Funktionen. **Opus 4.6 für E2E-Tests** — die Tests müssen viele Module korrekt zusammenspielen lassen.
|
||||
|
||||
- [x] `_documentTools.py` → UDM-Tools (`walkUdmBlocks`, `filterUdmByType`, `getUdmStructure`)
|
||||
- [x] E2E-Test: PDF → Extract (UDM) → Loop (pro Seite) → AI → Aggregate → Consolidate
|
||||
- [x] E2E-Test: PDF → Extract → Filter (nur Tabellen) → AI → kein Loop
|
||||
- [x] E2E-Test: ZIP → Extract (references) → Loop (Fan-out) → Vision + Text → Merge → Aggregate → Consolidate
|
||||
|
||||
### Phase 8: Dokumentation & Abschluss
|
||||
|
||||
> **Cursor-Empfehlung:** Composer (Fast) reicht. Reine Dokumentation und Verifikation.
|
||||
|
||||
- [x] RBAC / Permissions: Keine Änderung nötig
|
||||
- [x] Neutralisierung: ContentBlock.raw mit sensiblen Daten → Bridge zu ContentPart
|
||||
- [x] Navigation / Routing: Keine Änderung
|
||||
- [x] Billing-Impact: Loop-Iterationen zählen als einzelne AI-Calls → bestehende Logik greift
|
||||
|
||||
---
|
||||
|
||||
## Akzeptanzkriterien
|
||||
|
||||
| # | Kriterium (Given-When-Then) | Prio |
|
||||
|---|---------------------------|------|
|
||||
| 1 | Given ein PDF mit 5 Seiten, When `context.extractContent`-Node ausgeführt, Then UdmDocument mit 5 StructuralNodes (role=page) — **kein AI-Call**, nur Parsing | must |
|
||||
| 2 | Given der Extract-Node-Output (UDM), When mit `data.filter` auf `contentType=="table"` gefiltert, Then nur Tabellen-ContentBlocks im Output | must |
|
||||
| 3 | Given ein DOCX mit 3 Sections, When UDM-Extraktion, Then 3 StructuralNodes mit role=section und korrekten Labels | must |
|
||||
| 4 | Given ein UDM-Dokument, When `_udmToContentParts()`, Then identische ContentPart-Liste wie bei direkter Extraktion (Bridge) | must |
|
||||
| 5 | Given ein Workflow mit Loop über `document.children`, When ausgeführt, Then Body-Nodes pro StructuralNode einmal durchlaufen | must |
|
||||
| 6 | Given ForEach → AI → Aggregate → Consolidate, When 3 Seiten verarbeitet, Then Consolidate erhält 3 Ergebnisse und erzeugt Zusammenfassung | must |
|
||||
| 7 | Given Loop mit Fan-out zu 2 AI-Nodes + Merge, When pro Seite ausgeführt, Then Merge erhält beide AI-Ergebnisse pro Iteration | must |
|
||||
| 8 | Given `outputDetail=references` auf ZIP mit 1000 PDFs, When Extract ausgeführt, Then Referenzliste mit 1000 Einträgen, Memory < 100 MB | must |
|
||||
| 9 | Given Loop mit `concurrency: 5` über 100 Items, When ausgeführt, Then ~5x schneller als `concurrency: 1` | should |
|
||||
| 10 | Given Loop mit 10.000 Iterationen, When ausgeführt, Then weniger als 1000 AutoStepLog-Einträge (Batching) | should |
|
||||
| 11 | Given `context.extractContent` mit `outputDetail=structure`, When ausgeführt, Then nur Skelett ohne `raw`-Daten | should |
|
||||
| 12 | Given Loop-Workflow im Editor, When User den Graph betrachtet, Then Loop-Body-Nodes visuell als zusammengehörig erkennbar | should |
|
||||
| 13 | Given `data.consolidate` mit mode=table, When 100 CSV-Zeilen als Input, Then ein zusammengefügtes CSV als Output | should |
|
||||
| 14 | Given ZIP mit 10.000 PDFs, When E2E-Workflow (Extract refs → Loop → AI Vision Seite 1 → CSV), Then erfolgreich in < 24h (mit concurrency: 10) | nice |
|
||||
| 15 | Given ZIP mit 3 PDFs, When UDM-Extraktion, Then UdmArchive mit 3 UdmDocuments | nice |
|
||||
| 16 | Given ein Workflow mit `ai.prompt` und `data.filter` Nodes, When User den Graph betrachtet, Then AI-Badge nur auf `ai.prompt` sichtbar, nicht auf `data.filter` | must |
|
||||
| 17 | Given `data.consolidate` und `ai.consolidate` nebeneinander im Editor, When User vergleicht, Then nur `ai.consolidate` hat AI-Badge, `data.consolidate` nicht | must |
|
||||
|
||||
---
|
||||
|
||||
## Testplan
|
||||
|
||||
*Stand beim Archivieren: Kerntests liegen in den unter „Abschlussbericht“ genannten Dateien. Die ursprünglich geplanten Einzelpfade (T2, T3, T6–T17) können bei Bedarf als separates Backlog ergänzt werden.*
|
||||
|
||||
| ID | AC | Art | Automatisiert | Repo-Pfad | Status |
|
||||
|----|----|-----|--------------|-----------|--------|
|
||||
| T1 | 1 | unit | ja | gateway/tests/unit/datamodels/test_udm_models.py | done |
|
||||
| T2 | 1 | integration | ja | gateway/tests/integration/extraction/test_extract_udm_pipeline.py (statt urspr. `test_extract_node.py`) | done |
|
||||
| T3 | 2 | integration | ja | — (Backlog: dedizierter Filter-Flow) | backlog |
|
||||
| T4 | 3 | unit | ja | gateway/tests/unit/datamodels/test_udm_models.py | done |
|
||||
| T5 | 4 | unit | ja | gateway/tests/unit/datamodels/test_udm_bridge.py | done |
|
||||
| T6 | 1,3 | integration | ja | gateway/tests/integration/extraction/test_extract_udm_pipeline.py | done |
|
||||
| T7 | 5 | integration | ja | — (Backlog) | backlog |
|
||||
| T8 | 6 | integration | ja | gateway/tests/integration/workflows/test_execute_graph_loop_aggregate_consolidate.py | done |
|
||||
| T9 | 7 | integration | ja | — (Backlog) | backlog |
|
||||
| T10 | 8 | integration | ja | — (Backlog) | backlog |
|
||||
| T11 | 9 | integration | ja | — (Backlog) | backlog |
|
||||
| T12 | 10 | unit | ja | — (Backlog) | backlog |
|
||||
| T13 | 11 | unit | ja | — (Backlog) | backlog |
|
||||
| T14 | 12 | manual | nein | — | backlog |
|
||||
| T15 | 13 | integration | ja | — (Backlog) | backlog |
|
||||
| T16 | 14 | load | nein | — (manueller Load-Test) | backlog |
|
||||
| T17 | 15 | integration | ja | — (Backlog) | backlog |
|
||||
| T18 | 16 | unit | ja | gateway/tests/unit/nodeDefinitions/test_usesai_flag.py | done |
|
||||
| T19 | 17 | manual | nein | — (visueller Check im Editor) | backlog |
|
||||
|
||||
---
|
||||
|
||||
## Links
|
||||
|
||||
- Idee: `c-work/0-ideas/unified-document-model.md`
|
||||
- Workflow-Engine Referenz: `b-reference/gateway/workflow.md`
|
||||
- AI-Agent Referenz: `b-reference/gateway/ai-agent.md`
|
||||
- Port-System Referenz: `c-work/4-done/2026-04-generic-graph-editor.md`
|
||||
|
||||
---
|
||||
|
||||
## Abschluss
|
||||
|
||||
- [x] b-reference/ aktualisiert: `gateway/workflow.md` (neue Nodes, UDM-Loop-Pattern, Concurrency), `gateway/ai-agent.md` (neue UDM-Tools)
|
||||
- [x] TOPICS.md aktualisiert (neues Thema: UDM)
|
||||
- [x] Dieses Dokument → `wiki/z-archive/2026-04-unified-document-model.md` archiviert
|
||||
511
z-archive/unified-document-model.md
Normal file
511
z-archive/unified-document-model.md
Normal file
|
|
@ -0,0 +1,511 @@
|
|||
# Unified Document Model (UDM)
|
||||
|
||||
## Konzept & Zielsetzung
|
||||
|
||||
Das Unified Document Model definiert eine **generische, formatunabhängige Baumstruktur**, in die jeder Dokumenttyp (PDF, DOCX, PPTX, XLSX, HTML, ZIP) durch einen Extractor überführt wird. Dadurch können AI-Workflows, Nodes und Tools mit einem einzigen Objektmodell arbeiten – unabhängig vom Quellformat.
|
||||
|
||||
### Designprinzipien
|
||||
|
||||
- **3-Ebenen-Garantie**: Jedes Dokument hat exakt drei Verschachtelungsebenen (ausgenommen ZIP als Meta-Container).
|
||||
- **Einheitliche Blattknoten**: Alle atomaren Inhalte sind `ContentBlock`-Objekte mit identischer Attributstruktur.
|
||||
- **Generische Traversierung**: Workflow-Nodes (Loop, Filter, Transform, Map) arbeiten formatunabhängig über dieselbe Baumstruktur.
|
||||
- **Keine formatspezifischen Zwischenschichten**: Konzepte wie "Paragraph", "Row" oder "Cell" werden in den `ContentBlock` absorbiert, nicht als eigene Ebenen modelliert.
|
||||
|
||||
---
|
||||
|
||||
## Architekturübersicht
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ Level 1 — Document │
|
||||
│ ┌───────────────────────────────────────────────┐ │
|
||||
│ │ Level 2 — StructuralNode │ │
|
||||
│ │ ┌─────────────────────────────────────────┐ │ │
|
||||
│ │ │ Level 3 — ContentBlock (Blattknoten) │ │ │
|
||||
│ │ └─────────────────────────────────────────┘ │ │
|
||||
│ └───────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Ebenen im Detail
|
||||
|
||||
| Ebene | Typ | Beschreibung |
|
||||
|-------|-----|-------------|
|
||||
| **Level 1** | `Document` | Wurzelknoten. Repräsentiert ein einzelnes Quelldokument. |
|
||||
| **Level 2** | `StructuralNode` | Strukturelle Gliederungseinheit: Seite, Abschnitt, Slide oder Sheet. |
|
||||
| **Level 3** | `ContentBlock` | Atomarer Inhalt: Text, Bild, Tabelle, Code, Medien, Link oder Formel. |
|
||||
|
||||
---
|
||||
|
||||
## Datenmodell
|
||||
|
||||
### Document (Level 1)
|
||||
|
||||
Der Wurzelknoten pro Quelldatei.
|
||||
|
||||
```typescript
|
||||
interface Document {
|
||||
id: string;
|
||||
role: "document";
|
||||
source_type: "pdf" | "docx" | "pptx" | "xlsx" | "html";
|
||||
source_path: string;
|
||||
metadata: Metadata;
|
||||
children: StructuralNode[];
|
||||
}
|
||||
```
|
||||
|
||||
| Feld | Typ | Beschreibung |
|
||||
|------|-----|-------------|
|
||||
| `id` | `string` | Eindeutige ID (UUID) |
|
||||
| `role` | `"document"` | Immer `"document"` auf Level 1 |
|
||||
| `source_type` | `string` | Originalformat der Quelldatei |
|
||||
| `source_path` | `string` | Pfad der Originaldatei (relativ zum Workspace oder Archiv) |
|
||||
| `metadata` | `Metadata` | Dokument-Metadaten |
|
||||
| `children` | `StructuralNode[]` | Liste der strukturellen Einheiten |
|
||||
|
||||
---
|
||||
|
||||
### StructuralNode (Level 2)
|
||||
|
||||
Die Gliederungseinheit innerhalb eines Dokuments.
|
||||
|
||||
```typescript
|
||||
interface StructuralNode {
|
||||
id: string;
|
||||
role: "page" | "section" | "slide" | "sheet";
|
||||
index: number;
|
||||
label: string | null;
|
||||
metadata: Metadata;
|
||||
children: ContentBlock[];
|
||||
}
|
||||
```
|
||||
|
||||
| Feld | Typ | Beschreibung |
|
||||
|------|-----|-------------|
|
||||
| `id` | `string` | Eindeutige ID |
|
||||
| `role` | `string` | Art der Struktureinheit (formatabhängig, aber aus fester Menge) |
|
||||
| `index` | `number` | 0-basierte Position innerhalb des Dokuments |
|
||||
| `label` | `string?` | Optionaler Name (Sheet-Name, Abschnittsüberschrift, Slide-Titel) |
|
||||
| `metadata` | `Metadata` | Zusätzliche Informationen zur Struktureinheit |
|
||||
| `children` | `ContentBlock[]` | Liste der atomaren Inhalte |
|
||||
|
||||
#### Rollen-Zuordnung pro Format
|
||||
|
||||
| Quellformat | `role` | Entspricht im Original |
|
||||
|-------------|--------|----------------------|
|
||||
| PDF | `page` | Seite |
|
||||
| DOCX | `section` | Abschnitt (Heading-basierte Gliederung) |
|
||||
| PPTX | `slide` | Folie |
|
||||
| XLSX | `sheet` | Tabellenblatt |
|
||||
| HTML | `section` | Semantischer Bereich (`<header>`, `<main>`, `<nav>`, `<footer>`, `<aside>`) |
|
||||
|
||||
---
|
||||
|
||||
### ContentBlock (Level 3)
|
||||
|
||||
Der atomare Inhaltsknoten. **Alle Formate erzeugen identisch strukturierte ContentBlocks.**
|
||||
|
||||
```typescript
|
||||
interface ContentBlock {
|
||||
id: string;
|
||||
content_type: "text" | "image" | "table" | "code" | "media" | "link" | "formula";
|
||||
raw: string;
|
||||
mime_type: string | null;
|
||||
language: string | null;
|
||||
attributes: Record<string, any>;
|
||||
position: Position;
|
||||
metadata: Metadata;
|
||||
}
|
||||
```
|
||||
|
||||
| Feld | Typ | Beschreibung |
|
||||
|------|-----|-------------|
|
||||
| `id` | `string` | Eindeutige ID |
|
||||
| `content_type` | `string` | Art des Inhalts (aus fester Menge) |
|
||||
| `raw` | `string` | Rohinhalt: Plaintext, Base64-kodierte Binärdaten oder JSON-serialisierte Struktur |
|
||||
| `mime_type` | `string?` | MIME-Type (`text/plain`, `image/png`, `text/html`, `application/json`, …) |
|
||||
| `language` | `string?` | Programmiersprache bei `code`-Blocks (`python`, `sql`, `javascript`, …) |
|
||||
| `attributes` | `Record` | Zusätzliche Eigenschaften (Styling, Alt-Text, Grösse, …) |
|
||||
| `position` | `Position` | Lokalisierung innerhalb der Struktureinheit |
|
||||
| `metadata` | `Metadata` | Block-spezifische Metadaten |
|
||||
|
||||
#### Content-Typen im Detail
|
||||
|
||||
| `content_type` | `raw` enthält | `mime_type` Beispiel | Typische `attributes` |
|
||||
|-----------------|--------------|---------------------|----------------------|
|
||||
| `text` | Plaintext-Inhalt | `text/plain` | `{ style, heading_level, list_type, bold, italic }` |
|
||||
| `image` | Base64-kodierte Bilddaten | `image/png`, `image/jpeg` | `{ width, height, alt_text, caption }` |
|
||||
| `table` | JSON-Matrix `{ headers: [...], rows: [[...], ...] }` | `application/json` | `{ row_count, col_count, has_header, name }` |
|
||||
| `code` | Quellcode als Text | `text/plain` | `{ language, line_count, executable }` |
|
||||
| `media` | Base64-kodierte Daten oder URI | `audio/mp3`, `video/mp4` | `{ duration, format, embedded }` |
|
||||
| `link` | URL als Text | `text/uri-list` | `{ display_text, target, rel }` |
|
||||
| `formula` | Formelausdruck (LaTeX, Excel-Syntax) | `text/plain` | `{ notation, result_value, result_type }` |
|
||||
|
||||
---
|
||||
|
||||
### Position
|
||||
|
||||
Lokalisiert einen ContentBlock innerhalb seiner Struktureinheit.
|
||||
|
||||
```typescript
|
||||
interface Position {
|
||||
index: number;
|
||||
page: number | null;
|
||||
row: number | null;
|
||||
col: number | null;
|
||||
bbox: BoundingBox | null;
|
||||
}
|
||||
|
||||
interface BoundingBox {
|
||||
x: number;
|
||||
y: number;
|
||||
width: number;
|
||||
height: number;
|
||||
unit: "px" | "pt" | "mm";
|
||||
}
|
||||
```
|
||||
|
||||
| Feld | Typ | Beschreibung |
|
||||
|------|-----|-------------|
|
||||
| `index` | `number` | 0-basierte Reihenfolge innerhalb der Struktureinheit |
|
||||
| `page` | `number?` | Seitennummer (relevant für PDF) |
|
||||
| `row` | `number?` | Zeilenposition (relevant für tabellarische Daten) |
|
||||
| `col` | `number?` | Spaltenposition (relevant für tabellarische Daten) |
|
||||
| `bbox` | `BoundingBox?` | Bounding Box für visuell positionierte Inhalte (PDF, PPTX) |
|
||||
|
||||
---
|
||||
|
||||
### Metadata
|
||||
|
||||
Einheitliches Metadaten-Objekt, verwendbar auf allen drei Ebenen.
|
||||
|
||||
```typescript
|
||||
interface Metadata {
|
||||
title: string | null;
|
||||
author: string | null;
|
||||
created_at: string | null; // ISO 8601
|
||||
modified_at: string | null; // ISO 8601
|
||||
source_path: string;
|
||||
tags: string[];
|
||||
custom: Record<string, any>;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Archive (Sonderfall ZIP)
|
||||
|
||||
ZIP-Dateien fungieren als Meta-Container und erzeugen eine zusätzliche Wrapper-Ebene. Jede enthaltene Datei wird als eigenständiges `Document` extrahiert.
|
||||
|
||||
```typescript
|
||||
interface Archive {
|
||||
id: string;
|
||||
role: "archive";
|
||||
source_type: "zip" | "tar" | "gz";
|
||||
source_path: string;
|
||||
metadata: Metadata;
|
||||
children: (Archive | Document)[];
|
||||
}
|
||||
```
|
||||
|
||||
Die 3-Ebenen-Garantie gilt **pro Dokument innerhalb** des Archivs. Das Archiv selbst ist eine Hülle.
|
||||
|
||||
```
|
||||
Archive (ZIP)
|
||||
├── Document (PDF)
|
||||
│ ├── StructuralNode (page 0)
|
||||
│ │ ├── ContentBlock (text)
|
||||
│ │ └── ContentBlock (image)
|
||||
│ └── StructuralNode (page 1)
|
||||
│ └── ContentBlock (text)
|
||||
├── Document (DOCX)
|
||||
│ └── StructuralNode (section 0)
|
||||
│ ├── ContentBlock (text)
|
||||
│ └── ContentBlock (table)
|
||||
└── Archive (nested ZIP)
|
||||
└── Document (…)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Format-Mapping-Referenz
|
||||
|
||||
Übersicht, wie jedes Quellformat in das 3-Ebenen-Modell abgebildet wird.
|
||||
|
||||
### PDF → UDM
|
||||
|
||||
```
|
||||
PDF-Datei
|
||||
├── Document (source_type: "pdf")
|
||||
│ ├── StructuralNode (role: "page", index: 0)
|
||||
│ │ ├── ContentBlock (text) ← Textblöcke der Seite
|
||||
│ │ ├── ContentBlock (image) ← Eingebettete Bilder
|
||||
│ │ └── ContentBlock (table) ← Erkannte Tabellen
|
||||
│ ├── StructuralNode (role: "page", index: 1)
|
||||
│ │ └── ...
|
||||
```
|
||||
|
||||
- Jede PDF-Seite wird ein `StructuralNode(page)`.
|
||||
- Text wird als zusammenhängende Blöcke extrahiert, nicht zeilenweise.
|
||||
- Bilder werden als Base64 in `raw` gespeichert.
|
||||
- Tabellen werden als JSON-Matrix serialisiert.
|
||||
- `bbox` in `Position` enthält die visuelle Position auf der Seite.
|
||||
|
||||
### DOCX → UDM
|
||||
|
||||
```
|
||||
DOCX-Datei
|
||||
├── Document (source_type: "docx")
|
||||
│ ├── StructuralNode (role: "section", label: "Einleitung")
|
||||
│ │ ├── ContentBlock (text) ← Absätze
|
||||
│ │ ├── ContentBlock (image) ← Inline-Bilder
|
||||
│ │ └── ContentBlock (table) ← Word-Tabellen
|
||||
│ ├── StructuralNode (role: "section", label: "Methodik")
|
||||
│ │ └── ...
|
||||
```
|
||||
|
||||
- Sections werden anhand von Heading-Ebenen gegliedert (H1 → neue Section).
|
||||
- Absätze (Paragraphs) werden direkt zu `ContentBlock(text)` – keine Zwischenebene.
|
||||
- Styling-Informationen (bold, italic, heading_level) landen in `attributes`.
|
||||
- Tabellen werden als JSON-Matrix in `raw` serialisiert.
|
||||
|
||||
### PPTX → UDM
|
||||
|
||||
```
|
||||
PPTX-Datei
|
||||
├── Document (source_type: "pptx")
|
||||
│ ├── StructuralNode (role: "slide", index: 0, label: "Titelfolie")
|
||||
│ │ ├── ContentBlock (text) ← Textboxen
|
||||
│ │ ├── ContentBlock (image) ← Bilder/Grafiken
|
||||
│ │ └── ContentBlock (table) ← Slide-Tabellen
|
||||
│ ├── StructuralNode (role: "slide", index: 1)
|
||||
│ │ └── ...
|
||||
```
|
||||
|
||||
- Jede Folie wird ein `StructuralNode(slide)`.
|
||||
- Textboxen werden zu `ContentBlock(text)` mit `bbox` für die Position.
|
||||
- Speaker Notes landen als `ContentBlock(text)` mit `attributes.note: true`.
|
||||
|
||||
### XLSX → UDM
|
||||
|
||||
```
|
||||
XLSX-Datei
|
||||
├── Document (source_type: "xlsx")
|
||||
│ ├── StructuralNode (role: "sheet", index: 0, label: "Umsatz 2024")
|
||||
│ │ ├── ContentBlock (table) ← Tabellenbereich A
|
||||
│ │ └── ContentBlock (table) ← Tabellenbereich B (falls disjunkt)
|
||||
│ ├── StructuralNode (role: "sheet", index: 1, label: "Kosten")
|
||||
│ │ └── ContentBlock (table)
|
||||
```
|
||||
|
||||
- Jedes Sheet wird ein `StructuralNode(sheet)`.
|
||||
- Das gesamte Daten-Grid eines Sheets wird als ein `ContentBlock(table)` serialisiert.
|
||||
- Bei mehreren disjunkten Tabellenbereichen im selben Sheet → mehrere ContentBlocks.
|
||||
- Formeln werden als `ContentBlock(formula)` extrahiert, wenn gewünscht.
|
||||
|
||||
### HTML → UDM
|
||||
|
||||
```
|
||||
HTML-Datei
|
||||
├── Document (source_type: "html")
|
||||
│ ├── StructuralNode (role: "section", label: "header")
|
||||
│ │ └── ContentBlock (text)
|
||||
│ ├── StructuralNode (role: "section", label: "nav")
|
||||
│ │ └── ContentBlock (link)
|
||||
│ ├── StructuralNode (role: "section", label: "main")
|
||||
│ │ ├── ContentBlock (text)
|
||||
│ │ ├── ContentBlock (image)
|
||||
│ │ └── ContentBlock (table)
|
||||
│ ├── StructuralNode (role: "section", label: "footer")
|
||||
│ │ └── ContentBlock (text)
|
||||
```
|
||||
|
||||
- Semantische HTML5-Elemente (`<header>`, `<main>`, `<nav>`, `<footer>`, `<aside>`) werden zu Sections.
|
||||
- Falls keine semantischen Elemente vorhanden: gesamter `<body>` als eine Section.
|
||||
- HTML-Inhalte werden in Plaintext konvertiert, nicht als HTML-Markup gespeichert.
|
||||
|
||||
---
|
||||
|
||||
## Workflow-Integration
|
||||
|
||||
### Generische Traversierung
|
||||
|
||||
Da alle Formate dieselbe Struktur haben, funktioniert ein einzelner rekursiver Walker für alle Dokumenttypen:
|
||||
|
||||
```python
|
||||
def walk_content_blocks(document: Document) -> Iterator[ContentBlock]:
|
||||
"""Iteriert über alle ContentBlocks eines Dokuments, formatunabhängig."""
|
||||
for structural_node in document.children:
|
||||
for block in structural_node.children:
|
||||
yield block
|
||||
```
|
||||
|
||||
### Filter-Node
|
||||
|
||||
```python
|
||||
def filter_by_type(document: Document, content_type: str) -> list[ContentBlock]:
|
||||
"""Filtert alle ContentBlocks nach Typ (z.B. 'image', 'table')."""
|
||||
return [
|
||||
block for block in walk_content_blocks(document)
|
||||
if block.content_type == content_type
|
||||
]
|
||||
```
|
||||
|
||||
### Loop-Node
|
||||
|
||||
```python
|
||||
def process_all_documents(archive: Archive):
|
||||
"""Verarbeitet alle Dokumente in einem Archiv mit identischer Logik."""
|
||||
for document in archive.children:
|
||||
if isinstance(document, Archive):
|
||||
process_all_documents(document) # rekursiv für verschachtelte ZIPs
|
||||
else:
|
||||
for block in walk_content_blocks(document):
|
||||
# Identische Verarbeitung, egal ob PDF, DOCX, PPTX, ...
|
||||
transform(block)
|
||||
```
|
||||
|
||||
### Map-Node
|
||||
|
||||
```python
|
||||
def map_blocks(document: Document, fn: Callable[[ContentBlock], T]) -> list[T]:
|
||||
"""Wendet eine Funktion auf jeden ContentBlock an."""
|
||||
return [fn(block) for block in walk_content_blocks(document)]
|
||||
```
|
||||
|
||||
### Beispiel: Alle Bilder aus beliebigem Dokument extrahieren
|
||||
|
||||
```python
|
||||
images = filter_by_type(document, "image")
|
||||
for img in images:
|
||||
save_image(
|
||||
data=base64_decode(img.raw),
|
||||
filename=f"{img.id}.{img.mime_type.split('/')[1]}",
|
||||
alt_text=img.attributes.get("alt_text", "")
|
||||
)
|
||||
```
|
||||
|
||||
### Beispiel: Alle Tabellen als CSV exportieren
|
||||
|
||||
```python
|
||||
tables = filter_by_type(document, "table")
|
||||
for table in tables:
|
||||
data = json.loads(table.raw)
|
||||
write_csv(
|
||||
headers=data["headers"],
|
||||
rows=data["rows"],
|
||||
filename=f"{table.id}.csv"
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## JSON-Beispiel
|
||||
|
||||
Vollständiges Beispiel eines extrahierten PDF-Dokuments:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "doc-a1b2c3",
|
||||
"role": "document",
|
||||
"source_type": "pdf",
|
||||
"source_path": "reports/quarterly-report-q3.pdf",
|
||||
"metadata": {
|
||||
"title": "Quarterly Report Q3 2025",
|
||||
"author": "Finance Team",
|
||||
"created_at": "2025-10-01T08:00:00Z",
|
||||
"modified_at": "2025-10-15T14:30:00Z",
|
||||
"source_path": "reports/quarterly-report-q3.pdf",
|
||||
"tags": ["finance", "quarterly"],
|
||||
"custom": {}
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "sn-page-0",
|
||||
"role": "page",
|
||||
"index": 0,
|
||||
"label": null,
|
||||
"metadata": {
|
||||
"title": null,
|
||||
"author": null,
|
||||
"created_at": null,
|
||||
"modified_at": null,
|
||||
"source_path": "reports/quarterly-report-q3.pdf#page=1",
|
||||
"tags": [],
|
||||
"custom": {}
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "cb-001",
|
||||
"content_type": "text",
|
||||
"raw": "Quarterly Report Q3 2025\n\nThis report summarizes the financial performance...",
|
||||
"mime_type": "text/plain",
|
||||
"language": null,
|
||||
"attributes": {
|
||||
"heading_level": 1,
|
||||
"style": "title"
|
||||
},
|
||||
"position": {
|
||||
"index": 0,
|
||||
"page": 1,
|
||||
"row": null,
|
||||
"col": null,
|
||||
"bbox": { "x": 50, "y": 30, "width": 500, "height": 40, "unit": "pt" }
|
||||
},
|
||||
"metadata": {
|
||||
"title": null,
|
||||
"author": null,
|
||||
"created_at": null,
|
||||
"modified_at": null,
|
||||
"source_path": "reports/quarterly-report-q3.pdf#page=1",
|
||||
"tags": [],
|
||||
"custom": {}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "cb-002",
|
||||
"content_type": "table",
|
||||
"raw": "{\"headers\":[\"Metric\",\"Q2\",\"Q3\",\"Delta\"],\"rows\":[[\"Revenue\",\"1.2M\",\"1.5M\",\"+25%\"],[\"Costs\",\"800K\",\"850K\",\"+6%\"]]}",
|
||||
"mime_type": "application/json",
|
||||
"language": null,
|
||||
"attributes": {
|
||||
"row_count": 2,
|
||||
"col_count": 4,
|
||||
"has_header": true,
|
||||
"name": "Financial Overview"
|
||||
},
|
||||
"position": {
|
||||
"index": 1,
|
||||
"page": 1,
|
||||
"row": null,
|
||||
"col": null,
|
||||
"bbox": { "x": 50, "y": 200, "width": 500, "height": 120, "unit": "pt" }
|
||||
},
|
||||
"metadata": {
|
||||
"title": null,
|
||||
"author": null,
|
||||
"created_at": null,
|
||||
"modified_at": null,
|
||||
"source_path": "reports/quarterly-report-q3.pdf#page=1",
|
||||
"tags": [],
|
||||
"custom": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Zusammenfassung
|
||||
|
||||
| Eigenschaft | Wert |
|
||||
|-------------|------|
|
||||
| **Ebenen** | Exakt 3 pro Dokument (Archive als optionaler Wrapper) |
|
||||
| **Struktureinheiten** | `page`, `section`, `slide`, `sheet` |
|
||||
| **Content-Typen** | `text`, `image`, `table`, `code`, `media`, `link`, `formula` |
|
||||
| **Formate** | PDF, DOCX, PPTX, XLSX, HTML (erweiterbar) |
|
||||
| **Traversierung** | Ein generischer Walker für alle Formate |
|
||||
| **Serialisierung** | JSON-kompatibel, sofort einsetzbar in Workflow-Engines |
|
||||
Loading…
Reference in a new issue