# AI Agent & Knowledge Store ## Überblick Der **AI-Agent** ist der Service `serviceAgent` (`AgentService`) im **ServiceCenter**. Er orchestriert einen **ReAct-Loop** mit nativer Function Calling-Unterstützung: pro Runde ruft das Modell `serviceAi` auf; bei Tool-Calls werden Handler über die **Tool Registry** ausgeführt und Ergebnisse wieder in den Konversationskontext gegeben. Er ist die zentrale Schicht für den **Workspace** (Chat, Streaming, Tools, Dateien, externe Quellen). Darunter liegt **`serviceAi`** als Low-Level-Gateway (Billing-Preflight, Provider-/Modellwahl, Neutralisierung vor Modellaufruf). **`serviceKnowledge`** liefert **RAG**: Indexierung extrahierter Inhalte, semantische Suche (pgvector) und **`buildAgentContext`** für kontextuelle Injektion vor jeder Agent-Runde. Der **AI-Core** (`gateway/modules/aicore/`) kapselt Provider-Plugins und die Modellwahl; der Agent nutzt ihn indirekt über `serviceAi` (u. a. `OperationTypeEnum.AGENT`, Embeddings für den Knowledge Store). --- ## Agent Core ### ServiceCenter-Integration | Registry-Key | Klasse | `objectKey` | Dependencies (laut `registry.py`) | |--------------|--------|-------------|-----------------------------------| | `agent` | `AgentService` | `service.agent` | `ai`, `chat`, `utils`, `extraction`, `billing`, `streaming`, `knowledge` | | `knowledge` | `KnowledgeService` | `service.knowledge` | `ai` | Pro Request propagiert der **ServiceCenterContext** u. a. `userId`, `mandateId`, `featureInstanceId` und optional `requireNeutralization` (siehe Invarianten). ### Ablauf (Ist-Code) - **Einstieg:** `AgentService.runAgent` (`mainServiceAgent.py`) — baut die Tool Registry, optional Anreicherung des Prompts mit Datei-Metadaten/`FileContentIndex`, startet `runAgentLoop` (`agentLoop.py`). - **Loop:** `runAgentLoop` — `ConversationManager` + Systemprompt; **pro Runde:** optional RAG via `buildRagContextFn`, Budget-Check, **Progressive Summarization** bei Bedarf (`ConversationManager`, Modell-Operation `DATA_ANALYSE`), dann `AiCallRequest` mit `OperationTypeEnum.AGENT`, `messages`, `tools`. - **Tool Registry:** `registerCoreTools` in `coreTools/registerCore.py` delegiert an domänenspezifische Module (`_workspaceTools`, `_connectionTools`, `_dataSourceTools`, `_documentTools`, `_mediaTools`, `_featureSubAgentTools`, `_crossWorkflowTools`); **`ActionToolAdapter`** registriert alle Workflow-Actions mit `dynamicMode=True` als zusätzliche Tools (`method_action` → intern `executeAction`). - **Parallele Ausführung:** In `_executeToolCalls` werden als **`readOnly=True`** markierte Tool-Calls parallel (`asyncio.gather`), schreibende/übrige sequentiell — vermeidet Race Conditions bei zustandsändernden Tools. - **Budget:** `AgentConfig.maxCostCHF` — vor jeder Runde Abgleich mit Workflow-Kosten; bei Überschreitung Status `budgetExceeded` und abschliessender Fortschritts-Text (`FINAL`). - **Rundenlimit:** `AgentConfig.maxRounds` (Default 25); bei Erreichen während `running` → Status `maxRoundsReached` und Fortschritts-Zusammenfassung. - **Degradation:** Schlägt RAG-Injektion fehl, wird nur geloggt (**non-blocking**); der Agent läuft ohne diesen Kontext weiter. Fehlgeschlagene RoundMemory-Persistenz ebenfalls non-blocking. - **Streaming:** Optional `aiCallStreamFn` — Chunks als `CHUNK`-Events; Abschluss u. a. `AGENT_SUMMARY` mit Kosten- und Round-Metriken; Trace kann über Knowledge-Entities persistiert werden (`_persistTrace`). ### Konfiguration (`datamodelAgent.AgentConfig`, Ist) | Feld | Bedeutung | |------|-----------| | `maxRounds` | Max. Schleifenrunden (Default 25, 1–100) | | `maxCostCHF` | Optionales Workflow-Budget in CHF | | `toolSet` | Aktives Tool-Set (Default `"core"`) | | `temperature` | Optional für den Agent-AI-Call | ### System-Prompt: temporaler Kontext (Ist) `buildSystemPrompt` (`conversationManager.py`) injiziert beim Bauen des System-Prompts einen **Datums-/Zeitblock** (`_buildTemporalContext`). Der Sub-Agent-Prompt in `featureDataAgent._buildSchemaContext` macht dasselbe. Ohne diesen Block halluzinieren LLMs das aktuelle Datum aus ihrem Training-Cutoff (typisch: »Juni 2025«), was bei relativen Zeit-Filtern (»letzten Monat«, »Q1«) sofort zu falschen SQL-Filtern und falschen Antworten führt. Datenfluss der Zeitzone: 1. **Browser**: `Intl.DateTimeFormat().resolvedOptions().timeZone` (z. B. `"Europe/Zurich"`). 2. **Frontend**: `frontend_nyla/src/api.ts` Axios-Interceptor sendet den IANA-Namen als `X-User-Timezone`-Header. 3. **Gateway**: `_requestContextMiddleware` (`app.py`) liest den Header und schreibt ihn via `_setRequestTimezone` in eine `ContextVar`. 4. **Konsumenten**: `getRequestNow()` / `getRequestTimezone()` aus `modules/shared/timeUtils.py` liefern die Werte für den Prompt-Block (gleiche Pattern wie `_setLanguage` / `_getLanguage`). 5. **Fallback**: Header fehlt oder ist ungültig → `UTC`. Niemals server-seitige Hardcoded-TZ. Storage bleibt UTC (`getUtcTimestamp`, `getIsoTimestamp`). Nur **user-sichtbare** Now-Werte (Agent-Prompt, formatierte Display-Strings) gehen über `getRequestNow()`. ### Toolbox Registry **Datei:** `serviceCenter/services/serviceAgent/toolboxRegistry.py` Statt alle Tools flach zu exponieren, gruppiert die **Toolbox Registry** Tools in thematische Toolboxes. Der Agent startet schlank und kann Spezial-Toolboxes zur Laufzeit nachfordern. | Toolbox | Anzahl Tools | Default | Requires Connection | |---------|-------------|---------|-------------------| | `core` | 20 | Ja | -- | | `ai` | 9 | Ja | -- | | `datasources` | 8 | Ja | -- | | `email` | 5 | Nein | `microsoft` | | `sharepoint` | 3 | Nein | `microsoft` | | `clickup` | 3 | Nein | `clickup` | | `jira` | 3 | Nein | `jira` | | `workflow` | 9 | Nein | -- (featureCode: graphicalEditor) | | `trustee` | 1 | Nein | -- (featureCode: trustee) | **Aktivierung (`_activateToolboxes` in `mainServiceAgent.py`):** - Default-Toolboxes sind immer aktiv - Connection-abhaengige Toolboxes werden aktiviert, wenn der User eine passende Connection hat - Tools aus inaktiven Toolboxes werden aus der Registry entfernt - `workflow`-Tools werden explizit via `workflowTools.getWorkflowToolDefinitions` registriert **`requestToolbox` Meta-Tool:** - Erlaubt dem Agent, zur Laufzeit inaktive Toolboxes anzufordern - Schema: `{ toolboxId: enum[inactive IDs], reason: string }` - Handler in `mainServiceAgent._registerRequestToolbox()` - Nach erfolgreichem Aufruf refresht `agentLoop.py` die `toolDefinitions` fuer die naechste Runde - Nur Toolboxes, die aktuell nicht aktiv sind, erscheinen als Optionen ### RBAC (Architekturentscheid) Keine separate Tool-Level-RBAC: Zugriff wird über **Datenbank-RBAC** (z. B. File-Queries), **OAuth/Connections** bei externen Quellen und **`serviceAi`** (Billing, Provider-/Modell-RBAC) sowie **`can_access_service`** für `service.agent` durchgesetzt. --- ## Agent Tools ### Prinzip Tools sind registrierte Handler mit JSON-Schema für Argumente, **`readOnly`-Flag** (Parallelisierung) und optional `toolSet`. Tool-Ausgaben sind Rohdaten für das Modell; **Neutralisierung vor dem LLM** erfolgt zentral in `serviceAi`, nicht in den Tools. Zusätzlich zu den unten genannten **Kern-Tools** existieren **dynamische Tools** aus dem Workflow-System: `ActionToolAdapter` liest `methodDiscovery.methods` und registriert jede Action mit `dynamicMode=True` unter einem zusammengesetzten Namen (`{methodShort}_{actionName}`); im Adapter sind diese derzeit **alle als nicht-readOnly** registriert. > **Tool-Generierung aus dem Catalog (Typed Action Architecture, 2026-04):** `ActionToolAdapter` leitet das JSON-Schema fuer jeden Tool-Aufruf direkt aus der **typisierten Action-Signatur** ab (Pflicht-Felder, Typen, Default-Werte, `FeatureInstanceRef`-Discriminator). Es gibt keine handgepflegte Heuristik mehr -- Editor, AI-Agent und Adapter konsumieren denselben Catalog (`/api/automation2/catalog`). Aenderungen an einer Action-Signatur schlagen automatisch auf das Tool-Schema durch; veraltete oder gedriftete Felder werden vom Snapshot-Test `test_staticNodesHaveNoDriftAgainstLiveMethods` blockiert (`_KNOWN_ADAPTER_DRIFTS = frozenset()`). Details: `wiki/c-work/3-validate/2026-04-typed-action-architecture.md`. **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/`; Stand 2026-04 inkl. UDM-Helfer) **Workspace / Dateien** | Tool | Kurzbeschreibung | |------|------------------| | `readFile` | Dateiinhalt lesen (optional zeilenweise) | | `listFiles` | Dateien filtern (Ordner, Tags, Suche) | | `searchInFileContent` | Suche im Dateiinhalt | | `writeFile` | Datei anlegen / anhängen / überschreiben | | `deleteFile` | Datei löschen | | `renameFile` | Umbenennen | | `moveFile` | Verschieben (Ordner) | | `tagFile` | Tags setzen | | `copyFile` | Unabhängige Kopie | | `replaceInFile` | Ersetzen im Text | **Ordner** | Tool | Kurzbeschreibung | |------|------------------| | `listFolders` | Ordner auflisten | | `createFolder` | Ordner anlegen | | `deleteFolder` | Ordner löschen (optional rekursiv) | | `renameFolder` | Ordner umbenennen | | `moveFolder` | Ordner verschieben | **Web & Sprache** | Tool | Kurzbeschreibung | |------|------------------| | `webSearch` | Websuche | | `readUrl` | Inhalt einer bekannten URL laden | | `translateText` | Übersetzung (Voice/Translation-Pipeline) | | `textToSpeech` | TTS | | `speechToText` | Transkription Audio-Datei (Gateway: `VoiceObjects.speechToText`; optionale Connector-Parameter `model`/`lightweight`/`audioFormat` — Agent-Tool nutzt Defaults) | | `detectLanguage` | Spracherkennung für Text | **Externe Datenquellen / Mail** | Tool | Kurzbeschreibung | |------|------------------| | `listConnections` | User-Connections auflisten | | `browseDataSource` | Externe Quelle durchsuchen | | `searchDataSource` | Suche in Datenquelle | | `downloadFromDataSource` | Download → FileItem (inkl. Vererbung `neutralize` auf Datei, siehe Invarianten) | | `uploadToExternal` | Upload zu externer Quelle | | `sendMail` | E-Mail senden | **Dokumente / Content-Objekte** | Tool | Kurzbeschreibung | |------|------------------| | `browseContainer` | Struktur-Index (Seiten, Abschnitte, …) | | `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 | | `createChart` | Chart erzeugen | **Feature / Workflow** | Tool | Kurzbeschreibung | |------|------------------| | `queryFeatureInstance` | Abfrage anderer Feature-Instanz (setzt bei Bedarf `requireNeutralization` für Sub-Calls). Sub-Agent hat `browseTable`, `queryTable` und `aggregateTable` (SUM/COUNT/AVG/MIN/MAX mit GROUP BY). DB-Connection-Pooling und Result-Caching (5 Min TTL). System-Prompt wird aus drei Schichten gebaut: (1) generischer Header mit Tabellen + Pydantic-Schema (`_buildSchemaContext`), (2) generische Regeln inkl. **kein SUM/AVG auf bereits aggregierten Saldo-/Total-Feldern** (`closingBalance`, `openingBalance`, `debitTotal`, `creditTotal`, …), (3) optionale **feature-spezifische Domain-Hints** via `getAgentDomainHints()` in `mainXxx.py` — z. B. KMU-Kontoplan-Präfixe (1xxx Aktiven, 102x Bank, 100x Kasse), `periodMonth=0`-Konvention und kanonische Query-Patterns für Trustee. Round-/Cost-Budget wird vom Parent-Agent geerbt (`AgentConfig.maxRounds` → Tool-Context → `runFeatureDataAgent`). | | `listWorkflowHistory` | Workflow-Historie | | `readWorkflowMessages` | Nachrichten eines Workflows lesen | **Sicherheit / Ausführung** | Tool | Kurzbeschreibung | |------|------------------| | `neutralizeData` | Anonymisierter Text (non-destructive) | | `executeCode` | Sandboxed Code-Ausführung (siehe `sandboxExecutor.py`) | --- ## AI-Core (Provider-Abstraction) | Komponente | Datei / Rolle | |------------|----------------| | **Model Registry** | `aicoreModelRegistry.py` — registriert Provider-Plugins und Modelle | | **Model Selector** | `aicoreModelSelector.py` — Auswahl nach Operationstyp, Promptgrösse, Restriktionen, Ranking | ### Provider-Plugins (Dateien unter `gateway/modules/aicore/`) | Plugin-Modul | Typische Rolle | |--------------|----------------| | `aicorePluginAnthropic.py` | Claude-Modelle | | `aicorePluginOpenai.py` | GPT, Embeddings, Bild — modellabhaengiges Payload-Tuning: GPT-5.x und o-Serie (o1/o3/o4) sind Reasoning-Modelle und akzeptieren weder `max_tokens` (-> immer `max_completion_tokens`) noch ein custom `temperature` (-> Feld bei diesen Modellen weggelassen, OpenAI erzwingt sonst HTTP 400 `unsupported_value`) | | `aicorePluginMistral.py` | Mistral Chat / Embed | | `aicorePluginPerplexity.py` | Sonar / Recherche | | `aicorePluginTavily.py` | Web-Suche | | `aicorePluginPrivateLlm.py` | Private LLM | | `aicorePluginInternal.py` | Interne Extraktion/Generierung/Rendering | ### Operation Types (`datamodelAi.OperationTypeEnum`, Auszug) u. a. `plan`, `dataAnalyse`, `dataGenerate`, `dataExtract`, `imageAnalyse`, `imageGenerate`, `neutralizationText`, `neutralizationImage`, `webSearch`, `webCrawl`, **`agent`**, **`embedding`**, `speechTeams`. Der Agent-Loop verwendet **`AGENT`** für Hauptrunden und **`DATA_ANALYSE`** für Summarization-Calls; Embeddings laufen über die Knowledge-Service-Pipeline (`callEmbedding`). --- ## Knowledge Store (RAG) ### Datenmodelle (`datamodelKnowledge.py`) | Modell | Zweck | |--------|--------| | **FileContentIndex** | Strukturindex pro Datei (ohne LLM): `structure`, `objectSummary`, `status`, Spiegelung von Mandat/Instanz über **`scope`** (`personal`, `featureInstance`, `mandate`, `global`), Neutralisierungs-Felder `isNeutralized`, `neutralizationStatus` | | **ContentChunk** | Persistente Chunks mit Embedding (`vector(1536)`), `data`, `contextRef`, `contentType` | | **RoundMemory** | Pro Runde: `file_ref`, Tool-Ergebnisse, Entscheidungen — mit Embedding für semantische Wiederverwendung trotz ConversationManager-Kürzung | | **WorkflowMemory** | Workflow-scoped Key-Value-Cache (Entities, Fakten, inkl. optional Embedding) | Zugriff über **`interfaceDbKnowledge`** (`FileContentIndex`, `ContentChunk`, RoundMemory, WorkflowMemory). ### Indexierung **`KnowledgeService.indexFile`** — nach Extraktion (Content-Objekte): übernimmt Scope aus **FileItem** als Single Source of Truth; bei `FileItem.neutralize=True` werden Inhalte vor dem Speichern neutralisiert; Chunking (u. a. `DEFAULT_CHUNK_TOKENS` / Zeichen-Heuristik), Embedding via AI-Service, Persistenz von Index + Chunks; optional Billing-Reconciliation für Mandats-Speicher. ### Semantische Suche & Kontext **`buildAgentContext`** — priorisierte Schichten (Ist-Code, vereinfacht): 1. **RoundMemory** `file_ref` („Known Files“) 2. **Instance/personal/mandate/global** — `semanticSearch` mit Query-Embedding (Limit/Score wie im Code) 3. **RoundMemory** semantisch (`semanticSearchRoundMemory`) 4. **Workflow-Entities** (`getWorkflowEntities`) 5. **Mandate-Scope** — geteilte Mandats-Dokumente („Shared Knowledge“) 6. Optional **Cross-Workflow-Hints** (`workflowHintItems`) Rückgabe: formatierter String für Injektion in den Agent-Systemkontext. **Wenn Embedding fehlschlägt**, liefert `buildAgentContext` einen **leeren String** (Agent arbeitet ohne diesen RAG-Block). Erweiterte Hilfen (z. B. **`readSection`**, Caching) für selektives Lesen sind im selben Service dokumentiert. ### RAG Consent & Control (ab 2026-05) Volle Architektur für transparente, datenzentrierte Steuerung der RAG-Indexierung durch den Benutzer. #### Prinzipien 1. **Datenquelle ist die Single Source of Truth** — `DataSource.ragIndexEnabled` steuert, ob ein Baum-Element indexiert wird. Vererbung entlang der Baumstruktur (wie `scope` und `neutralize`). 2. **Kein Wizard-Seiteneffekt ohne UI-Revoke** — Jede Einstellung die im AddConnectionWizard gesetzt wird, ist in der UDB (Unified Data Bar) sicht- und revertbar. 3. **Konsent-Entzug = sofortige Purge** — Deaktivierung von `ragIndexEnabled` oder `knowledgeIngestionEnabled` löscht zugehörige Chunks synchron. #### Datenmodell-Erweiterungen | Modell | Feld | Zweck | |--------|------|-------| | `DataSource` | `ragIndexEnabled: bool` | Pro Baum-Element: Auto-Indexierung an/aus | | `DataSource` | `lastIndexed: Optional[float]` | Zeitstempel des letzten erfolgreichen Index-Laufs | | `UserConnection` | `knowledgeIngestionEnabled: bool` | Globaler Konsent pro Verbindung | #### Walker-Architektur Jeder Walker (`subConnectorSync*.py`) iteriert über `ragIndexEnabled=True` DataSources: - Empfängt `dataSources: List[Dict]` und `progressCb: JobProgressCallback` - Prüft `progressCb.isCancelled()` periodisch für graceful Abort - Enthält `dataSourceId` in der Provenance jedes `IngestionJob` - Nutzt per-DataSource `neutralize`-Policy (aus `subPolicyResolver`) #### Job-Cancellation `mainBackgroundJobService.py` bietet: - `cancelJob(jobId)` — setzt Status auf `CANCELLED` - `cancelJobsByConnection(connectionId)` — bulk-cancel aller laufenden Jobs - `JobProgressCallback.isCancelled()` — kooperativer Check mit 3s-Cache #### API-Endpunkte | Methode | Pfad | Zweck | |---------|------|-------| | `PATCH` | `/api/datasources/{id}/rag-index` | Toggle `ragIndexEnabled` (Trigger mini-bootstrap oder Purge) | | `PATCH` | `/api/connections/{id}/knowledge-consent` | Globaler Konsent-Toggle | | `POST` | `/api/connections/{id}/knowledge-stop` | Alle laufenden Jobs stoppen | | `GET` | `/api/rag/inventory/me` | Persönliche RAG-Übersicht | | `GET` | `/api/rag/inventory/mandate` | Mandant-Aggregation (Admin) | | `GET` | `/api/rag/inventory/platform` | Plattform-Statistik (SysAdmin) | | `GET` | `/api/rag/inventory/jobs` | Aktive RAG-Jobs des Users | #### Frontend-Komponenten | Komponente | Ort | Funktion | |-----------|-----|----------| | UDB `SourcesTab` | 4. Toggle (🧠) | ragIndexEnabled pro Tree-Element | | `AddConnectionWizard` | Connector-aware Steps | MS Admin Consent + Infomaniak PAT integriert | | `RagInventoryPage` | Start > Nutzung > RAG | Globale Übersicht & Steuerung | | `RagRunningBadge` | MainLayout (fixed) | Floating-Badge bei aktiven Jobs | #### Vererbung (Policy Resolver) `subPolicyResolver.py` implementiert Longest-Prefix-Matching: - Eltern-DataSource mit explizitem `ragIndexEnabled` vererbt an Kind-Pfade - Gleiches Pattern wie `neutralize` und `scope` --- ## Teamsbot-Integration (Hybrid-Routing, kein eigenes Toolset) Der Teamsbot ruft den **selben** `AgentService.runAgent` über das ServiceCenter auf — es gibt **kein** Teamsbot-spezifisches Toolset. Aufrufer ist `gateway/modules/features/teamsbot/service.py::_runAgentForMeeting` mit `AgentConfig(maxRounds=5, maxCostCHF=0.10, toolSet="core", initialToolboxes=["core","web"], excludeActionTools=True)`. | Trigger | Pfad | Wer ruft `runAgent`? | |---|---|---| | Operator schickt **Director Prompt** (One-Shot oder Persistent) via Regie-Panel | `routeFeatureTeamsbot.submitDirectorPrompt` → `service.submitDirectorPrompt` → `asyncio.create_task(_processDirectorPrompt)` → `_runAgentForMeeting` | direkt, umgeht `SPEECH_TEAMS` | | `SPEECH_TEAMS` setzt **`needsAgent=true`** + `agentReason` (z. B. „recherchier das im Internet") | `service._analyzeAndRespond` erkennt `needsAgent`, ruft `_runAgentForMeeting` mit `taskBrief = agentReason` | Eskalation aus dem schnellen Pfad | **FINAL-Delivery:** Der `FINAL`-Event-Text wird im Teamsbot-Service über die bestehenden Kanäle (TTS + `sendChatMessage`) ins Meeting gespielt — der Agent „spricht" nicht selbst, sondern liefert nur den Text. Damit braucht es **kein** Teamsbot-spezifisches Tool wie `sendChat` oder `readAloud` im Agent. **Workflow-ID-Konvention:** `workflowId = f"teamsbot:{sessionId}"` — RoundMemory und RAG akkumulieren pro Meeting, getrennt von anderen Sessions. **Persistente Direktiven:** `service._buildPersistentDirectorContext` rendert aktive `persistent`-Direktiven als `OPERATOR_DIRECTIVES`-Block in den `SPEECH_TEAMS`-Kontext, damit sie auch ohne erneuten Director-Prompt-Aufruf wirken (z. B. „Antworte immer in Englisch"). Siehe [`b-reference/teams-bot/architecture.md`](../teams-bot/architecture.md) für die vollständige Hybrid-Architektur und Director-Prompt-Lifecycle. --- ## Schlüssel-Dateien | Datei | Rolle | |-------|--------| | `gateway/modules/serviceCenter/registry.py` | Registrierung `agent`, `knowledge`, Dependencies, `objectKey` | | `gateway/modules/serviceCenter/services/serviceAgent/mainServiceAgent.py` | `AgentService`, `runAgent`, Prompt-Enrichment, Registry-Orchestrierung | | `gateway/modules/serviceCenter/services/serviceAgent/coreTools/registerCore.py` | Orchestrator: delegiert Tool-Registrierung an Domänen-Module | | `gateway/modules/serviceCenter/services/serviceAgent/coreTools/_workspaceTools.py` | Dateien, Ordner, Web, Übersetzung | | `gateway/modules/serviceCenter/services/serviceAgent/coreTools/_connectionTools.py` | Externe Connections, Upload, Mail | | `gateway/modules/serviceCenter/services/serviceAgent/coreTools/_dataSourceTools.py` | DataSource Browse/Search/Download | | `gateway/modules/serviceCenter/services/serviceAgent/coreTools/_documentTools.py` | Container, Content-Objects, Vision | | `gateway/modules/serviceCenter/services/serviceAgent/coreTools/_mediaTools.py` | Rendering, TTS, STT, Bildgenerierung, Charts, Neutralize, Code | | `gateway/modules/serviceCenter/services/serviceAgent/coreTools/_featureSubAgentTools.py` | Feature Data Sub-Agent (queryFeatureInstance) | | `gateway/modules/serviceCenter/services/serviceAgent/coreTools/_crossWorkflowTools.py` | Workflow-Historie, Messages, `_CORE_ONLY_TOOLS`-Tagging | | `gateway/modules/serviceCenter/services/serviceAgent/agentLoop.py` | ReAct-Loop, Budget, RAG-Injektion, Tool-Dispatch, Summaries | | `gateway/modules/serviceCenter/services/serviceAgent/toolRegistry.py` | Registrierung, Dispatch, `readOnly`, Function-Calling-Format | | `gateway/modules/serviceCenter/services/serviceAgent/conversationManager.py` | Kontextfenster, Summarization, Systemprompt | | `gateway/modules/serviceCenter/services/serviceAgent/datamodelAgent.py` | `AgentConfig`, Events, Trace-Modelle | | `gateway/modules/serviceCenter/services/serviceAgent/actionToolAdapter.py` | Workflow-Actions → Agent-Tools (Schema-Generierung; Param-Validierung erfolgt zentral im `ActionExecutor`) | | `gateway/modules/workflows/processing/shared/parameterValidation.py` | Universelle Action-Parameter-Validierung + Coercion (Required-Enforcement, Ref-Schema → id-String, Primitive-Coercion); aufgerufen aus `ActionExecutor.executeAction` für **alle** Aufrufpfade (Agent, Workflow-Graph, REST) | | `gateway/modules/connectors/connectorDbPostgre.py` | DB-Connector; Read-Methoden raisen `DatabaseQueryError` bei echten Query-Fehlern (Postgres-Adapt, UndefinedTable/Column, OperationalError, …); Empty Result Sets bleiben `[]`/`None` | | `gateway/modules/serviceCenter/services/serviceAgent/toolboxRegistry.py` | Toolbox-Definitionen, `requestToolbox` Meta-Tool-Schema | | `gateway/modules/serviceCenter/services/serviceAgent/workflowTools.py` | Workflow-Editing-Tools (readWorkflowGraph, addNode, ...) | | `gateway/modules/serviceCenter/services/serviceAgent/sandboxExecutor.py` | `executeCode`-Sandbox | | `gateway/modules/serviceCenter/services/serviceKnowledge/mainServiceKnowledge.py` | Index, `buildAgentContext`, Workflow-/Round-Memory-Helfer | | `gateway/modules/interfaces/interfaceDbKnowledge.py` | DB-Zugriff Knowledge / RAG | | `gateway/modules/datamodels/datamodelKnowledge.py` | FileContentIndex, ContentChunk, RoundMemory, WorkflowMemory | | `gateway/modules/serviceCenter/services/serviceAi/mainServiceAi.py` | Zentrales Neutralisierungs-Gate vor LLM, Billing, Provider-Aufruf | | `gateway/modules/aicore/aicoreModelRegistry.py` | Provider-/Modell-Registry | | `gateway/modules/aicore/aicoreModelSelector.py` | Modellauswahl | | `gateway/modules/aicore/aicorePlugin*.py` | Provider-Implementierungen | | `gateway/modules/datamodels/datamodelAi.py` | `OperationTypeEnum`, `AiCallRequest`, Optionen | --- ## Regeln / Invarianten 1. **Neutralisierung vor dem Modell:** Alle an ein LLM gehenden Inhalte (Prompt, Kontext, Messages) werden ausschliesslich in **`serviceAi`** entsprechend Policy neutralisiert bzw. blockiert — nicht in den Agent-Tools. 2. **Tools liefern Rohdaten:** Kein zweites Neutralisieren in Tools; Ausnahme ist bewusste **Data-at-rest**-Neutralisierung beim **Indexieren** (`indexFile` bei `FileItem.neutralize=True`). 3. **DataSource → FileItem:** `_downloadFromDataSource` vererbt das **`neutralize`-Flag** der DataSource auf erzeugte Dateien; **`queryFeatureInstance`** kann Sub-Agent-Calls mit **`requireNeutralization=True`** auslösen, wenn die Feature-Datenquelle das vorsieht. 4. **Kein Tool-RBAC:** Autorisierung über Datenbank-, Connection- und Service-Schicht. 5. **Parallele Tools nur bei `readOnly=True`:** Schreibende Tools nicht parallel zueinander aus demselben Batch. 6. **RAG optional:** Embedding- oder DB-Fehler im Kontextpfad führen nicht zwingend zum Abbruch des Agent-Runs (RAG wird übersprungen/entfällt). 7. **Billing:** AI-Calls und eingebettete Pfade (Embeddings, Summaries) laufen über bestehende Billing-/Subscription-Checks in `serviceAi` bzw. aufrufenden Schichten. Detaillierte Neutralisierungs-Kette und Compliance: **`wiki/compliance/Neutralisierung.md`**. Architektur-Roadmap (Unified Workspace, ProviderConnector 1:n, Phasenplan): **`wiki/concepts/AI-Agent-Architecture-Konzept.md`** — als Zielbild lesen, nicht als Ist-Abbild aller Codepfade.