Zum Inhalt

Architektur

Das Recherche-Tool ist als containerisierte Python-Anwendung mit klar getrennten Schichten aufgebaut: einer Gradio-basierten Oberfläche, einer Orchestrierungsschicht für die Recherche- und Analyse-Pipelines, einer Konnektoren-Schicht für externe und hochschulinterne Quellen, einer LLM-Schicht mit zwei separaten Modell-Endpunkten sowie einer Persistenzschicht im Dateisystem. Die Pipeline läuft vollständig asynchron; mehrere Phasen sind parallelisiert.

Auf einen Blick

  • Container-Setup mit drei Diensten: Anwendungscontainer, SearXNG-Metasuche und Redis-Cache für SearXNG.
  • Schichtentrennung in Oberfläche, Orchestrierung, Pipeline, Konnektoren, LLM-Schicht und Persistenz.
  • Asynchrone Verarbeitung über asyncio mit Per-Domain-Rate-Limiting und konfigurierbaren Parallelitätsgrenzen für Fetch- und Harvest-Phase.
  • Dual-LLM-Konfiguration mit separat konfigurierbaren Endpunkten für Primary- und Harvest-Modell.
  • Externe Embedder- und Reranker-Dienste über OpenAI-kompatible Schnittstellen für die hybride Personensuche.
  • Konfiguration ausschließlich über Umgebungsvariablen mit Default-Werten in einer zentralen Konfigurationsklasse.
  • Persistenz pro Recherche-Lauf als Verzeichnis mit Bericht, Quellen, Extrakten und Metadaten.

Architekturbeschreibung

Die Anwendung ist in fünf logische Schichten gegliedert. Die Oberfläche nimmt Anfragen entgegen, verwaltet Sitzungszustand und Kontextdokumente und ruft je nach gewähltem Modus den passenden Pipeline-Pfad auf. Die Orchestrierung steuert den Ablauf durch die Phasen einer Recherche und delegiert pro Phase an LLM-Aufrufe oder Konnektoren. Die Konnektoren kapseln den Zugriff auf einzelne Quellen hinter einem gemeinsamen Interface mit den Methoden search und fetch. Die LLM-Schicht stellt zwei voneinander unabhängige Clients zur Verfügung. Die Persistenzschicht legt Eingabedokumente, Zwischenergebnisse und Endberichte im Dateisystem ab.

Datenfluss

flowchart TB
    User([Nutzer])
    UI[Gradio-Oberfläche]

    subgraph Orchestrierung
        FormatAgent[Format-Agent]
        Planner[Recherche-Planer]
        Loop[Such- und Harvest-Schleife]
        Gap[Lückenanalyse]
        Contradict[Widerspruchsprüfung]
        Synth[Synthese]
    end

    subgraph Konnektoren
        SearXNG[SearXNG]
        Web[Web-Scraper]
        Git[GitHub / GitLab]
        Solr[Solr-Index]
        ES[Elasticsearch]
        ZIS[Personenverzeichnis]
        ZSearch[Personen-Suche]
        LitAPI[Literatur-APIs]
        Local[Lokale Dateien / WebDAV]
    end

    subgraph LLM-Schicht
        Primary[Primary-LLM]
        Harvest[Harvest-LLM]
        Embed[Embedder]
        Rerank[Reranker]
    end

    subgraph Analyse-Pipeline
        Decompose[Decomposer]
        DAG[DAG-Executor]
        Tasks[Sub-Tasks]
    end

    Storage[(Persistenz: Berichte, Quellen, Extrakte)]
    LitChecker[Literaturprüfer]

    User --> UI
    UI -->|Recherche-Modi| FormatAgent
    UI -->|Analyse-Modi| Decompose
    UI -->|Literaturlisten| LitChecker

    FormatAgent --> Planner
    Planner --> Loop
    Loop --> Gap
    Gap -->|Folgefragen| Loop
    Gap -->|abgeschlossen| Contradict
    Contradict --> Synth
    Synth --> Storage
    Synth --> UI

    Loop --> SearXNG
    Loop --> Web
    Loop --> Git
    Loop --> Solr
    Loop --> ES
    Loop --> ZIS
    Loop --> ZSearch
    Loop --> Local

    LitChecker --> LitAPI
    LitChecker --> Storage

    Decompose --> DAG
    DAG --> Tasks
    Tasks --> Storage

    FormatAgent -.-> Primary
    Planner -.-> Primary
    Synth -.-> Primary
    Gap -.-> Primary
    Contradict -.-> Primary
    Loop -.-> Harvest
    Tasks -.-> Primary
    Tasks -.-> Harvest
    ZSearch -.-> Embed
    ZSearch -.-> Rerank

Eine Anfrage wird zunächst an den Format-Agenten übergeben, der aus Anfrage, gewählter Vorlage und Chat-Verlauf ein Ausgabe-Schema mit Titel, Sektionen und Stilvorgaben erzeugt. Anschließend erstellt der Recherche-Planer auf Basis des Schemas einen Recherche-Plan mit konkreten Recherche-Fragen, mehrsprachigen Suchbegriffen, gezielt zu fetchenden URLs und gegebenenfalls konkreten Repository- oder Personensuche-Anfragen. Optional wird der Plan an dieser Stelle dem Nutzer zur Bestätigung angezeigt.

In der Such- und Harvest-Schleife werden pro Runde Anfragen gegen die Konnektoren ausgeführt, die Treffer dedupliziert und die zugehörigen Inhalte heruntergeladen. Aus jedem geladenen Dokument extrahiert ein Harvest-LLM-Aufruf relevante Fakten in strukturierter Form, jeweils zugeordnet zu einer Plan-Frage. Negativ-Extrakte werden gefiltert. Nach jeder Runde prüft eine Lückenanalyse, ob noch offene Fragen bestehen, und plant gegebenenfalls Folge-Anfragen für eine weitere Runde; bleibt eine Runde ohne neue positive Extrakte, wird die Schleife abgebrochen.

Auf den gesammelten Extrakten läuft eine Widerspruchsprüfung. Anschließend erzeugt die Synthese-Phase aus Extrakten, Plan, Schema und Kontextdokumenten den Endbericht in Markdown. Während des gesamten Laufs werden Fortschritt, Token-Verbrauch und Phasen-Status erfasst und an die Oberfläche gestreamt.

Rolle der KI-Komponenten

Die Anwendung trennt LLM-Aufgaben nach Komplexität und Parallelitätsbedarf. Format-Agent, Recherche-Planer, Lückenanalyse, Widerspruchsprüfung und Synthese laufen über das Primary-Modell, da sie längeren Kontext und höhere sprachliche Qualität benötigen. Die Fakten-Extraktion pro Quelle ist hochgradig parallelisierbar und läuft über das Harvest-Modell, das mit kürzerem Kontext, niedrigerer Temperatur und ohne Thinking-Modus betrieben wird. Eine Semaphore begrenzt die Anzahl gleichzeitiger Harvest-Aufrufe.

Für die hybride Personensuche kommen zwei zusätzliche KI-Dienste zum Einsatz. Ein Embedder erzeugt Vektoren für die semantische Ähnlichkeitssuche im lokalen Personen-Index. Ein Cross-Encoder-Reranker sortiert Kandidaten aus Volltext- und Vektorsuche final um. Beide Dienste werden über OpenAI-kompatible Schnittstellen angesprochen und sind unabhängig vom eigentlichen Recherche-LLM konfigurierbar.

Die Analyse-Modi verwenden eine zweite Pipeline-Variante. Ein Decomposer zerlegt eine Aufgabe in Sub-Tasks mit Abhängigkeiten und Phasenzuordnung. Ein DAG-Executor sortiert die Sub-Tasks topologisch in Schichten und führt Tasks innerhalb einer Schicht parallel aus. Jeder Sub-Task wählt selbst, ob er das Primary- oder Harvest-Modell nutzt und ob der Thinking-Modus aktiviert wird. Zwischen den Schichten werden Checkpoints gespeichert, so dass nach Abbruch oder Neuladen fortgesetzt werden kann.

Nebenläufigkeit und Robustheit

Die gesamte Pipeline läuft asynchron über asyncio. Auf der Konnektor-Ebene werden Fetches parallelisiert; ein Per-Domain-Rate-Limiter sorgt für einen Mindestabstand zwischen Anfragen an dieselbe Domain. Für die Literatur-APIs steuert ein gemeinsamer Rate-Limiter die parallelen Aufrufe. URLs werden über eine Normalisierung dedupliziert, eine Blockliste filtert thematisch unpassende oder niedrigqualitative Domains heraus.

Modell-Aufrufe werden bei vorübergehenden Server-Fehlern mit exponentiellem Backoff wiederholt. JSON-Antworten werden defensiv geparst; abgeschnittene oder ungültige JSON-Strukturen werden mit einer Reparatur-Heuristik behandelt und im Fehlerfall ein zweites Mal mit explizitem JSON-Hinweis angefragt. Laufende Recherchen können jederzeit über ein Stop-Signal abgebrochen werden; das Signal wird zwischen den Phasen geprüft und löst eine kontrollierte Beendigung mit Status-Persistierung aus.

Konfiguration und Deployment

Die Anwendung wird als Container betrieben und liest ihre Konfiguration vollständig aus Umgebungsvariablen. Eine zentrale Konfigurationsklasse bündelt die Einstellungen für UI, Pipeline, Konnektoren, LLM-Endpunkte und Dokumentenverarbeitung mit Default-Werten. Pro Konnektor lassen sich Aktivierung, Endpunkte, Authentifizierung, Feldzuordnungen und Limits getrennt steuern.

Im Standardbetrieb werden drei Container kombiniert: der Anwendungscontainer mit der Gradio-Oberfläche, ein SearXNG-Container mit angepasster Engine-Konfiguration und ein Redis-Container als Cache für SearXNG. Externe Dienste wie Suchmaschinen, Literatur-APIs, das hochschulinterne Personenverzeichnis, Solr- und Elasticsearch-Indizes sowie LLM-, Embedder- und Reranker-Endpunkte werden über das Netzwerk angebunden. Die Anwendung legt einen persistenten Datenordner für gespeicherte Recherchen an; Aufräum-Parameter steuern die Aufbewahrungsdauer.

Ein separates Synchronisationsskript baut den lokalen Personen-Index auf: Es crawlt das hochschulinterne Personeninformationssystem, lädt verlinkte Homepages und Vita-Seiten, erzeugt einen SQLite-Index mit FTS5 und berechnet Vektor-Embeddings für die semantische Suche. Einwilligungen werden dabei berücksichtigt und Widerrufe regelmäßig nachgezogen.

Technologie-Übersicht

  • Sprache und Kernframeworks: Python 3.12, Gradio für die Oberfläche, asyncio für die Nebenläufigkeit, httpx als asynchroner HTTP-Client.
  • LLM-Anbindung: OpenAI-Python-SDK gegen OpenAI-kompatible Endpunkte, mit Thinking-Modus über chat_template_kwargs für vLLM-basierte Backends.
  • Web- und Dokumentenverarbeitung: trafilatura für die Textextraktion aus Webseiten, optional Playwright für stark JavaScript-basierte Seiten, pdfminer.six, python-docx, python-pptx und openpyxl für Office- und PDF-Dokumente, python-docx mit eigenen XML-Erweiterungen für den Word-Export.
  • Persistenz und Indexe: SQLite mit FTS5 für den Personen-Index, NumPy für Vektor-Operationen, Dateisystem-basierte Persistenz für Recherche-Läufe.
  • Externe Such- und Index-Dienste: SearXNG-Metasuche, Solr und Elasticsearch für interne Indizes, Redis als SearXNG-Cache.
  • Externe Daten-APIs: GitHub-REST-API, GitLab-REST-API sowie arXiv, CrossRef, OpenAlex, Semantic Scholar, DBLP und OpenLibrary für die Literaturarbeit.
  • Deployment: Docker mit python:3.12-slim als Basis, mehrteilige Container-Topologie (Anwendung, SearXNG, Redis), Konfiguration ausschließlich über Umgebungsvariablen.