20 KiB

AISE AI Agent — Code Editor & Coding Assistant

Ein browserbasierter Code-Editor mit integriertem AI-Chat und autonomem Coding-Agent. Das Projekt kombiniert eine Streamlit-Oberfläche mit einem LLM-Backend und dem Model Context Protocol (MCP), um einen vollständigen AI-gestützten Entwicklungsworkflow zu ermöglichen.


Was kann das Projekt?

  • Code schreiben und bearbeiten im Browser mit Syntax-Highlighting (Ace Editor)
  • Code direkt ausführen und Output anzeigen, ohne die App zu verlassen
  • Mit einem AI-Assistenten chatten, der den aktuell geöffneten Code als Kontext kennt
  • Fehler mit AI debuggen — ein Klick schickt den Fehler + Code automatisch an den Chat
  • Einen autonomen Coding-Agent starten, der selbstständig Aufgaben plant und umsetzt (Dateien lesen/schreiben, Code ausführen, im Web suchen) — jeder Schritt wird dem Benutzer zur Genehmigung vorgelegt

Voraussetzungen

  • Python 3.13 oder neuer
  • pip
  • Zugang zu einem OpenAI-kompatiblen LLM-Endpoint (z.B. FHGR Silicon Server)

Installation

# 1. Repository klonen
git clone <repo-url>
cd AISE_AIAgent

# 2. Virtuelle Umgebung erstellen und aktivieren
python -m venv .venv

# Windows:
.venv\Scripts\activate
# macOS / Linux:
source .venv/bin/activate

# 3. Abhängigkeiten installieren
pip install -r requirements.txt

# 4. Umgebungsvariablen konfigurieren
copy .env.example .env   # Windows
# cp .env.example .env   # macOS/Linux

Dann .env öffnen und die Werte anpassen:

HOST=silicon.fhgr.ch     # Hostname des LLM-Servers
PORT=7080                 # Port des LLM-Servers
API_KEY=EMPTY             # API-Key (EMPTY wenn kein Key benötigt)
MODEL=qwen3.5-35b-a3b    # Modell-Name

Starten

# Streamlit Web-App starten (Hauptinterface)
streamlit run frontend/app.py

# Coding Agent im Terminal testen (ohne Streamlit)
python run_agent.py

Nach streamlit run frontend/app.py öffnet sich die App automatisch im Browser unter http://localhost:8501.


Projektstruktur

AISE_AIAgent/
│
├── frontend/                        # Gesamte Streamlit-Benutzeroberfläche
│   ├── app.py                       # Einstiegspunkt, App-Routing
│   ├── state.py                     # Session-State Initialisierung
│   ├── sidebar.py                   # Navigation + File Explorer
│   ├── editor.py                    # Code-Editor Ansicht
│   └── chat.py                      # Chat + Agent Mode Ansicht
│
├── backend/
│   ├── agent/                       # Coding Agent Logik
│   │   ├── coding_agent.py          # Agent Klasse (Plan→Act→Observe Loop)
│   │   ├── mcp_server_adapter.py    # MCP-Client (verbindet Agent mit Servern)
│   │   ├── mcp_server_adapter_RAG.py
│   │   ├── mcp_server_config.json    
│   │   └── servers/                 # MCP-Server (laufen als eigene Prozesse)
│   │       ├── mcp_server_code_execution.py
│   │       ├── mcp_server_file_search.py
│   │       └── mcp_server_web_search.py
│   │
│   └── managers/                    # Business-Logik Module
│       ├── chat_manager.py          # LLM-API Kommunikation + Chat-History
│       ├── file_manager.py          # Datei/Ordner-Operationen im Workspace
│       ├── execution_engine.py      # Code-Ausführung via Subprocess
│       ├── system_prompter.py       # System-Prompt Generierung
│       └── debug_logger.py          # Einfacher In-Memory Logger
│       └── search_manager.py         # Einfacher In-Memory Such-Manager
├── tests/ 
|   ├── conftest.py          
|   ├── test_file_manager.py          # Unit-Tests für FileManager
|   ├── test_chat_manager.py          # Unit-Tests für ChatManager
|   ├── test_execution_engine.py      # Unit-Tests für ExecutionEngine
|   ├── test_coding_agent.py          # Unit-Tests für CodingAgent
|   ├── test_search_manager.py        # Unit-Tests für SearchManager
|   ├── test_debug_logger.py          # Unit-Tests für DebugLogger
|   ├── test_mcp_server_code_execution.py
|   ├── test_mcp_server_web_search.py 
|   ├── test_system_prompter.py    
|   └── test_mcp_server_file_search.py          
|                          
├── workspace/                       # Arbeitsverzeichnis (Dateien des Editors/Agents)
└── .env.example                     # Vorlage für Umgebungsvariablen

Frontend

frontend/app.py — Einstiegspunkt der App

Dieser File ist der Startpunkt der gesamten Streamlit-Applikation. Er wird direkt mit streamlit run aufgerufen und übernimmt drei Aufgaben: Er setzt das globale Seitenlayout (Titel, breites Layout, minimales CSS-Padding), ruft init_state() auf um alle Session-State-Variablen zu initialisieren, und leitet den Benutzer basierend auf der Sidebar-Auswahl entweder zur Editor-Ansicht oder zur Chat-Ansicht weiter.


frontend/state.py — Session State Verwaltung

Streamlit rendert die gesamte App bei jeder Benutzerinteraktion neu. Um Daten zwischen diesen Reruns zu erhalten (offene Dateien, Chat-Verlauf, Agent-Status etc.), nutzt Streamlit session_state. Dieser File definiert und initialisiert alle verwendeten Keys mit ihren Standardwerten an einem zentralen Ort — damit kein anderer Teil der App auf einen nicht-existierenden Key trifft.

Wichtige State-Keys:

  • open_files — Liste aller aktuell geöffneten Dateipfade (bestimmt die Tab-Reihenfolge)
  • files_content — Dict {Dateipfad: aktueller Editorinhalt} (ungespeicherte Änderungen inklusive)
  • active_file — Absoluter Pfad der aktuell aktiven Datei
  • chat_history — Flache Liste aller Chat-Nachrichten [{role, content}, ...]
  • agent_mode — Boolean ob der Agent Mode aktiv ist
  • agent_status — Aktueller Agent-Zustand: "idle" | "waiting_approval" | "done"
  • agent_log — Liste aller abgeschlossenen Agent-Schritte
  • agent_pending_action — Die vom Agent vorgeschlagene, noch nicht ausgeführte Aktion

frontend/sidebar.py — Navigation & File Explorer

Die Sidebar ist in zwei Bereiche aufgeteilt:

Navigation: Ein Radio-Button schaltet zwischen "Code Editor" und "Chat with AI Assistant" um. Die aktuelle Auswahl wird in session_state.radio_interface_options gespeichert und von app.py ausgewertet.

File Explorer: Ein interaktiver Dateibaum zeigt den gesamten workspace/-Ordner an. Implementiert mit streamlit-arborist, das einen klickbaren Baum mit Ordner-Icons rendert. Ein Klick auf eine Datei öffnet sie im Editor und wechselt automatisch zur Editor-Ansicht. Ein Klick auf einen Ordner zeigt eine Aktionsleiste mit Buttons zum Erstellen von Dateien/Unterordnern und zum Löschen des Ordners.

Modale Dialoge (via @st.dialog):

  • _add_file_dialog — Neuen Dateinamen eingeben und Datei erstellen
  • _add_folder_dialog — Neuen Ordnernamen eingeben und Ordner erstellen
  • _rename_file_dialog — Datei umbenennen (Extension wird automatisch beibehalten)
  • _delete_file_dialog — Löschbestätigung für Dateien
  • _delete_folder_dialog — Löschbestätigung für Ordner inkl. Inhalt

frontend/editor.py — Code-Editor

Der Code-Editor ist die zentrale Arbeitsfläche für das direkte Bearbeiten von Dateien.

Ace Editor (streamlit-ace): Jede offene Datei wird in einem Tab mit dem Ace-Editor angezeigt. Der Editor erkennt die Dateiendung automatisch und stellt das passende Syntax-Highlighting ein (Python, JavaScript, HTML, CSS, JSON, YAML, LaTeX, Bash). Das Theme ist "Monokai". auto_update=True bedeutet, dass Änderungen sofort in session_state.files_content landen — ohne expliziten Submit.

Tab-Verwaltung: Jede offene Datei erscheint als Tab. Wenn eine Datei aus dem File Explorer geöffnet wird, springt ein JavaScript-Snippet automatisch auf den richtigen Tab (da st.tabs keinen programmatischen Tab-Wechsel unterstützt).

Aktions-Buttons pro Datei:

  • Save Changes — Schreibt den aktuellen Editorinhalt auf Disk
  • Close File — Entfernt die Datei aus den offenen Tabs
  • Rename File — Öffnet Rename-Dialog
  • Delete File — Öffnet Lösch-Bestätigungsdialog
  • ▶ Run Code — Führt die Datei aus (Python via py, LaTeX via pdflatex)

Ausführungs-Output: Nach dem Ausführen erscheinen stdout, stderr und der Exit-Code unterhalb des Editors. Bei einem Fehler erscheint zusätzlich der Button "🐛 Debug with AI" — dieser baut automatisch eine Fehlernachricht zusammen (Fehlertext + kompletter Code) und schickt sie an den Chat-Assistenten.


frontend/chat.py — Chat & Agent Mode

Dieser File enthält zwei grundlegend verschiedene Interfaces, die über einen Toggle umgeschaltet werden.

Normaler Chat

Ein klassischer Multi-Turn-Chatbot. Bei der ersten Nachricht wird automatisch ein System-Prompt generiert. Falls eine Datei im Editor geöffnet ist und "Include current file as context" aktiviert ist, wird der Dateiinhalt in den System-Prompt eingebettet — der AI-Assistent "sieht" also den Code und kann gezielt darauf eingehen.

Bei jeder Folgenachricht wird der System-Prompt aktualisiert falls eine andere Datei aktiv ist. Das Modell, die maximale Tokenzahl und ein eigener System-Prompt können in einem aufklappbaren Settings-Panel konfiguriert werden. Der "Clear Chat" Button öffnet einen Bestätigungsdialog.

Agent Mode

Der Agent Mode verwandelt den Chat in ein Step-by-Step Kontrollinterface für den autonomen CodingAgent.

Ablauf:

  1. Benutzer gibt eine Aufgabe ein (z.B. "Schreibe eine Funktion die eine Liste sortiert und speichere sie als sorted.py")
  2. Der Agent analysiert die Aufgabe und schlägt einen ersten Schritt vor (z.B. write_new_file mit dem generierten Code)
  3. Die UI zeigt den Gedankengang des Agents ("Thought"), das gewählte Tool und die Argumente an
  4. Benutzer klickt Approve → Aktion wird ausgeführt, nächster Schritt wird vorgeschlagen
  5. Oder Reject → Benutzer gibt Feedback ein, Agent plant neu ohne die Aktion auszuführen
  6. Oder Abort Task → Agent wird sofort gestoppt
  7. Nach Abschluss kann eine Follow-up Frage gestellt werden ohne den Kontext zu verlieren

Der Agent Log zeigt alle abgeschlossenen Schritte in einem aufklappbaren Bereich.


Backend — Managers

backend/managers/chat_manager.py — ChatManager

Kapselt die gesamte Kommunikation mit dem LLM. Verbindet sich mit einem OpenAI-kompatiblen REST-Endpoint (/v1/chat/completions) dessen Adresse und Key aus der .env gelesen werden.

Der Chat-Verlauf wird als Liste von {role, content}-Dicts in-memory gehalten. Bei jedem send_message-Aufruf wird die vollständige History mitgeschickt, sodass das Modell immer den gesamten Gesprächskontext kennt.

Methode Beschreibung
send_message(user_message) Nachricht zur History hinzufügen, API-Call machen, Antwort zurückgeben und in History speichern
add_message(role, content) Nachricht direkt zur History hinzufügen (z.B. für System-Prompt)
get_history() Kopie der aktuellen History zurückgeben
clear_history() Gesamten Verlauf löschen (neuer Chat)

backend/managers/file_manager.py — FileManager

Abstrahiert alle Dateioperationen und stellt sicher, dass jeder Zugriff innerhalb des workspace/-Verzeichnisses bleibt. Jede Methode löst den angegebenen Pfad zu einem absoluten Pfad auf und prüft mit startswith(workspace.resolve()) ob der Pfad im erlaubten Bereich liegt — damit sind Path-Traversal-Angriffe wie ../../etc/passwd ausgeschlossen.

Methode Beschreibung
create_file(relative_path, name) Neue leere Datei erstellen. Ohne Extension wird .txt ergänzt. Schlägt fehl wenn Datei existiert.
create_folder(relative_path, name) Neuen Ordner erstellen. Schlägt fehl wenn Ordner existiert.
read_file(path) Dateiinhalt als String lesen. Gibt leeren String bei Fehler zurück.
save_file(path, content) Datei mit neuem Inhalt überschreiben (erstellt falls nicht vorhanden).
rename_file(old_path, new_name) Datei umbenennen. Die Dateiendung wird immer vom Original übernommen.
delete_file(relative_path) Einzelne Datei löschen.
delete_folder(relative_path) Ordner und gesamten Inhalt rekursiv löschen.
get_file_tree() Gesamten Workspace als verschachteltes Dict zurückgeben: Ordner als {name: dict}, Dateien als {name: None}.

backend/managers/execution_engine.py — ExecutionEngine

Führt Dateien aus dem Editor als Subprocess aus. Die Datei wird immer aus ihrem eigenen Verzeichnis heraus gestartet (cwd=file.parent), damit relative Imports und Pfade korrekt funktionieren.

Unterstützte Dateitypen:

  • Python (.py) → py <dateiname> (nutzt den Windows Python Launcher)
  • LaTeX (.tex) → pdflatex -interaction=nonstopmode <dateiname>

Timeout: 30 Sekunden. Gibt immer ein Dict {stdout, stderr, rc} zurück.


backend/managers/system_prompter.py — SystemPrompter

Baut den System-Prompt für den normalen Chat zusammen. Ohne Datei-Kontext enthält er nur eine allgemeine Beschreibung des AI-Assistenten. Mit Datei-Kontext wird der Dateiname und der Inhalt (max. 4000 Zeichen, danach abgeschnitten) in XML-ähnliche Tags eingebettet:

<file name="fibonacci.py">
<code>
def fib(n): ...
</code>
</file>

Das Modell wird angewiesen sich auf diese Datei zu beziehen wenn es Fragen zum Code beantwortet.


backend/managers/debug_logger.py — DebugLogger

Einfacher In-Memory-Logger der Ausführungsmeldungen für den Code-Editor speichert. Wird von editor.py genutzt um den Status einer Code-Ausführung (log(), log_error(), clear()) zu protokollieren.


Backend — Coding Agent

backend/agent/coding_agent.py — CodingAgent

Das Herzstück des autonomen Agents. Implementiert den Plan → Act → Observe → Fix → Done Loop.

Wie der Loop funktioniert:

  1. start_task(task) initialisiert den Agenten mit dem System-Prompt (enthält Beschreibungen aller verfügbaren MCP-Tools) und der Aufgabe
  2. propose_next_action() schickt den bisherigen Konversationsverlauf an das LLM. Das Modell antwortet immer mit einem JSON-Objekt {"thought": "...", "tool": "...", "arguments": {...}}. Die Antwort wird geparsed und als pending_action gespeichert — aber noch nicht ausgeführt
  3. approve() führt die pending_action aus, fügt das Resultat als User-Nachricht zur History hinzu und ruft sofort propose_next_action() auf
  4. reject(feedback) verwirft die pending_action ohne sie auszuführen und injiziert das Feedback als User-Nachricht
  5. Wenn das LLM "done" als Tool wählt, setzt der Agent is_done = True

Robustheit: LLM-Antworten die kein valides JSON enthalten werden durch extract_json() und _repair_json_strings() bereinigt (entfernt Markdown-Fences, repariert unescapte Newlines in Strings). Wenn die History das Limit von 80'000 Zeichen überschreitet, werden ältere Nachrichten entfernt und ein Erinnerungs-Hinweis injiziert.

Methode Beschreibung
start_task(task) Agent neu initialisieren, System-Prompt + Aufgabe setzen
propose_next_action() LLM-Aufruf → JSON-Antwort parsen → als pending_action speichern
approve() pending_action ausführen, Resultat zur History hinzufügen, nächste Aktion vorschlagen
reject(feedback) pending_action verwerfen, Feedback injizieren
follow_up(question) Nach Abschluss: Folgefrage stellen ohne Kontext zu verlieren

backend/agent/mcp_server_adapter.py — MCPToolAdapter

Der Adapter ist die Brücke zwischen dem CodingAgent und den MCP-Servern. Er liest beim Start die Konfigurationsdatei mcp_server_config.json, verbindet sich zu jedem Server, fragt dessen Tool-Liste ab und speichert alle Tools in einer internen Registry.

Bei jedem call_tool-Aufruf öffnet der Adapter eine neue stdio-Verbindung zum zuständigen Server, führt das Tool aus und schliesst die Verbindung wieder. Das heisst: Die Server laufen nicht permanent, sondern werden für jeden Aufruf frisch gestartet. Das macht das System robuster (kein veralteter State in einem Server) aber etwas langsamer.

Methode Beschreibung
initialize_all_servers() Alle konfigurierten Server starten, Tool-Liste abfragen, in Registry speichern
call_tool(tool_name, arguments) Passenden Server für das Tool finden, Verbindung aufbauen, Tool aufrufen, Ergebnis zurückgeben
get_all_tools() Komplette Tool-Registry zurückgeben

MCP-Server

Die drei MCP-Server sind eigenständige Python-Prozesse die über das stdio-Transportprotokoll kommunizieren. Sie werden vom Adapter gestartet und stellen dem Coding Agent Tools zur Verfügung.


backend/agent/servers/mcp_server_code_execution.py — CodeExecutionServer

Ermöglicht dem Agent das sichere Ausführen von Python-Code. Bevor Code ausgeführt wird, durchläuft er eine zweistufige Sicherheitsprüfung:

  1. Statische AST-Analyse (check_code_safety): Der Code wird mit Pythons ast-Modul geparst. Jeder Import-Node und jeder Funktionsaufruf wird gegen Blocklisten geprüft. Blockierte Imports umfassen u.a. os, sys, subprocess, socket, pickle. Blockierte Builtins umfassen exec, eval, open, compile.

  2. String-Suche nach verbotenen Sequenzen: Zusätzlich wird der Code-String direkt nach gefährlichen Mustern durchsucht (../, os., sys., subprocess. etc.).

Erst wenn beide Prüfungen bestanden sind, wird der Code via subprocess.run([sys.executable, "-c", code], stdin=subprocess.DEVNULL, ...) ausgeführt. stdin=subprocess.DEVNULL ist entscheidend: Da der MCP-Server über asyncio-verwaltetes stdio läuft, würde der Kind-Prozess sonst stdin erben und blockieren.

Tool Beschreibung
run_python_sandboxed(code) Code nach Sicherheitsprüfung ausführen. Gibt stdout+stderr zurück (max. 3000 Zeichen). Timeout: 45s.
python_code_validation(code) Nur prüfen (Syntax + Sicherheit), nicht ausführen.
lint_code(code) Pyflakes-Analyse: ungenutzte Imports, undefinierte Variablen, Syntax-Fehler.
analyse_structure(code) Imports, Klassen (mit Methoden) und Top-Level-Funktionen als strukturierte Zusammenfassung.

backend/agent/servers/mcp_server_file_search.py — FileSearchServer

Gibt dem Agent Lese- und Schreibzugriff auf den workspace/-Ordner. Alle Pfade werden über _safe_path() gegen Path-Traversal validiert — ein Zugriff ausserhalb des Workspace ist nicht möglich.

Tool Beschreibung
list_files() Alle Dateien im Workspace rekursiv auflisten (ohne __pycache__).
get_file_tree(dir_path) Verzeichnisstruktur als formatierter Text (ähnlich tree-Befehl).
search_files(query) Dateien deren Name oder Inhalt den Suchbegriff enthält (case-insensitive).
read_file(path) Vollständigen Inhalt einer Datei lesen.
write_new_file(path, content) Neue Datei erstellen und Inhalt schreiben. Bestehende Dateien können nicht überschrieben werden.
create_new_directory(path) Neues Verzeichnis im Workspace erstellen.

backend/agent/servers/mcp_server_web_search.py — WebSearchServer

Gibt dem Agent Zugriff auf das Internet. Enthält einen SSRF-Schutz der verhindert, dass der Agent interne Adressen abruft.

SSRF-Schutz (_validate_url): Nur http:// und https://-URLs sind erlaubt. Hostnamen wie localhost, 127.0.0.1, 0.0.0.0, 169.254.169.254 (AWS Metadata) sowie alle privaten IP-Ranges (10.x, 172.16-31.x, 192.168.x) sind blockiert.

Tool Beschreibung
web_search(query, max_results) DuckDuckGo-Suche. Gibt Titel, URL und Snippet für jedes Ergebnis zurück (Standard: 5 Ergebnisse).
fetch_page(url) URL abrufen, HTML parsen, Fliesstext extrahieren (max. 4000 Zeichen).

CLI-Test mit run_agent.py

Für schnelles Testen des Coding Agents ohne die Streamlit-App:

python run_agent.py

Das Script startet eine interaktive Konsolen-Session:

  • Aufgabe eingeben → Agent startet
  • Enter drücken → Vorgeschlagene Aktion genehmigen und ausführen
  • Text eingeben + Enter → Feedback geben, Agent plant neu
  • stop eingeben → Abbrechen