automation concept
This commit is contained in:
parent
9be05652b9
commit
14dbd418a6
2 changed files with 1734 additions and 0 deletions
740
concepts/Automation Business Spec.md
Normal file
740
concepts/Automation Business Spec.md
Normal file
|
|
@ -0,0 +1,740 @@
|
|||
# PowerOn Automation Unification — Business Specification
|
||||
|
||||
**Version:** 2.0
|
||||
**Datum:** 2026-04-05
|
||||
**Status:** Draft
|
||||
|
||||
---
|
||||
|
||||
## 1. Executive Summary
|
||||
|
||||
PowerOn enthält heute drei teilweise überlappende Systeme für die Automatisierung von Workflows. Die Ziel-Architektur konsolidiert diese zu einer einheitlichen Platform mit klaren Grenzen zwischen Services, Features und Shared Libraries.
|
||||
|
||||
**Leitprinzipien:**
|
||||
- Keine Migration von Altdaten — Automation v1 ist nicht produktiv, wird nicht migriert
|
||||
- Automation v1 bleibt bestehen, bis es durch das neue Feature abgelöst ist
|
||||
- Klare Datenmodelle mit definierten State Machines als stabile, erweiterbare Grundlage
|
||||
- Klare Trennung von Mandant/Feature/Feature-Instanz und zugehöriger RBAC-Logik
|
||||
- Skalierbare AI-Tool-Architektur mit Toolboxes und Sub-Agents für 50–100+ Tools
|
||||
|
||||
---
|
||||
|
||||
## 2. Ist-Analyse
|
||||
|
||||
### 2.1 Drei bestehende Systeme
|
||||
|
||||
| System | Modell | Stärke | Schwäche |
|
||||
|--------|--------|--------|----------|
|
||||
| **Automation v1** | Template + Cron + Placeholders | Stabiles, bewährtes Scheduling mit Callback-basiertem Sync, inkrementeller Job-Registrierung, Execution-Logs | Kein visuelles Editing, kein Branching, starres Template-Modell |
|
||||
| **Automation2** | Graph-Editor (n8n-style) + Run/Task-Modell | Visuelles Modellieren, Branching, Loops, Human-in-the-loop, Pause/Resume, diverse Node-Typen | Kein AI-Chat im Editor, kein UDB-Zugang, Scheduler-Sync weniger robust als v1 |
|
||||
| **AI Workspace** | Chat-driven AI Agent mit Tools | Natural-language Interaktion, 40+ Core Tools + Action Tools, Streaming, RAG | Kein persistiertes Workflow-Modell, kein Scheduling |
|
||||
|
||||
### 2.2 Wer nutzt die Method/Action Library?
|
||||
|
||||
Die `workflows/methods/` Library (methodAi, methodOutlook, methodSharepoint, etc.) ist bereits die de facto gemeinsame Basis. Alle Systeme nutzen `ActionExecutor.executeAction()`:
|
||||
|
||||
| Consumer | Pfad zur Action Library |
|
||||
|----------|------------------------|
|
||||
| **Workspace AI Agent** | `ActionToolAdapter` registriert `dynamicMode=True` Actions als Agent-Tools → LLM wählt Tool → `ActionExecutor` |
|
||||
| **Automation2 Graph Engine** | `ActionNodeExecutor` liest `_method`/`_action` aus Node-Definition → `ActionExecutor` |
|
||||
| **Automation v1** | `WorkflowProcessor` → `modeAutomation` → `ActionExecutor` |
|
||||
| **Workspace Dynamic Mode** | `WorkflowProcessor` → `modeDynamic` → AI plant Tasks → `ActionExecutor` |
|
||||
|
||||
### 2.3 Wer nutzt die AI Tools?
|
||||
|
||||
| Tool-Typ | Registrierung | Nutzung |
|
||||
|----------|--------------|---------|
|
||||
| **Core Tools** (40: readFile, webSearch, sendMail, etc.) | `_registerCoreTools()` | Workspace Agent, CommCoach Agent |
|
||||
| **Action Tools** (dynamicMode Actions) | `ActionToolAdapter.registerAll()` | Workspace Agent (toolSet-übergreifend) |
|
||||
| **Graph Node Types** (ai.prompt, email.checkEmail, etc.) | `STATIC_NODE_TYPES` in `nodeDefinitions/` | Automation2 `executeGraph` |
|
||||
|
||||
**Benötigen die AI Tools die Method/Actions?** Ja, direkt. `ActionToolAdapter` transformiert jede `dynamicMode=True` Action in ein Agent-Tool. Und die Automation2-Nodes mappen über `_method`/`_action` auf dieselbe Library.
|
||||
|
||||
### 2.4 Identifizierte Inkonsistenzen
|
||||
|
||||
| # | Inkonsistenz |
|
||||
|---|-------------|
|
||||
| **I-1** | Zwei Datenmodelle: `AutomationDefinition` (v1) vs `Automation2Workflow` (v2) — kein gemeinsames Workflow-Konzept |
|
||||
| **I-2** | Zwei Datenbanken: `poweron_automation` vs `poweron_automation2` |
|
||||
| **I-3** | Zwei Scheduler: v1 (inkrementell, callback `automation.changed`) vs v2 (full wipe + re-register, callback `automation2.workflow.changed`) |
|
||||
| **I-4** | Zwei Execution Engines: `WorkflowManager` + `WorkflowProcessor` vs `executionEngine.executeGraph` |
|
||||
| **I-5** | Kein AI-Chat im Graphical Editor |
|
||||
| **I-6** | `WorkspaceInput` ist nicht als wiederverwendbare Komponente extrahiert |
|
||||
| **I-7** | UDB nur in Workspace und CommCoach, nicht im Graphical Editor |
|
||||
| **I-8** | AI Tools skalieren nicht: 50+ Tools ohne Gruppierungslogik, alle werden dem LLM gleichzeitig exponiert |
|
||||
|
||||
---
|
||||
|
||||
## 3. Ziel-Architektur
|
||||
|
||||
### 3.1 Vier Säulen
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────────┐
|
||||
│ POWERON UNIFIED PLATFORM │
|
||||
│ │
|
||||
│ ┌─────────────┐ ┌──────────────────────┐ ┌──────────────────┐ │
|
||||
│ │ AI Service │ │ Graphical Editor │ │ Automation │ │
|
||||
│ │ (Säule 1) │ │ (Säule 2) │ │ Scheduler │ │
|
||||
│ │ │ │ │ │ (Säule 3) │ │
|
||||
│ │ • AI Gateway │ │ • Visual Flow Builder │ │ │ │
|
||||
│ │ • Agent Loop │ │ • Node Palette │ │ • Cron Engine │ │
|
||||
│ │ • Toolboxes │ │ • AI Chat Sidebar │ │ • Event Trigger │ │
|
||||
│ │ • Sub-Agents │ │ • UDB Integration │ │ • Webhook Entry │ │
|
||||
│ │ • Streaming │ │ • Tracing/Debug Log │ │ • Run History │ │
|
||||
│ │ • Failover │ │ • Test Runner │ │ • Monitoring │ │
|
||||
│ │ • Neutraliz. │ │ • Version Management │ │ • Notifications │ │
|
||||
│ └──────┬───────┘ └──────────┬─────────────┘ └────────┬─────────┘ │
|
||||
│ │ │ │ │
|
||||
│ ┌──────▼──────────────────────▼───────────────────────────▼─────────┐ │
|
||||
│ │ SÄULE 4: UNIFIED ACTION LIBRARY (Toolboxes) │ │
|
||||
│ │ │ │
|
||||
│ │ Toolbox "ai" │ Toolbox "email" │ Toolbox "files" │ │
|
||||
│ │ ───────────── │ ────────────── │ ────────────── │ │
|
||||
│ │ ai.process │ outlook.readEmails │ context.extract │ │
|
||||
│ │ ai.webResearch │ outlook.searchEmails │ context.neutralize │ │
|
||||
│ │ ai.summarize │ outlook.draftEmail │ file.create │ │
|
||||
│ │ ai.translate │ outlook.sendDraft │ context.transform │ │
|
||||
│ │ ai.generate │ │ │ │
|
||||
│ │ │ │ │ │
|
||||
│ │ Toolbox "sharepoint"│ Toolbox "clickup" │ Toolbox "jira" │ │
|
||||
│ │ ─────────────────── │ ────────────── │ ───────────── │ │
|
||||
│ │ sharepoint.findDoc │ clickup.searchTasks │ jira.connect │ │
|
||||
│ │ sharepoint.readDocs │ clickup.createTask │ jira.exportTickets │ │
|
||||
│ │ sharepoint.upload │ clickup.updateTask │ jira.importTickets │ │
|
||||
│ │ ... │ ... │ ... │ │
|
||||
│ │ │ │
|
||||
│ │ Toolbox "trustee" │ Toolbox "chatbot" │ [future toolboxes] │ │
|
||||
│ │ ────────────────── │ ────────────── │ │ │
|
||||
│ │ trustee.extract │ chatbot.queryDB │ slack.sendMessage │ │
|
||||
│ │ trustee.process │ │ teams.postChannel │ │
|
||||
│ │ trustee.syncAcctng │ │ google.readDrive │ │
|
||||
│ └───────────────────────────────────────────────────────────────────┘ │
|
||||
└──────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 3.2 Säule 1: AI Service (besteht, zu erweitern um Toolbox-Architektur)
|
||||
|
||||
**Status:** Kern bereit (`serviceAi` + `serviceAgent` + `aicore`).
|
||||
|
||||
**Erweiterung: Toolbox-Architektur für 50–100+ Tools** → siehe Abschnitt 5.
|
||||
|
||||
### 3.3 Säule 2: Graphical Editor (Feature "graphicalEditor")
|
||||
|
||||
**Status:** Basis vorhanden (Automation2FlowEditor), zu erweitern.
|
||||
|
||||
**Funktionalität:**
|
||||
1. **Visual Flow Builder** (besteht): Canvas, Nodes, Connections, Branching, Loops
|
||||
2. **Node Palette** (besteht): Abgeleitet aus Toolbox-Registry — jede Toolbox-Action kann als Node exponiert werden
|
||||
3. **AI Chat Sidebar** (neu): Agent mit Toolbox `"workflow"` für Graph-Manipulation
|
||||
4. **UDB Integration** (neu): UnifiedDataBar im Editor
|
||||
5. **Tracing/Debug Log** (zu erweitern): RunStepLog mit Input-Snapshot, Duration, Error pro Node
|
||||
6. **Test Runner** (zu erweitern): Visual Highlighting, Step-by-Step, AI-assisted Debugging
|
||||
7. **Version Management** (neu): Draft/Published Lifecycle
|
||||
|
||||
### 3.4 Säule 3: Automation Scheduler (zu konsolidieren)
|
||||
|
||||
**Status:** v1-Scheduling hat bewährte Patterns, die direkt in v2 übernommen werden.
|
||||
|
||||
**Von v1 übernehmen:**
|
||||
- Inkrementeller Sync (nur geänderte Jobs registrieren/entfernen, nicht full wipe)
|
||||
- Persistierter Job-Handle (`eventId`) auf dem Workflow für Debugging
|
||||
- `automation.changed` Callback-Pattern für reaktive Synchronisation
|
||||
- Handler lädt Workflow neu und prüft `active` vor Execution
|
||||
- Execution-Logs als Audit Trail (capped auf 50 Einträge)
|
||||
- Creator-User Tracking via `sysCreatedBy` für Execution-Kontext
|
||||
|
||||
**Von v2 beibehalten:**
|
||||
- `IntervalTrigger` für einfache Wiederholungen
|
||||
- Main-Loop-Bridging (`call_soon_threadsafe`) für Thread-Safety
|
||||
- Delayed Startup Sync (5s) für DB-Readiness
|
||||
- Striktes Cron-Parsing (5/6 Felder)
|
||||
|
||||
### 3.5 Säule 4: Unified Action Library mit Toolboxes
|
||||
|
||||
**Status:** Infrastruktur besteht (`workflows/methods/` + `methodDiscovery` + `ActionExecutor`). Zu erweitern um Toolbox-Konzept.
|
||||
|
||||
→ Detailliert in Abschnitt 5.
|
||||
|
||||
---
|
||||
|
||||
## 4. Mandant/Feature/RBAC-Architektur
|
||||
|
||||
### 4.1 Hierarchie
|
||||
|
||||
```
|
||||
System (PowerOn Platform)
|
||||
│
|
||||
├── System Template Rollen (isSystemRole=true, mandateId=null, featureCode=null)
|
||||
│ "admin", "user", "viewer"
|
||||
│ → kopiert bei Mandant-Erstellung via copySystemRolesToMandate()
|
||||
│
|
||||
├── Feature Template Rollen (isSystemRole=false, mandateId=null, featureCode=X)
|
||||
│ "workspace-admin", "workspace-user", "workspace-viewer"
|
||||
│ "graphicalEditor-admin", "graphicalEditor-user", ...
|
||||
│ → kopiert bei Feature-Instanz-Erstellung via _copyTemplateRoles()
|
||||
│
|
||||
├── Mandant (Tenant)
|
||||
│ ├── Mandate-Rollen (kopiert von System Templates)
|
||||
│ │ mandateId=X, featureInstanceId=null, featureCode=null
|
||||
│ │ "admin", "user", "viewer"
|
||||
│ │ └── UserMandateRole ← UserMandate ← User
|
||||
│ │
|
||||
│ ├── Feature-Instanz A (z.B. "AI Workspace Produktion")
|
||||
│ │ ├── Feature-Instanz-Rollen (kopiert von Feature Templates)
|
||||
│ │ │ mandateId=X, featureInstanceId=Y, featureCode="workspace"
|
||||
│ │ │ "workspace-admin", "workspace-user", "workspace-viewer"
|
||||
│ │ │ └── FeatureAccessRole ← FeatureAccess ← User
|
||||
│ │ └── Daten (Workflows, Files, Chats, ...)
|
||||
│ │
|
||||
│ ├── Feature-Instanz B (z.B. "Graphical Editor Dev")
|
||||
│ │ ├── Feature-Instanz-Rollen (kopiert von Feature Templates)
|
||||
│ │ │ mandateId=X, featureInstanceId=Z, featureCode="graphicalEditor"
|
||||
│ │ │ "graphicalEditor-admin", "graphicalEditor-user", ...
|
||||
│ │ └── Daten (Workflows, Runs, Tasks, ...)
|
||||
│ │
|
||||
│ └── Feature-Instanz C (z.B. "Trustee Buchhaltung")
|
||||
│ ├── Feature-Instanz-Rollen
|
||||
│ └── Daten (Positionen, Belege, ...)
|
||||
│
|
||||
└── Feature Catalog (Global)
|
||||
├── Feature "workspace" (code, label, icon)
|
||||
├── Feature "graphicalEditor" (code, label, icon)
|
||||
├── Feature "trustee" (code, label, icon)
|
||||
└── ...
|
||||
```
|
||||
|
||||
### 4.2 Zwei getrennte Template-Rollen-Systeme
|
||||
|
||||
Es gibt **zwei getrennte** Template-Systeme — diese sind **nicht** dieselben:
|
||||
|
||||
**System Template Rollen (für Mandanten)**
|
||||
|
||||
| Feld | Wert |
|
||||
|------|------|
|
||||
| `isSystemRole` | `true` |
|
||||
| `mandateId` | `null` |
|
||||
| `featureInstanceId` | `null` |
|
||||
| `featureCode` | `null` |
|
||||
| `roleLabel` | `"admin"`, `"user"`, `"viewer"` |
|
||||
| Kopier-Mechanismus | `copySystemRolesToMandate()` in `interfaceBootstrap.py` |
|
||||
| Ausgelöst bei | Mandant-Erstellung |
|
||||
| Ziel-Rolle | `mandateId=X`, `featureInstanceId=null`, `isSystemRole=false` + kopierte AccessRules |
|
||||
|
||||
**Feature Template Rollen (für Feature-Instanzen)**
|
||||
|
||||
| Feld | Wert |
|
||||
|------|------|
|
||||
| `isSystemRole` | `false` |
|
||||
| `mandateId` | `null` |
|
||||
| `featureInstanceId` | `null` |
|
||||
| `featureCode` | z.B. `"workspace"`, `"automation2"` |
|
||||
| `roleLabel` | z.B. `"workspace-admin"`, `"workspace-user"`, `"workspace-viewer"` |
|
||||
| Kopier-Mechanismus | `_copyTemplateRoles()` in `interfaceFeatures.py` |
|
||||
| Ausgelöst bei | Feature-Instanz-Erstellung |
|
||||
| Ziel-Rolle | `mandateId=X`, `featureInstanceId=Y`, `isSystemRole=false` + kopierte AccessRules |
|
||||
|
||||
**Wichtig:** Templates werden **nur bei Erstellung** kopiert. Spätere Änderungen werden **nicht** automatisch propagiert. (Ausnahme: `_syncTemplateRolesToDb()` synct neue UI-Objekte auf bestehende Mandate-Rollen mit gleichem `roleLabel`.)
|
||||
|
||||
### 4.3 RBAC-Resolution (Priority-System)
|
||||
|
||||
| Rollen-Typ | Scope | RBAC Priority |
|
||||
|------------|-------|---------------|
|
||||
| **Global/Template** | `mandateId=null, featureInstanceId=null` | 1 (niedrigste) |
|
||||
| **Mandate-Rolle** | `mandateId=X, featureInstanceId=null` | 2 |
|
||||
| **Instanz-Rolle** | `mandateId=X, featureInstanceId=Y` | 3 (höchste) |
|
||||
|
||||
**Resolution:**
|
||||
1. Lade Mandate-Rollen (via `UserMandate` → `UserMandateRole`)
|
||||
2. Lade Feature-Instanz-Rollen (via `FeatureAccess` → `FeatureAccessRole`)
|
||||
3. Lade `AccessRules` für alle gefundenen Rollen
|
||||
4. **Höchste Priorität gewinnt** bei DATA-Permissions (Instanz schlägt Mandant schlägt Global)
|
||||
5. **View wird OR-verknüpft** über alle Rollen der höchsten Prioritätsstufe
|
||||
6. **Item-Spezifität** innerhalb einer Stufe: exact > prefix > generic
|
||||
|
||||
### 4.3 AccessRule-Kontexte
|
||||
|
||||
| Kontext | Item-Beispiel | Prüft |
|
||||
|---------|---------------|-------|
|
||||
| `UI` | `ui.feature.graphicalEditor.editor` | Seitenleiste, Navigation, Buttons |
|
||||
| `RESOURCE` | `resource.feature.graphicalEditor.execute` | Ausführungsberechtigungen |
|
||||
| `DATA` | Auto-generiert aus Tabellenname + featureCode | CRUD auf Datenbank-Ebene |
|
||||
|
||||
### 4.4 Feature-Container-Pattern
|
||||
|
||||
Jedes Feature folgt einem einheitlichen Container-Pattern:
|
||||
|
||||
```
|
||||
features/<featureName>/
|
||||
├── main<FeatureName>.py ◄── Feature-Definition
|
||||
│ ├── FEATURE_CODE = "<featureName>"
|
||||
│ ├── UI_OBJECTS = [...] ◄── RBAC UI-Objekte
|
||||
│ ├── RESOURCE_OBJECTS = [...] ◄── RBAC Resource-Objekte
|
||||
│ ├── TEMPLATE_ROLES = [...] ◄── Rollen-Templates mit AccessRules
|
||||
│ ├── REQUIRED_SERVICES = [...] ◄── Service-Dependencies
|
||||
│ ├── registerFeature(catalog) ◄── Registriert Objekte + synct Rollen
|
||||
│ └── getFeatureDefinition() ◄── Katalog-Metadaten
|
||||
│
|
||||
├── routeFeature<FeatureName>.py ◄── FastAPI Router (HTTP)
|
||||
│ └── router = APIRouter(prefix="/api/<featureName>")
|
||||
│
|
||||
├── interfaceFeature<FeatureName>.py ◄── DB-Interface (CRUD + RBAC)
|
||||
│ └── getInterface(user, mandateId, featureInstanceId)
|
||||
│
|
||||
├── datamodelFeature<FeatureName>.py ◄── Pydantic-Modelle
|
||||
│
|
||||
└── [optional] service<Name>/ ◄── Feature-spezifische Services
|
||||
└── mainService<Name>.py
|
||||
```
|
||||
|
||||
**Automatische Discovery:** `registry.py` scannt `features/*/routeFeature*.py` und `features/*/main*.py`. Routers werden automatisch gemountet, Features automatisch im Katalog registriert. Kein manuelles Wiring nötig.
|
||||
|
||||
---
|
||||
|
||||
## 5. AI-Tool-Architektur: Toolboxes und Sub-Agents
|
||||
|
||||
### 5.1 Problem: Tool-Explosion
|
||||
|
||||
Aktuell werden ~50 Tools dem LLM in einem einzigen Prompt exponiert. Bei 100+ Tools:
|
||||
- LLM-Context wird überladen → schlechtere Tool-Auswahl
|
||||
- Irrelevante Tools für den aktuellen Kontext → Confusion und Halluzinationen
|
||||
- Keine Feature-isolierte Tool-Bereitstellung
|
||||
|
||||
### 5.2 Konzept: Toolboxes
|
||||
|
||||
Eine **Toolbox** ist eine thematisch gebündelte Gruppe von Tools mit einem klaren Scope. Toolboxes ersetzen das bisherige flache `_CORE_ONLY_TOOLS`-Set.
|
||||
|
||||
```
|
||||
Toolbox-Registry
|
||||
│
|
||||
├── Toolbox "core" (immer verfügbar)
|
||||
│ ├── readFile, listFiles, searchInFileContent
|
||||
│ ├── writeFile, deleteFile, renameFile, copyFile
|
||||
│ ├── listFolders, createFolder, deleteFolder
|
||||
│ ├── webSearch, readUrl
|
||||
│ └── detectLanguage, translateText
|
||||
│
|
||||
├── Toolbox "ai" (AI-Operationen)
|
||||
│ ├── summarizeContent, describeImage
|
||||
│ ├── generateImage, textToSpeech, speechToText
|
||||
│ ├── renderDocument, createChart, executeCode
|
||||
│ └── neutralizeData
|
||||
│
|
||||
├── Toolbox "datasources" (Externe Verbindungen)
|
||||
│ ├── listConnections, browseDataSource, searchDataSource
|
||||
│ ├── downloadFromDataSource, uploadToExternal
|
||||
│ └── browseContainer, readContentObjects, extractContainerItem
|
||||
│
|
||||
├── Toolbox "email" (Outlook-Integration)
|
||||
│ ├── sendMail
|
||||
│ └── outlook.readEmails, outlook.searchEmails, outlook.draftEmail
|
||||
│
|
||||
├── Toolbox "sharepoint" (SharePoint-Integration)
|
||||
│ └── sharepoint.findDoc, sharepoint.read, sharepoint.upload, ...
|
||||
│
|
||||
├── Toolbox "clickup" (ClickUp-Integration)
|
||||
│ └── clickup.searchTasks, clickup.createTask, ...
|
||||
│
|
||||
├── Toolbox "jira" (Jira-Integration)
|
||||
│ └── jira.connect, jira.exportTickets, ...
|
||||
│
|
||||
├── Toolbox "workflow" (Graph-Manipulation für Editor-Chat)
|
||||
│ ├── readWorkflowGraph, addNode, removeNode
|
||||
│ ├── connectNodes, setNodeParameter
|
||||
│ ├── listAvailableNodeTypes, validateGraph
|
||||
│ └── listWorkflowHistory, readWorkflowMessages
|
||||
│
|
||||
├── Toolbox "trustee" (Feature-spezifisch)
|
||||
│ └── trustee.extract, trustee.process, trustee.syncAccounting
|
||||
│
|
||||
└── Toolbox "chatbot" (Feature-spezifisch)
|
||||
└── chatbot.queryDatabase
|
||||
```
|
||||
|
||||
### 5.3 Toolbox-Bereitstellung: Initiales Set + dynamische Eskalation
|
||||
|
||||
**Kern-Idee:** Der Agent startet mit einem kompakten, eingeschränkten Tool-Set. Er kennt aber den **Katalog aller verfügbaren Toolboxes**. Wenn er für eine Aufgabe Spezial-Tools braucht, kann er eine Toolbox **anfordern** — und bekommt sie in der nächsten Runde bereitgestellt.
|
||||
|
||||
**Warum nicht alle Tools sofort?**
|
||||
- Bei 50–100+ Tools wird der LLM-Context überladen → schlechtere Tool-Auswahl
|
||||
- Irrelevante Tools führen zu Confusion und Halluzinationen
|
||||
- Die meisten Interaktionen brauchen nur 10–15 Core-Tools
|
||||
|
||||
**Mechanismus: `requestToolbox` Meta-Tool**
|
||||
|
||||
```
|
||||
Runde 1:
|
||||
Tools: core (readFile, webSearch, ...) + requestToolbox
|
||||
System-Prompt enthält: "Verfügbare Toolboxes: email, sharepoint, clickup,
|
||||
jira, workflow, ai, datasources, trustee, ..."
|
||||
|
||||
User: "Lies meine letzten Emails und fasse sie zusammen"
|
||||
LLM: → requestToolbox("email") [braucht Email-Tools]
|
||||
|
||||
Runde 2:
|
||||
Tools: core + email (sendMail, outlook_readEmails, outlook_searchEmails, ...)
|
||||
+ requestToolbox
|
||||
|
||||
LLM: → outlook_readEmails(connectionRef, folder="Inbox", limit=10)
|
||||
|
||||
Runde 3:
|
||||
LLM: → (Text-Antwort mit Zusammenfassung)
|
||||
→ COMPLETED
|
||||
```
|
||||
|
||||
**`requestToolbox` Tool-Definition:**
|
||||
|
||||
```python
|
||||
{
|
||||
"name": "requestToolbox",
|
||||
"description": "Request additional specialized tools for the current task. "
|
||||
"Call this when you need capabilities beyond your current tools.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"toolboxId": {
|
||||
"type": "string",
|
||||
"description": "ID of the toolbox to activate",
|
||||
"enum": [...] # Dynamisch: nur verfügbare Toolboxes
|
||||
},
|
||||
"reason": {
|
||||
"type": "string",
|
||||
"description": "Why you need this toolbox"
|
||||
}
|
||||
},
|
||||
"required": ["toolboxId"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**AgentConfig:**
|
||||
|
||||
```python
|
||||
class AgentConfig(BaseModel):
|
||||
maxRounds: int = 25
|
||||
maxCostCHF: Optional[float] = None
|
||||
initialToolboxes: List[str] = ["core"] # Initiales Set
|
||||
availableToolboxes: List[str] = [] # Was angefordert werden darf
|
||||
temperature: Optional[float] = None
|
||||
```
|
||||
|
||||
**Konfiguration pro Feature:**
|
||||
|
||||
| Feature | `initialToolboxes` | `availableToolboxes` (anforderbar) |
|
||||
|---------|--------------------|------------------------------------|
|
||||
| **Workspace** | `["core"]` | `["ai", "datasources", "email", "sharepoint", "clickup", "jira", "workflow"]` — gefiltert nach User-Connections |
|
||||
| **Graphical Editor Chat** | `["core", "workflow"]` | `["ai"]` |
|
||||
| **CommCoach** | `["core"]` | `[]` (keine Eskalation erlaubt) |
|
||||
| **Chatbot** | `["core"]` | `[]` |
|
||||
|
||||
**Vorteile:**
|
||||
- **Lean Context:** Die meisten Chats brauchen nur Core-Tools → schnellere, präzisere Antworten
|
||||
- **Selbst-navigierend:** LLM entscheidet selbst, wann Spezial-Tools nötig sind
|
||||
- **Kontrolliert:** `availableToolboxes` limitiert, was der Agent anfordern darf (RBAC)
|
||||
- **Skaliert:** 100+ Tools kein Problem — der LLM sieht immer nur was er gerade braucht
|
||||
- **Transparent:** `requestToolbox("email", reason="User fragt nach Emails")` ist nachvollziehbar im Tracing
|
||||
|
||||
**Automatische Toolbox-Verfügbarkeit basierend auf Connections:**
|
||||
Welche Toolboxes in `availableToolboxes` erscheinen, hängt von den aktiven User-Connections ab. Hat der User keine Outlook-Verbindung, erscheint `"email"` gar nicht in der Liste — der Agent kann sie auch nicht anfordern.
|
||||
|
||||
### 5.4 Sub-Agents (Feature Data Agents)
|
||||
|
||||
Für datenintensive Features mit eigenem Schema (z.B. Trustee Buchhaltung) existiert bereits das Sub-Agent-Pattern:
|
||||
|
||||
```
|
||||
User Prompt im Workspace
|
||||
│
|
||||
▼
|
||||
Main Agent (Toolboxes: core, ai, datasources, ...)
|
||||
│
|
||||
├── Tool: queryFeatureInstance("trustee", instanceId, question)
|
||||
│ │
|
||||
│ ▼
|
||||
│ Sub-Agent (Feature Data Agent)
|
||||
│ Eigene ToolRegistry: browseTable, queryTable
|
||||
│ Eigenes System-Prompt mit Schema-Wissen
|
||||
│ Limitiert: maxRounds=5, maxCost=0.10 CHF
|
||||
│ │
|
||||
│ ▼
|
||||
│ Antwort zurück an Main Agent
|
||||
│
|
||||
├── Tool: readFile(fileId)
|
||||
│ (Core Tool, direkt)
|
||||
│
|
||||
└── Tool: outlook_readEmails(connectionRef)
|
||||
(Action Tool, direkt)
|
||||
```
|
||||
|
||||
**Empfehlung: Sub-Agent-Pattern ausweiten auf:**
|
||||
- **Trustee** (besteht): browseTable, queryTable auf Buchhaltungsdaten
|
||||
- **Workflow** (neu): Graph-Manipulation als Sub-Agent im Editor-Chat
|
||||
- **Future Features** mit eigenem Datenmodell: Gleicher Pattern
|
||||
|
||||
**Vorteil:** Der Main Agent bleibt schlank. Feature-spezifisches Datenwissen ist im Sub-Agent gekapselt. Der LLM des Main Agents sieht nur ein einzelnes Tool (`queryFeatureInstance`) statt 10+ tabellenspezifische Tools.
|
||||
|
||||
### 5.5 State Machine: AI Agent Run (mit Toolbox-Eskalation)
|
||||
|
||||
```
|
||||
┌──────────┐
|
||||
│ IDLE │
|
||||
└────┬─────┘
|
||||
│ runAgent(prompt, initialToolboxes, availableToolboxes)
|
||||
▼
|
||||
┌──────────┐
|
||||
┌────►│ THINKING │◄──────────────────────────────────┐
|
||||
│ └────┬─────┘ │
|
||||
│ │ │
|
||||
│ ├── LLM Response (Text only) ──────────────┼──► COMPLETED
|
||||
│ │ │
|
||||
│ └── LLM Response (ToolCalls) │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ ┌────────────────────┐ │
|
||||
│ │ EXECUTING_TOOLS │ │
|
||||
│ └────┬───────────────┘ │
|
||||
│ │ │
|
||||
│ ├── requestToolbox(id) ───┐ │
|
||||
│ │ ▼ │
|
||||
│ │ ┌─────────────────────┐ │
|
||||
│ │ │ TOOLBOX_ESCALATION │ │
|
||||
│ │ │ Toolbox wird aktiviert│ │
|
||||
│ │ │ Tools der nächsten │ │
|
||||
│ │ │ Runde erweitert │ │
|
||||
│ │ └──────────┬──────────┘ │
|
||||
│ │ │ │
|
||||
│ ├── Sub-Agent call ───┐ │ │
|
||||
│ │ ▼ │ │
|
||||
│ │ ┌──────────────┐│ │
|
||||
│ │ │SUB_AGENT_CALL ││ │
|
||||
│ │ └──────┬───────┘│ │
|
||||
│ │ │ │ │
|
||||
│ ├── Regular tools ─┼────────┼────────────────┘
|
||||
│ │ (results → next round)
|
||||
│
|
||||
│ maxRounds / maxCost reached
|
||||
▼
|
||||
┌──────────┐
|
||||
│ COMPLETED │ (oder FAILED / CANCELLED)
|
||||
└──────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Use Cases
|
||||
|
||||
### UC-1: Datenquellen einbinden → besteht, keine Änderung
|
||||
|
||||
### UC-2: AI Workspace mit UDB → besteht, keine Änderung
|
||||
|
||||
### UC-3: Workflow im Workspace erstellen/bearbeiten
|
||||
|
||||
**Beschreibung:** User beschreibt im Workspace-Chat einen gewünschten Workflow → Agent nutzt Toolbox `"workflow"` → generiert Graph → speichert als Draft → User öffnet im Graphical Editor.
|
||||
|
||||
**Voraussetzung:** Die `"workflow"` Toolbox muss im Workspace aktivierbar sein. Die Tools validieren jeden Graph-Mutationsschritt:
|
||||
- `addNode(type, parameters)` → prüft: existiert der Node-Typ? Sind die Parameter valide?
|
||||
- `connectNodes(source, target)` → prüft: sind die I/O-Typen kompatibel? Keine Zyklen?
|
||||
- `validateGraph()` → vollständige Validierung inkl. Trigger-Prüfung
|
||||
|
||||
### UC-4: AI Chat im Graphical Editor
|
||||
|
||||
**Beschreibung:** Der Graphical Editor hat eine Sidebar mit Chat. Der Agent nutzt Toolbox `"workflow"` mit speziellem System-Prompt, der den aktuellen Graph kennt. Er kann Nodes hinzufügen, entfernen, umkonfigurieren und den Graph erklären.
|
||||
|
||||
**Architektur:** Gleicher `serviceAgent.runAgent()` Pfad wie Workspace, aber:
|
||||
- Anderes ToolSet: `toolboxes: ["core", "workflow"]`
|
||||
- System-Prompt enthält den aktuellen Graph als Kontext
|
||||
- Frontend: Extrahierte `ChatBar`-Komponente + `ChatStream`
|
||||
|
||||
### UC-5: Workflow testen mit Tracing Log
|
||||
|
||||
**Beschreibung:** User klickt "Test Run" → Graph wird ausgeführt → jeder Node wird visuell hervorgehoben (Status-Farben) → Tracing Log zeigt pro Node: Input, Output, Dauer, Fehler.
|
||||
|
||||
**Bei Fehlern:** User bespricht den Fehler im AI Chat → Agent liest den RunStepLog und schlägt Korrekturen vor.
|
||||
|
||||
**Technisch:**
|
||||
- `RunStepLog` pro Node mit Timestamps, Input-Snapshot, Duration, Error-Stack
|
||||
- SSE/WebSocket für Live-Updates des Node-Status
|
||||
- Run-Modes: "Full Run" und "Step-by-Step" (Pause nach jedem Node)
|
||||
|
||||
### UC-6: Workflow automatisieren (Scheduler)
|
||||
|
||||
**Beschreibung:** User konfiguriert Schedule im Editor (via trigger.schedule Node oder Invocation) → konsolidierter Scheduler registriert Cron-Job → Run-History wird persistiert → bei Fehlern Notifications.
|
||||
|
||||
---
|
||||
|
||||
## 7. State Machines
|
||||
|
||||
### 7.1 Workflow Lifecycle
|
||||
|
||||
```
|
||||
┌───────┐
|
||||
create ──►│ DRAFT │◄──── edit (neuer graph)
|
||||
└───┬───┘
|
||||
│ publish
|
||||
▼
|
||||
┌───────────┐
|
||||
unpublish│ PUBLISHED │◄──── edit → erzeugt neuen Draft,
|
||||
│ └───────────┘ Published bleibt aktiv
|
||||
│ │
|
||||
│ │ archive (manuell oder nach Deaktivierung)
|
||||
│ ▼
|
||||
│ ┌───────────┐
|
||||
│ │ ARCHIVED │
|
||||
│ └───────────┘
|
||||
│ │
|
||||
└───────┘ re-publish (aus Archiv zurückholen)
|
||||
```
|
||||
|
||||
**Regeln:**
|
||||
- Ein Workflow hat genau **eine** published Version (oder keine)
|
||||
- Scheduled Runs nutzen immer die published Version
|
||||
- Edit erzeugt immer einen neuen Draft — die published Version bleibt stabil
|
||||
- Archive ist reversibel
|
||||
|
||||
### 7.2 WorkflowRun Lifecycle
|
||||
|
||||
```
|
||||
┌─────────┐
|
||||
executeGraph──►│ RUNNING │
|
||||
└────┬────┘
|
||||
│
|
||||
┌────────────────┼────────────────┐
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌─────────┐ ┌──────────┐ ┌────────┐
|
||||
│ PAUSED │ │COMPLETED │ │ FAILED │
|
||||
└────┬────┘ └──────────┘ └────────┘
|
||||
│
|
||||
│ resume (human task complete / email received)
|
||||
│
|
||||
▼
|
||||
┌─────────┐
|
||||
│ RUNNING │ (Weiter ab pausiertem Node)
|
||||
└────┬────┘
|
||||
│
|
||||
├──► COMPLETED
|
||||
├──► FAILED
|
||||
├──► PAUSED (nächster Input-Node)
|
||||
└──► CANCELLED (manuell abgebrochen)
|
||||
```
|
||||
|
||||
**Pause-Gründe:**
|
||||
- `input.*` Node → HumanTask erstellt → wartet auf User-Eingabe
|
||||
- `email.checkEmail` → wartet auf neue Email (Background Poller)
|
||||
|
||||
### 7.3 HumanTask Lifecycle
|
||||
|
||||
```
|
||||
┌─────────┐
|
||||
│ PENDING │ (erstellt bei Pause)
|
||||
└────┬────┘
|
||||
│
|
||||
├── User completes → COMPLETED (Run wird resumed)
|
||||
├── User cancels → CANCELLED (Run wird cancelled)
|
||||
└── Timeout → EXPIRED (Run failsafe: cancel oder default)
|
||||
```
|
||||
|
||||
### 7.4 Scheduler Job Lifecycle
|
||||
|
||||
```
|
||||
┌─────────────┐
|
||||
│ UNREGISTERED │ (Workflow exists, kein Schedule)
|
||||
└──────┬──────┘
|
||||
│ activate + publish mit schedule invocation
|
||||
▼
|
||||
┌─────────────┐
|
||||
│ REGISTERED │ (APScheduler Job aktiv, eventId gesetzt)
|
||||
└──────┬──────┘
|
||||
│
|
||||
├── Cron fires → executeGraph → WorkflowRun
|
||||
├── Workflow deactivated → UNREGISTERED (Job entfernt, eventId=null)
|
||||
├── Schedule geändert → Job replaced (replaceExisting=true)
|
||||
└── Workflow gelöscht → UNREGISTERED (Job entfernt)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Empfehlungen und offene Punkte
|
||||
|
||||
### 8.1 Starke Konzepte (umsetzen)
|
||||
|
||||
| Konzept | Bewertung |
|
||||
|---------|-----------|
|
||||
| **Toolbox-Architektur** | Skaliert von 50 auf 100+ Tools ohne LLM-Überlastung. Klare Isolierung pro Thema. |
|
||||
| **Sub-Agents pro Feature** | Bewährtes Pattern (Trustee), erweiterbar. Hält Main Agent schlank. |
|
||||
| **Validierungs-Layer für Workflow-Modellierung** | Graph-Mutation nur über validierte Tools. LLM kann keinen inkonsistenten Graph erzeugen. |
|
||||
| **v1-Scheduling-Patterns in v2 übernehmen** | Inkrementeller Sync ist robuster als Full-Wipe. Execution-Logs sind essentiell für Audit. |
|
||||
| **State Machines als Grundlage** | Klare Zustände, klare Übergänge. Erweiterbar ohne Breaking Changes. |
|
||||
|
||||
### 8.2 Was User brauchen, um das als zentrales Arbeitsinstrument zu nutzen
|
||||
|
||||
| User Need | Feature | Priorität |
|
||||
|-----------|---------|-----------|
|
||||
| **Schneller Einstieg** | Workflow-Templates für häufige Use Cases (z.B. "Email-Attachment → AI Analyse → SharePoint Upload") | Hoch |
|
||||
| **Vertrauen** | Tracing Log + Test Runner — User muss sehen, was der Workflow tut | Hoch |
|
||||
| **Effizienz** | AI-gestützte Workflow-Erstellung per Chat | Hoch |
|
||||
| **Fehlertoleranz** | Retry-Policies, Pause/Resume, klare Fehlermeldungen | Hoch |
|
||||
| **Kontext** | UDB im Editor — Zugriff auf Files/Sources bei der Konfiguration | Hoch |
|
||||
| **Zusammenarbeit** | Workflows teilen im Mandant, Rollen-basierter Zugriff | Mittel |
|
||||
| **Monitoring** | Dashboard: laufende Automationen, nächste Runs, Fehlerrate, Kosten | Mittel |
|
||||
| **Flexibilität** | Custom Script Nodes (Python-Sandbox), AI Decision Nodes | Mittel |
|
||||
| **Mobile** | Notifications + Approval-Tasks per Mobile | Phase 2 |
|
||||
|
||||
### 8.3 Offene Architektur-Fragen
|
||||
|
||||
| Frage | Empfehlung |
|
||||
|-------|------------|
|
||||
| **Toolbox-Selektion: statisch oder dynamisch?** | Hybrid: Basis-Toolboxes pro Feature statisch konfiguriert, Connection-basierte Toolboxes dynamisch aktiviert. |
|
||||
| **Sub-Agent Tiefe: verschachtelt?** | Maximal 1 Level tief. Main Agent → Sub-Agent. Kein Sub-Sub-Agent (zu komplex, unkontrollierbar). |
|
||||
| **Graph-Execution und WorkflowManager: zusammenführen?** | Nein, getrennt lassen. `executeGraph` für den Graphical Editor, `WorkflowManager` bleibt für den dynamischen Workspace-Modus. Verschiedene Orchestrierungsmodelle. |
|
||||
| **Feature-Code: `automation2` → `graphicalEditor`?** | Ja, Rename in Phase 1. Klarer Name für User und Entwickler. |
|
||||
|
||||
---
|
||||
|
||||
## 9. Phasen-Plan
|
||||
|
||||
### Phase 1: Foundation
|
||||
- Unified Workflow Datenmodell (Workflow + WorkflowVersion + WorkflowRun + RunStepLog + HumanTask)
|
||||
- Toolbox-Registry implementieren (Ablösung `_CORE_ONLY_TOOLS`)
|
||||
- ChatBar-Komponente extrahieren
|
||||
- Feature Rename: `automation2` → `graphicalEditor`
|
||||
- Scheduler konsolidieren (v1 Patterns in v2 Engine)
|
||||
|
||||
### Phase 2: Editor Enhancement
|
||||
- AI Chat Sidebar im Editor (Toolbox `"workflow"` + Graph-Manipulation-Tools)
|
||||
- UDB Integration im Editor
|
||||
- WorkflowVersion Lifecycle (Draft/Published)
|
||||
- Enhanced RunStepLog + Visual Tracing
|
||||
|
||||
### Phase 3: Productization
|
||||
- Workflow Templates / Marketplace
|
||||
- Monitoring Dashboard
|
||||
- Retry Policies pro Node
|
||||
- Notifications bei Scheduler-Fehlern
|
||||
- Automation v1 Feature entfernen (wird nicht mehr benötigt)
|
||||
|
||||
### Phase 4: Advanced
|
||||
- AI Decision Node (`ai.decide`)
|
||||
- Custom Script Node (Python Sandbox)
|
||||
- Dynamische Toolbox-Aktivierung basierend auf Connections
|
||||
- Sub-Agent Pattern für weitere Features
|
||||
|
||||
---
|
||||
|
||||
## 10. Glossar
|
||||
|
||||
| Begriff | Definition |
|
||||
|---------|-----------|
|
||||
| **Toolbox** | Thematisch gebündelte Gruppe von AI-Tools, die kontextabhängig aktiviert werden |
|
||||
| **Sub-Agent** | Spezialisierter Mini-Agent mit eigener Tool-Registry, aufgerufen vom Main Agent |
|
||||
| **Workflow** | Persistiertes Automatisierungsmodell mit Graph-Struktur |
|
||||
| **WorkflowVersion** | Immutable Snapshot eines Workflow-Graphen (draft, published, archived) |
|
||||
| **WorkflowRun** | Einzelne Ausführung einer WorkflowVersion |
|
||||
| **RunStepLog** | Detaillierter Log pro Node-Execution innerhalb eines Runs |
|
||||
| **HumanTask** | Aufgabe für menschliche Eingabe, erstellt bei Pause eines Runs |
|
||||
| **Node** | Ausführungsschritt im Graph (Trigger, Action, Flow-Control, Input/Human) |
|
||||
| **Method** | Integrations-Kategorie (z.B. methodOutlook) mit mehreren Actions |
|
||||
| **Action** | Spezifische Operation innerhalb einer Method (z.B. outlook.readEmails) |
|
||||
| **Invocation** | Entry-Point für einen Workflow (Manual, Schedule, Webhook, Form, Event) |
|
||||
| **UDB** | Unified Data Bar — Multi-Tab-Panel für Chats, Files und Sources |
|
||||
| **ChatBar** | Wiederverwendbare Prompt-Input-Komponente |
|
||||
| **Feature Container** | Einheitliches Verzeichnis-Pattern für ein Feature (main, route, interface, datamodel) |
|
||||
994
concepts/Automation Data Model.md
Normal file
994
concepts/Automation Data Model.md
Normal file
|
|
@ -0,0 +1,994 @@
|
|||
# PowerOn Automation Unification — Datenmodell & Architektur
|
||||
|
||||
**Version:** 2.0
|
||||
**Datum:** 2026-04-05
|
||||
**Referenz:** [Automation Business Spec.md](./Automation%20Business%20Spec.md)
|
||||
|
||||
---
|
||||
|
||||
## 1. Plattform-Architektur: Schichten
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────┐
|
||||
│ FRONTEND (React/TS) │
|
||||
│ │
|
||||
│ Shared Components: ChatBar, ChatStream, UnifiedDataBar, │
|
||||
│ FlowEditor, FormGenerator, FolderTree │
|
||||
│ │
|
||||
│ Feature Pages: WorkspacePage, GraphicalEditorPage, │
|
||||
│ CommcoachDossierView, ChatbotView, ... │
|
||||
└──────────────────────────────┬────────────────────────────────────┘
|
||||
│ HTTP/SSE/WebSocket
|
||||
┌──────────────────────────────▼────────────────────────────────────┐
|
||||
│ GATEWAY (FastAPI) │
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────────────────────┐ │
|
||||
│ │ Layer 1: FEATURES (Feature-Instanz-gebunden) │ │
|
||||
│ │ │ │
|
||||
│ │ features/workspace/ features/graphicalEditor/ │ │
|
||||
│ │ features/commcoach/ features/chatbot/ │ │
|
||||
│ │ features/trustee/ features/realEstate/ │ │
|
||||
│ │ features/neutralization/ features/teamsbot/ │ │
|
||||
│ └──────────────────────────┬──────────────────────────────────┘ │
|
||||
│ │ nutzt │
|
||||
│ ┌──────────────────────────▼──────────────────────────────────┐ │
|
||||
│ │ Layer 2: SERVICES (Feature-unabhängig, pro Request) │ │
|
||||
│ │ │ │
|
||||
│ │ serviceAgent serviceAi serviceKnowledge serviceBilling │ │
|
||||
│ │ serviceChat serviceExtraction serviceGeneration │ │
|
||||
│ │ serviceMessaging serviceWeb serviceSubscription │ │
|
||||
│ └──────────────────────────┬──────────────────────────────────┘ │
|
||||
│ │ nutzt │
|
||||
│ ┌──────────────────────────▼──────────────────────────────────┐ │
|
||||
│ │ Layer 3: SHARED INFRASTRUCTURE │ │
|
||||
│ │ │ │
|
||||
│ │ workflows/methods/ (Unified Action Library / Toolboxes)│ │
|
||||
│ │ workflows/processing/ (ActionExecutor, methodDiscovery) │ │
|
||||
│ │ workflows/automation2/ (Graph Execution Engine) │ │
|
||||
│ │ interfaces/ (DB-Abstraktion + RBAC) │ │
|
||||
│ │ aicore/ (Provider Plugins + Model Selector) │ │
|
||||
│ │ datamodels/ (Pydantic Models) │ │
|
||||
│ │ security/ (RBAC Engine) │ │
|
||||
│ │ shared/ (Utilities, eventManagement) │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
└───────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Feature-Container-Pattern (Ist-Zustand, bewährt)
|
||||
|
||||
### 2.1 Verzeichnisstruktur
|
||||
|
||||
```
|
||||
features/<featureName>/
|
||||
├── main<FeatureName>.py ◄── Feature-Definition + Registrierung
|
||||
│ FEATURE_CODE: str Feature-Identifikator
|
||||
│ UI_OBJECTS: List[Dict] RBAC-fähige UI-Objekte
|
||||
│ RESOURCE_OBJECTS: List[Dict] RBAC-fähige Resource-Objekte
|
||||
│ TEMPLATE_ROLES: List[Dict] Rollen-Templates mit AccessRules
|
||||
│ REQUIRED_SERVICES: List[Dict] Service-Dependencies (optional)
|
||||
│ registerFeature(catalog) → bool Registriert Objekte, synct Rollen in DB
|
||||
│ getFeatureDefinition() → Dict Katalog-Metadaten (Label, Icon)
|
||||
│ get<Feature>Services(user, ...) → Hub Service-Hub-Factory (optional)
|
||||
│
|
||||
├── routeFeature<FeatureName>.py ◄── HTTP API
|
||||
│ router = APIRouter(prefix="/api/<featureName>")
|
||||
│ [templateRouter] = APIRouter(...) Zusätzliche Router (optional)
|
||||
│
|
||||
├── interfaceFeature<FeatureName>.py ◄── Datenbankzugriff
|
||||
│ <Feature>Objects CRUD + RBAC-Filterung
|
||||
│ getInterface(user, mandateId, featureInstanceId) → Singleton
|
||||
│
|
||||
├── datamodelFeature<FeatureName>.py ◄── Pydantic-Datenmodelle
|
||||
│ class <Model>(PowerOnModel): ... Feature-spezifische Entitäten
|
||||
│
|
||||
├── nodeDefinitions/ (optional) ◄── Graph-Node-Typen (für graphicalEditor)
|
||||
│ triggers.py, flow.py, ai.py, ...
|
||||
│
|
||||
└── service<Name>/ (optional) ◄── Feature-interne Services
|
||||
└── mainService<Name>.py
|
||||
```
|
||||
|
||||
### 2.2 Automatische Discovery
|
||||
|
||||
```python
|
||||
# system/registry.py — Kein manuelles Wiring nötig
|
||||
|
||||
discoverFeatureContainers()
|
||||
# Scannt features/*/routeFeature*.py → Liste von Feature-Dirs
|
||||
|
||||
loadFeatureRouters(app)
|
||||
# Importiert routeFeature*.py, mountet router + *Router
|
||||
|
||||
loadFeatureMainModules()
|
||||
# Importiert main*.py, cached
|
||||
|
||||
registerAllFeaturesInCatalog(catalogService)
|
||||
# Für jedes main-Module:
|
||||
# getFeatureDefinition() → catalogService.registerFeatureDefinition()
|
||||
# registerFeature() → UI/Resource-Objekte + Template-Rollen sync
|
||||
```
|
||||
|
||||
### 2.3 Service-Hub-Wiring
|
||||
|
||||
```python
|
||||
# serviceHub/__init__.py — Dynamisches Service-Wiring
|
||||
|
||||
class ServiceHub:
|
||||
def __init__(self, user, workflow, mandateId, featureInstanceId):
|
||||
# ServiceCenterContext erstellen
|
||||
self._serviceCenterContext = ServiceCenterContext(...)
|
||||
|
||||
# Core Interfaces laden
|
||||
self.interfaceDbApp = getAppInterface(user, mandateId)
|
||||
self.interfaceDbChat = getChatInterface(user, mandateId, featureInstanceId)
|
||||
|
||||
# Feature-Interfaces dynamisch laden (glob features/*/interfaceFeature*.py)
|
||||
self._loadFeatureInterfaces()
|
||||
|
||||
# Feature-Services dynamisch laden (glob features/*/service*/mainService*.py)
|
||||
self._loadFeatureServices()
|
||||
|
||||
def __getattr__(self, name):
|
||||
# Lazy: shared services via getService(name, context) bei Erstzugriff
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. RBAC-Architektur
|
||||
|
||||
### 3.1 Zwei getrennte Template-Rollen-Systeme
|
||||
|
||||
**WICHTIG:** Es gibt zwei getrennte Template-Systeme — diese sind **nicht** dieselben:
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────────────────────────────────┐
|
||||
│ TEMPLATE-ROLLEN (Global) │
|
||||
│ │
|
||||
│ ┌──────────────────────────────────────────────────────────┐ │
|
||||
│ │ System Templates (für Mandanten) Priority 1 │ │
|
||||
│ │ isSystemRole=true, mandateId=null, featureCode=null │ │
|
||||
│ │ │ │
|
||||
│ │ "admin" → Mandant-Administration │ │
|
||||
│ │ "user" → Standard-User │ │
|
||||
│ │ "viewer" → Nur-Lesen │ │
|
||||
│ │ │ │
|
||||
│ │ → kopiert bei Mandant-Erstellung (copySystemRolesToMandate)│ │
|
||||
│ └──────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌──────────────────────────────────────────────────────────┐ │
|
||||
│ │ Feature Templates (für Feature-Instanzen) Priority 1 │ │
|
||||
│ │ isSystemRole=false, mandateId=null, featureCode="workspace"│ │
|
||||
│ │ │ │
|
||||
│ │ "workspace-admin" → alle UI/RESOURCE/DATA Rechte │ │
|
||||
│ │ "workspace-user" → eingeschränkte DATA Rechte │ │
|
||||
│ │ "workspace-viewer" → nur View │ │
|
||||
│ │ │ │
|
||||
│ │ → kopiert bei Feature-Instanz-Erstellung (_copyTemplateRoles)│ │
|
||||
│ └──────────────────────────────────────────────────────────┘ │
|
||||
└────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
┌────────────────────────────────────────────────────────────────────┐
|
||||
│ MANDATE-ROLLEN Priority 2 │
|
||||
│ mandateId=X, featureInstanceId=null, featureCode=null │
|
||||
│ │
|
||||
│ "admin" (kopiert von System Template) │
|
||||
│ "user" (kopiert von System Template) │
|
||||
│ "viewer" (kopiert von System Template) │
|
||||
│ → zugewiesen via UserMandate → UserMandateRole │
|
||||
└────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
┌────────────────────────────────────────────────────────────────────┐
|
||||
│ INSTANZ-ROLLEN Priority 3 │
|
||||
│ mandateId=X, featureInstanceId=Y, featureCode="workspace" │
|
||||
│ │
|
||||
│ "workspace-admin" (kopiert von Feature Template) │
|
||||
│ "workspace-user" (kopiert von Feature Template) │
|
||||
│ "workspace-viewer" (kopiert von Feature Template) │
|
||||
│ → zugewiesen via FeatureAccess → FeatureAccessRole │
|
||||
└────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 3.2 Datenmodell RBAC
|
||||
|
||||
```python
|
||||
class Role(PowerOnModel):
|
||||
# Scope bestimmt die Priorität:
|
||||
mandateId: Optional[str] # null = Global/Template
|
||||
featureInstanceId: Optional[str] # null = nicht Instanz-spezifisch
|
||||
featureCode: Optional[str] # z.B. "workspace", "graphicalEditor"
|
||||
roleLabel: str # z.B. "workspace-admin"
|
||||
isSystemRole: bool # System-Rollen nicht löschbar
|
||||
|
||||
class AccessRule(PowerOnModel):
|
||||
roleId: str # FK → Role
|
||||
context: AccessRuleContext # DATA | UI | RESOURCE
|
||||
item: Optional[str] # null=generic, oder spezifisch
|
||||
view: bool
|
||||
read: Optional[AccessLevel] # "n" | "o" | "m" | "a"
|
||||
create: Optional[AccessLevel]
|
||||
update: Optional[AccessLevel]
|
||||
delete: Optional[AccessLevel]
|
||||
|
||||
class AccessLevel(str, Enum):
|
||||
NONE = "n" # kein Zugriff
|
||||
OWN = "o" # nur eigene Records
|
||||
MANDATE = "m" # alle Records im Mandant
|
||||
ALL = "a" # alle Records (System-Admin)
|
||||
```
|
||||
|
||||
### 3.3 User-Zuweisung
|
||||
|
||||
```
|
||||
User ──► UserMandate ──► UserMandateRole ──► Role (Mandate-Scope)
|
||||
User ──► FeatureAccess ──► FeatureAccessRole ──► Role (Instanz-Scope)
|
||||
```
|
||||
|
||||
```python
|
||||
class UserMandate(PowerOnModel):
|
||||
userId: str
|
||||
mandateId: str
|
||||
|
||||
class UserMandateRole(PowerOnModel):
|
||||
userMandateId: str # FK → UserMandate
|
||||
roleId: str # FK → Role (mandateId=X, featureInstanceId=null)
|
||||
|
||||
class FeatureAccess(PowerOnModel):
|
||||
userId: str
|
||||
featureInstanceId: str
|
||||
enabled: bool
|
||||
|
||||
class FeatureAccessRole(PowerOnModel):
|
||||
featureAccessId: str # FK → FeatureAccess
|
||||
roleId: str # FK → Role (mandateId=X, featureInstanceId=Y)
|
||||
```
|
||||
|
||||
### 3.4 Resolution-Algorithmus
|
||||
|
||||
```
|
||||
1. Sammle roleIds:
|
||||
- Mandate-Rollen: UserMandate → UserMandateRole → roleId
|
||||
- Instanz-Rollen: FeatureAccess → FeatureAccessRole → roleId
|
||||
|
||||
2. Lade AccessRules für alle roleIds (context + item filter)
|
||||
|
||||
3. Bestimme Priorität pro Rolle:
|
||||
- Priority 3: role.featureInstanceId gesetzt (Instanz)
|
||||
- Priority 2: role.mandateId gesetzt (Mandant)
|
||||
- Priority 1: beides null (Global/Template)
|
||||
|
||||
4. DATA-Permissions: Nur Rules der HÖCHSTEN Priorität zählen
|
||||
View: OR über alle Rules der höchsten Priorität
|
||||
Item-Spezifität: exact > prefix > generic (innerhalb Priorität)
|
||||
|
||||
5. SQL WHERE Clause: buildRbacWhereClause(permissions, user, table)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Unified Workflow Datenmodell
|
||||
|
||||
### 4.1 Entitäten
|
||||
|
||||
```python
|
||||
# Ziel: datamodels/datamodelWorkflowUnified.py oder
|
||||
# features/graphicalEditor/datamodelFeatureGraphicalEditor.py
|
||||
|
||||
class WorkflowStatus(str, Enum):
|
||||
DRAFT = "draft"
|
||||
PUBLISHED = "published"
|
||||
ARCHIVED = "archived"
|
||||
|
||||
class Workflow(PowerOnModel):
|
||||
"""Workflow-Metadaten und Ownership."""
|
||||
id: str
|
||||
mandateId: str
|
||||
featureInstanceId: str
|
||||
label: str
|
||||
description: Optional[str]
|
||||
tags: List[str] = []
|
||||
isTemplate: bool = False
|
||||
templateSourceId: Optional[str] # geklont von diesem Template
|
||||
currentVersionId: Optional[str] # aktive/published Version
|
||||
active: bool = True # Scheduler-enabled
|
||||
eventId: Optional[str] # APScheduler Job-ID (v1-Pattern)
|
||||
|
||||
class WorkflowVersion(PowerOnModel):
|
||||
"""Immutable Snapshot eines Workflow-Graphen."""
|
||||
id: str
|
||||
workflowId: str # FK → Workflow
|
||||
versionNumber: int # auto-increment
|
||||
status: WorkflowStatus # draft | published | archived
|
||||
graph: Dict[str, Any] # { nodes: [...], connections: [...] }
|
||||
invocations: List[Dict[str, Any]] # Entry-Points (manual, schedule, webhook, form)
|
||||
publishedAt: Optional[float]
|
||||
publishedBy: Optional[str]
|
||||
```
|
||||
|
||||
### 4.2 State Machine: WorkflowVersion.status
|
||||
|
||||
```
|
||||
┌───────┐
|
||||
create ───►│ DRAFT │◄─── edit (graph mutation via API oder AI Tools)
|
||||
└───┬───┘
|
||||
│ publish
|
||||
▼
|
||||
┌───────────┐
|
||||
unpublish─►│ PUBLISHED │
|
||||
(→ DRAFT) └─────┬─────┘
|
||||
│ archive
|
||||
▼
|
||||
┌───────────┐
|
||||
│ ARCHIVED │──► re-publish (→ PUBLISHED)
|
||||
└───────────┘
|
||||
|
||||
Invariante: Pro Workflow maximal 1 Version mit status=PUBLISHED.
|
||||
Scheduler nutzt immer die PUBLISHED Version.
|
||||
```
|
||||
|
||||
### 4.3 Run-Modell
|
||||
|
||||
```python
|
||||
class RunStatus(str, Enum):
|
||||
PENDING = "pending"
|
||||
RUNNING = "running"
|
||||
PAUSED = "paused"
|
||||
COMPLETED = "completed"
|
||||
FAILED = "failed"
|
||||
CANCELLED = "cancelled"
|
||||
|
||||
class WorkflowRun(PowerOnModel):
|
||||
"""Einzelne Ausführung einer WorkflowVersion."""
|
||||
id: str
|
||||
workflowId: str # FK → Workflow
|
||||
versionId: str # FK → WorkflowVersion
|
||||
status: RunStatus
|
||||
trigger: Dict[str, Any] # { type: "manual"|"schedule"|"webhook"|..., metadata }
|
||||
startedAt: float
|
||||
completedAt: Optional[float]
|
||||
nodeOutputs: Dict[str, Any] # Outputs pro Node-ID
|
||||
currentNodeId: Optional[str] # Paused bei diesem Node
|
||||
resumeContext: Dict[str, Any] # Kontext für Resume
|
||||
error: Optional[str] # Top-Level Fehler
|
||||
costTokens: Optional[int] # Aggregierte Token-Kosten
|
||||
costCredits: Optional[float] # Aggregierte Credit-Kosten
|
||||
```
|
||||
|
||||
### 4.4 State Machine: WorkflowRun.status
|
||||
|
||||
```
|
||||
┌─────────┐
|
||||
executeGraph──►│ RUNNING │
|
||||
└────┬────┘
|
||||
│
|
||||
┌────────────────┼────────────────┐
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌────────┐ ┌──────────┐ ┌────────┐
|
||||
│ PAUSED │ │COMPLETED │ │ FAILED │
|
||||
└───┬────┘ └──────────┘ └────────┘
|
||||
│
|
||||
│ resume(taskResult | emailReceived)
|
||||
▼
|
||||
┌─────────┐
|
||||
│ RUNNING │───► COMPLETED | FAILED | PAUSED | CANCELLED
|
||||
└─────────┘
|
||||
|
||||
Pause-Gründe:
|
||||
- input.* Node → HumanTask erstellt
|
||||
- email.checkEmail → EmailWait (Background Poller)
|
||||
|
||||
Cancel:
|
||||
- Manuell durch User
|
||||
- Timeout bei HumanTask (expiresAt)
|
||||
```
|
||||
|
||||
### 4.5 RunStepLog und HumanTask
|
||||
|
||||
```python
|
||||
class StepStatus(str, Enum):
|
||||
RUNNING = "running"
|
||||
COMPLETED = "completed"
|
||||
FAILED = "failed"
|
||||
SKIPPED = "skipped"
|
||||
|
||||
class RunStepLog(PowerOnModel):
|
||||
"""Detaillierter Log pro Node-Execution."""
|
||||
id: str
|
||||
runId: str # FK → WorkflowRun
|
||||
nodeId: str # Node-ID im Graph
|
||||
nodeType: str # z.B. "ai.prompt", "email.checkEmail"
|
||||
status: StepStatus
|
||||
inputSnapshot: Dict[str, Any] # Parameters + Upstream-Daten bei Execution
|
||||
output: Optional[Dict[str, Any]] # Node-Output
|
||||
error: Optional[str]
|
||||
startedAt: float
|
||||
completedAt: Optional[float]
|
||||
durationMs: Optional[int]
|
||||
tokensUsed: Optional[int] # AI-Calls in diesem Step
|
||||
retryCount: int = 0
|
||||
|
||||
class TaskStatus(str, Enum):
|
||||
PENDING = "pending"
|
||||
COMPLETED = "completed"
|
||||
CANCELLED = "cancelled"
|
||||
EXPIRED = "expired"
|
||||
|
||||
class HumanTask(PowerOnModel):
|
||||
"""Aufgabe für menschliche Eingabe bei Pause."""
|
||||
id: str
|
||||
runId: str # FK → WorkflowRun
|
||||
workflowId: str # FK → Workflow (Convenience-FK)
|
||||
nodeId: str
|
||||
nodeType: str # input.approval, input.form, etc.
|
||||
config: Dict[str, Any] # Node-Parameter (Formular-Felder, Approval-Text)
|
||||
assigneeId: str
|
||||
status: TaskStatus
|
||||
result: Optional[Dict[str, Any]]
|
||||
expiresAt: Optional[float] # Timeout
|
||||
```
|
||||
|
||||
### 4.6 State Machine: HumanTask.status
|
||||
|
||||
```
|
||||
┌─────────┐
|
||||
│ PENDING │ (erstellt bei Run-Pause)
|
||||
└────┬────┘
|
||||
│
|
||||
├── complete(result) → COMPLETED (Run wird resumed mit result)
|
||||
├── cancel() → CANCELLED (Run wird cancelled)
|
||||
└── expiresAt passed → EXPIRED (Run wird cancelled oder Default)
|
||||
```
|
||||
|
||||
### 4.7 ER-Diagramm
|
||||
|
||||
```
|
||||
┌──────────────────┐ ┌──────────────────────┐
|
||||
│ Workflow │ 1───N │ WorkflowVersion │
|
||||
├──────────────────┤ ├──────────────────────┤
|
||||
│ id │ │ id │
|
||||
│ mandateId │ │ workflowId (FK) │
|
||||
│ featureInstanceId│ │ versionNumber │
|
||||
│ label │ │ status │
|
||||
│ description │ │ graph {} │
|
||||
│ tags [] │ │ invocations [] │
|
||||
│ isTemplate │ │ publishedAt │
|
||||
│ currentVersionId ├───1──►│ publishedBy │
|
||||
│ active │ └──────────┬───────────┘
|
||||
│ eventId │ │ 1:N
|
||||
└──────────────────┘ ┌──────────▼───────────┐
|
||||
│ WorkflowRun │
|
||||
├──────────────────────┤
|
||||
│ id │
|
||||
│ workflowId (FK) │
|
||||
│ versionId (FK) │
|
||||
│ status │
|
||||
│ trigger {} │
|
||||
│ startedAt │
|
||||
│ completedAt │
|
||||
│ nodeOutputs {} │
|
||||
│ currentNodeId │
|
||||
│ resumeContext {} │
|
||||
│ error │
|
||||
│ costTokens │
|
||||
│ costCredits │
|
||||
└──────────┬───────────┘
|
||||
1:N │ │ 1:N
|
||||
┌─────────────┘ └──────────┐
|
||||
▼ ▼
|
||||
┌──────────────────┐ ┌──────────────────┐
|
||||
│ RunStepLog │ │ HumanTask │
|
||||
├──────────────────┤ ├──────────────────┤
|
||||
│ id │ │ id │
|
||||
│ runId (FK) │ │ runId (FK) │
|
||||
│ nodeId │ │ workflowId (FK) │
|
||||
│ nodeType │ │ nodeId │
|
||||
│ status │ │ nodeType │
|
||||
│ inputSnapshot {} │ │ config {} │
|
||||
│ output {} │ │ assigneeId │
|
||||
│ error │ │ status │
|
||||
│ startedAt │ │ result {} │
|
||||
│ completedAt │ │ expiresAt │
|
||||
│ durationMs │ └──────────────────┘
|
||||
│ tokensUsed │
|
||||
│ retryCount │
|
||||
└──────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. AI-Tool-Architektur: Toolbox-Datenmodell
|
||||
|
||||
### 5.1 Toolbox-Definition
|
||||
|
||||
```python
|
||||
# Ziel: serviceCenter/services/serviceAgent/toolboxRegistry.py
|
||||
|
||||
class ToolboxDefinition(BaseModel):
|
||||
"""Definition einer thematischen Tool-Gruppe."""
|
||||
id: str # z.B. "core", "email", "sharepoint", "workflow"
|
||||
label: Dict[str, str] # Multilingual: {"en": "Email", "de": "E-Mail"}
|
||||
description: str
|
||||
featureCode: Optional[str] # null = Feature-unabhängig, "trustee" = Feature-spezifisch
|
||||
tools: List[str] # Tool-Namen in dieser Toolbox
|
||||
isDefault: bool = False # Immer aktiv (z.B. "core")
|
||||
requiresConnection: Optional[str] # "outlook", "sharepoint" → auto-aktiviert bei Connection
|
||||
|
||||
class ToolboxRegistry:
|
||||
"""Verwaltet Toolboxes und deren Zuordnung zu Tools."""
|
||||
_toolboxes: Dict[str, ToolboxDefinition]
|
||||
_toolRegistry: ToolRegistry # Referenz auf die bestehende ToolRegistry
|
||||
|
||||
def registerToolbox(self, toolbox: ToolboxDefinition): ...
|
||||
|
||||
def getActiveToolboxes(
|
||||
self,
|
||||
featureCode: str,
|
||||
userConnections: List[str], # Aktive Connection-Typen des Users
|
||||
explicitToolboxes: List[str], # Explizit aktivierte Toolboxes
|
||||
) -> List[str]: ...
|
||||
|
||||
def getToolsForToolboxes(
|
||||
self,
|
||||
activeToolboxIds: List[str],
|
||||
) -> List[ToolDefinition]: ...
|
||||
```
|
||||
|
||||
### 5.2 AgentConfig (erweitert mit Eskalation)
|
||||
|
||||
```python
|
||||
class AgentConfig(BaseModel):
|
||||
maxRounds: int = 25
|
||||
maxCostCHF: Optional[float] = None
|
||||
initialToolboxes: List[str] = ["core"] # Tools bei Runde 1
|
||||
availableToolboxes: List[str] = [] # Via requestToolbox anforderbar
|
||||
temperature: Optional[float] = None
|
||||
```
|
||||
|
||||
### 5.3 Dynamische Toolbox-Eskalation
|
||||
|
||||
**Kern-Idee:** Der Agent startet mit einem kompakten Initial-Set (`initialToolboxes`). Er kennt den Katalog aller `availableToolboxes`. Wenn er Spezial-Tools braucht, ruft er `requestToolbox(id)` auf — die Tools werden in der nächsten Runde bereitgestellt.
|
||||
|
||||
```
|
||||
Runde 1:
|
||||
Aktive Tools: core (readFile, webSearch, ...) + requestToolbox
|
||||
System-Prompt: "Verfügbare Toolboxes: email, sharepoint, ai, ..."
|
||||
|
||||
User: "Lies meine letzten Emails und fasse sie zusammen"
|
||||
LLM: → requestToolbox("email")
|
||||
|
||||
Runde 2:
|
||||
Aktive Tools: core + email (outlook_readEmails, ...) + requestToolbox
|
||||
LLM: → outlook_readEmails(connectionRef, folder="Inbox")
|
||||
|
||||
Runde 3:
|
||||
LLM: → (Text-Antwort mit Zusammenfassung) → COMPLETED
|
||||
```
|
||||
|
||||
**requestToolbox Meta-Tool:**
|
||||
|
||||
```python
|
||||
{
|
||||
"name": "requestToolbox",
|
||||
"description": "Request specialized tools for the current task.",
|
||||
"parameters": {
|
||||
"toolboxId": {
|
||||
"type": "string",
|
||||
"enum": [...] # Dynamisch: nur availableToolboxes
|
||||
},
|
||||
"reason": {"type": "string"}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5.4 Toolbox-Registrierung (Architektur)
|
||||
|
||||
```
|
||||
App Startup
|
||||
│
|
||||
├── _registerCoreTools(registry) → 40 Core-Tools in ToolRegistry
|
||||
├── ActionToolAdapter.registerAll(registry) → dynamicMode Actions als Tools
|
||||
│
|
||||
└── ToolboxRegistry.registerAll()
|
||||
├── Toolbox "core" → readFile, listFiles, webSearch, ... (isDefault=true)
|
||||
├── Toolbox "ai" → summarizeContent, generateImage, ...
|
||||
├── Toolbox "datasources" → browseDataSource, downloadFromDataSource, ...
|
||||
├── Toolbox "email" → sendMail, outlook_readEmails, ... (requiresConnection="outlook")
|
||||
├── Toolbox "sharepoint" → sharepoint_findDocumentPath, ... (requiresConnection="sharepoint")
|
||||
├── Toolbox "clickup" → clickup_searchTasks, ... (requiresConnection="clickup")
|
||||
├── Toolbox "jira" → jira_connectJira, ... (requiresConnection="jira")
|
||||
├── Toolbox "workflow" → readWorkflowGraph, addNode, connectNodes, ...
|
||||
├── Toolbox "trustee" → trustee_extractFromFiles, ... (featureCode="trustee")
|
||||
└── Toolbox "chatbot" → chatbot_queryDatabase, ... (featureCode="chatbot")
|
||||
|
||||
Per Agent-Run:
|
||||
│
|
||||
├── Feature-Instanz Config → initialToolboxes + availableToolboxes
|
||||
├── User Connections → filtert availableToolboxes (nur was User nutzen kann)
|
||||
│
|
||||
├── Runde 1: getToolsForToolboxes(initialToolboxes) + requestToolbox
|
||||
│
|
||||
└── Nach requestToolbox("email"):
|
||||
Runde 2+: getToolsForToolboxes(initialToolboxes + ["email"]) + requestToolbox
|
||||
```
|
||||
|
||||
### 5.5 Sub-Agent-Architektur
|
||||
|
||||
```python
|
||||
# Bestehendes Pattern: featureDataAgent.py
|
||||
|
||||
# Aufruf-Kette:
|
||||
# Main Agent → Tool "queryFeatureInstance" → _queryFeatureInstance()
|
||||
# → runFeatureDataAgent(question, featureInstanceId, ...)
|
||||
# → Eigene ToolRegistry (browseTable, queryTable)
|
||||
# → runAgentLoop(prompt, registry, config=AgentConfig(maxRounds=5))
|
||||
# → Antwort zurück → ToolResult → Main Agent
|
||||
```
|
||||
|
||||
**Sub-Agents sind komplementär zu Toolboxes:**
|
||||
- **Toolbox-Eskalation:** Agent bekommt Tools direkt und nutzt sie selbst (z.B. Email-Tools)
|
||||
- **Sub-Agent:** Agent delegiert an spezialisierten Mini-Agent mit eigenem Wissen (z.B. Trustee DB-Schema)
|
||||
|
||||
Faustregel: Toolbox wenn die Tools generisch sind. Sub-Agent wenn Feature-spezifisches Kontext-/Schema-Wissen nötig ist.
|
||||
|
||||
### 5.6 Toolbox-Zuordnung pro Feature
|
||||
|
||||
| Feature | `initialToolboxes` | `availableToolboxes` (anforderbar) |
|
||||
|---------|--------------------|------------------------------------|
|
||||
| **Workspace** | `["core"]` | `["ai", "datasources", "email", "sharepoint", "clickup", "jira"]` — gefiltert nach User-Connections |
|
||||
| **Graphical Editor Chat** | `["core", "workflow"]` | `["ai"]` |
|
||||
| **CommCoach** | `["core"]` | `[]` (keine Eskalation) |
|
||||
| **Chatbot** | (Eigenes Tool-System) | — |
|
||||
|
||||
---
|
||||
|
||||
## 6. Graph-Struktur und Node-Definitionen
|
||||
|
||||
### 6.1 Graph-Schema (innerhalb WorkflowVersion.graph)
|
||||
|
||||
```json
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "node-uuid",
|
||||
"type": "trigger.schedule",
|
||||
"position": { "x": 100, "y": 200 },
|
||||
"parameters": {
|
||||
"cronExpression": "0 8 * * 1-5"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "node-uuid-2",
|
||||
"type": "ai.prompt",
|
||||
"position": { "x": 300, "y": 200 },
|
||||
"parameters": {
|
||||
"prompt": "Analysiere die Emails und erstelle eine Zusammenfassung"
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": [
|
||||
{
|
||||
"source": "node-uuid",
|
||||
"target": "node-uuid-2",
|
||||
"sourceOutput": 0,
|
||||
"targetInput": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 6.2 Node-Definition-Schema
|
||||
|
||||
```python
|
||||
# Jede Node-Definition ist ein Dict mit folgender Struktur:
|
||||
{
|
||||
"id": "ai.prompt", # Unique Node-Type-ID
|
||||
"category": "ai", # Kategorie (für Palette-Gruppierung)
|
||||
"label": {"en": "Prompt", "de": "Prompt"}, # Multilingual
|
||||
"description": {"en": "...", "de": "..."},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "prompt",
|
||||
"type": "string",
|
||||
"required": True,
|
||||
"description": {"en": "AI prompt", "de": "KI-Prompt"}
|
||||
}
|
||||
],
|
||||
"inputs": 1, # Anzahl Eingänge
|
||||
"outputs": 1, # Anzahl Ausgänge
|
||||
"meta": {"icon": "mdi-robot", "color": "#9C27B0"},
|
||||
|
||||
# Interne Felder (nicht an Frontend exponiert):
|
||||
"_method": "ai", # Method für ActionExecutor
|
||||
"_action": "process", # Action für ActionExecutor
|
||||
"_paramMap": {"prompt": "aiPrompt"}, # Parameter-Mapping Node → Action
|
||||
}
|
||||
```
|
||||
|
||||
### 6.3 Node-Type zu Method/Action Mapping
|
||||
|
||||
| Node Type | `_method` | `_action` | `_paramMap` |
|
||||
|-----------|-----------|-----------|-------------|
|
||||
| `trigger.manual` | — | — | — (TriggerExecutor) |
|
||||
| `trigger.schedule` | — | — | — (TriggerExecutor) |
|
||||
| `trigger.form` | — | — | — (TriggerExecutor) |
|
||||
| `flow.ifElse` | — | — | — (FlowExecutor) |
|
||||
| `flow.switch` | — | — | — (FlowExecutor) |
|
||||
| `flow.loop` | — | — | — (FlowExecutor) |
|
||||
| `input.*` | — | — | — (InputExecutor → HumanTask) |
|
||||
| `ai.prompt` | `ai` | `process` | `prompt→aiPrompt` |
|
||||
| `ai.webResearch` | `ai` | `webResearch` | `query→prompt` |
|
||||
| `ai.summarizeDocument` | `ai` | `summarizeDocument` | — |
|
||||
| `ai.translateDocument` | `ai` | `translateDocument` | `targetLanguage→targetLanguage` |
|
||||
| `ai.generateDocument` | `ai` | `generateDocument` | `prompt→prompt` |
|
||||
| `email.checkEmail` | `outlook` | `readEmails` | `connectionId→connectionReference` |
|
||||
| `email.searchEmail` | `outlook` | `searchEmails` | `connectionId→connectionReference` |
|
||||
| `email.draftEmail` | `outlook` | `composeAndDraft...` | `connectionId→connectionReference` |
|
||||
| `sharepoint.*` | `sharepoint` | entsprechend | `connectionId→connectionReference` |
|
||||
| `clickup.*` | `clickup` | entsprechend | `connectionId→connectionReference` |
|
||||
| `file.create` | `file` | `create` | `template→template` |
|
||||
|
||||
### 6.4 Execution-Routing
|
||||
|
||||
```
|
||||
executeGraph(graph, services, ...)
|
||||
│
|
||||
├── parseGraph() → nodes, connections, nodeIds
|
||||
├── validateGraph() → Konsistenzprüfung
|
||||
├── topoSort(nodes, connectionMap) → geordnete Node-Liste
|
||||
│
|
||||
└── Pro Node:
|
||||
│
|
||||
├── nodeType.startsWith("trigger.") → TriggerExecutor
|
||||
│ → runEnvelope passthrough
|
||||
│
|
||||
├── nodeType.startsWith("flow.") → FlowExecutor
|
||||
│ → ifElse: evaluiert Bedingung, setzt active path
|
||||
│ → switch: evaluiert Match, setzt active path
|
||||
│ → loop: iteriert mit _loopState
|
||||
│
|
||||
├── nodeType.startsWith("input.") → InputExecutor
|
||||
│ → erstellt HumanTask
|
||||
│ → setzt Run auf PAUSED
|
||||
│ → raise PauseForHumanTaskError
|
||||
│
|
||||
└── nodeType.startsWith("ai." | "email." | "sharepoint." | ...) → ActionNodeExecutor
|
||||
│
|
||||
├── _getNodeDefinition(nodeType) → _method, _action, _paramMap
|
||||
├── Parameter-Mapping (Node-Params → Action-Params)
|
||||
├── Upstream-Daten mergen (documents von vorherigen Nodes)
|
||||
└── ActionExecutor.executeAction(method, action, params)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Scheduler-Architektur (konsolidiert)
|
||||
|
||||
### 7.1 Konsolidierter Scheduler (Ziel)
|
||||
|
||||
```python
|
||||
# Ziel: workflows/scheduler/mainScheduler.py
|
||||
# Übernimmt die besten Patterns aus v1 und v2
|
||||
|
||||
class WorkflowScheduler:
|
||||
"""Konsolidierter Scheduler für Workflow-Automationen."""
|
||||
|
||||
def start(self, eventUser):
|
||||
"""Startup: Initial Sync + Delayed Sync + Callback Registration."""
|
||||
eventManager.start()
|
||||
self._syncScheduledWorkflows(eventUser)
|
||||
self._registerDelayedSync(eventUser, delaySeconds=5)
|
||||
callbackRegistry.register("workflow.changed",
|
||||
lambda _: self._syncScheduledWorkflows(eventUser))
|
||||
|
||||
def stop(self, eventUser):
|
||||
"""Shutdown: Cleanup."""
|
||||
pass # APScheduler handles job cleanup
|
||||
|
||||
def _syncScheduledWorkflows(self, eventUser):
|
||||
"""Inkrementeller Sync (v1-Pattern)."""
|
||||
workflows = self._getAllSchedulableWorkflows()
|
||||
|
||||
for wf in workflows:
|
||||
jobId = f"workflow.{wf.id}"
|
||||
|
||||
if wf.active and wf.currentVersionId:
|
||||
# Register/Replace Job
|
||||
version = self._getPublishedVersion(wf.currentVersionId)
|
||||
cronKwargs = self._extractSchedule(version)
|
||||
|
||||
if cronKwargs:
|
||||
handler = self._createHandler(wf, version, eventUser)
|
||||
eventManager.registerCron(
|
||||
jobId=jobId,
|
||||
func=handler,
|
||||
cronKwargs=cronKwargs,
|
||||
replaceExisting=True # v1-Pattern: atomic replace
|
||||
)
|
||||
# Persist eventId on Workflow (v1-Pattern: debugging)
|
||||
if wf.eventId != jobId:
|
||||
self._updateEventId(wf.id, jobId)
|
||||
else:
|
||||
# Deactivated: Remove Job + Clear eventId
|
||||
self._removeJob(jobId)
|
||||
if wf.eventId:
|
||||
self._updateEventId(wf.id, None)
|
||||
|
||||
def _createHandler(self, wf, version, eventUser):
|
||||
"""Handler: Reload + Active-Check + Execute (v1-Pattern)."""
|
||||
async def handler():
|
||||
# Reload Workflow (v1-Pattern: Zustand könnte sich geändert haben)
|
||||
current = self._getWorkflow(wf.id)
|
||||
if not current or not current.active:
|
||||
return
|
||||
|
||||
# Execute published version
|
||||
services = self._buildServices(current, eventUser)
|
||||
result = await executeGraph(
|
||||
graph=version.graph,
|
||||
services=services,
|
||||
workflowId=current.id,
|
||||
...
|
||||
)
|
||||
|
||||
# Execution Log (v1-Pattern: capped audit trail)
|
||||
self._appendExecutionLog(current.id, result)
|
||||
|
||||
# Thread-Bridge (v2-Pattern)
|
||||
return self._wrapAsync(handler)
|
||||
```
|
||||
|
||||
### 7.2 Scheduler State Machine
|
||||
|
||||
```
|
||||
Workflow.active=false ──► UNREGISTERED (kein APScheduler Job)
|
||||
│
|
||||
│ activate + published version mit schedule invocation
|
||||
▼
|
||||
Workflow.active=true ──► REGISTERED (eventId gesetzt, Job aktiv)
|
||||
│
|
||||
├── Cron fires → handler() → executeGraph → WorkflowRun
|
||||
│
|
||||
├── Workflow deactivated → remove job → UNREGISTERED (eventId=null)
|
||||
│
|
||||
├── Schedule geändert → replaceExisting=true → REGISTERED (neuer Trigger)
|
||||
│
|
||||
└── Workflow gelöscht → remove job → (Workflow gelöscht)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Backend Code-Struktur (Ziel)
|
||||
|
||||
```
|
||||
gateway/modules/
|
||||
│
|
||||
├── features/
|
||||
│ ├── graphicalEditor/ ◄── NEUES FEATURE (ersetzt automation2)
|
||||
│ │ ├── mainGraphicalEditor.py
|
||||
│ │ │ FEATURE_CODE = "graphicalEditor"
|
||||
│ │ │ UI_OBJECTS = [
|
||||
│ │ │ "ui.feature.graphicalEditor.editor",
|
||||
│ │ │ "ui.feature.graphicalEditor.workflows",
|
||||
│ │ │ "ui.feature.graphicalEditor.tasks",
|
||||
│ │ │ ]
|
||||
│ │ │ RESOURCE_OBJECTS = [
|
||||
│ │ │ "resource.feature.graphicalEditor.execute",
|
||||
│ │ │ "resource.feature.graphicalEditor.schedule",
|
||||
│ │ │ ]
|
||||
│ │ │ TEMPLATE_ROLES = [
|
||||
│ │ │ "graphicalEditor-admin",
|
||||
│ │ │ "graphicalEditor-user",
|
||||
│ │ │ "graphicalEditor-viewer",
|
||||
│ │ │ ]
|
||||
│ │ │
|
||||
│ │ ├── routeFeatureGraphicalEditor.py
|
||||
│ │ │ router prefix: /api/workflows
|
||||
│ │ │ Endpoints: CRUD Workflows, Versions, Execute, Runs, Tasks, NodeTypes, Chat
|
||||
│ │ │
|
||||
│ │ ├── interfaceFeatureGraphicalEditor.py
|
||||
│ │ │ DB: poweron_workflows (oder poweron_automation2 renamed)
|
||||
│ │ │ Tables: Workflow, WorkflowVersion, WorkflowRun, RunStepLog, HumanTask
|
||||
│ │ │
|
||||
│ │ ├── datamodelFeatureGraphicalEditor.py
|
||||
│ │ │ Workflow, WorkflowVersion, WorkflowRun, RunStepLog, HumanTask
|
||||
│ │ │ WorkflowStatus, RunStatus, StepStatus, TaskStatus (State Machines)
|
||||
│ │ │
|
||||
│ │ └── nodeDefinitions/
|
||||
│ │ triggers.py, flow.py, input.py, ai.py, email.py, sharepoint.py, clickup.py, file.py
|
||||
│ │
|
||||
│ ├── workspace/ ◄── BLEIBT (AI Chat mit UDB)
|
||||
│ ├── automation/ ◄── BLEIBT bis Ablösung durch graphicalEditor
|
||||
│ ├── commcoach/ ◄── BLEIBT
|
||||
│ ├── chatbot/ ◄── BLEIBT
|
||||
│ ├── trustee/ ◄── BLEIBT
|
||||
│ └── ...
|
||||
│
|
||||
├── serviceCenter/services/
|
||||
│ ├── serviceAgent/
|
||||
│ │ ├── mainServiceAgent.py ◄── Core Tools + Toolbox-Tagging
|
||||
│ │ ├── agentLoop.py ◄── Toolbox-Filtering statt toolSet
|
||||
│ │ ├── toolRegistry.py ◄── BLEIBT
|
||||
│ │ ├── toolboxRegistry.py ◄── NEU: Toolbox-Verwaltung
|
||||
│ │ ├── actionToolAdapter.py ◄── BLEIBT (+ Toolbox-Zuordnung)
|
||||
│ │ ├── featureDataAgent.py ◄── BLEIBT (Sub-Agent Pattern)
|
||||
│ │ └── datamodelAgent.py ◄── toolboxes: List[str] statt toolSet
|
||||
│ └── ...
|
||||
│
|
||||
├── workflows/
|
||||
│ ├── methods/ ◄── BLEIBT (Unified Action Library)
|
||||
│ ├── processing/ ◄── BLEIBT (ActionExecutor, methodDiscovery)
|
||||
│ ├── automation2/ ◄── Graph Engine BLEIBT (execution, executors, graphUtils)
|
||||
│ ├── scheduler/ ◄── NEU (konsolidierter Scheduler)
|
||||
│ │ └── mainScheduler.py
|
||||
│ ├── automation/ ◄── BLEIBT bis Ablösung
|
||||
│ └── workflowManager.py ◄── BLEIBT (für Workspace Dynamic Mode)
|
||||
│
|
||||
└── ...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. Frontend Code-Struktur (Ziel)
|
||||
|
||||
```
|
||||
frontend_nyla/src/
|
||||
│
|
||||
├── components/ ◄── SHARED COMPONENTS
|
||||
│ ├── ChatBar/ ◄── NEU (extrahiert aus WorkspaceInput)
|
||||
│ │ ├── ChatBar.tsx Props: onSend, onStop, onVoice,
|
||||
│ │ │ fileAttachments, showProviderSelector,
|
||||
│ │ │ showVoice, placeholder
|
||||
│ │ ├── ChatBar.module.css
|
||||
│ │ └── index.ts
|
||||
│ │
|
||||
│ ├── ChatStream/ ◄── NEU (extrahiert aus workspace/ChatStream)
|
||||
│ │ ├── ChatStream.tsx SSE-driven message stream, wiederverwendbar
|
||||
│ │ └── index.ts
|
||||
│ │
|
||||
│ ├── UnifiedDataBar/ ◄── BLEIBT
|
||||
│ │
|
||||
│ ├── FlowEditor/ ◄── RENAMED von Automation2FlowEditor
|
||||
│ │ ├── editor/
|
||||
│ │ │ ├── FlowEditor.tsx + AI Chat Panel (ChatBar + ChatStream)
|
||||
│ │ │ ├── FlowCanvas.tsx + Visual Run Tracing
|
||||
│ │ │ ├── EditorChatPanel.tsx ◄── NEU
|
||||
│ │ │ └── ...
|
||||
│ │ └── ...
|
||||
│ │
|
||||
│ └── ...
|
||||
│
|
||||
├── pages/views/
|
||||
│ ├── workspace/
|
||||
│ │ ├── WorkspacePage.tsx Nutzt ChatBar, ChatStream, UDB
|
||||
│ │ └── ...
|
||||
│ │
|
||||
│ ├── graphicalEditor/ ◄── RENAMED von automation2
|
||||
│ │ ├── GraphicalEditorPage.tsx FlowEditor + UDB + ChatPanel
|
||||
│ │ ├── WorkflowsListPage.tsx
|
||||
│ │ └── TasksPage.tsx
|
||||
│ │
|
||||
│ └── ...
|
||||
│
|
||||
├── api/
|
||||
│ ├── workflowApi.ts ◄── Konsolidiert (ersetzt automationApi + automation2Api)
|
||||
│ └── ...
|
||||
│
|
||||
└── ...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. Zusammenfassung: Architektur-Prinzipien
|
||||
|
||||
| Prinzip | Umsetzung |
|
||||
|---------|-----------|
|
||||
| **Feature-Container-Pattern** | Jedes Feature folgt dem gleichen Verzeichnis-Pattern mit automatischer Discovery. Kein manuelles Wiring. |
|
||||
| **RBAC auf zwei Ebenen** | Mandate-Rollen (generell) + Instanz-Rollen (feature-spezifisch). Höchste Priorität gewinnt. |
|
||||
| **State Machines als Grundlage** | WorkflowVersion, WorkflowRun, HumanTask und Scheduler haben klar definierte Zustände und Übergänge. |
|
||||
| **Unified Action Library** | Eine Method/Action-Library für alle Consumers (Agent Tools, Graph Nodes, Workflow Processor). |
|
||||
| **Toolboxes statt flache Tool-Liste** | Thematisch gruppierte Tools, kontextabhängig aktiviert. Skaliert auf 100+ Tools. |
|
||||
| **Sub-Agents für Feature-Daten** | Main Agent bleibt schlank. Feature-spezifisches Wissen in dedizierten Sub-Agents. |
|
||||
| **Keine Migration** | Direkte Implementierung auf Automation2-Basis. v1 bleibt bis Ablösung. |
|
||||
| **Scheduler konsolidiert** | Beste Patterns aus v1 (inkrementell, eventId, reload+check) und v2 (interval, thread-bridge). |
|
||||
Loading…
Reference in a new issue