Zum Inhalt

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_PATH vorgesehen

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:

  1. Eingabe. Die UI nimmt den Text und die aktiven Reglereinstellungen entgegen. Der Token-Counter prüft die Eingabegröße gegen das konfigurierte Limit.
  2. 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.
  3. Stilisierung. Der Prompt-Builder übersetzt die aktiven Regler in textuelle Anweisungen. Numerische Werte werden über _get_intensitaet_details in Stufenbeschreibungen (leicht, moderat, deutlich, stark, EXTREM) und zugehörige Anweisungssätze umgewandelt. Polare Regler erhalten zusätzlich eine Vermeidungs-Klausel für den Gegenpol.
  4. LLM-Aufruf. Der LLM-Client ruft die Chat-Completions-API auf. Bei Fehlern wird die Anfrage gemäß LLM_MAX_RETRIES mit Wartezeit LLM_RETRY_DELAY_SECONDS wiederholt.
  5. 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 (Encoding o200k_base)
  • Konfiguration: python-dotenv
  • Containerisierung: Docker, Docker Compose
  • Sprache und Laufzeit: Python 3.11