This commit is contained in:
valueon 2025-03-14 00:29:45 +01:00
parent 4f40537f92
commit b6fd69e65c
54 changed files with 2923 additions and 26 deletions

162
.gitignore vendored Normal file
View file

@ -0,0 +1,162 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
.pdm.toml
.pdm-python
.pdm-build/
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

255
backend/agent_service.py Normal file
View file

@ -0,0 +1,255 @@
import asyncio
import uuid
from typing import List, Dict, Any, Optional
from datetime import datetime
import logging
import json
import os
logger = logging.getLogger(__name__)
class AgentService:
"""
Service für die Verwaltung und Ausführung von Multi-Agent-Workflows.
In einer Produktionsumgebung würde hier eine Integration mit echten KI-Diensten
und eine Orchestrierung der verschiedenen Agenten stattfinden.
"""
def __init__(self):
self.workflows = {}
self.results_dir = "./results"
os.makedirs(self.results_dir, exist_ok=True)
async def execute_workflow(
self,
workflow_id: str,
prompt: str,
agents: List[Dict[str, Any]],
files: List[Dict[str, Any]]
) -> str:
"""
Führt einen Workflow mit den angegebenen Agenten und Dateien aus.
Diese Methode simuliert die Ausführung für Demo-Zwecke.
In einer echten Implementierung würde hier die tatsächliche Orchestrierung
der Agenten und die Verarbeitung der Dateien stattfinden.
"""
logger.info(f"Starte Workflow {workflow_id} mit {len(agents)} Agenten und {len(files)} Dateien")
# Workflow initialisieren
self.workflows[workflow_id] = {
"id": workflow_id,
"status": "running",
"progress": 0.0,
"started_at": datetime.now().isoformat(),
"completed_at": None,
"agent_statuses": {},
"logs": [],
"results": []
}
# Log-Eintrag für den Start des Workflows
self._add_log(workflow_id, "Workflow gestartet", "info")
self._add_log(workflow_id, f"Verarbeite {len(files)} Dateien...", "info")
self.workflows[workflow_id]["progress"] = 0.1
await asyncio.sleep(1) # Simuliere Verarbeitungszeit
# Verarbeitung pro Agent simulieren
for i, agent in enumerate(agents):
agent_id = agent["id"]
agent_name = agent["name"]
agent_type = agent["type"]
self.workflows[workflow_id]["agent_statuses"][agent_id] = "running"
self._add_log(
workflow_id,
f"Agent '{agent_name}' beginnt mit der Verarbeitung...",
"start",
agent_id,
agent_name
)
# Simuliere Verarbeitungszeit
self.workflows[workflow_id]["progress"] = 0.1 + (i + 1) * (0.8 / len(agents)) * 0.5
await asyncio.sleep(2)
# Agent-Ergebnis simulieren
result = self._generate_simulated_result(workflow_id, agent, i, prompt, files)
self.workflows[workflow_id]["results"].append(result)
self._add_log(
workflow_id,
f"Agent '{agent_name}' hat die Verarbeitung abgeschlossen",
"complete",
agent_id,
agent_name
)
self.workflows[workflow_id]["agent_statuses"][agent_id] = "completed"
self.workflows[workflow_id]["progress"] = 0.1 + (i + 1) * (0.8 / len(agents))
await asyncio.sleep(1)
# Workflow abschließen
self._add_log(workflow_id, "Alle Agenten haben ihre Aufgaben abgeschlossen", "info")
self._add_log(workflow_id, "Workflow erfolgreich beendet", "success")
self.workflows[workflow_id]["status"] = "completed"
self.workflows[workflow_id]["progress"] = 1.0
self.workflows[workflow_id]["completed_at"] = datetime.now().isoformat()
# Speichere Ergebnisse in Datei für spätere Verwendung
self._save_workflow_results(workflow_id)
return workflow_id
def _add_log(
self,
workflow_id: str,
message: str,
log_type: str,
agent_id: Optional[str] = None,
agent_name: Optional[str] = None
) -> None:
"""Fügt einen Protokolleintrag zum Workflow hinzu"""
log_entry = {
"id": f"log_{uuid.uuid4()}",
"message": message,
"type": log_type,
"timestamp": datetime.now().isoformat(),
"agent_id": agent_id,
"agent_name": agent_name
}
workflow = self.workflows.get(workflow_id)
if workflow:
workflow["logs"].append(log_entry)
logger.info(f"Workflow {workflow_id}: {message}")
def _generate_simulated_result(
self,
workflow_id: str,
agent: Dict[str, Any],
index: int,
prompt: str,
files: List[Dict[str, Any]]
) -> Dict[str, Any]:
"""Generiert ein simuliertes Ergebnis basierend auf dem Agententyp"""
agent_type = agent["type"]
agent_id = agent["id"]
agent_name = agent["name"]
# Dateien als Teil des Kontexts einbinden
file_names = [f["name"] for f in files]
file_context = f"Basierend auf der Analyse von: {', '.join(file_names)}"
result = {
"id": f"result_{workflow_id}_{index}",
"agent_id": agent_id,
"agent_name": agent_name,
"timestamp": datetime.now().isoformat(),
"metadata": {
"files_processed": file_names,
"prompt": prompt
}
}
if agent_type == "analyzer":
result.update({
"title": "Datenanalyse-Ergebnis",
"type": "text",
"content": f"{file_context}\n\nDie Analyse der bereitgestellten Daten zeigt folgende Schlüsselerkenntnisse:\n\n1. Die Umsätze stiegen im Q1 2025 um 12% im Vergleich zum Vorjahr\n2. Produktkategorie A zeigt die stärkste Leistung mit 23% Wachstum\n3. In den Regionen Nord und West wurde das größte Wachstum beobachtet\n4. Die Betriebskosten sind um 5% gesunken, was zu einer verbesserten Marge führt"
})
elif agent_type == "visualizer":
result.update({
"title": "Umsatzentwicklung nach Quartal",
"type": "chart",
"content": "Diagramm der Umsatzentwicklung (simuliert)",
"metadata": {
**result["metadata"],
"chart_type": "line",
"chart_data": {
"labels": ["Q1 2024", "Q2 2024", "Q3 2024", "Q4 2024", "Q1 2025"],
"datasets": [
{
"label": "Umsatz (Mio. €)",
"data": [2.1, 2.4, 2.8, 3.2, 3.6]
}
]
}
}
})
elif agent_type == "writer":
result.update({
"title": "Zusammenfassung und Empfehlungen",
"type": "text",
"content": f"{file_context}\n\n# Zusammenfassung Q1 2025\n\nDie Unternehmensdaten zeigen ein starkes erstes Quartal mit signifikanten Verbesserungen in allen Geschäftsbereichen. Der Gesamtumsatz stieg um 12%, während die Betriebskosten um 5% sanken, was zu einer verbesserten Gewinnmarge führte.\n\n## Empfehlungen\n\n1. **Investitionen in Produktkategorie A ausbauen** - Diese Kategorie zeigt das stärkste Wachstum und sollte weiter gefördert werden\n2. **Marketingaktivitäten in Süd- und Ostregionen verstärken** - Diese Regionen zeigen Potenzial für Wachstum\n3. **Kostenoptimierungen beibehalten** - Die Maßnahmen zur Kostensenkung haben sich als effektiv erwiesen\n\n## Prognose für Q2 2025\n\nBei gleichbleibenden Marktbedingungen erwarten wir ein weiteres Wachstum von 8-10% im Q2 2025."
})
elif agent_type == "scraper":
result.update({
"title": "Web-Scraping Ergebnisse",
"type": "text",
"content": f"# Marktdaten aus Web-Quellen\n\nBasierend auf der Analyse von 15 führenden Branchenwebsites wurden folgende Trends identifiziert:\n\n1. Der Gesamtmarkt wächst mit einer Rate von 8,5% jährlich\n2. Hauptwettbewerber A und B haben kürzlich neue Produktlinien gestartet\n3. Die durchschnittlichen Marktpreise sind in den letzten 6 Monaten um 3,2% gestiegen"
})
else:
result.update({
"title": "Verarbeitungsergebnis",
"type": "text",
"content": f"Allgemeines Ergebnis der Verarbeitung durch {agent_name}"
})
return result
def _save_workflow_results(self, workflow_id: str) -> None:
"""Speichert die Workflow-Ergebnisse in einer Datei"""
workflow = self.workflows.get(workflow_id)
if workflow:
try:
file_path = os.path.join(self.results_dir, f"workflow_{workflow_id}.json")
with open(file_path, 'w', encoding='utf-8') as f:
json.dump(workflow, f, indent=2, ensure_ascii=False)
logger.info(f"Workflow-Ergebnisse gespeichert: {file_path}")
except Exception as e:
logger.error(f"Fehler beim Speichern der Workflow-Ergebnisse: {e}")
def get_workflow_status(self, workflow_id: str) -> Optional[Dict[str, Any]]:
"""Gibt den Status eines Workflows zurück"""
workflow = self.workflows.get(workflow_id)
if not workflow:
return None
return {
"id": workflow["id"],
"status": workflow["status"],
"progress": workflow["progress"],
"started_at": workflow["started_at"],
"completed_at": workflow["completed_at"],
"agent_statuses": workflow["agent_statuses"]
}
def get_workflow_logs(self, workflow_id: str) -> Optional[List[Dict[str, Any]]]:
"""Gibt die Protokolle eines Workflows zurück"""
workflow = self.workflows.get(workflow_id)
if not workflow:
return None
return workflow["logs"]
def get_workflow_results(self, workflow_id: str) -> Optional[List[Dict[str, Any]]]:
"""Gibt die Ergebnisse eines Workflows zurück"""
workflow = self.workflows.get(workflow_id)
if not workflow:
return None
return workflow["results"]
# Singleton-Instanz des AgentService
_agent_service_instance = None
def get_agent_service():
"""Gibt eine Singleton-Instanz des AgentService zurück"""
global _agent_service_instance
if _agent_service_instance is None:
_agent_service_instance = AgentService()
return _agent_service_instance

305
backend/app.py Normal file
View file

@ -0,0 +1,305 @@
from fastapi import FastAPI, File, UploadFile, HTTPException, Depends, Body, Query
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from fastapi.staticfiles import StaticFiles
from typing import List, Dict, Any, Optional
import uuid
import os
import json
import asyncio
from datetime import datetime
import logging
from models import (
Workspace,
Agent,
DataObject,
Prompt,
WorkflowRequest,
WorkflowResponse,
LogEntry,
Result
)
from database import get_db, Database
from agent_service import AgentService, get_agent_service
# Konfiguration des Loggers
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[logging.StreamHandler()]
)
logger = logging.getLogger(__name__)
app = FastAPI(title="Data Platform API", description="Backend-API für die Multi-Agent Datenplattform")
# CORS-Konfiguration für Frontend-Anfragen
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # In Produktion einschränken
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Statische Dateien (für Frontend)
app.mount("/static", StaticFiles(directory="static"), name="static")
# Verzeichnis für hochgeladene Dateien erstellen
UPLOAD_DIR = os.path.join(os.getcwd(), "uploads")
os.makedirs(UPLOAD_DIR, exist_ok=True)
@app.get("/", tags=["General"])
async def root():
"""API-Statusendpunkt"""
return {"status": "online", "message": "Data Platform API ist aktiv"}
# Workspace-Endpunkte
@app.get("/workspaces", tags=["Workspaces"], response_model=List[Workspace])
async def get_workspaces(db: Database = Depends(get_db)):
"""Alle verfügbaren Workspaces abrufen"""
return db.get_all_workspaces()
@app.get("/workspaces/{workspace_id}", tags=["Workspaces"], response_model=Workspace)
async def get_workspace(workspace_id: str, db: Database = Depends(get_db)):
"""Einen bestimmten Workspace mit allen Details abrufen"""
workspace = db.get_workspace(workspace_id)
if not workspace:
raise HTTPException(status_code=404, detail=f"Workspace mit ID {workspace_id} nicht gefunden")
return workspace
@app.post("/workspaces", tags=["Workspaces"], response_model=Workspace)
async def create_workspace(workspace: Dict[str, Any] = Body(...), db: Database = Depends(get_db)):
"""Einen neuen Workspace erstellen"""
workspace_id = str(uuid.uuid4())
workspace_data = {
"id": workspace_id,
"name": workspace.get("name", "Neuer Workspace"),
"created_at": datetime.now().isoformat(),
"prompts": [],
"agents": [],
"dataObjectReferences": []
}
new_workspace = db.create_workspace(workspace_data)
return new_workspace
# Agent-Endpunkte
@app.get("/agents", tags=["Agents"], response_model=List[Agent])
async def get_agents(workspace_id: Optional[str] = Query(None), db: Database = Depends(get_db)):
"""Alle Agenten oder Agenten eines bestimmten Workspaces abrufen"""
if workspace_id:
return db.get_agents_by_workspace(workspace_id)
return db.get_all_agents()
@app.post("/agents", tags=["Agents"], response_model=Agent)
async def create_agent(
agent: Dict[str, Any] = Body(...),
db: Database = Depends(get_db)
):
"""Einen neuen Agenten erstellen"""
agent_id = str(uuid.uuid4())
workspace_id = agent.get("workspace_id")
if not workspace_id:
raise HTTPException(status_code=400, detail="workspace_id ist erforderlich")
# Workspace existiert?
workspace = db.get_workspace(workspace_id)
if not workspace:
raise HTTPException(status_code=404, detail=f"Workspace mit ID {workspace_id} nicht gefunden")
agent_data = {
"id": agent_id,
"name": agent.get("name", "Neuer Agent"),
"type": agent.get("type", "generic"),
"capabilities": agent.get("capabilities", []),
"description": agent.get("description", "")
}
new_agent = db.create_agent(agent_data, workspace_id)
return new_agent
# Datei-Endpunkte
@app.get("/files", tags=["Files"], response_model=List[DataObject])
async def get_files(db: Database = Depends(get_db)):
"""Alle verfügbaren Dateien abrufen"""
return db.get_all_files()
@app.post("/files/upload", tags=["Files"])
async def upload_file(
file: UploadFile = File(...),
db: Database = Depends(get_db)
):
"""Eine Datei hochladen"""
try:
file_id = str(uuid.uuid4())
file_ext = os.path.splitext(file.filename)[1]
file_path = os.path.join(UPLOAD_DIR, f"{file_id}{file_ext}")
# Datei speichern
with open(file_path, "wb") as f:
content = await file.read()
f.write(content)
# Dateityp bestimmen
file_type = "image" if file.content_type and "image" in file.content_type else "document"
# In Datenbank speichern
file_data = {
"id": file_id,
"name": file.filename,
"type": file_type,
"path": file_path,
"content_type": file.content_type,
"size": os.path.getsize(file_path),
"upload_date": datetime.now().isoformat()
}
new_file = db.create_file(file_data)
return {
"id": new_file["id"],
"name": new_file["name"],
"type": new_file["type"],
"size": f"{new_file['size'] / (1024 * 1024):.1f} MB",
"upload_date": new_file["upload_date"]
}
except Exception as e:
logger.error(f"Fehler beim Hochladen der Datei: {e}")
raise HTTPException(status_code=500, detail=f"Fehler beim Hochladen der Datei: {str(e)}")
# Prompt-Endpunkte
@app.get("/prompts", tags=["Prompts"], response_model=List[Prompt])
async def get_prompts(workspace_id: Optional[str] = Query(None), db: Database = Depends(get_db)):
"""Alle Prompts oder Prompts eines bestimmten Workspaces abrufen"""
if workspace_id:
return db.get_prompts_by_workspace(workspace_id)
return db.get_all_prompts()
@app.post("/prompts", tags=["Prompts"], response_model=Prompt)
async def create_prompt(
prompt: Dict[str, Any] = Body(...),
db: Database = Depends(get_db)
):
"""Einen neuen Prompt erstellen"""
prompt_id = str(uuid.uuid4())
workspace_id = prompt.get("workspace_id")
if not workspace_id:
raise HTTPException(status_code=400, detail="workspace_id ist erforderlich")
# Workspace existiert?
workspace = db.get_workspace(workspace_id)
if not workspace:
raise HTTPException(status_code=404, detail=f"Workspace mit ID {workspace_id} nicht gefunden")
prompt_data = {
"id": prompt_id,
"content": prompt.get("content", ""),
"created_at": datetime.now().isoformat()
}
new_prompt = db.create_prompt(prompt_data, workspace_id)
return new_prompt
# Workflow-Endpunkte
@app.post("/workflow/run", tags=["Workflow"], response_model=WorkflowResponse)
async def run_workflow(
workflow_request: WorkflowRequest,
db: Database = Depends(get_db),
agent_service: AgentService = Depends(get_agent_service)
):
"""Führt einen Workflow mit den ausgewählten Agenten und Dateien aus"""
workspace = db.get_workspace(workflow_request.workspace_id)
if not workspace:
raise HTTPException(status_code=404, detail=f"Workspace mit ID {workflow_request.workspace_id} nicht gefunden")
# Prüfen, ob Dateien existieren
files = []
for file_id in workflow_request.files:
file = db.get_file(file_id)
if not file:
raise HTTPException(status_code=404, detail=f"Datei mit ID {file_id} nicht gefunden")
files.append(file)
# Prüfen, ob Agenten existieren
agents = []
for agent_id in workflow_request.agents:
agent = db.get_agent(agent_id)
if not agent:
raise HTTPException(status_code=404, detail=f"Agent mit ID {agent_id} nicht gefunden")
agents.append(agent)
# Workflow ID generieren
workflow_id = str(uuid.uuid4())
# Workflow starten (asynchron)
workflow_task = asyncio.create_task(
agent_service.execute_workflow(
workflow_id,
workflow_request.prompt,
agents,
files
)
)
# Sofort eine Antwort zurückgeben
return WorkflowResponse(
workflow_id=workflow_id,
status="running",
message="Workflow wurde gestartet"
)
@app.get("/workflow/{workflow_id}/status", tags=["Workflow"])
async def get_workflow_status(
workflow_id: str,
agent_service: AgentService = Depends(get_agent_service)
):
"""Status eines laufenden Workflows abrufen"""
status = agent_service.get_workflow_status(workflow_id)
if not status:
raise HTTPException(status_code=404, detail=f"Workflow mit ID {workflow_id} nicht gefunden")
return status
@app.get("/workflow/{workflow_id}/logs", tags=["Workflow"], response_model=List[LogEntry])
async def get_workflow_logs(
workflow_id: str,
agent_service: AgentService = Depends(get_agent_service)
):
"""Protokolle eines Workflows abrufen"""
logs = agent_service.get_workflow_logs(workflow_id)
if logs is None:
raise HTTPException(status_code=404, detail=f"Workflow mit ID {workflow_id} nicht gefunden")
return logs
@app.get("/workflow/{workflow_id}/results", tags=["Workflow"], response_model=List[Result])
async def get_workflow_results(
workflow_id: str,
agent_service: AgentService = Depends(get_agent_service)
):
"""Ergebnisse eines Workflows abrufen"""
results = agent_service.get_workflow_results(workflow_id)
if results is None:
raise HTTPException(status_code=404, detail=f"Workflow mit ID {workflow_id} nicht gefunden")
return results
if __name__ == "__main__":
import uvicorn
uvicorn.run("app:app", host="0.0.0.0", port=8000, reload=True)

4
backend/data/.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

299
backend/database.py Normal file
View file

@ -0,0 +1,299 @@
import json
import os
from typing import List, Dict, Any, Optional
from datetime import datetime
import logging
logger = logging.getLogger(__name__)
class Database:
"""
Eine einfache JSON-basierte Datenbank für die Datenspeicherung.
In einer Produktionsumgebung würde hier eine richtige Datenbank verwendet werden.
"""
def __init__(self, data_dir="./data"):
self.data_dir = data_dir
os.makedirs(data_dir, exist_ok=True)
# Standardpfade für die Datendateien
self.workspaces_file = os.path.join(data_dir, "workspaces.json")
self.agents_file = os.path.join(data_dir, "agents.json")
self.files_file = os.path.join(data_dir, "files.json")
self.prompts_file = os.path.join(data_dir, "prompts.json")
# Initiale Daten erstellen, falls die Dateien nicht existieren
self._initialize_data()
def _initialize_data(self):
"""Initialisiert die Datendateien, falls sie nicht existieren"""
# Workspaces
if not os.path.exists(self.workspaces_file):
self._save_data(self.workspaces_file, [
{
"id": "workspace_001",
"name": "Datenanalyse-Projekt",
"created_at": datetime.now().isoformat(),
"prompts": [],
"agents": [],
"dataObjectReferences": []
},
{
"id": "workspace_002",
"name": "Marktforschung",
"created_at": datetime.now().isoformat(),
"prompts": [],
"agents": [],
"dataObjectReferences": []
}
])
# Agenten
if not os.path.exists(self.agents_file):
self._save_data(self.agents_file, [
{
"id": "agent_001",
"name": "Datenanalyse-Agent",
"type": "analyzer",
"workspace_id": "workspace_001",
"capabilities": ["Datenanalyse", "Statistik", "Trendanalyse"],
"description": "Spezialisiert auf die Analyse von strukturierten Daten"
},
{
"id": "agent_002",
"name": "Visualisierungs-Agent",
"type": "visualizer",
"workspace_id": "workspace_001",
"capabilities": ["Datenvisualisierung", "Diagrammerstellung"],
"description": "Erstellt visuelle Darstellungen aus Daten"
},
{
"id": "agent_003",
"name": "Text-Generator",
"type": "writer",
"workspace_id": "workspace_001",
"capabilities": ["Texterstellung", "Zusammenfassung"],
"description": "Verfasst verständliche Berichte und Zusammenfassungen"
},
{
"id": "agent_004",
"name": "Web-Scraper",
"type": "scraper",
"workspace_id": "workspace_002",
"capabilities": ["Datensammlung", "Webanalyse"],
"description": "Sammelt Daten aus verschiedenen Webquellen"
},
{
"id": "agent_005",
"name": "Marktanalyse-Agent",
"type": "analyzer",
"workspace_id": "workspace_002",
"capabilities": ["Wettbewerbsanalyse", "Markttrends"],
"description": "Spezialisiert auf Wettbewerbsanalyse und Markttrends"
}
])
# Dateien
if not os.path.exists(self.files_file):
self._save_data(self.files_file, [
{
"id": "file_001",
"name": "Quartalsbericht Q1 2025.pdf",
"type": "document",
"content_type": "application/pdf",
"size": 2500000, # ca. 2.4 MB
"upload_date": datetime.now().isoformat(),
"path": "uploads/dummy_file1.pdf"
},
{
"id": "file_002",
"name": "Marktanalyse-Diagramm.png",
"type": "image",
"content_type": "image/png",
"size": 1150000, # ca. 1.1 MB
"upload_date": datetime.now().isoformat(),
"path": "uploads/dummy_file2.png"
},
{
"id": "file_003",
"name": "Finanzdaten_2024-2025.xlsx",
"type": "document",
"content_type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"size": 3880000, # ca. 3.7 MB
"upload_date": datetime.now().isoformat(),
"path": "uploads/dummy_file3.xlsx"
}
])
# Prompts
if not os.path.exists(self.prompts_file):
self._save_data(self.prompts_file, [
{
"id": "prompt_001",
"content": "Analysiere die Quartalsdaten und erstelle eine Zusammenfassung mit wichtigsten Trends",
"workspace_id": "workspace_001",
"created_at": datetime.now().isoformat()
},
{
"id": "prompt_002",
"content": "Erstelle eine Prognose für das nächste Quartal basierend auf historischen Daten",
"workspace_id": "workspace_001",
"created_at": datetime.now().isoformat()
}
])
def _load_data(self, file_path):
"""Lädt Daten aus einer JSON-Datei"""
try:
if os.path.exists(file_path):
with open(file_path, 'r', encoding='utf-8') as f:
return json.load(f)
return []
except Exception as e:
logger.error(f"Fehler beim Laden der Daten aus {file_path}: {e}")
return []
def _save_data(self, file_path, data):
"""Speichert Daten in einer JSON-Datei"""
try:
with open(file_path, 'w', encoding='utf-8') as f:
json.dump(data, f, indent=2, ensure_ascii=False)
except Exception as e:
logger.error(f"Fehler beim Speichern der Daten in {file_path}: {e}")
# Workspace-Methoden
def get_all_workspaces(self) -> List[Dict[str, Any]]:
"""Gibt alle Workspaces zurück"""
workspaces = self._load_data(self.workspaces_file)
# Füge Agenten und Prompts hinzu
for workspace in workspaces:
workspace_id = workspace["id"]
workspace["agents"] = self.get_agents_by_workspace(workspace_id)
workspace["prompts"] = self.get_prompts_by_workspace(workspace_id)
return workspaces
def get_workspace(self, workspace_id: str) -> Optional[Dict[str, Any]]:
"""Gibt einen Workspace anhand seiner ID zurück"""
workspaces = self._load_data(self.workspaces_file)
for workspace in workspaces:
if workspace["id"] == workspace_id:
# Füge Agenten und Prompts hinzu
workspace["agents"] = self.get_agents_by_workspace(workspace_id)
workspace["prompts"] = self.get_prompts_by_workspace(workspace_id)
return workspace
return None
def create_workspace(self, workspace_data: Dict[str, Any]) -> Dict[str, Any]:
"""Erstellt einen neuen Workspace"""
workspaces = self._load_data(self.workspaces_file)
workspaces.append(workspace_data)
self._save_data(self.workspaces_file, workspaces)
return workspace_data
# Agent-Methoden
def get_all_agents(self) -> List[Dict[str, Any]]:
"""Gibt alle Agenten zurück"""
return self._load_data(self.agents_file)
def get_agents_by_workspace(self, workspace_id: str) -> List[Dict[str, Any]]:
"""Gibt alle Agenten eines Workspaces zurück"""
agents = self._load_data(self.agents_file)
return [agent for agent in agents if agent.get("workspace_id") == workspace_id]
def get_agent(self, agent_id: str) -> Optional[Dict[str, Any]]:
"""Gibt einen Agenten anhand seiner ID zurück"""
agents = self._load_data(self.agents_file)
for agent in agents:
if agent["id"] == agent_id:
return agent
return None
def create_agent(self, agent_data: Dict[str, Any], workspace_id: str) -> Dict[str, Any]:
"""Erstellt einen neuen Agenten"""
agents = self._load_data(self.agents_file)
agent_data["workspace_id"] = workspace_id
agents.append(agent_data)
self._save_data(self.agents_file, agents)
# Aktualisiere die Workspace-Referenz
workspaces = self._load_data(self.workspaces_file)
for workspace in workspaces:
if workspace["id"] == workspace_id:
if "agents" not in workspace:
workspace["agents"] = []
workspace["agents"].append(agent_data["id"])
break
self._save_data(self.workspaces_file, workspaces)
return agent_data
# Datei-Methoden
def get_all_files(self) -> List[Dict[str, Any]]:
"""Gibt alle Dateien zurück"""
files = self._load_data(self.files_file)
return files
def get_file(self, file_id: str) -> Optional[Dict[str, Any]]:
"""Gibt eine Datei anhand ihrer ID zurück"""
files = self._load_data(self.files_file)
for file in files:
if file["id"] == file_id:
return file
return None
def create_file(self, file_data: Dict[str, Any]) -> Dict[str, Any]:
"""Erstellt einen neuen Dateieintrag"""
files = self._load_data(self.files_file)
files.append(file_data)
self._save_data(self.files_file, files)
return file_data
# Prompt-Methoden
def get_all_prompts(self) -> List[Dict[str, Any]]:
"""Gibt alle Prompts zurück"""
return self._load_data(self.prompts_file)
def get_prompts_by_workspace(self, workspace_id: str) -> List[Dict[str, Any]]:
"""Gibt alle Prompts eines Workspaces zurück"""
prompts = self._load_data(self.prompts_file)
return [prompt for prompt in prompts if prompt.get("workspace_id") == workspace_id]
def get_prompt(self, prompt_id: str) -> Optional[Dict[str, Any]]:
"""Gibt einen Prompt anhand seiner ID zurück"""
prompts = self._load_data(self.prompts_file)
for prompt in prompts:
if prompt["id"] == prompt_id:
return prompt
return None
def create_prompt(self, prompt_data: Dict[str, Any], workspace_id: str) -> Dict[str, Any]:
"""Erstellt einen neuen Prompt"""
prompts = self._load_data(self.prompts_file)
prompt_data["workspace_id"] = workspace_id
prompts.append(prompt_data)
self._save_data(self.prompts_file, prompts)
# Aktualisiere die Workspace-Referenz
workspaces = self._load_data(self.workspaces_file)
for workspace in workspaces:
if workspace["id"] == workspace_id:
if "prompts" not in workspace:
workspace["prompts"] = []
workspace["prompts"].append(prompt_data["id"])
break
self._save_data(self.workspaces_file, workspaces)
return prompt_data
# Singleton-Instanz der Datenbank
_db_instance = None
def get_db():
"""Gibt eine Singleton-Instanz der Datenbank zurück"""
global _db_instance
if _db_instance is None:
_db_instance = Database()
return _db_instance

Binary file not shown.

80
backend/models.py Normal file
View file

@ -0,0 +1,80 @@
from pydantic import BaseModel, Field
from typing import List, Dict, Any, Optional
from datetime import datetime
class Agent(BaseModel):
"""Datenmodell für einen Agenten"""
id: str
name: str
type: str
capabilities: List[str] = []
description: Optional[str] = None
class DataObject(BaseModel):
"""Datenmodell für ein Datenobjekt"""
id: str
name: str
type: str # 'document', 'image', etc.
size: Optional[str] = None
upload_date: Optional[str] = None
content_type: Optional[str] = None
path: Optional[str] = None
class Prompt(BaseModel):
"""Datenmodell für einen Prompt"""
id: str
content: str
created_at: str
class Workspace(BaseModel):
"""Datenmodell für einen Workspace"""
id: str
name: str
created_at: str
prompts: List[Prompt] = []
agents: List[Agent] = []
dataObjectReferences: List[str] = []
class WorkflowRequest(BaseModel):
"""Anforderung zur Ausführung eines Workflows"""
workspace_id: str
prompt: str
agents: List[str] # Liste von Agent-IDs
files: List[str] # Liste von Datei-IDs
workflow_name: Optional[str] = None
class WorkflowResponse(BaseModel):
"""Antwort nach dem Start eines Workflows"""
workflow_id: str
status: str
message: str
class LogEntry(BaseModel):
"""Protokolleintrag während der Workflow-Ausführung"""
id: str
message: str
type: str # 'info', 'error', 'start', 'complete', 'success'
timestamp: str
agent_id: Optional[str] = None
agent_name: Optional[str] = None
class Result(BaseModel):
"""Ergebnis eines Agenten während der Workflow-Ausführung"""
id: str
title: str
agent_id: str
agent_name: str
type: str # 'text', 'chart', 'image', etc.
content: str
timestamp: str
metadata: Optional[Dict[str, Any]] = None
class WorkflowStatus(BaseModel):
"""Status eines Workflows"""
id: str
name: Optional[str] = None
status: str # 'running', 'completed', 'failed'
progress: float # 0.0 - 1.0
started_at: str
completed_at: Optional[str] = None
agent_statuses: Optional[Dict[str, str]] = None

4
backend/results/.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

4
backend/static/.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

4
backend/uploads/.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

View file

@ -1 +0,0 @@
REACT_APP_API_URL=https://volucy-gateway-e3d2bzbxdeaaayhz.westeurope-01.azurewebsites.net

204
frontend/index.html Normal file
View file

@ -0,0 +1,204 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Data Platform - Multi-Agent Service</title>
<link rel="stylesheet" href="./styles.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
</head>
<body>
<!-- Navigation -->
<nav class="navbar">
<div class="navbar-container">
<h1 class="navbar-logo">Data Platform</h1>
<div class="navbar-user">
<span>Demo User</span>
<button class="icon-button">
<i class="fas fa-cog"></i>
</button>
</div>
</div>
</nav>
<div class="app-container">
<!-- Seitenleiste -->
<aside class="sidebar">
<div class="workspace-section">
<h2>Workspaces</h2>
<ul class="workspace-list" id="workspace-list">
<!-- Workspace-Liste wird vom JavaScript gefüllt -->
</ul>
</div>
<ul class="sidebar-nav">
<li class="sidebar-item active">
<a href="#workflow">
<i class="fas fa-play-circle"></i>
<span>Workflow-Ausführung</span>
</a>
</li>
<li class="sidebar-item">
<a href="#data">
<i class="fas fa-database"></i>
<span>Meine Daten</span>
</a>
</li>
<li class="sidebar-item">
<a href="#prompts">
<i class="fas fa-comment-alt"></i>
<span>Meine Prompts</span>
</a>
</li>
<li class="sidebar-item">
<a href="#agents">
<i class="fas fa-wrench"></i>
<span>Meine Agents</span>
</a>
</li>
</ul>
</aside>
<!-- Hauptinhalt -->
<main class="main-content">
<div class="workflow-container">
<!-- Meine Prompts Ansicht -->
<div id="prompts-view" style="display:none;">
<h2>Meine Prompts</h2>
<div class="card">
<h3>Gespeicherte Prompts</h3>
<div id="prompts-list">
<!-- Wird dynamisch gefüllt -->
</div>
<button class="add-btn" id="add-prompt-btn">
<i class="fas fa-plus"></i> Neuen Prompt erstellen
</button>
</div>
</div>
<!-- Meine Agents Ansicht -->
<div id="agents-view" style="display:none;">
<h2>Meine Agents</h2>
<div class="card">
<h3>Verfügbare Agents</h3>
<div id="agents-list-container">
<!-- Wird dynamisch gefüllt -->
</div>
<button class="add-btn" id="add-agent-btn">
<i class="fas fa-plus"></i> Neuen Agent hinzufügen
</button>
</div>
</div>
<!-- Linke Seite - Workflow-Konfiguration -->
<div class="config-panel">
<h2>Workflow-Konfiguration</h2>
<!-- 1. Dateien auswählen -->
<div class="card">
<h3>1. Dateien auswählen</h3>
<div class="files-container">
<div class="file-selection">
<div class="section-header">
<span>Verfügbare Dateien</span>
<button class="upload-btn" id="upload-btn">
<i class="fas fa-upload"></i> Hochladen
</button>
<input type="file" id="file-input" multiple hidden>
</div>
<ul class="file-list" id="available-files">
<!-- Wird dynamisch gefüllt -->
</ul>
</div>
<div class="selected-files">
<h4>Ausgewählte Dateien</h4>
<div class="selected-files-container" id="selected-files-container">
<div class="empty-state" id="empty-files-state">
<i class="fas fa-upload"></i>
<span>Keine Dateien ausgewählt</span>
</div>
<ul class="selected-file-list" id="selected-files">
<!-- Wird dynamisch gefüllt -->
</ul>
</div>
</div>
</div>
</div>
<!-- 2. Prompt eingeben -->
<div class="card">
<h3>2. Prompt eingeben</h3>
<textarea
id="prompt-input"
placeholder="Beschreiben Sie die Aufgabe für die Agenten..."
></textarea>
</div>
<!-- 3. Agenten auswählen -->
<div class="card">
<h3>3. Agenten auswählen</h3>
<ul class="agent-list" id="agent-list">
<!-- Wird dynamisch gefüllt -->
</ul>
</div>
<!-- Aktionsbuttons -->
<div class="action-buttons">
<button class="reset-btn" id="reset-btn">Zurücksetzen</button>
<button class="start-btn" id="start-workflow-btn">Workflow starten</button>
</div>
</div>
<!-- Rechte Seite - Ausführung und Ergebnisse -->
<div class="results-panel">
<h2>Ausführung & Ergebnisse</h2>
<!-- Ausführungsprotokoll -->
<div class="card">
<h3>Ausführungsprotokoll</h3>
<div class="log-container" id="execution-log">
<div class="log-empty-state">Workflow noch nicht gestartet. Protokoll wird hier angezeigt.</div>
</div>
</div>
<!-- Ergebnisse -->
<div class="card">
<h3>Ergebnisse</h3>
<div class="results-container" id="results-container">
<div class="results-empty-state" id="empty-results-state">
<div>Keine Ergebnisse verfügbar</div>
<div class="sub-text">Führen Sie einen Workflow aus, um Ergebnisse zu sehen</div>
</div>
<div class="results-list" id="results-list">
<!-- Wird dynamisch gefüllt -->
</div>
</div>
</div>
</div>
</div>
<!-- Meine Daten Ansicht -->
<div id="data-view" style="display:none;">
<h2>Meine Daten</h2>
<div class="card">
<h3>Verfügbare Dateien</h3>
<div class="files-container">
<ul class="file-list" id="my-files-list">
<!-- Wird dynamisch gefüllt -->
</ul>
<button class="upload-btn" id="upload-files-btn">
<i class="fas fa-upload"></i> Neue Dateien hochladen
</button>
</div>
</div>
</div>
</main>
</div>
<script src="script.js"></script>
</body>
</html>

654
frontend/script.js Normal file
View file

@ -0,0 +1,654 @@
document.addEventListener('DOMContentLoaded', function() {
// Backend API Basis-URL
const API_BASE_URL = 'http://localhost:8000';
// Status-Management
let state = {
workspaces: [],
files: [],
currentWorkspace: null,
selectedFiles: [],
selectedAgents: [],
prompt: "",
logs: [],
results: [],
isRunning: false,
currentWorkflowId: null
};
let activeView = 'workflow'; // Standard-Ansicht beim Start
// DOM-Elemente für Menüpunkte
const workflowLink = document.querySelector('a[href="#workflow"]').parentElement;
const dataLink = document.querySelector('a[href="#data"]').parentElement;
const promptsLink = document.querySelector('a[href="#prompts"]').parentElement;
const agentsLink = document.querySelector('a[href="#agents"]').parentElement;
// DOM-Elemente für die Ansichten
const workflowView = document.querySelector('.workflow-container');
const dataView = document.getElementById('data-view');
const promptsView = document.getElementById('prompts-view');
const agentsView = document.getElementById('agents-view');
// DOM-Elemente
const workspaceList = document.getElementById('workspace-list');
const availableFiles = document.getElementById('available-files');
const selectedFiles = document.getElementById('selected-files');
const emptyFilesState = document.getElementById('empty-files-state');
const fileInput = document.getElementById('file-input');
const uploadBtn = document.getElementById('upload-btn');
const promptInput = document.getElementById('prompt-input');
const agentList = document.getElementById('agent-list');
const resetBtn = document.getElementById('reset-btn');
const startWorkflowBtn = document.getElementById('start-workflow-btn');
const executionLog = document.getElementById('execution-log');
const resultsContainer = document.getElementById('results-container');
const emptyResultsState = document.getElementById('empty-results-state');
const resultsList = document.getElementById('results-list');
// API-Funktionen
async function fetchWorkspaces() {
try {
const response = await fetch(`${API_BASE_URL}/workspaces`);
if (!response.ok) throw new Error('Fehler beim Abrufen der Workspaces');
const workspaces = await response.json();
state.workspaces = workspaces;
if (workspaces.length > 0) {
state.currentWorkspace = workspaces[0];
}
renderWorkspaces();
return workspaces;
} catch (error) {
console.error('Fehler beim Abrufen der Workspaces:', error);
return [];
}
}
async function fetchFiles() {
try {
const response = await fetch(`${API_BASE_URL}/files`);
if (!response.ok) throw new Error('Fehler beim Abrufen der Dateien');
const files = await response.json();
state.files = files;
renderFiles();
return files;
} catch (error) {
console.error('Fehler beim Abrufen der Dateien:', error);
return [];
}
}
async function uploadFile(file) {
try {
const formData = new FormData();
formData.append('file', file);
const response = await fetch(`${API_BASE_URL}/files/upload`, {
method: 'POST',
body: formData
});
if (!response.ok) throw new Error('Fehler beim Hochladen der Datei');
const newFile = await response.json();
return newFile;
} catch (error) {
console.error('Fehler beim Hochladen der Datei:', error);
return null;
}
}
async function runWorkflowAPI(workflowData) {
try {
const response = await fetch(`${API_BASE_URL}/workflow/run`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(workflowData)
});
if (!response.ok) throw new Error('Fehler beim Starten des Workflows');
const result = await response.json();
return result;
} catch (error) {
console.error('Fehler beim Starten des Workflows:', error);
return null;
}
}
async function fetchWorkflowStatus(workflowId) {
try {
const response = await fetch(`${API_BASE_URL}/workflow/${workflowId}/status`);
if (!response.ok) throw new Error('Fehler beim Abrufen des Workflow-Status');
const status = await response.json();
return status;
} catch (error) {
console.error('Fehler beim Abrufen des Workflow-Status:', error);
return null;
}
}
async function fetchWorkflowLogs(workflowId) {
try {
const response = await fetch(`${API_BASE_URL}/workflow/${workflowId}/logs`);
if (!response.ok) throw new Error('Fehler beim Abrufen der Workflow-Logs');
const logs = await response.json();
return logs;
} catch (error) {
console.error('Fehler beim Abrufen der Workflow-Logs:', error);
return [];
}
}
async function fetchWorkflowResults(workflowId) {
try {
const response = await fetch(`${API_BASE_URL}/workflow/${workflowId}/results`);
if (!response.ok) throw new Error('Fehler beim Abrufen der Workflow-Ergebnisse');
const results = await response.json();
return results;
} catch (error) {
console.error('Fehler beim Abrufen der Workflow-Ergebnisse:', error);
return [];
}
}
// Funktion zum Wechseln der Ansicht
function setActiveView(view) {
// Aktive Klasse von allen Menüpunkten entfernen
workflowLink.classList.remove('active');
dataLink.classList.remove('active');
promptsLink.classList.remove('active');
agentsLink.classList.remove('active');
// Alle Ansichten ausblenden
workflowView.style.display = 'none';
dataView.style.display = 'none';
promptsView.style.display = 'none';
agentsView.style.display = 'none';
// Aktive Ansicht und Menüpunkt setzen
activeView = view;
switch (view) {
case 'workflow':
workflowLink.classList.add('active');
workflowView.style.display = 'flex';
break;
case 'data':
dataLink.classList.add('active');
dataView.style.display = 'block';
// Aktualisiere Daten bei Ansichtswechsel
fetchFiles();
break;
case 'prompts':
promptsLink.classList.add('active');
promptsView.style.display = 'block';
// Prompts laden wenn wir die Ansicht wechseln
if (state.currentWorkspace) {
renderPrompts();
}
break;
case 'agents':
agentsLink.classList.add('active');
agentsView.style.display = 'block';
// Agenten laden wenn wir die Ansicht wechseln
if (state.currentWorkspace) {
renderAgentsList();
}
break;
}
}
// Workspaces rendern
function renderWorkspaces() {
workspaceList.innerHTML = '';
state.workspaces.forEach(workspace => {
const li = document.createElement('li');
li.className = `workspace-item ${workspace.id === state.currentWorkspace?.id ? 'active' : ''}`;
li.innerHTML = `
<i class="fas fa-folder"></i>
<span>${workspace.name}</span>
`;
li.addEventListener('click', async () => {
state.currentWorkspace = workspace;
state.selectedFiles = [];
state.selectedAgents = [];
state.prompt = "";
renderWorkspaces();
renderFiles();
renderSelectedFiles();
renderAgents();
});
workspaceList.appendChild(li);
});
}
// Verfügbare Dateien rendern
function renderFiles() {
availableFiles.innerHTML = '';
state.files.forEach(file => {
const li = document.createElement('li');
li.className = 'file-item';
const isSelected = state.selectedFiles.some(f => f.id === file.id);
li.innerHTML = `
<div class="file-item-name">
<input type="checkbox" id="file-${file.id}" ${isSelected ? 'checked' : ''}>
<label for="file-${file.id}">
<i class="fas ${file.type === 'document' ? 'fa-file-alt' : 'fa-file-image'}"></i>
${file.name}
</label>
</div>
<span class="file-item-info">${file.size || ''}</span>
`;
const checkbox = li.querySelector(`#file-${file.id}`);
checkbox.addEventListener('change', () => {
if (checkbox.checked) {
state.selectedFiles.push(file);
} else {
state.selectedFiles = state.selectedFiles.filter(f => f.id !== file.id);
}
renderSelectedFiles();
});
availableFiles.appendChild(li);
});
}
// Ausgewählte Dateien rendern
function renderSelectedFiles() {
if (state.selectedFiles.length === 0) {
emptyFilesState.style.display = 'flex';
selectedFiles.innerHTML = '';
} else {
emptyFilesState.style.display = 'none';
selectedFiles.innerHTML = '';
state.selectedFiles.forEach(file => {
const li = document.createElement('li');
li.className = 'selected-file-item';
li.innerHTML = `
<div class="file-item-name">
<i class="fas ${file.type === 'document' ? 'fa-file-alt' : 'fa-file-image'}"></i>
${file.name}
</div>
<button class="remove-file-btn" data-id="${file.id}">
<i class="fas fa-times"></i>
</button>
`;
selectedFiles.appendChild(li);
});
// Event-Listener für Entfernen-Buttons
document.querySelectorAll('.remove-file-btn').forEach(btn => {
btn.addEventListener('click', () => {
const fileId = btn.getAttribute('data-id');
state.selectedFiles = state.selectedFiles.filter(f => f.id !== fileId);
renderSelectedFiles();
renderFiles();
});
});
}
}
// Prompts rendern
function renderPrompts() {
const promptsContainer = document.getElementById('prompts-list');
if (!promptsContainer) return;
promptsContainer.innerHTML = '';
if (!state.currentWorkspace || !state.currentWorkspace.prompts || state.currentWorkspace.prompts.length === 0) {
promptsContainer.innerHTML = '<div class="empty-state">Keine Prompts im aktuellen Workspace verfügbar.</div>';
return;
}
state.currentWorkspace.prompts.forEach(prompt => {
const promptItem = document.createElement('div');
promptItem.className = 'prompt-item';
promptItem.innerHTML = `
<div class="prompt-content">${prompt.content}</div>
<div class="prompt-meta">Erstellt am: ${new Date(prompt.created_at).toLocaleString()}</div>
<div class="prompt-actions">
<button class="use-prompt-btn" data-id="${prompt.id}">
<i class="fas fa-play"></i> Verwenden
</button>
</div>
`;
// Event-Listener für "Verwenden"-Button
const useBtn = promptItem.querySelector('.use-prompt-btn');
useBtn.addEventListener('click', () => {
// Prompt zum aktuellen Workflow hinzufügen
promptInput.value = prompt.content;
state.prompt = prompt.content;
setActiveView('workflow');
});
promptsContainer.appendChild(promptItem);
});
}
// Agentenliste rendern
function renderAgentsList() {
const agentsContainer = document.getElementById('agents-list-container');
if (!agentsContainer) return;
agentsContainer.innerHTML = '';
if (!state.currentWorkspace || !state.currentWorkspace.agents || state.currentWorkspace.agents.length === 0) {
agentsContainer.innerHTML = '<div class="empty-state">Keine Agenten im aktuellen Workspace verfügbar.</div>';
return;
}
state.currentWorkspace.agents.forEach(agent => {
const agentItem = document.createElement('div');
agentItem.className = 'agent-list-item';
agentItem.innerHTML = `
<div class="agent-header">
<h4>${agent.name}</h4>
<span class="agent-type">${agent.type}</span>
</div>
<div class="agent-description">${agent.description || ''}</div>
<div class="agent-capabilities">
${agent.capabilities ? agent.capabilities.map(cap => `<span class="capability-tag">${cap}</span>`).join('') : ''}
</div>
`;
agentsContainer.appendChild(agentItem);
});
}
// Agenten für Workflow rendern
function renderAgents() {
agentList.innerHTML = '';
if (!state.currentWorkspace || !state.currentWorkspace.agents || state.currentWorkspace.agents.length === 0) {
agentList.innerHTML = '<div class="empty-state">Keine Agenten im ausgewählten Workspace verfügbar.</div>';
return;
}
state.currentWorkspace.agents.forEach(agent => {
const li = document.createElement('li');
li.className = 'agent-item';
const isSelected = state.selectedAgents.some(a => a.id === agent.id);
li.innerHTML = `
<div class="agent-header">
<input type="checkbox" id="agent-${agent.id}" class="agent-checkbox" ${isSelected ? 'checked' : ''}>
<label for="agent-${agent.id}" class="agent-name">${agent.name}</label>
</div>
<div class="agent-description">${agent.description || ''}</div>
`;
const checkbox = li.querySelector(`#agent-${agent.id}`);
checkbox.addEventListener('change', () => {
if (checkbox.checked) {
state.selectedAgents.push(agent);
} else {
state.selectedAgents = state.selectedAgents.filter(a => a.id !== agent.id);
}
});
agentList.appendChild(li);
});
}
// Ausführungsprotokoll aktualisieren und rendern
function updateLogs(logs) {
state.logs = logs;
renderLogs();
}
// Protokoll rendern
function renderLogs() {
if (state.logs.length === 0) {
executionLog.innerHTML = '<div class="log-empty-state">Workflow noch nicht gestartet. Protokoll wird hier angezeigt.</div>';
return;
}
executionLog.innerHTML = '';
state.logs.forEach(log => {
const logEntry = document.createElement('div');
logEntry.className = 'log-entry';
logEntry.innerHTML = `
<span class="log-time">[${new Date(log.timestamp).toLocaleTimeString()}]</span>
<span class="log-message log-${log.type}">${log.message}</span>
`;
executionLog.appendChild(logEntry);
});
// Zum Ende scrollen
executionLog.scrollTop = executionLog.scrollHeight;
}
// Ergebnisse aktualisieren und rendern
function updateResults(results) {
state.results = results;
renderResults();
}
// Ergebnisse rendern
function renderResults() {
if (state.results.length === 0) {
emptyResultsState.style.display = 'flex';
resultsList.style.display = 'none';
return;
}
emptyResultsState.style.display = 'none';
resultsList.style.display = 'block';
resultsList.innerHTML = '';
state.results.forEach(result => {
const resultItem = document.createElement('div');
resultItem.className = 'result-item';
if (result.type === 'text') {
resultItem.innerHTML = `
<div class="result-header">
<div>
<div class="result-title">${result.title}</div>
<div class="result-agent">Erstellt von: ${result.agent_name}</div>
</div>
<button class="save-btn">
<i class="fas fa-save"></i> Speichern
</button>
</div>
<div class="result-content">${result.content}</div>
`;
} else if (result.type === 'chart') {
resultItem.innerHTML = `
<div class="result-header">
<div>
<div class="result-title">${result.title}</div>
<div class="result-agent">Erstellt von: ${result.agent_name}</div>
</div>
<button class="save-btn">
<i class="fas fa-save"></i> Speichern
</button>
</div>
<div class="chart-container">
<div class="chart-title">Umsatzentwicklung nach Quartal</div>
<div>${result.content}</div>
</div>
`;
}
resultsList.appendChild(resultItem);
});
}
// Workflow-Ausführung
async function runWorkflow() {
if (state.selectedFiles.length === 0 || state.selectedAgents.length === 0 || !promptInput.value.trim()) {
alert("Bitte wählen Sie Dateien und Agenten aus und geben Sie einen Prompt ein.");
return;
}
state.isRunning = true;
state.prompt = promptInput.value;
state.logs = [];
state.results = [];
startWorkflowBtn.textContent = 'Wird ausgeführt...';
startWorkflowBtn.classList.add('running');
startWorkflowBtn.disabled = true;
renderLogs();
renderResults();
// API-Aufruf an das Backend
const workflowData = {
workspace_id: state.currentWorkspace.id,
files: state.selectedFiles.map(file => file.id),
agents: state.selectedAgents.map(agent => agent.id),
prompt: state.prompt,
workflow_name: "Workflow " + new Date().toLocaleString()
};
try {
// Workflow starten
const response = await runWorkflowAPI(workflowData);
if (response && response.workflow_id) {
state.currentWorkflowId = response.workflow_id;
// Polling für Status, Logs und Ergebnisse
pollWorkflowStatus();
} else {
throw new Error("Workflow konnte nicht gestartet werden");
}
} catch (error) {
console.error("Fehler beim Starten des Workflows:", error);
state.isRunning = false;
startWorkflowBtn.textContent = 'Workflow starten';
startWorkflowBtn.classList.remove('running');
startWorkflowBtn.disabled = false;
alert("Fehler beim Starten des Workflows: " + error.message);
}
}
// Polling für Workflow-Status, Logs und Ergebnisse
async function pollWorkflowStatus() {
if (!state.currentWorkflowId || !state.isRunning) return;
try {
// Status abrufen
const status = await fetchWorkflowStatus(state.currentWorkflowId);
// Logs abrufen und anzeigen
const logs = await fetchWorkflowLogs(state.currentWorkflowId);
updateLogs(logs);
// Wenn der Workflow abgeschlossen ist, Ergebnisse abrufen
if (status && (status.status === 'completed' || status.status === 'failed')) {
const results = await fetchWorkflowResults(state.currentWorkflowId);
updateResults(results);
state.isRunning = false;
startWorkflowBtn.textContent = 'Workflow starten';
startWorkflowBtn.classList.remove('running');
startWorkflowBtn.disabled = false;
return; // Polling beenden
}
// Weiteres Polling nach kurzer Verzögerung
setTimeout(pollWorkflowStatus, 2000);
} catch (error) {
console.error("Fehler beim Abrufen des Workflow-Status:", error);
state.isRunning = false;
startWorkflowBtn.textContent = 'Workflow starten';
startWorkflowBtn.classList.remove('running');
startWorkflowBtn.disabled = false;
}
}
// Zurücksetzen
function resetWorkflow() {
state.selectedFiles = [];
state.selectedAgents = [];
promptInput.value = "";
state.prompt = "";
renderFiles();
renderSelectedFiles();
renderAgents();
}
// Event-Listener für Menüpunkte
workflowLink.addEventListener('click', function(e) {
e.preventDefault();
setActiveView('workflow');
});
dataLink.addEventListener('click', function(e) {
e.preventDefault();
setActiveView('data');
});
promptsLink.addEventListener('click', function(e) {
e.preventDefault();
setActiveView('prompts');
});
agentsLink.addEventListener('click', function(e) {
e.preventDefault();
setActiveView('agents');
});
// Event-Listener
uploadBtn.addEventListener('click', () => fileInput.click());
fileInput.addEventListener('change', async (e) => {
if (e.target.files.length > 0) {
// Dateien hochladen
for (const file of e.target.files) {
try {
const newFile = await uploadFile(file);
if (newFile) {
state.files.push(newFile);
state.selectedFiles.push(newFile);
}
} catch (error) {
console.error("Fehler beim Hochladen der Datei:", error);
}
}
renderFiles();
renderSelectedFiles();
fileInput.value = "";
}
});
promptInput.addEventListener('input', (e) => {
state.prompt = e.target.value;
});
resetBtn.addEventListener('click', resetWorkflow);
startWorkflowBtn.addEventListener('click', runWorkflow);
// Initialisierung
async function init() {
// Daten vom Backend laden
await fetchWorkspaces();
await fetchFiles();
// UI initialisieren
renderFiles();
renderSelectedFiles();
renderAgents();
renderLogs();
renderResults();
// Initial die Standard-Ansicht setzen
setActiveView(activeView);
}
// Anwendung initialisieren
init();
});

555
frontend/styles.css Normal file
View file

@ -0,0 +1,555 @@
/* Grundlegende Resets und Fonts */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: #f0f2f5;
color: #333;
line-height: 1.6;
}
ul {
list-style: none;
}
a {
text-decoration: none;
color: inherit;
}
button {
cursor: pointer;
border: none;
background: none;
font-family: inherit;
}
/* Navbar */
.navbar {
background-color: #2563eb;
color: white;
padding: 1rem;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.navbar-container {
display: flex;
justify-content: space-between;
align-items: center;
max-width: 1800px;
margin: 0 auto;
}
.navbar-logo {
font-size: 1.5rem;
font-weight: 600;
}
.navbar-user {
display: flex;
align-items: center;
gap: 0.75rem;
}
.icon-button {
display: flex;
align-items: center;
justify-content: center;
width: 2rem;
height: 2rem;
border-radius: 50%;
color: white;
transition: background-color 0.2s;
}
.icon-button:hover {
background-color: rgba(255, 255, 255, 0.2);
}
/* App Container Layout */
.app-container {
display: flex;
max-width: 1800px;
margin: 0 auto;
min-height: calc(100vh - 4rem);
}
/* Sidebar */
.sidebar {
width: 250px;
background-color: white;
box-shadow: 2px 0 5px rgba(0, 0, 0, 0.05);
padding: 1.5rem 1rem;
flex-shrink: 0;
}
.workspace-section h2 {
font-size: 1.2rem;
margin-bottom: 0.5rem;
font-weight: 600;
}
.workspace-list {
margin-bottom: 1.5rem;
}
.workspace-item {
padding: 0.5rem 0.75rem;
border-radius: 0.375rem;
margin-bottom: 0.25rem;
display: flex;
align-items: center;
cursor: pointer;
}
.workspace-item i {
margin-right: 0.5rem;
color: #4b5563;
}
.workspace-item:hover {
background-color: #f3f4f6;
}
.workspace-item.active {
background-color: #e0f2fe;
color: #1d4ed8;
}
.sidebar-nav {
margin-top: 1rem;
}
.sidebar-item {
padding: 0.75rem;
border-radius: 0.375rem;
margin-bottom: 0.25rem;
}
.sidebar-item a {
display: flex;
align-items: center;
}
.sidebar-item i {
margin-right: 0.75rem;
width: 1.25rem;
text-align: center;
}
.sidebar-item:hover {
background-color: #f3f4f6;
}
.sidebar-item.active {
background-color: #e0f2fe;
color: #1d4ed8;
}
/* Main Content */
.main-content {
flex: 1;
padding: 1.5rem;
overflow-y: auto;
}
.workflow-container {
display: flex;
gap: 1.5rem;
}
.config-panel, .results-panel {
flex: 1;
}
/* Cards */
.card {
background-color: white;
border-radius: 0.5rem;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
padding: 1.25rem;
margin-bottom: 1.25rem;
}
h2 {
font-size: 1.5rem;
font-weight: 600;
margin-bottom: 1rem;
}
h3 {
font-size: 1.125rem;
font-weight: 600;
margin-bottom: 1rem;
color: #4b5563;
}
h4 {
font-size: 0.875rem;
font-weight: 600;
margin-bottom: 0.5rem;
color: #6b7280;
}
/* File Selection */
.files-container {
display: flex;
flex-direction: column;
gap: 1rem;
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 0.5rem;
font-weight: 500;
}
.upload-btn {
background-color: #fff;
border: 1px solid #d1d5db;
border-radius: 0.375rem;
padding: 0.375rem 0.75rem;
font-size: 0.875rem;
color: #4b5563;
display: flex;
align-items: center;
gap: 0.375rem;
transition: all 0.2s;
}
.upload-btn:hover {
background-color: #f9fafb;
border-color: #9ca3af;
}
.file-list {
max-height: 200px;
overflow-y: auto;
border: 1px solid #e5e7eb;
border-radius: 0.375rem;
background-color: #f9fafb;
}
.file-item {
padding: 0.625rem;
border-bottom: 1px solid #e5e7eb;
display: flex;
align-items: center;
justify-content: space-between;
background-color: white;
}
.file-item:last-child {
border-bottom: none;
}
.file-item-name {
display: flex;
align-items: center;
gap: 0.5rem;
flex: 1;
}
.file-item-info {
font-size: 0.75rem;
color: #6b7280;
}
.selected-files-container {
border: 1px dashed #d1d5db;
border-radius: 0.375rem;
min-height: 100px;
background-color: #f9fafb;
}
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100px;
color: #9ca3af;
gap: 0.5rem;
}
.empty-state i {
font-size: 1.5rem;
}
.selected-file-list {
padding: 0.5rem;
}
.selected-file-item {
background-color: white;
border: 1px solid #e5e7eb;
border-radius: 0.375rem;
padding: 0.5rem 0.75rem;
margin-bottom: 0.5rem;
display: flex;
justify-content: space-between;
align-items: center;
}
.selected-file-item:last-child {
margin-bottom: 0;
}
.remove-file-btn {
color: #ef4444;
width: 1.5rem;
height: 1.5rem;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
transition: background-color 0.2s;
}
.remove-file-btn:hover {
background-color: #fee2e2;
}
/* Prompt input */
#prompt-input {
width: 100%;
min-height: 150px;
border: 1px solid #d1d5db;
border-radius: 0.375rem;
padding: 0.75rem;
font-family: inherit;
resize: vertical;
font-size: 0.875rem;
}
#prompt-input:focus {
outline: none;
border-color: #2563eb;
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
}
/* Agent list */
.agent-list {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.agent-item {
background-color: #f9fafb;
border: 1px solid #e5e7eb;
border-radius: 0.375rem;
padding: 0.75rem;
}
.agent-header {
display: flex;
align-items: center;
}
.agent-checkbox {
margin-right: 0.75rem;
}
.agent-name {
font-weight: 500;
}
.agent-description {
margin-top: 0.25rem;
font-size: 0.875rem;
color: #6b7280;
margin-left: 2rem;
}
/* Action buttons */
.action-buttons {
display: flex;
justify-content: space-between;
gap: 0.75rem;
}
.reset-btn {
background-color: #f3f4f6;
color: #4b5563;
border-radius: 0.375rem;
padding: 0.625rem 1.25rem;
font-weight: 500;
transition: background-color 0.2s;
}
.reset-btn:hover {
background-color: #e5e7eb;
}
.start-btn {
background-color: #10b981;
color: white;
border-radius: 0.375rem;
padding: 0.625rem 1.25rem;
font-weight: 500;
transition: background-color 0.2s;
}
.start-btn:hover {
background-color: #059669;
}
.start-btn.running {
background-color: #9ca3af;
cursor: not-allowed;
}
/* Log Container */
.log-container {
background-color: #111827;
color: #34d399;
border-radius: 0.375rem;
padding: 0.75rem;
font-family: 'Consolas', 'Monaco', monospace;
font-size: 0.875rem;
height: 300px;
overflow-y: auto;
}
.log-empty-state {
color: #6b7280;
font-style: italic;
}
.log-entry {
margin-bottom: 0.25rem;
}
.log-time {
color: #9ca3af;
}
.log-message {
margin-left: 0.5rem;
}
.log-info {
color: #ffffff;
}
.log-success {
color: #34d399;
}
.log-error {
color: #f87171;
}
.log-start {
color: #60a5fa;
}
.log-complete {
color: #c084fc;
}
/* Results Container */
.results-container {
min-height: 200px;
}
.results-empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background-color: #f9fafb;
border: 1px solid #e5e7eb;
border-radius: 0.375rem;
padding: 2.5rem 1rem;
color: #6b7280;
text-align: center;
}
.sub-text {
font-size: 0.875rem;
color: #9ca3af;
margin-top: 0.25rem;
}
.results-list {
display: flex;
flex-direction: column;
gap: 1rem;
}
.result-item {
background-color: #f9fafb;
border: 1px solid #e5e7eb;
border-radius: 0.375rem;
padding: 1rem;
}
.result-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 0.75rem;
}
.result-title {
font-weight: 600;
font-size: 1.125rem;
}
.result-agent {
font-size: 0.75rem;
color: #6b7280;
}
.save-btn {
display: flex;
align-items: center;
gap: 0.25rem;
color: #2563eb;
font-size: 0.875rem;
font-weight: 500;
padding: 0.25rem 0.5rem;
border-radius: 0.25rem;
transition: background-color 0.2s;
}
.save-btn:hover {
background-color: #e0f2fe;
}
.result-content {
background-color: white;
border: 1px solid #e5e7eb;
border-radius: 0.375rem;
padding: 0.75rem;
font-size: 0.875rem;
white-space: pre-line;
}
.chart-container {
background-color: white;
border: 1px solid #e5e7eb;
border-radius: 0.375rem;
padding: 0.75rem;
text-align: center;
height: 200px;
display: flex;
align-items: center;
justify-content: center;
color: #6b7280;
}
.chart-title {
border-bottom: 1px solid #e5e7eb;
padding-bottom: 0.5rem;
margin-bottom: 0.75rem;
font-weight: 500;
}

View file

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

View file

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

View file

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

View file

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View file

@ -0,0 +1,9 @@
Django
gunicorn
django-cors-headers
djangorestframework
whitenoise
openai
Flask
requests
gunicorn

22
ida_prod/start.sh Normal file
View file

@ -0,0 +1,22 @@
#!/bin/bash
# Installiere Abhängigkeiten
cd backend
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt
# Migration durchführen
python manage.py migrate
python manage.py collectstatic --noinput
# React bauen
cd ../frontend
npm install
npm run build
# React-Frontend nach Django-Statisches-Verzeichnis verschieben
cp -r build/* ../backend/staticfiles/
# Starte Gunicorn (Django-Server)
cd ../backend
gunicorn backend.wsgi --bind 0.0.0.0:8000

168
readme.md Normal file
View file

@ -0,0 +1,168 @@
# Data Platform - Multi-Agent Service
Eine Full-Stack-Webapplikation für die Ausführung von Multi-Agent-Workflows zur Verarbeitung und Analyse von Daten basierend auf natürlichsprachlichen Benutzeranfragen.
Hier: http://localhost:8000/docs
## Übersicht
Das System ermöglicht Benutzern:
- Hochladen und Verwalten verschiedener Datendateien
- Definieren von Prompts/Anweisungen für KI-Agenten
- Auswählen und Kombinieren spezialisierter Agenten
- Ausführen von Workflows mit Echtzeit-Protokollierung
- Visualisieren und Verwalten der Ergebnisse
## Projektstruktur
Das Projekt besteht aus zwei Hauptkomponenten:
### Frontend (HTML/CSS/JavaScript)
- `index.html` - Hauptstruktur der Benutzeroberfläche
- `styles.css` - Umfangreiches CSS für das responsive Design
- `script.js` - Client-seitige Logik für Interaktionen
### Backend (Python/FastAPI)
- `app.py` - Hauptanwendung mit API-Endpunkten
- `models.py` - Datenmodelle und Validierungsschemas
- `database.py` - Datenpersistenz (JSON-basiert für Demo)
- `agent_service.py` - Multi-Agent-Orchestrierung
- `requirements.txt` - Python-Abhängigkeiten
## Hauptfunktionen
### Workspace-Management
- Mehrere Workspaces für verschiedene Projekte
- Organisierte Gruppenarbeit mit geteilten Ressourcen
### Datei-Verarbeitung
- Upload verschiedener Dateitypen (PDF, Excel, Bilder, etc.)
- Automatische Erkennung und Kategorisierung
### Agent-Orchestrierung
- Kombination verschiedener Agent-Typen:
- **Datenanalyse-Agent**: Extrahiert Insights aus strukturierten Daten
- **Visualisierungs-Agent**: Erstellt Diagramme und visuelle Darstellungen
- **Text-Generator**: Verfasst Berichte und Zusammenfassungen
- **Web-Scraper**: Sammelt externe Daten
- **Marktanalyse-Agent**: Spezialisiert auf Wettbewerbsanalyse
### Workflow-Ausführung
- Echtzeit-Protokollierung des Fortschritts
- Vollständige Nachverfolgbarkeit aller Schritte
### Ergebnis-Management
- Strukturierte Darstellung von Analysen, Diagrammen und Berichten
- Export- und Sharing-Funktionen
## Installation und Einrichtung
### Voraussetzungen
- Python 3.8+
- Ein moderner Webbrowser
- Optional: Node.js für Entwicklungswerkzeuge
### Frontend-Installation
1. Klonen des Repositories
2. Platzieren der Frontend-Dateien auf einem Webserver oder lokalen Entwicklungsserver:
```bash
# Mit Python einen einfachen HTTP-Server starten
python -m http.server 8080
```
### Backend-Installation
1. Virtuelle Umgebung erstellen und aktivieren:
```bash
python -m venv venv
source venv/bin/activate # Linux/Mac
venv\Scripts\activate # Windows
```
2. Abhängigkeiten installieren:
```bash
pip install -r requirements.txt
```
3. Server starten:
```bash
uvicorn app:app --reload --host 0.0.0.0 --port 8000
```
4. Zugangspunkte:
- Frontend: `http://localhost:8080`
- Backend API: `http://localhost:8000`
- API-Dokumentation: `http://localhost:8000/docs`
## Verwendung: Der Customer Journey
### 1. Workspace auswählen oder erstellen
- Wählen Sie einen vorhandenen Workspace oder erstellen Sie einen neuen für Ihr Projekt
### 2. Dateien hochladen
- Laden Sie die zu analysierenden Dateien hoch
- Das System erkennt automatisch Dateitypen und bereitet sie für die Verarbeitung vor
### 3. Prompt formulieren
- Definieren Sie in natürlicher Sprache, was Sie analysieren möchten
- Je präziser Ihre Anweisungen, desto zielgerichteter die Ergebnisse
### 4. Agenten konfigurieren
- Wählen Sie die passenden Agenten für Ihre Aufgabe
- Kombinieren Sie Agenten für umfassendere Analysen
- Datenanalyse → Visualisierung → Textgenerierung
### 5. Workflow ausführen
- Starten Sie den Workflow und verfolgen Sie die Ausführung in Echtzeit
- Im linken Bereich sehen Sie die Konfiguration
- Im rechten Bereich werden Protokoll und Ergebnisse angezeigt
### 6. Ergebnisse verwenden
- Sehen Sie Analysen, Diagramme und Berichte ein
- Exportieren oder teilen Sie die Ergebnisse
- Iterieren Sie bei Bedarf mit angepassten Prompts oder Agent-Konfigurationen
## Anpassung und Erweiterung
### Integration mit echten KI-Diensten
Die aktuelle Implementierung simuliert die Agent-Verarbeitung. Für eine produktive Nutzung:
1. Erweitern Sie `agent_service.py` mit Integrationen zu:
- OpenAI GPT-Modellen (ChatGPT, GPT-4)
- Claude von Anthropic
- Eigenentwickelten Modellen mit spezieller Expertise
2. Implementieren Sie robuste Datei-Parser für:
- PDF-Dokumente mit OCR
- Excel- und CSV-Verarbeitung
- Bild- und Medienanalyse
3. Ergänzen Sie Authentifizierung und Autorisierung:
- Benutzer-Accounts mit Rollenkonzept
- API-Schlüsselverwaltung für externe Dienste
- Sichere Datenspeicherung
### Datenbank-Migration
Für größere Installationen die JSON-basierte Datenbank ersetzen durch:
- PostgreSQL für relationale Daten
- MongoDB für Dokumente und unstrukturierte Daten
- Redis für Caching und Workflow-Status
## Technische Details
### Frontend-Architektur
- Vanilla JavaScript ohne Framework-Abhängigkeiten
- Modularer CSS-Ansatz für einfache Anpassungen
- Responsive Design für Desktop und mobile Nutzung
### Backend-Architektur
- FastAPI für hohe Performance und automatische API-Dokumentation
- Asynchrone Verarbeitung für parallele Agent-Ausführung
- Erweiterbare Service-Struktur für einfache Integration neuer Agententypen
## Lizenz
PRIVATE LICENSE PATRICK MOTSCH ValueOn AG
---
Für Fragen oder Unterstützung wenden Sie sich bitte an p.motsch@valueon.ch

View file

@ -1,9 +1,11 @@
Django
gunicorn
django-cors-headers
djangorestframework
whitenoise
openai
Flask
requests
gunicorn
fastapi==0.103.1
uvicorn==0.23.2
python-multipart==0.0.6
pydantic==2.4.2
typing-extensions==4.8.0
python-dateutil==2.8.2
six==1.16.0
starlette==0.27.0
anyio==3.7.1
idna==3.4
sniffio==1.3.0

53
start.bat Normal file
View file

@ -0,0 +1,53 @@
@echo off
echo Data Platform - Multi-Agent Service
echo Startskript fuer Frontend und Backend
echo ----------------------------------------
:: Verzeichnisstruktur erstellen, falls sie nicht existiert
if not exist backend\data mkdir backend\data
if not exist backend\uploads mkdir backend\uploads
if not exist backend\results mkdir backend\results
if not exist backend\static mkdir backend\static
:: Prüfen, ob Python installiert ist
python --version >nul 2>&1
if %errorlevel% neq 0 (
echo Python ist nicht installiert. Bitte installieren Sie Python 3.8 oder hoeher.
exit /b 1
)
:: Virtuelle Umgebung erstellen, falls sie nicht existiert
if not exist backend\venv (
echo Erstelle virtuelle Python-Umgebung...
cd backend
python -m venv venv
cd ..
)
:: Virtuelle Umgebung aktivieren
echo Aktiviere virtuelle Umgebung...
call backend\venv\Scripts\activate
:: Abhängigkeiten installieren
echo Installiere Abhaengigkeiten...
pip install -r requirements.txt
:: Starte Backend in neuem Fenster
echo Starte Backend-Server...
start cmd /k "cd backend && call venv\Scripts\activate && uvicorn app:app --reload --host 0.0.0.0 --port 8000"
:: Kurz warten, um sicherzustellen, dass das Backend startet
timeout /t 2 >nul
:: Starte Frontend-Server in neuem Fenster
echo Starte Frontend-Server...
start cmd /k "cd frontend && python -m http.server 8080"
echo ----------------------------------------
echo Server wurden gestartet!
echo Frontend laeuft auf: http://localhost:8080
echo Backend API laeuft auf: http://localhost:8000
echo API-Dokumentation: http://localhost:8000/docs
echo Schliesse die Kommandozeilenfenster, um die Server zu beenden.
pause

View file

@ -1,22 +1,81 @@
#!/bin/bash
# Installiere Abhängigkeiten
# Farben für die Ausgabe
GREEN='\033[0;32m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
echo -e "${GREEN}Data Platform - Multi-Agent Service${NC}"
echo -e "${BLUE}Startskript für Frontend und Backend${NC}"
echo "----------------------------------------"
# Verzeichnisstruktur erstellen, falls sie nicht existiert
mkdir -p backend/data
mkdir -p backend/uploads
mkdir -p backend/results
mkdir -p backend/static
# Prüfen, ob Python installiert ist
if command -v python3 &>/dev/null; then
PYTHON_CMD="python3"
elif command -v python &>/dev/null; then
PYTHON_CMD="python"
else
echo "Python ist nicht installiert. Bitte installieren Sie Python 3.8 oder höher."
exit 1
fi
# Virtuelle Umgebung erstellen, falls sie nicht existiert
if [ ! -d "backend/venv" ]; then
echo "Erstelle virtuelle Python-Umgebung..."
cd backend
python -m venv venv
source venv/bin/activate
$PYTHON_CMD -m venv venv
cd ..
fi
# Virtuelle Umgebung aktivieren
echo "Aktiviere virtuelle Umgebung..."
source backend/venv/bin/activate 2>/dev/null || . backend/venv/bin/activate
# Abhängigkeiten installieren
echo "Installiere Abhängigkeiten..."
pip install -r requirements.txt
# Migration durchführen
python manage.py migrate
python manage.py collectstatic --noinput
# Backend als Hintergrundprozess starten
echo "Starte Backend-Server..."
cd backend
uvicorn app:app --reload --host 0.0.0.0 --port 8000 &
BACKEND_PID=$!
cd ..
# React bauen
cd ../frontend
npm install
npm run build
# Kurz warten, um sicherzustellen, dass das Backend startet
sleep 2
# React-Frontend nach Django-Statisches-Verzeichnis verschieben
cp -r build/* ../backend/staticfiles/
# Frontend-Server starten
echo "Starte Frontend-Server..."
cd frontend
$PYTHON_CMD -m http.server 8080 &
FRONTEND_PID=$!
cd ..
# Starte Gunicorn (Django-Server)
cd ../backend
gunicorn backend.wsgi --bind 0.0.0.0:8000
echo "----------------------------------------"
echo -e "${GREEN}Server wurden gestartet!${NC}"
echo "Frontend läuft auf: http://localhost:8080"
echo "Backend API läuft auf: http://localhost:8000"
echo "API-Dokumentation: http://localhost:8000/docs"
echo -e "${BLUE}Drücke STRG+C, um beide Server zu beenden${NC}"
# Funktion zum Beenden der Server bei STRG+C
cleanup() {
echo -e "\n${GREEN}Beende Server...${NC}"
kill $BACKEND_PID
kill $FRONTEND_PID
echo "Server wurden beendet"
exit 0
}
# Signal-Handler für STRG+C
trap cleanup SIGINT
# Warten auf Benutzeraktion
wait

55
test/data.py Normal file
View file

@ -0,0 +1,55 @@
data_object_model = {
"Mandate": {
"id": "mandate_001",
"users": [
{
"id": "user_001",
"settings": {
"id": "setting_001",
"preferences": {}
},
"sessions": [
{
"id": "session_001",
"timestamp": "2025-03-13T12:00:00Z",
"active": True
}
],
"workspaces": [
{
"id": "workspace_001",
"prompts": [
{
"id": "prompt_001",
"content": "",
"created_at": "2025-03-13T10:30:00Z"
}
],
"agents": [
{
"id": "agent_001",
"type": "assistant",
"capabilities": []
}
],
"dataObjectReferences": ["dataobject_001", "dataobject_002"]
}
],
"dataObjects": [
{
"id": "dataobject_001",
"type": "document",
"content": {},
"metadata": {}
},
{
"id": "dataobject_002",
"type": "image",
"content": {},
"metadata": {}
}
]
}
]
}
}