Es ist Sonntagnacht, 23:14 Uhr
Die Mail kommt rein. Support-Anfrage. Ein Kunde kann sich nicht einloggen. Sein System zeigt einen Fehler, den er nicht einordnen kann.
Niemand sitzt am Schreibtisch. Ich schlafe. Die Mail landet in meinem selbst gehosteten FreeScout – einem Open-Source-Ticketsystem, das auf einem kleinen Server in meinem Keller läuft. Keine Cloud. Kein Drittanbieter. Nur ich und mein Server.
Und Einstein.
Einstein ist mein KI-Agent. Er läuft auf meiner eigenen Infrastruktur, in meinem eigenen Netzwerk. Für diesen Ablauf nutzt er gpt-oss:20b, das lokal auf meinem ASUS Ascent GX10 läuft. Kein Chatbot, dem man Fragen stellt – ein Agent mit Werkzeugen. Er kann Tickets lesen, sie zuweisen, Antworten formulieren, Aufgaben anlegen. Und er tut das, ohne dass ich ihm jede Anweisung einzeln gebe.
Ich habe ihm einen Ablauf gegeben. Einen nummerierten Plan. Wenn ein Ticket reinkommt, arbeitet er ihn ab. Schritt für Schritt.
Als ich am nächsten Morgen aufwachte, lag eine Nachricht in meinem verschlüsselten Chat:
Ticket zugewiesen, Draft erstellt, Vikunja-Aufgabe angelegt: [#8] Login-Fehler – Kunde kann sich nicht authentifizieren.
Ich musste nur noch den Entwurf lesen, auf “Senden” klicken. Fertig.
Warum ich das gebaut habe
Ich betreibe eine kleine Firma. Support ist wichtig – aber Support ist nicht mein Hauptberuf. Wenn ein Ticket reinkommt, vergeht Zeit. Ich muss es lesen, verstehen, zuweisen, eine Antwort formulieren, mir merken, dass ich es noch bearbeiten muss. Jeder dieser Schritte kostet Minuten, die sich zu Stunden addieren.
FreeScout selbst ist großartig. Open Source, selbst gehostet, saubere Oberfläche. Aber es ist wie ein Briefkasten: Die Post kommt rein, und dann muss jemand hingehen und etwas damit tun.
Die Frage war: Was wäre, wenn Einstein derjenige ist, der hingeht?
Nicht um mir die Arbeit abzunehmen – die finale Entscheidung, die Antwort zu senden, treffe immer noch ich. Aber alles davor: das Lesen, das Zuweisen, das Formulieren eines Entwurfs, das Eintragen in meinen Tagesplan – das kann ein Agent.
Was Einstein tatsächlich tut
Wenn ein neues Ticket in FreeScout eintrifft, passiert Folgendes – automatisch, in dieser Reihenfolge:
- Ticket lesen – Einstein ruft das Ticket ab. Er liest Betreff, Absender und den vollständigen Inhalt.
- Mir zuweisen – Das Ticket wird auf meinen Namen gesetzt. Nicht auf “unassigned”, nicht auf einen Bot – auf mich.
- Antwort formulieren – Einstein schreibt einen Entwurf. Formelle Anrede, höfliche Eingangsbestätigung, keine konkreten Versprechen, die er nicht halten kann. Signatur drunter. Hinweis, dass die Antwort mit KI-Unterstützung erstellt wurde – das ist mir wichtig.
- Tagesplan aktualisieren – Er trägt das Ticket in meinen Tagesplan ein. Mit Titel, Ticket-Nummer und dem vollständigen Mail-Inhalt als Beschreibung. Damit ich beim ersten Kaffee weiß, was ansteht.
Auch das Sprachmodell bleibt dabei zu Hause: Hermes spricht gpt-oss:20b über die OpenAI-kompatible Schnittstelle des GX10 an. Der vollständige Ticketinhalt muss für die Verarbeitung deshalb nicht an eine externe Modell-API geschickt werden. FreeScout, Relay, Hermes, Vikunja, Matrix und das Modell kommunizieren innerhalb meines eigenen Netzes.
Das Ganze dauert etwa 50 Sekunden. Dann liegt die Bestätigung in meinem Chat.
Der Teil, der nicht klappte
Und jetzt der Teil, den ich am liebsten erzähle – weil er ehrlich ist.
Es klappte nämlich nicht beim ersten Mal. Es klappte nicht beim zweiten. Und beim dritten Versuch habe ich meinen eigenen Agenten zerstört.
Das Signatur-Problem. FreeScout und Hermes sprechen unterschiedliche Signatur-Sprachen. FreeScout signiert Webhooks mit einem Verfahren namens HMAC-SHA1. Hermes erwartet HMAC-SHA256. Das ist nicht ein Config-Parameter, den man umstellt – das sind zwei verschiedene kryptografische Verfahren. Die Lösung war ein kleines Relay-Programm, 50 Zeilen Python, das zwischen den beiden sitzt und die Signatur übersetzt. Ein Dolmetscher.
Das Zustell-Problem. Der erste Webhook kam an – HTTP 200, alles gut. Aber es passierte nichts. Kein Agent-Run, keine Aufgabe, kein Draft. Der Grund: Die Zustellung war auf “origin” eingestellt – aber ein Webhook hat keinen Ursprungs-Chat. Der Agent wurde gestartet, wusste aber nicht, wohin er seine Antwort schicken sollte. Also machte er nichts. Stille. Als hätte niemand zugehört.
Das Profil-Problem. Der schmerzhafteste Fehler. Ich wollte dem Webhook-Agenten ein eigenes Profil mit separatem Modell-Routing geben. Das lokale Modell auf dem GX10 war bereits da – ich wollte die Konfiguration nur besonders sauber voneinander trennen. Klingt clever. War es nicht. Das Profil-Multiplexing hat den gesamten Gateway lahmgelegt. Einstein hat sich selbst zerstört. Ich saß um 15 Uhr an einem Dienstag und musste manuell eingreifen, um den Agenten wiederzubeleben.
Die Lehre daraus: Manchmal ist einfach besser als clever. Ein Profil. gpt-oss:20b als lokales Standard-Modell für diesen Ablauf. Es läuft. Es ist stabil. Und es ist schnell genug.
Der Moment
Ich erinnere mich genau an den Moment, als es zum ersten Mal funktionierte. Nicht der Test mit Dummy-Daten – der echte Moment.
Eine Mail war reingekommen. Ich saß nicht am Schreibtisch. Und dann, in meinem Chat:
[webhook] POST event=convo.created → 202 Accepted
Agent-Run: 6 API-Calls, 49.2s, 855 Zeichen
Ticket zugewiesen. Draft erstellt. Vikunja-Aufgabe angelegt.
Drei Nachrichten. In meinem verschlüsselten Chat. Geschrieben von einem Agenten, der den vorgegebenen Plan selbstständig ausgeführt und dafür die passenden Werkzeuge in der richtigen Reihenfolge verwendet hatte.
Ich habe den Entwurf gelesen. Er war gut. Höflich, korrekt, keine Versprechen, die ich nicht halten kann. KI-Hinweis drunter. Signatur drunter. Ich habe auf “Senden” geklickt.
Das war der Moment, wo ich wusste: Das funktioniert nicht nur in der Theorie. Das funktioniert sonntags um 23 Uhr, wenn niemand hinsieht.
Was ich gelernt habe
Drei Dinge, die dieser Bau mich gelehrt hat:
Erstens: Automatisierung heißt nicht Kontrollverlust. Einstein schreibt keine Antworten, die ich nicht gelesen habe. Er sendet nichts von sich aus. Er bereitet vor – ich entscheide. Das ist der Unterschied zwischen “KI ersetzt mich” und “KI macht den langweiligen Teil, damit ich den wichtigen Teil machen kann”.
Zweitens: Fehler sind die beste Dokumentation. Das Signatur-Problem, das Zustell-Problem, das Profil-Debakel – jeder dieser Fehler hat mir mehr über das System beigebracht als jeder Erfolg. Das Profil-Multiplexing habe ich inzwischen dokumentiert und mit einer Warnung versehen: “NIEMALS Profil-Multiplexing für Webhook-Routes.” Das wird mir und jedem, der das nachbaut, ersparen, denselben Fehler zu machen.
Drittens: Einfach ist besser als clever. Der stabile Zustand ist nicht ein separates Profil mit eigenem Modell-Routing, Multiplexing und Allowlist. Der stabile Zustand ist: Ein Agent. Das lokale gpt-oss:20b auf dem GX10. Ein Relay, das die Signatur validiert und neu erstellt. Fertig. Alles andere war Architektur-Selbstzweck ohne Nutzen.
Technical Deep Dive
Für alle, die das nachbauen wollen. Keine IPs, keine Secrets – aber die Architektur und die Entscheidungen.
Die Pipeline
FreeScout (Ticketsystem, selbst gehostet)
│ POST convo.created, HMAC-SHA1 Signatur
▼
Relay (50 Zeilen Python, nur Standardbibliothek)
│ validiert SHA1
│ signiert neu mit SHA256
▼
Hermes Agent (Webhook-Plattform)
│ validiert SHA256 → startet Agent-Run
│
├──→ FreeScout MCP: Ticket lesen, zuweisen, Draft erstellen
├──→ Terminal: Vikunja-API, Aufgabe im Tagesprojekt anlegen
└──→ Matrix: Bestätigung in den Chat senden
Das Signatur-Problem im Detail
FreeScout signiert Webhooks mit HMAC-SHA1 – der Header X-FreeScout-Signature enthält einen base64-kodierten Hash. Hermes validiert Webhooks mit HMAC-SHA256 – der Header X-Hub-Signature-256 enthält einen hex-kodierten Hash mit sha256=-Präfix (GitHub-Stil).
Das ist nicht ein Parameter, den man umstellt. Das sind zwei verschiedene Hash-Funktionen mit zwei verschiedenen Kodierungen. Kein Config-Wert überbrückt das.
Die Lösung: Ein 50-Zeilen-Python-Relay, der auf einem eigenen Port lauscht. Er macht genau drei Dinge:
- FreeScouts SHA1-Signatur validieren (timing-safe comparison)
- Den Body unverändert weiterleiten an Hermes
- Neu signieren mit SHA256, dabei das Event aus
X-FreeScout-EventnachX-GitHub-Eventmappen (denn Hermes liest nur den GitHub-Header)
Keine externen Abhängigkeiten. Nur Python-Standardbibliothek. Startet als Hintergrunddienst.
Der Prompt
Der Agent bekommt einen nummerierten Ablauf mit, nicht eine vage Beschreibung. Das war entscheidend. Ein Prompt wie “Bearbeite das Ticket” führt dazu, dass der Agent rät. Ein Prompt wie “Schritt 1: Ticket lesen. Schritt 2: Zuweisen. Schritt 3: Draft schreiben. Schritt 4: Vikunja-Aufgabe anlegen” führt dazu, dass er arbeitet.
Wichtig: Die Vikunja-Aufgabe enthält den Mail-Inhalt als Beschreibung, nicht die Mailadresse. Das war eine Anpassung, die ich nach dem ersten Test gemacht habe – die Mailadresse allein nützt mir nichts, wenn ich später in der Aufgabenliste stehe und nicht weiß, worum es ging.
Was man dafür braucht
- FreeScout – Open-Source-Ticketsystem, kostenlos, selbst gehostet
- Hermes Agent – mein Agenten-Framework der Wahl
- Vikunja – Open-Source-Task-Management, selbst gehostet
- Ein lokales Netzwerk – FreeScout, Relay, Hermes und die Werkzeuge kommunizieren intern
- Ein lokales Sprachmodell – bei mir
gpt-oss:20bauf dem GX10, erreichbar über eine OpenAI-kompatible API - Die Relay-Skripte – 50 Zeilen Python, dokumentiert in meinem Agenten-Skill
Was ich anders machen würde
Wenn ich nochmal anfangen würde, würde ich das Profil-Multiplexing gar nicht erst versuchen. Default-Profil, das lokale gpt-oss:20b als Modell für den Ablauf, fertig. Die zwei Stunden, die ich mit dem separaten Profil verbracht habe, waren reine Verschwendung – und hätten schlimmer enden können, wenn ich den Gateway nicht manuell hätte wiederbeleben können.
Ich schreibe diesen Blog, weil ich glaube, dass self-hosted KI-Agenten kein Geheimnis sein sollten. Wenn du das nachbauen willst – es ist machbar. Es kostet dich einen Tag, keinen Monat. Und das Gefühl, sonntags um 23 Uhr eine Bestätigung im Chat zu haben, die niemand außer dein eigener Agent geschrieben hat – das ist es wert.
Baust du selbst an sowas rum oder hast du eine Frage zum Setup? Schreib mir – ich freue mich über jeden Austausch.