Architektur¶
Die Anwendung ist als einzelner Container mit klarer Schichtentrennung aufgebaut: UI, Orchestrierungslogik, Prompt-Aufbereitung und LLM-Anbindung sind als getrennte Module umgesetzt. Sie kommuniziert über die OpenAI-kompatible API mit einer extern bereitgestellten LLM-Instanz und hält selbst keinen persistenten Zustand. Konfiguration und Anbindung erfolgen vollständig über Umgebungsvariablen.
Auf einen Blick¶
- Single-Container-Anwendung auf Basis von Gradio 6
- Schichten-Modell: UI · Orchestrierung · Prompt-Builder · LLM-Client
- Externe Abhängigkeit: lokal betriebene vLLM-Instanz mit OpenAI-kompatibler API
- Zweistufiger LLM-Workflow: Neutralisierung (optional) → Stilisierung
- Konfiguration ausschließlich über Umgebungsvariablen (
.env) - Sitzungsbasierter Zustand im UI, keine Datenpersistenz
- Reverse-Proxy-Betrieb über
GRADIO_ROOT_PATHvorgesehen
Komponentenüberblick¶
Die Anwendung gliedert sich in fünf Module:
- UI-Schicht (
app.py). Aufbau der Gradio-Oberfläche mit vier Tabs (Transformation, Stil-Regler, Neutralisierung, Historie), Verwaltung der Sitzungs-Zustände (Historie, Zähler) und Verdrahtung der Event-Handler. - Orchestrierung (
app.py:transform_text). Steuert den zweistufigen Ablauf — bei aktiver Neutralisierung wird zuerst der Neutralisierungs-Prompt erzeugt und ausgeführt, dessen Ergebnis dient als Eingabe der Stilisierung. - Prompt-Builder (
prompt_builder.py). Erzeugt die System- und User-Prompts für beide Stufen. Übersetzt numerische Reglerwerte in eine fünfstufige Intensitätssemantik und kombiniert die aktiven Regler zu einer strukturierten Anweisungsliste. - LLM-Client (
llm_client.py). Kapselt den OpenAI-Client, verwaltet Timeout und Retry-Logik und liefert die LLM-Antworten an die Orchestrierung zurück. - Datenmodelle (
models.py). Definiert die Datenklassen für Regler, Reglereinstellungen, Neutralisierungs-Konfiguration und Transformations-Ergebnisse.
Hinzu kommen ein Konfigurationsmodul (config.py), ein Token-Counter (token_counter.py) und zwei JSON-Dateien mit den Default-Reglern und Default-Presets.
Datenfluss¶
flowchart TB
User[Browser-Nutzer]
subgraph Container[Anwendungs-Container]
UI[Gradio-UI<br/>app.py]
Orchestrator[Orchestrierung<br/>transform_text]
PromptBuilder[Prompt-Builder]
LLMClient[LLM-Client<br/>OpenAI-kompatibel]
Token[Token-Counter<br/>tiktoken]
State[Sitzungs-Zustand<br/>Historie]
Defaults[(default_regler.json<br/>default_presets.json)]
end
vLLM[Lokal betriebene<br/>vLLM-Instanz]
User -->|Eingabetext, Reglerwerte| UI
UI --> Token
UI --> Orchestrator
Orchestrator -->|Stufe 1: optional| PromptBuilder
PromptBuilder -->|System + User Prompt| LLMClient
LLMClient -->|HTTP/JSON| vLLM
vLLM -->|Antwort| LLMClient
LLMClient --> Orchestrator
Orchestrator -->|Stufe 2| PromptBuilder
Orchestrator --> State
Orchestrator --> UI
UI -->|Ergebnis, Vergleich| User
Defaults --> UI
Workflow im Detail¶
Eine Transformation durchläuft folgenden Ablauf:
- Eingabe. Die UI nimmt den Text und die aktiven Reglereinstellungen entgegen. Der Token-Counter prüft die Eingabegröße gegen das konfigurierte Limit.
- Neutralisierung (optional). Ist die Neutralisierung aktiviert und mindestens eine Dimension ausgewählt, baut der Prompt-Builder einen System-Prompt mit den ausgewählten Dimensionen und den strikten Anti-Preamble-Regeln. Der LLM-Client führt die Anfrage gegen die vLLM-Instanz aus und liefert den neutralisierten Zwischentext.
- Stilisierung. Der Prompt-Builder übersetzt die aktiven Regler in textuelle Anweisungen. Numerische Werte werden über
_get_intensitaet_detailsin Stufenbeschreibungen (leicht, moderat, deutlich, stark, EXTREM) und zugehörige Anweisungssätze umgewandelt. Polare Regler erhalten zusätzlich eine Vermeidungs-Klausel für den Gegenpol. - LLM-Aufruf. Der LLM-Client ruft die Chat-Completions-API auf. Bei Fehlern wird die Anfrage gemäß
LLM_MAX_RETRIESmit WartezeitLLM_RETRY_DELAY_SECONDSwiederholt. - Ergebnisverarbeitung. Eingabe, Zwischentext, Ausgabe und gewählte Konfiguration werden in der Sitzungs-Historie abgelegt; die UI zeigt das Ergebnis als Markdown an und aktualisiert die Vergleichs-Auswahl.
Rolle des LLM¶
Die Anwendung nutzt das LLM als reines Textverarbeitungswerkzeug für zwei klar getrennte Aufgaben (Neutralisierung, Stilisierung). Es kommen weder Embedder noch Reranker zum Einsatz; eine agentische Orchestrierung im Sinne werkzeugbasierter Eigenständigkeit findet nicht statt. Stattdessen werden Determinismus und Steuerbarkeit über die explizite Schritttrennung, die strikten System-Prompts und die fünfstufige Intensitätssemantik erreicht.
Robustheit und Konfiguration¶
Die Robustheit ergibt sich aus drei Mechanismen: einstellbare Timeouts (LLM_TIMEOUT_SECONDS), automatische Wiederholungen (LLM_MAX_RETRIES) und vorgelagerte Eingabevalidierung über die Token-Zählung. Die gesamte Konfiguration — Basis-URL der LLM-API, API-Schlüssel, Modellname, Token-Limits, Server-Port und Reverse-Proxy-Pfad — wird über Umgebungsvariablen aus einer .env-Datei gelesen, die zur Laufzeit über python-dotenv geladen wird.
Deployment¶
Die Anwendung wird als Single-Container-Image bereitgestellt (Python 3.11 Slim, nicht-privilegierter Benutzer, Health-Check). Ein docker-compose.yml orchestriert den Start, exponiert Port 7860 und leitet die Konfiguration aus der .env-Datei in den Container weiter. Über extra_hosts ist der Zugriff auf eine vLLM-Instanz auf dem Host-System vorgesehen; alternativ kann eine beliebige andere erreichbare OpenAI-kompatible API verwendet werden. Die Variable GRADIO_ROOT_PATH erlaubt den Betrieb hinter einem Reverse-Proxy unter einem Unterpfad.
Technologie-Übersicht¶
- UI: Gradio 6
- LLM-Client:
openai(Python-SDK), genutzt gegen eine OpenAI-kompatible API - LLM-Backend: lokal betriebene vLLM-Instanz
- Token-Zählung:
tiktoken(Encodingo200k_base) - Konfiguration:
python-dotenv - Containerisierung: Docker, Docker Compose
- Sprache und Laufzeit: Python 3.11