AISE AI Code Editor — Technische Dokumentation
KI-unterstützter Lightweight Code Editor auf Basis von Streamlit (AISE501 Spring 2026).
Projektinformationen
| Modul | AI in Software Engineering 1 (AISE501) |
| Autoren | Irina Rüegg & Livio Meuli |
| Semester | Spring 2026 |
Inhaltsverzeichnis
- Projektstruktur
- Schnellstart
- Frontend
- Backend Manager
- Backend Agent (MCP-System)
- MCP-Server-Konfiguration
- Architektur-Übersicht
- Wichtige Designentscheidungen
- Tests
- Umgebungsvariablen
Projektstruktur
AISE_AIAgent/
├── frontend/ # Streamlit UI-Komponenten
│ ├── app.py # Einstiegspunkt der App; Seitenkonfiguration + Routing
│ ├── state.py # Zentrale Session-State-Initialisierung
│ ├── sidebar.py # Datei-Explorer + Navigations-Radio
│ ├── editor.py # Ace-Editor-Tabs + Ausführungs-Output
│ └── chat.py # Chat-Interface + Agent-Mode-UI
│
├── backend/
│ ├── managers/ # Business-Logik, direkt vom Frontend aufgerufen
│ │ ├── file_manager.py # Workspace-CRUD mit Path-Traversal-Schutz
│ │ ├── chat_manager.py # LLM-API-Wrapper + Sliding-Window-History
│ │ ├── system_prompter.py # Kontextbewusste System-Prompt-Generierung
│ │ ├── search_manager.py # DuckDuckGo-Websuche + Seitenabruf
│ │ ├── execution_engine.py # Subprocess-basierte Code-Ausführung (Python, LaTeX)
│ │ └── debug_logger.py # Rotierende Logdatei + Fehler-Aggregation
│ │
│ └── agent/ # Autonomes KI-Agenten-System (MCP-basiert)
│ ├── coding_agent.py # Plan→Aktion→Beobachten-Schleife
│ ├── mcp_server_adapter.py # Verbindet den Agenten mit MCP-Tool-Servern
│ ├── mcp_server_config.json # Welche MCP-Server gestartet werden (Pfade + Befehle)
│ └── servers/ # MCP-Server-Implementierungen (stdio-Transport)
│ ├── mcp_server_code_execution.py # Tool: Sandbox-Python-Ausführung + Linting
│ ├── mcp_server_file_search.py # Tool: Workspace-Datei lesen/schreiben/suchen
│ └── mcp_server_web_search.py # Tool: DuckDuckGo-Suche + Seitenabruf
│
├── tests/ # pytest-Unit-Tests
│ ├── conftest.py # Globaler MCP-Mock (keine echten Subprozesse in Tests)
│ ├── test_chat_manager.py
│ ├── test_coding_agent.py
│ ├── test_debug_logger.py
│ ├── test_execution_engine.py
│ ├── test_file_manager.py
│ ├── test_mcp_server_code_execution.py
│ ├── test_mcp_server_file_search.py
│ ├── test_mcp_server_web_search.py
│ ├── test_search_manager.py
│ └── test_system_prompter.py
│
├── workspace/ # Sandbox-Verzeichnis für Agent- und Editor-Dateien
├── logs/ # Rotierende Logdateien (app.log, errors.log)
├── .env # Lokale Umgebungsvariablen (nicht eingecheckt)
└── .env.example # Vorlage für erforderliche Umgebungsvariablen
Schnellstart
Die folgenden Schritte funktionieren auf Windows und macOS — abweichende Befehle sind jeweils mit dem Betriebssystem gekennzeichnet.
Schritt 1 — Voraussetzungen prüfen
Python 3.10 oder neuer muss installiert sein.
# Windows (PowerShell)
python --version
# macOS (Terminal)
python3 --version
Falls Python nicht installiert ist:
- Windows: python.org/downloads herunterladen und installieren. Bei der Installation „Add Python to PATH" aktivieren.
- macOS: python.org/downloads herunterladen und installieren, oder via Homebrew:
brew install python3
Git muss ebenfalls installiert sein:
git --version
Falls nicht vorhanden: git-scm.com (Windows) bzw. brew install git (macOS).
Schritt 2 — Repository klonen
git clone https://gitea.fhgr.ch/meulilivio/AISE1_Project_Irina_Livio.git
cd AISE1_Project_Irina_Livio
Schritt 3 — Virtuelle Umgebung erstellen
Eine virtuelle Umgebung isoliert die Projekt-Abhängigkeiten vom restlichen System. Sie muss einmalig erstellt werden.
# Windows (PowerShell)
python -m venv .venv
# macOS (Terminal)
python3 -m venv .venv
Schritt 4 — Virtuelle Umgebung aktivieren
Die Umgebung muss jedes Mal neu aktiviert werden, wenn ein neues Terminal geöffnet wird.
# Windows (PowerShell)
.\.venv\Scripts\Activate.ps1
Falls PowerShell die Ausführung blockiert, einmalig folgenden Befehl ausführen und danach erneut versuchen:
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
# macOS (Terminal)
source .venv/bin/activate
Nach erfolgreicher Aktivierung erscheint (.venv) am Anfang der Eingabezeile.
Schritt 5 — Abhängigkeiten installieren
pip install -r requirements.txt
Dieser Schritt lädt alle benötigten Pakete herunter (~2–5 Minuten je nach Internetverbindung). Er muss nur einmal ausgeführt werden.
Schritt 6 — Umgebungsvariablen konfigurieren
# Windows (PowerShell)
copy .env.example .env
# macOS (Terminal)
cp .env.example .env
Danach die Datei .env in einem Texteditor öffnen und die Werte eintragen welche per Mail mitgeteilt wurden (HOST, PORT, API_KEY, MODEL).
Schritt 7 — App starten
# Windows & macOS
streamlit run frontend/app.py
Streamlit öffnet die App automatisch im Standard-Browser unter http://localhost:8501.
Falls der Browser nicht automatisch aufgeht, die URL manuell eingeben.
Zum Beenden der App im Terminal Ctrl + C drücken.
Schritt 8 — Tests ausführen (optional)
pytest tests/ -v
Frontend
Alle Frontend-Module sind reine Streamlit-Komponenten. Sie enthalten keine Business-Logik, sondern delegieren alles an die Backend-Manager.
app.py
Einstiegspunkt der Applikation. Ruft init_state() auf Modul-Ebene auf (vor main()),
damit alle Session-State-Schlüssel existieren, bevor ein Widget gerendert wird. Delegiert
an render_sidebar(), render_editor() und render_chat() basierend auf dem Navigations-Radio.
state.py
Zentrale Quelle aller st.session_state-Schlüsselnamen und ihrer Standardwerte.
Alle Schlüssel nutzen if key not in st.session_state-Guards, damit bei Streamlit-Reruns
keine bestehenden Werte überschrieben werden. Aktuell verwaltete Schlüssel:
| Schlüssel | Standard | Zweck |
|---|---|---|
last_selected |
None |
Zuletzt angeklickter Baum-Knoten (verhindert erneutes Ausführen bei jedem Rerender) |
selected_folder / selected_folder_rel |
None |
Aktuell markierter Ordner |
chat_manager |
ChatManager() |
Live-ChatManager-Instanz |
open_files |
[] |
Geordnete Liste absoluter Pfade als Editor-Tabs |
files_content |
{} |
Pfad → aktueller Editor-Inhalt (kann von Disk abweichen) |
active_file |
None |
Absoluter Pfad des aktiven Editor-Tabs |
exec_results |
{} |
Pfad → letztes Ausführungsergebnis-Dict |
chat_history |
[] |
Flache Liste von {role, content}-Dicts zur Anzeige |
agent_mode |
False |
Ob die Agent-Mode-UI aktiv ist |
coding_agent |
None |
Live-CodingAgent-Instanz während einer Aufgabe |
agent_status |
"idle" |
"idle" / "waiting_approval" / "done" |
agent_log |
[] |
Liste abgeschlossener Schritt-Einträge |
agent_pending_action |
None |
Vorgeschlagene Aktion, die auf Benutzer-Genehmigung wartet |
search_results |
[] |
Aktive Websuchergebnisse für Kontext-Injektion |
sidebar.py
Rendert das Navigations-Radio und den Workspace-Datei-Explorer (basierend auf
streamlit-arborist für einen interaktiven Baum). Datei-Klicks öffnen einen neuen
Editor-Tab; Ordner-Klicks zeigen eine Aktionsleiste mit «Datei hinzufügen» / «Ordner
hinzufügen» / «Löschen». Ein Popover am unteren Rand ermöglicht das Erstellen und
Hochladen von Dateien (bis 1 MB) im Workspace-Wurzelverzeichnis.
editor.py
Verwendet streamlit-ace für syntaxhervorgehobene Bearbeitung. Jede geöffnete Datei
erhält einen eigenen Tab via st.tabs(). Die aktive Datei steuert die Schaltflächen
«Code ausführen», «Herunterladen», «Schliessen», «Umbenennen» und «Löschen». Der Button
Code ausführen führt für Python-Dateien zuerst ast.parse() durch, um Syntaxfehler
vor dem Subprocess zu erkennen. Der Button Mit KI debuggen (nach einem fehlgeschlagenen
Lauf eingeblendet) formatiert die Fehlerausgabe und navigiert zur Chat-Ansicht mit einer
vorausgefüllten Debug-Nachricht.
chat.py
Zwei sich gegenseitig ausschliessende Ansichten, umgeschaltet via st.toggle("Agent Mode"):
Normaler Chat (render_normal_chat()):
- Kompakte 4-spaltige Toolbar direkt über dem Chat-Input:
[● Agent Mode][🔍 Search][🗑️ Clear][⚙️ Settings]— Web-Suche und Einstellungen jeweils alsst.popover, Clear öffnet einen Bestätigungs-Dialog. - System-Prompt wird vor jeder ausgehenden Nachricht neu generiert (
_set_system_prompt()). - Unterstützt Slash-Befehle
/search <Abfrage>und/search clear. - Settings-Popover: Dateikontext-Toggle, Modell-Auswahl, Max-Token-Slider, benutzerdefinierter System-Prompt.
- «Mit KI debuggen»-Nachrichten vom Editor werden über
pending_debug_messageim Session-State weitergeleitet.
Agent Mode (render_agent_mode()):
idle→ Aufgabeneingabe + Start-Schaltfläche.waiting_approval→ zeigt vorgeschlagenen Gedanken + Tool + Argumente; Benutzer kann Genehmigen, Ablehnen (mit Feedback) oder Abbrechen.done→ Erfolgsmeldung + Folgefrage-Eingabe zum Weiterführen der Aufgabe.
Backend Manager
file_manager.py
Alle öffentlichen Methoden lösen Pfade auf und prüfen, ob sie innerhalb von workspace/
bleiben, bevor sie das Dateisystem berühren (Path-Traversal-Schutz). Je nach Operation
werden relative oder absolute Pfade akzeptiert und zurückgegeben:
| Methode | Pfad-Typ | Hinweise |
|---|---|---|
create_folder(relative_path, name) |
Workspace-relativ | Erstellt eine Ebene |
create_file(relative_path, name) |
Workspace-relativ | Standard: .txt |
read_file(absolute_path) |
Absolutes Path-Objekt |
Vom Editor verwendet |
save_file(absolute_path, content) |
Absoluter String | Überschreibt vorhandenes |
rename_file(relative_path, new_name) |
Workspace-relativ | Erweiterung bleibt immer erhalten |
delete_file(relative_path) |
Workspace-relativ | |
delete_folder(relative_path) |
Workspace-relativ | Rekursiv via shutil.rmtree |
get_file_tree() |
— | Gibt verschachteltes Dict zurück; Verzeichnisse → Dict, Dateien → None |
Dateien werden in get_file_tree() standardmässig auf CODE_EXTENSIONS gefiltert.
chat_manager.py
Kapselt einen OpenAI-kompatiblen REST-Endpunkt, konfiguriert via .env.
Sliding-Window-History: _build_payload_messages() sendet immer zuerst die
System-Nachricht (damit sie nie verworfen wird), gefolgt von den letzten
max_history_messages (20) Nicht-System-Nachrichten. Das begrenzt die Payload-Grösse,
ohne den System-Prompt zu verlieren.
Fehlerbehandlung: Verbindungs-Timeouts und HTTP-Fehler werden abgefangen, geloggt und als Assistenten-Nachrichten in der History gespeichert (sodass die UI den Fehler inline anzeigt).
API-Key: Falls API_KEY den Wert "EMPTY" hat oder fehlt, wird kein
Authorization-Header gesendet (unterstützt lokale/anonyme Endpunkte).
system_prompter.py
Generiert kontextbewusste System-Prompts. Signatur:
SystemPrompter.generate_prompt(
user_message="",
file_context=None, # {"name": str, "content": str}
search_context=None, # list[{"title", "url", "snippet"}] — in system_prompter.py implementiert,
# aber in chat.py nicht verwendet: dort wird Search-Kontext direkt
# als <search_context>-Block vor die Nachricht eingefügt
task_type="default", # "debug" | "explain" | "optimize" | "default"
)
Der Aufgabentyp wird anhand von Schlüsselwörtern in der Benutzernachricht durch
_detect_task_type() in chat.py ermittelt. Der Dateiinhalt wird wörtlich in einen
XML-ähnlichen <file>...<code>-Block eingebettet und bei MAX_FILE_CHARS Zeichen
abgeschnitten. _extract_relevant_context() nutzt ast.parse(), um nur die spezifische
Funktion oder Klasse zurückzugeben, nach der der Benutzer fragt, anstatt die gesamte Datei.
execution_engine.py
Führt Dateien in einem Subprocess mit capture_output=True, text=True und einem
RUN_TIMEOUT von 30 Sekunden aus. Aktuell unterstützt:
.py— viasys.executable(plattformübergreifend; zeigt auf den aktuell aktiven Python-Interpreter)
Rückgabe: {"stdout": str, "stderr": str, "rc": int}.
search_manager.py
DuckDuckGo-basierte Websuche und Seitenabruf für die Chat-Ansicht. SSRF-geschützt:
_validate_url() blockiert Nicht-HTTP(S)-Schemata, Loopback- und RFC-1918-private
IP-Bereiche. fetch_page() extrahiert lesbaren Text via BeautifulSoup, entfernt
<script>-, <style>-, <nav>- und <footer>-Tags und kürzt auf MAX_PAGE_CHARS.
debug_logger.py
Richtet einen rotierenden Datei-Handler für logs/app.log (5 MB × 5 Backups) und eine
separate logs/errors.log für ERROR/CRITICAL-Einträge ein. Verwendung im Code:
from backend.managers.debug_logger import get_logger
logger = get_logger(__name__) # Standard-Python-Logger
logger.info("Dienst gestartet")
logger.error("Etwas ist schiefgelaufen")
logger.exception("Unerwarteter Fehler") # loggt Stack-Trace
Zusätzliche Classmethods zur sitzungsweisen Fehler-Aggregation:
DebugLogger.log_error("Nachricht") # loggt + hängt an _error_log-Liste an
DebugLogger.get_errors() # gibt Liste der Fehlermeldungen dieser Sitzung zurück
DebugLogger.clear_errors() # leert die In-Memory-Liste
DebugLogger.format_debug_output({ # formatiert Ausführungsergebnis für die KI
"return_code": 1,
"stdout": "...",
"stderr": "...",
})
Backend Agent (MCP-System)
coding_agent.py
Implementiert eine Plan → Aktion → Beobachten-Schleife, die schrittweise durch die Streamlit-UI gesteuert wird. Das LLM antwortet immer mit einer strukturierten JSON-Aktion:
{"thought": "...", "tool": "<tool_name>", "arguments": {"key": "value"}}
Wichtige Methoden:
| Methode | Beschreibung |
|---|---|
start_task(task) |
Setzt den gesamten Zustand zurück, befüllt History mit System + Aufgabe |
propose_next_action() |
Ruft das LLM auf, parst JSON, speichert als pending_action |
approve() |
Führt das ausstehende Tool via dispatch_tool() aus, loggt Ergebnis |
reject(feedback) |
Injiziert Feedback + Replan-Tag in die History; propose_next_action() wird danach separat aufgerufen |
follow_up(question) |
Fügt nach «done» eine Folgefrage ein, setzt Schleife fort |
Hilfsfunktionen (Modul-Ebene):
| Funktion | Beschreibung |
|---|---|
truncate_result(text) |
Begrenzt Tool-Output auf MAX_RESULT_LENGTH (10 000 Zeichen) |
trim_messages(msgs) |
Entfernt alte Turns, wenn History MAX_HISTORY_CHARS (80 000 Zeichen) überschreitet; System-Nachricht + Original-Aufgabe bleiben immer erhalten |
_strip_code_fences(text) |
Entfernt ```json- / ```-Wrapper aus LLM-Antworten |
dispatch_tool(name, arguments) |
Leitet weiter an MCPToolAdapter.call_tool() |
build_all_tool_description() |
Erstellt eine menschenlesbare Tool-Liste für den System-Prompt |
mcp_server_adapter.py
Liest mcp_server_config.json, startet jeden Server als stdio-Subprocess (immer mit
sys.executable, unabhängig vom literalen Befehl in der Konfiguration) und registriert
alle Tools in einem flachen tool_registry. Verbindungen werden pro Aufruf geöffnet
(nicht dauerhaft gehalten), da Streamlits synchrones Rerun-Modell langlebige
async-Kontextmanager unpraktisch macht.
MCP-Server (in servers/)
Alle drei Server sind FastMCP-Applikationen, die über stdio kommunizieren.
mcp_server_file_search.py — Workspace-Dateioperationen:
list_files()— flache rekursive Auflistungget_file_tree(dir_path)— baumförmige Verzeichnisstruktursearch_files(query)— Name- und Inhaltssuche (bis 30 Treffer)read_file(path)— Textdatei lesenwrite_new_file(path, content)— Erstellen (kein Überschreiben)create_new_directory(path)— Verzeichnis erstellen
mcp_server_web_search.py — Webzugriff:
web_search(query, max_results=5)— DuckDuckGo-Suchefetch_page(url)— Abrufen + Text extrahieren (max.MAX_PAGE_LENGTHZeichen)- Beide Tools nutzen SSRF-Schutz (gleiche URL-Validierung wie
search_manager.py)
mcp_server_code_execution.py — Sandbox-Python-Analyse:
analyse_structure(code)— AST-basierte Strukturzusammenfassunglint_code(code)— pyflakes-Analysepython_code_validation(code)— Sicherheits- + Syntaxprüfung ohne Ausführungrun_python_sandboxed(code)— Ausführung in einem Subprocess mitPYTHONIOENCODING=utf-8, 15 s Timeout, Output begrenzt aufMAX_OUTPUT_LENGTH
MCP-Server-Konfiguration
Format: backend/agent/mcp_server_config.json
{
"Servername": {
"command": "py",
"args": ["servers/mcp_server_beispiel.py"]
}
}
| Feld | Pflicht | Beschreibung |
|---|---|---|
command |
Ja | Ausführbare Datei (py, python3, node, …) — wird für Python immer durch sys.executable ersetzt |
args |
Ja | Argumente-Array — erstes Element ist der Server-Script-Pfad relativ zu backend/agent/ |
env |
Nein | Zusätzliche Umgebungsvariablen für den Serverprozess |
Neuen MCP-Server hinzufügen
- Neues FastMCP-Script in
backend/agent/servers/erstellen:from mcp.server.fastmcp import FastMCP mcp = FastMCP("MeinServer") @mcp.tool() def mein_tool(param: str) -> str: """Tool-Beschreibung, die dem Agenten angezeigt wird.""" return f"Ergebnis: {param}" if __name__ == "__main__": mcp.run(transport="stdio") - Eintrag in
mcp_server_config.jsonergänzen:"MeinServer": { "command": "py", "args": ["servers/mcp_server_mein_tool.py"] } - Der Adapter erkennt den neuen Server beim nächsten App-Start automatisch und bindet seine Tools in den System-Prompt des Agenten ein.
Architektur-Übersicht
┌─────────────────────────────────────────────────────────────────┐
│ Frontend (Streamlit) │
│ app.py ──► sidebar.py / editor.py / chat.py │
│ │ │
│ state.py (alle Session-State-Schlüssel) │
└──────────────┬──────────────────────────────────────────────────┘
│ direkte Python-Aufrufe
▼
┌─────────────────────────────────────────────────────────────────┐
│ Backend Manager │
│ FileManager ChatManager SystemPrompter │
│ SearchManager ExecutionEngine DebugLogger │
└──────────────┬──────────────────────────────────────────────────┘
│ async-Aufrufe (via _run_async-Bridge)
▼
┌─────────────────────────────────────────────────────────────────┐
│ Coding Agent │
│ coding_agent.py ◄──► mcp_server_adapter.py │
│ │ stdio (Subprocess pro Aufruf) │
│ ┌────────────────┼───────────────┐ │
│ ▼ ▼ ▼ │
│ mcp_server_file_search mcp_server_web mcp_server_code │
└──────────────┬──────────────────────────────────────────────────┘
│ lesen / schreiben
▼
┌─────────────────────────────────────────────────────────────────┐
│ workspace/ (isoliertes Sandbox-Verzeichnis) │
└─────────────────────────────────────────────────────────────────┘
Async-Bridge: Streamlit läuft synchron. Der CodingAgent verwendet async-Methoden
(da die MCP-Client-Bibliothek async ist). _run_async(coro) in chat.py erstellt pro
Aufruf eine neue Event-Loop (asyncio.new_event_loop()), was im Thread-Modell von
Streamlit sicher ist, da auf dem UI-Thread keine Loop läuft.
MCP-Verbindungsmodell: Der Adapter öffnet pro Tool-Aufruf eine neue stdio-Verbindung statt eine persistente Session zu halten. Das vermeidet die Komplexität langlebiger async-Kontextmanager über Streamlit-Reruns hinweg.
Wichtige Designentscheidungen
Warum get_file_tree() statt einer flachen Dateiliste?
Der interaktive Datei-Explorer in der Sidebar benötigt eine verschachtelte Struktur,
um das Baum-Widget aufzubauen. Eine flache Liste würde die clientseitige Rekonstruktion
von Eltern-Kind-Beziehungen erfordern. Der MCP-Server mcp_server_file_search.py stellt
sein eigenes list_files()-Tool für den Agenten bereit, wo eine flache Auflistung
für das LLM nützlicher ist.
Warum wird der System-Prompt bei jeder Nachricht neu generiert?
Die im Editor aktive Datei kann sich zwischen Nachrichten ändern. Durch das Neuerstellen des Prompts hat die KI immer den aktuellen Dateikontext. Der vorhandene System-Nachrichten-Eintrag wird in-place aktualisiert (nicht angehängt), sodass die History stets genau eine System-Nachricht enthält.
Warum ist die Chat-History auf ein Sliding-Window begrenzt?
ChatManager._build_payload_messages() behält die System-Nachricht und die letzten 20 Turns.
Das verhindert, dass die Payload während langer Sitzungen das Kontextlimit des Modells
überschreitet, während der System-Prompt immer erhalten bleibt. Der Agent hat eine eigene,
separate Kürzungslogik (trim_messages()), die zusätzlich die ursprüngliche Aufgabenbeschreibung
bewahrt.
Warum werden MCP-Tool-Verbindungen pro Aufruf geöffnet?
Streamlit führt das gesamte Script bei jeder Benutzerinteraktion erneut aus. Eine lebende async-MCP-Session über Reruns hinweg zu erhalten würde entweder einen Hintergrund-Thread oder eine persistente asyncio-Loop erfordern — beides erhöht Komplexität und Fehleranfälligkeit. Verbindungen pro Aufruf sind einfacher und zuverlässiger, auf Kosten eines kleinen Subprocess-Start-Overheads pro Tool-Aufruf.
Warum wird Fehler-Output NICHT automatisch in den normalen Chat injiziert?
Das automatische Einschleusen jedes Laufzeitfehlers würde die Chat-History schnell mit Rauschen überfluten. Stattdessen entscheidet der Benutzer selbst, wann er die KI über den Button «Mit KI debuggen» im Editor einbezieht. Der Agent-Mode behandelt dies anders: Tool-Fehler werden immer als Beobachtungen an das LLM zurückgegeben und lösen automatisches Replanning aus.
Warum setzt run_python_sandboxed() PYTHONIOENCODING=utf-8?
Unter Windows ist die Standard-Konsolen-Kodierung cp1252, die Unicode-Zeichen ausserhalb
des Latin-1-Bereichs (z. B. Emoji, CJK) nicht kodieren kann. Das Setzen von
PYTHONIOENCODING=utf-8 in der Subprocess-Umgebung stellt sicher, dass print() für
beliebige Unicode-Inhalte korrekt funktioniert.
Tests
Tests ausführen
pytest tests/ -v # alle Tests
pytest tests/test_chat_manager.py -v # einzelne Datei
pytest tests/ -q # Kurzausgabe
Test-Architektur
conftest.py patcht MCPToolAdapter auf sys.modules-Ebene vor dem Import eines
Testmoduls. Das verhindert, dass coding_agent.py's Modul-Level-Aufruf
asyncio.run(adapter.initialize_all_servers()) echte MCP-Subprozesse startet.
| Testdatei | Getestetes Modul | Wichtige Muster |
|---|---|---|
test_chat_manager.py |
ChatManager |
@patch("requests.post") für HTTP |
test_coding_agent.py |
CodingAgent |
@pytest.mark.asyncio, mock _call_api |
test_debug_logger.py |
DebugLogger |
autouse-Fixture setzt _error_log-Klassenvariable zurück |
test_execution_engine.py |
ExecutionEngine |
@patch("subprocess.run") |
test_file_manager.py |
FileManager |
tmp_path-Fixture, mock st |
test_mcp_server_code_execution.py |
MCP-Code-Server | führt echten Python-Code aus |
test_mcp_server_file_search.py |
MCP-Datei-Server | monkeypatch tauscht ALLOWED_DIR |
test_mcp_server_web_search.py |
MCP-Web-Server | patcht DDGS im Server-Namespace |
test_search_manager.py |
SearchManager |
mockt DDGS-Kontextmanager + requests |
test_system_prompter.py |
SystemPrompter |
reine Funktion, kein Mocking nötig |
Async-Tests
Die Tests in test_coding_agent.py verwenden @pytest.mark.asyncio aus pytest-asyncio.
Der MCPToolAdapter ist vollständig via conftest.py gemockt, sodass kein MCP-Subprocess
beteiligt ist.
Umgebungsvariablen
.env.example nach .env kopieren und ausfüllen:
| Variable | Beschreibung | Beispiel |
|---|---|---|
HOST |
Hostname des LLM-API-Endpunkts | localhost |
PORT |
Port des LLM-API-Endpunkts | 8000 |
API_KEY |
Bearer-Token — EMPTY für offene Endpunkte verwenden |
sk-... |
MODEL |
Modellname, der in API-Payloads gesendet wird | mistral-7b |
Die App funktioniert mit jedem OpenAI-kompatiblen API-Endpunkt (vLLM, Ollama mit OpenAI-Shim, OpenAI selbst usw.).
Einsatz von KI-Werkzeugen
Während der Entwicklung wurden KI-Assistenten (Claude, GitHub Copilot) als Werkzeuge eingesetzt — vergleichbar mit der Nutzung von Dokumentation, Stack Overflow oder einer IDE mit Autocomplete.
Konkret bedeutet das:
- Eigenständige Konzeption und Architektur: Die Gesamtarchitektur (Schichtentrennung Frontend / Manager / Agent), die Designentscheidungen und die Aufteilung in Komponenten wurden selbst erarbeitet und geplant.
- Implementierung mit Unterstützung: Boilerplate-Code, Docstrings und einzelne Hilfsfunktionen wurden teils mit KI-Unterstützung geschrieben, verstanden und anschliessend in das Projekt integriert.
- MCP-Integration und Chat-Logik: Für das Model Context Protocol und den Chat-Assistenten haben wir uns an den Kursbeispielen des Dozenten orientiert und diese als Ausgangsbasis adaptiert und erweitert.
- Debugging und Refactoring: KI wurde als Gesprächspartner genutzt, um Fehler zu analysieren und Lösungsansätze zu diskutieren — die Entscheidungen wurden jedoch eigenständig getroffen und umgesetzt.
Der gesamte Code wurde von uns gelesen, verstanden und bewusst eingesetzt. Unkritisch übernommener oder nicht verstandener Code wurde nicht ins Projekt aufgenommen.