Was Sloppy macht

Sloppy ist ein vollautomatisiertes KI-System, das täglich Weltnachrichten analysiert und daraus eine Feldnotiz generiert — geschrieben aus der Perspektive eines außerirdischen Wissenschaftlers, der den Auftrag hat, die Spezies Mensch zu erforschen.

Die Figur kennt keine Geschichte, keine Kulturen, keine Staatsgrenzen. Ihr einziger Zugang zur Menschheit sind Nachrichtenmedien. Aus diesem Material erschließt sie Strukturen, bildet Hypothesen, klassifiziert Akteure — und berichtet darüber für ihre Heimatwelt, nicht für ein menschliches Publikum.

Was dabei täglich entsteht, ist kein Nachrichtenkommentar und keine Satire. Es ist eine konsequent fremde Beobachterperspektive auf das Weltgeschehen — nüchtern, sachlich, ohne kulturelle Vorannahmen. Der Effekt entsteht nicht durch Inszenierung, sondern durch die Logik des Systems selbst.

Warum Sloppy

Sloppy verbindet drei Disziplinen, die in der Praxis selten so konsequent zusammengedacht werden: Automatisierung, künstliche Intelligenz in Form von generativen Sprachmodellen und semantisches Wissensmanagement.

Das Projekt zeigt, wie diese drei Ebenen ineinandergreifen. Automatisierung, realisiert mit n8n, orchestriert den gesamten täglichen Ablauf ohne manuellen Eingriff. Generative KI, hier Google Gemini, übernimmt nicht nur die Inhaltsgenerierung, sondern auch die strukturierte Extraktion und Aufbereitung von Wissen aus diesem Inhalt. Dieses Wissen fließt in ein semantisches Speichersystem, aus dem es täglich wieder abgerufen, kontextualisiert und in die nächste Generierung eingespeist wird.

Das Prinzip dahinter ist Retrieval Augmented Generation, kurz RAG. Statt einem Sprachmodell alles Wissen im Prompt mitzugeben oder auf sein Trainings-Vorwissen zu vertrauen, wird gezielt relevantes Wissen aus einer Vektordatenbank abgerufen und als Kontext bereitgestellt. Sloppy erinnert sich nicht zufällig, er sucht semantisch nach dem, was relevant ist.

Das gesamte System läuft auf dedizierter eigener Infrastruktur. n8n, die Vektordatenbank Qdrant und das Website-Deployment sind selbst gehostet und vollständig unter eigener Kontrolle. Keine externen Datenabflüsse, keine Abhängigkeit von Drittplattformen jenseits der KI-APIs selbst.

Technischer Deep Dive

Der folgende Abschnitt richtet sich an alle, die verstehen wollen, wie Sloppy unter der Haube funktioniert — Schritt für Schritt, ohne Vereinfachungen.


Infrastruktur

Die gesamte Plattform läuft auf einem dedizierten Linux-Server bei Hetzner Cloud. Docker übernimmt die Containerisierung aller Dienste — jede Komponente läuft isoliert in einem eigenen Container, mit definierten Abhängigkeiten und reproduzierbaren Konfigurationen. Coolify fungiert als selbst gehostete Deployment-Plattform und übernimmt das Management der Container, automatische Deployments und die Verwaltung von Umgebungsvariablen. Traefik läuft als Reverse Proxy vor allen Diensten, übernimmt das Routing eingehender Anfragen und terminiert SSL automatisch über Let's Encrypt.


n8n

n8n ist ein selbst gehosteter Low-Code-Automatisierungsserver. Workflows bestehen aus einzelnen Nodes, die jeweils eine definierte Aufgabe erfüllen und ihre Ausgabe als strukturierte Daten an den nächsten Node weitergeben. Das ermöglicht komplexe, mehrstufige Prozesse ohne klassische Programmierung, mit vollem Zugriff auf JavaScript für alle Logik-Schritte, die über einfache Transformationen hinausgehen. Sloppy wird vollständig durch zwei n8n-Workflows betrieben, die täglich automatisch ausgeführt werden.


Qdrant

Qdrant ist eine Vektordatenbank. Der entscheidende Unterschied zu einer relationalen Datenbank: Inhalte werden nicht als Text gespeichert und per exaktem Abgleich gefunden, sondern als hochdimensionale Vektoren, mathematische Repräsentationen von Bedeutung. Diese Vektoren werden durch ein Embedding-Modell erzeugt, hier Googles Gemini Embedding. Semantisch ähnliche Inhalte landen im Vektorraum nah beieinander, unabhängig von der konkreten Wortwahl. Eine Suche nach „Militärkonflikt“ findet dadurch auch Einträge, die „Krieg“ oder „Kampfhandlungen“ enthalten. Das ist die technische Grundlage, die RAG erst sinnvoll macht und Sloppys Gedächtnis von einer simplen Textsuche unterscheidet.


Google Gemini

Gemini ist das Sprachmodell, das Sloppy zum Schreiben, Analysieren und Extrahieren befähigt. Es kommt im Workflow nicht als eine monolithische Einheit zum Einsatz, sondern an mehreren Stellen mit unterschiedlichen Konfigurationen und präzisen Aufgaben. Temperature, also der Parameter, der steuert, wie deterministisch oder kreativ ein Modell antwortet, wird je nach Aufgabe unterschiedlich gesetzt. Für strukturierte Datenextraktion niedrig, für das Schreiben der Feldnotiz höher. Das Embedding-Modell, ebenfalls von Google, übernimmt die Vektorisierung aller Inhalte, die in Qdrant gespeichert oder dort gesucht werden.

Gemini ist die einzige Komponente im System, bei der Daten den eigenen Server verlassen. Der Einsatz eines cloudbasierten Sprachmodells hat folgenden Grund: Ein leistungsfähiges Sprachmodell selbst zu hosten würde eine dedizierte GPU-Infrastruktur voraussetzen, die mit erheblichen monatlichen Kosten verbunden wäre. Google stellt über seine API kostenfreie Kontingente bereit, die für ein Projekt in diesem Umfang vollständig ausreichen.

Der Workflow

Datenbeschaffung und Aufbereitung

Bevor Sloppy auch nur einen Satz schreibt, muss das Rohmaterial beschafft, zeitlich gefiltert und in eine verarbeitbare Struktur gebracht werden. Dieser erste Block hat keine KI-Beteiligung — er ist reine Datenpipeline. RSS-Feed lesen, irrelevante Artikel ausfiltern, Menge begrenzen, alles zu einem einzigen strukturierten Datensatz zusammenfassen. Das Ergebnis ist ein Array aller relevanten Artikel des Tages, das als gemeinsame Grundlage für alle nachfolgenden Schritte dient.

Der Workflow startet täglich um 06:15 Uhr durch einen Schedule Trigger. Das ist ein einfacher Cron-basierter Auslöser in n8n, keine externe Abhängigkeit, kein Webhook.

RSS Read liest direkt im Anschluss den RSS-Feed der Deutschen Welle (rss.dw.com/xml/rss-de-top). Der RSS-Node in n8n parst das XML des Feeds und gibt jeden Artikel als separates Item weiter. Jedes Item enthält unter anderem Titel, Beschreibungstext (contentSnippet), die Artikel-URL sowie einen ISO-8601-Zeitstempel der Veröffentlichung.

Filter schränkt die Artikelmenge auf den relevanten Zeitraum ein. Die Bedingung prüft das Feld isoDate jedes Items gegen einen dynamisch berechneten Schwellenwert: $now.minus(24, 'hours').toISO(). Nur Artikel, die innerhalb der letzten 24 Stunden veröffentlicht wurden, passieren diesen Node. Alle anderen werden verworfen. Das stellt sicher, dass Sloppy ausschließlich aktuelle Nachrichten verarbeitet und nicht ältere Artikel aus dem Feed erneut aufgreift.

Limit kappt die Ergebnismenge auf maximal zehn Artikel. Das ist eine bewusste Begrenzung: Mehr Input würde den nachfolgenden Prompt für das Sprachmodell unverhältnismäßig verlängern, ohne dass der Mehrwert die zusätzlichen Token-Kosten rechtfertigt.

Aggregate fasst alle einzelnen Items, die bis hier als separate Datensätze durch den Workflow fließen, in ein einziges Item zusammen. Das Ergebnis ist ein JSON-Objekt mit einem Array items[], das alle Artikel enthält. Dieser Aggregationsschritt ist notwendig, weil die nachfolgenden KI-Nodes das gesamte Artikelpaket als zusammenhängenden Kontext benötigen und nicht als einzelne unabhängige Aufrufe verarbeiten sollen.

Gedächtnisabruf

Damit Sloppy nicht jeden Tag bei null anfängt, wird vor dem eigentlichen Schreiben sein Gedächtnis befragt. Das ist konzeptionell der Kern des RAG-Prinzips: Nicht das Sprachmodell trägt das Wissen, sondern die Datenbank. Dieser Block extrahiert aus den heutigen Artikeln präzise Suchbegriffe, sucht damit semantisch in Sloppys Gedächtnisdatenbank und bereitet die relevantesten Erinnerungen für die Übergabe an das Sprachmodell auf. Was den Block verlässt, ist ein konsolidierter Gedächtniskontext — abgestimmt auf die Themen des Tages.

Basic LLM Chain ist der erste KI-Aufruf im Workflow. Gemini wird hier nicht zum Schreiben eingesetzt, sondern zur strukturierten Extraktion. Das Modell läuft mit Temperature 0.1, also nahe dem deterministischen Minimum — kreative Variation ist hier nicht erwünscht, Präzision schon. Die Aufgabe: Aus jedem Artikel Eigennamen und Kernthemen extrahieren und als JSON-Array zurückgeben. Ein Objekt pro Artikel, ein keywords-Feld mit einer kommagetrennten Liste. Aus einem Artikel über Sanktionen gegen den Iran wird also beispielsweise "Iran, Trump, Sanktionen, Nahostkonflikt". Der Grund für diesen Schritt liegt in der Funktionsweise von Vektordatenbanken: Eine semantische Suche mit einem vollständigen Artikeltext als Query erzeugt einen Vektor, der den Durchschnitt aller darin enthaltenen Themen repräsentiert. Das verwässert die Suchanfrage erheblich. Komprimierte Schlüsselbegriffe hingegen erzeugen präzisere Vektoren und damit treffsichere Suchergebnisse.

Code in JavaScript parst den JSON-Output von Gemini und bereitet daraus pro Artikel eine separate searchQuery auf. Der Node fängt dabei Inkonsistenzen im Gemini-Output ab, etwa unterschiedliche Schreibweisen der Feldnamen (keywords vs. Keywords), und normalisiert sie. Ergebnis ist eine Liste einzelner Items, jedes mit einer sauberen Suchanfrage als String.

Qdrant Vector Store führt für jede dieser Suchanfragen eine semantische Suche in Sloppys Gedächtnisdatenbank durch. Das Embedding-Modell gemini-embedding-001 vektorisiert den Suchstring, und Qdrant gibt die drei ähnlichsten Einträge zurück (topK: 3). Gesucht wird über die gesamte Collection sloppy_gedaechtnis, die strukturierte Einträge zu Einheiten, Individuen, Hypothesen und offenen Fragen enthält, sowie absatzweise gespeicherte Passagen aus früheren Feldnotizen. Ähnlichkeit bedeutet hier semantische Nähe im Vektorraum, nicht Textübereinstimmung.

Code in JavaScript dedupliziert die Ergebnisse. Da pro Artikel separat gesucht wird, können dieselben Gedächtniseinträge mehrfach zurückgegeben werden, etwa wenn drei verschiedene Artikel alle auf Deutschland als relevante Einheit verweisen. Der Node filtert anhand der metadata.id und stellt sicher, dass jeder Eintrag nur einmal in den nachfolgenden Kontext einfließt.

Aggregate fasst alle deduplizierten Gedächtniseinträge wieder zu einem einzigen Item zusammen. Was diesen Node verlässt, ist eine konsolidierte Liste der relevantesten Erinnerungen aus Sloppys bisheriger Forschung, direkt abgestimmt auf die heutigen Nachrichten — bereit für die Übergabe an die nächste KI-Stufe.

Hypothesenprüfung und Feldnotiz

Sloppy akkumuliert über Zeit vorläufige Hypothesen über menschliches Verhalten. Bevor er die heutige Feldnotiz schreibt, wird geprüft, ob das neue Material diese Hypothesen stützt, ihnen widerspricht oder schlicht keine relevante Aussage darüber erlaubt. Dieses Ergebnis fließt als zusätzlicher Kontext in die Feldnotiz-Generierung ein. Der Block endet mit dem zentralen Output des gesamten Workflows: der fertigen Feldnotiz.

Basic LLM Chain (Gemini Hypothesen-Vorprüfung) erhält zwei Inputs: die Hypothesen aus dem Gedächtniskontext, gefiltert nach dem Typ hypothesen, sowie das heutige Quellmaterial. Das Modell läuft mit Temperature 0.2 — die Aufgabe ist analytisch, nicht generativ. Für jede Hypothese gibt es genau einen von drei möglichen Status-Werten zurück: gestützt, infrage_gestellt oder nicht_prüfbar, jeweils mit einer kurzen sachlichen Begründung. Der Output ist valides JSON. Keine Prosa, keine Interpretation über das Material hinaus.

Basic LLM Chain Gemini (Gemini Feldnotiz) ist der einzige Node im gesamten Workflow, der mit erhöhter Temperature läuft, konkret 0.7. Hier ist kreative Variation erwünscht — der Text soll nicht deterministisch und formelhaft klingen, sondern Sloppys Stimme tragen. Das Modell erhält als Kontext das vollständige Quellmaterial, die aufbereiteten Gedächtniseinträge aus Block 2 mit Typ, Datum und ID als Metadaten, sowie den Hypothesen-Status aus dem vorherigen Schritt. Der System-Prompt definiert Sloppys Charakter, seine Sprache, seine Wissensgrenzen und die formalen Anforderungen an die Feldnotiz vollständig. Was den Node verlässt, ist der fertige Feldnotiz-Text — der zentrale Output des gesamten Workflows.

Gedächtnisextraktion und Speicherung

Die fertige Feldnotiz ist nicht nur ein Publikationsartefakt — sie ist gleichzeitig Rohmaterial für Sloppys wachsendes Gedächtnis. Dieser Block extrahiert strukturierte Wissenseinträge aus dem Fließtext der Feldnotiz und schreibt sie in zwei Formen in Qdrant: als semantisch typisierte Einträge für Einheiten, Individuen, Hypothesen und offene Fragen, und als absatzweise Segmente der Feldnotiz selbst. Beides zusammen stellt sicher, dass Sloppy beim nächsten Lauf sowohl auf komprimiertes Faktenwissen als auch auf den vollen Beobachtungskontext früherer Tage zugreifen kann.

Basic LLM Chain Gemini (Gedächtnisextraktion) erhält die fertige Feldnotiz als einzigen Input. Das Modell läuft mit Temperature 0.2. Die Aufgabe ist rein extraktiv: Aus dem Fließtext sollen strukturierte JSON-Einträge für die vier Basis-Arrays des Gedächtnissystems erzeugt werden — Einheiten, Individuen, Hypothesen und offene Fragen. Dabei ist dem Modell explizit erlaubt, eigene zusätzliche Arrays anzulegen, wenn das Material Kategorien enthält, die in keine der vier Basis-Typen passen. Der Output besteht aus einem lesbaren Zusammenfassungstext gefolgt von einem validen JSON-Block.

Code in JavaScript (Gedächtnis-Aufbereitung) parst diesen JSON-Output und bereitet zwei Arten von Einträgen für Qdrant vor. Erstens iteriert er über alle Arrays im JSON — die vier Basis-Typen sowie etwaige Eigenkategorien — und erzeugt pro Eintrag ein strukturiertes Item mit dem serialisierten Inhalt als Text und den Metadaten typ, id, datum und feldnotiz. Zweitens splittet er den Feldnotiz-Text in einzelne Absätze und erzeugt pro Absatz ein eigenes Item mit dem Metadaten-Typ feldnotiz_absatz. Beide Typen verlassen den Node als gemischtes Array einzelner Items.

Qdrant Vector Store (Insert) schreibt alle Items in die Collection sloppy_gedaechtnis. Der Default Data Loader übernimmt dabei die Zuordnung von Text und Metadaten für jeden Eintrag, das Embedding-Modell gemini-embedding-001 vektorisiert den Text, und Qdrant speichert beides zusammen. Jeder Eintrag ist damit über semantische Suche auffindbar — sowohl die strukturierten Gedächtniseinträge als auch die Feldnotiz-Absätze.

Datensicherung

Dieser Block läuft parallel zur Gedächtnisverarbeitung und dient der menschenlesbaren Archivierung. Während Qdrant die strukturierten und vektorisierten Einträge für die maschinelle Weiterverarbeitung speichert, landen hier die Rohtexte als Markdown-Dateien in Google Drive — als lesbares Backup und zur manuellen Kontrolle der täglichen Outputs.

Google Drive (Feldnotiz) schreibt den vollständigen Feldnotiz-Text als Markdown-Datei in einen definierten Drive-Ordner. Der Dateiname folgt dem Schema YYYY-MM-DD_feldnotiz.md. Diese Datei ist das tägliche Primärarchiv von Sloppys Output in lesbarer Form.

Google Drive (Gedächtnis) schreibt den Output der Gedächtnisextraktion — also den lesbaren Zusammenfassungstext plus den vollständigen JSON-Block — als separate Markdown-Datei in denselben Ordner, unter dem Schema YYYY-MM-DD_Gedaechtnis.md. Das ermöglicht die manuelle Nachvollziehbarkeit dessen, was täglich in Qdrant gespeichert wird, ohne direkt in die Datenbank schauen zu müssen.

Telegram (Benachrichtigung) sendet abschließend den Link zur frisch veröffentlichten Feldnotiz auf der Website in einen Telegram-Kanal. Der Link wird dynamisch aus dem aktuellen Datum zusammengesetzt.

Der Website-Workflow

Dieser Workflow übernimmt die gesamte Publikationskette — von der fertigen Feldnotiz bis zur aktualisierten, öffentlich erreichbaren Website. Er wird nicht durch einen Timer ausgelöst, sondern durch einen Webhook-Call vom Feldnotiz-Workflow. Das Ergebnis ist ein vollständig automatisiertes Deployment: Audiodatei, Einzelseite der Feldnotiz und aktualisierte Archivübersicht landen per Git-Commit im Repository, woraufhin Coolify die Website automatisch neu deployt.


Webhook empfängt den POST-Request des Feldnotiz-Workflows. Der Payload enthält vier Felder: datum im Format YYYY-MM-DD, nr als numerische ID im Format YYYYMMDD, den vollständigen Feldnotiz-Text und ein Array der Quell-URLs.

Text Splitter bereinigt den Feldnotiz-Text für die anschließende Sprachsynthese. Die Feldnotiz endet mit einer Liste von Quell-URLs — diese würden von einer Text-to-Speech-Engine vorgelesen werden, was unerwünscht ist. Der Node schneidet den Text beim ersten Vorkommen von http ab und gibt nur den reinen Fließtext weiter.

Google TTS — Charon ruft die Google Cloud Text-to-Speech API auf und synthetisiert den bereinigten Text zu einer MP3-Datei. Die verwendete Stimme ist de-DE-Chirp3-HD-Charon — eine hochqualitative neuronale Stimme aus der Chirp3-HD-Generation von Google. Die API gibt die Audiodatei als Base64-kodierten String zurück.

Code (HTML Feldnotiz generieren) baut die vollständige HTML-Seite für den Tageseintrag. Der Node liest Datum, Feldnotiz-Text und Quellen aus dem Webhook-Payload sowie den Base64-Audiostring aus dem TTS-Node, strukturiert den Text in HTML-Absätze, generiert die Quellenliste und bettet einen Audio-Player ein. Das Ergebnis ist eine fertige, in sich geschlossene HTML-Datei.

GitHub — index.html SHA lesen liest den aktuellen SHA-Hash der index.html aus dem GitHub-Repository über die GitHub Contents API. Dieser SHA ist zwingend erforderlich: Die GitHub API verweigert Update-Operationen auf bestehende Dateien ohne den korrekten SHA des aktuellen Stands — ein Mechanismus zur Konfliktvermeidung bei konkurrierenden Schreibzugriffen.

GitHub — entries.json lesen liest den aktuellen Inhalt von data/entries.json aus dem Repository. Diese Datei ist das strukturierte Inhaltsverzeichnis aller bisher publizierten Feldnotizen mit Datum, numerischer ID, einem Preview-Text der ersten 200 Zeichen und der Anzahl der Quellen. Der Request verwendet den Header Accept: application/vnd.github.v3.raw, um den Dateiinhalt direkt als Text zu erhalten statt als Base64-kodiertes Objekt.

Code (entries.json und index.html bauen) ist der zentrale Aufbereitungsnode des Workflows. Er parst die bestehende entries.json, hängt den neuen Tageseintrag vorne ein, generiert daraus die vollständige index.html mit allen Archivkarten, stellt die SHAs für die nachfolgenden Update-Operationen bereit und konvertiert den Base64-Audiostring in n8n Binary Data, das der MP3-Push-Node als Datei an GitHub senden kann.

MP3 pushen schreibt die Audiodatei als neue Datei unter audio/DATUM.mp3 in das GitHub-Repository. Da es sich um eine täglich neue Datei handelt, ist das eine Create-Operation.

HTML pushen schreibt die Feldnotiz-HTML-Seite als neue Datei unter feldnotizen/DATUM.html in das Repository.

entries pushen aktualisiert data/entries.json mit dem um den neuen Eintrag erweiterten Array. Das ist eine Edit-Operation auf eine bestehende Datei.

index pushen aktualisiert index.html mit der neu generierten Archivübersicht. Dieser letzte Push löst über einen in GitHub konfigurierten Webhook einen Deployment-Trigger in Coolify aus, das die statische Website automatisch neu baut und ausliefert. Die Kette von der fertigen Feldnotiz bis zur aktualisierten öffentlichen Website ist damit vollständig automatisiert.