Zurück zum Blog

Ein lokaler KI-Assistent mit Kalender, Mail und Browser

Ein lokaler KI-Assistent mit Kalender, Mail und Browser

Was mit OpenClaw funktioniert und was nicht

Datum 30. September 2026
Kategorie KI-Agenten & Automatisierung
Lesezeit 12 Minuten
Worum es geht: OpenClaw verbindet als Agent-Gateway ein lokal laufendes Sprachmodell mit Kalender, Websuche, Browser und einer eigenen Mailadresse. An einem Wochenende war alles angeschlossen. Die Tage danach haben gezeigt, dass „angeschlossen" und „zuverlässig" zwei verschiedene Dinge sind. Dieser Beitrag beschreibt beides: den Aufbau und die Stellen, an denen er scheiterte.

Seit einigen Tagen läuft im Homelab OpenClaw, ein Agent-Gateway, das ein Sprachmodell mit Werkzeugen verbindet und über Telegram erreichbar ist. Das Modell dahinter läuft komplett lokal: qwen3.6 mit 35 Milliarden Parametern in Mixture-of-Experts-Bauweise, davon rund 3 Milliarden aktiv, über llama.cpp auf zwei Consumer-Grafikkarten.

Das Ziel klang einfach: ein Assistent, der die eigenen Termine kennt, im Web sucht, Webseiten bedienen kann, eine eigene Mailadresse hat und morgens ein Briefing schickt.

Am Ende läuft folgendes:

  • Google-Kalender nur lesend, Termine anlegen trotzdem möglich (per Einladung)
  • Websuche über Google (Gemini-Grounding), DuckDuckGo als Reserve
  • Headless-Browser im Container für Seiten, die ihren Inhalt per JavaScript aufbauen
  • Eigene Mailadresse mit technisch erzwungenen Allowlists
  • Hintergrund-Wächter für Mails, für das Homelab und für den Agenten selbst
  • Ein Gedächtnis, das der Agent selbst pflegt

Der Aufbau

Telegram ──▶ OpenClaw-Gateway (Docker) ──▶ LiteLLM ──▶ llama-swap ──▶ qwen3.6 (2 GPUs)
                 │
                 ├─ gog ─────────▶ Google Calendar API (read-only)
                 ├─ web_search ──▶ Gemini + Google Search Grounding (Fallback: DuckDuckGo)
                 ├─ browser ─────▶ headless Chromium (im Container)
                 ├─ claw-mail ───▶ mailcow (IMAP/SMTP, eigene Adresse)
                 └─ Gedächtnis ──▶ SQLite + lokale Embeddings (Ollama, CPU)

Automationen: Morgen-Briefing 7:00 · Mail-Waechter 15 min · Homelab-Waechter 5 min · Aufpasser 2 min

Kalender: lesen erlauben, schreiben umleiten

Freigegeben ist bewusst nur der Lesezugriff. Ein lokales Modell, das Termine löschen kann, wollten wir nicht.

Drei Dinge haben dabei Zeit gekostet.

Solange die OAuth-Anwendung im Status „Test" steht, verfällt das Refresh-Token nach sieben Tagen. Erst nach dem Veröffentlichen bleibt der Zugriff bestehen; unverifiziert reicht für den Eigengebrauch. Eine Meldung „403 access_denied" bedeutet im Test-Modus fast immer, dass das eigene Konto nicht als Testnutzer eingetragen ist.

Das Gateway hat keinen Browser, die Anmeldung läuft deshalb in zwei Schritten. Die Fehlerseite, die nach der Zustimmung erscheint, ist dabei erwartet — in ihrer Adresse steckt der Code. Der zweite Schritt muss exakt dieselben Aufrufparameter bekommen wie der erste, sonst bricht die Anmeldung mit einer Zustandsmeldung ab.

Damit gog einen Neubau des Containers übersteht, liegen Programm, Konfiguration und der verschlüsselte Schlüsselbund im dauerhaften Volume, nicht im Container selbst:

# docker-compose.override.yml (Auszug)
environment:
  PATH: /home/node/.openclaw/tools/bin:/usr/local/bin:/usr/bin:/bin
  GOG_HOME: /home/node/.openclaw/gogcli
  GOG_ACCOUNT: <Konto>
  GOG_KEYRING_PASSWORD: <Platzhalter, Wert kommt aus der .env>
Der wichtigste Entwurfsgriff des Projekts: Wie legt ein Agent Termine an, der nicht schreiben darf? Er verschickt eine Kalendereinladung per Mail. Der Mailclient zeigt „Ja/Nein", ein Klick trägt den Termin ein. Der Mensch bestätigt, der Agent bleibt ohne Schreibrechte.

Websuche mit Reserve

Genutzt wird ein Dienst, der statt einer Linkliste eine zusammengefasste Antwort mit Quellen liefert. Der Zugang läuft ohne hinterlegte Zahlungsdaten. Ist das kostenlose Kontingent aufgebraucht, gibt es eine Fehlermeldung statt einer Rechnung.

Als Reserve ist ein zweiter, schlüsselloser Dienst installiert. Wichtig dabei: Ist ein Anbieter ausdrücklich gesetzt, wird nicht automatisch umgeschaltet. Der Wechsel ist ein Einzeiler, aber eben ein manueller.

Ein Browser im Container

Viele Seiten — Konzertkarten, Zeitungen — bauen ihren Inhalt per JavaScript auf; ein einfacher Abruf sieht dort nichts. OpenClaw bringt ein Browser-Werkzeug mit, im Abbild fehlte aber Chromium selbst. Statt das ganze Abbild neu zu bauen, liegt ein kleines Zusatz-Abbild obendrauf:

FROM openclaw:local
USER root
RUN apt-get update && apt-get install -y --no-install-recommends xvfb && \
    node /app/node_modules/playwright-core/cli.js install --with-deps chromium && \
    chown -R node:node "$PLAYWRIGHT_BROWSERS_PATH" && rm -rf /var/lib/apt/lists/*
USER node

Dazu kamen drei Stellschrauben:

  • Die Chromium-Sandbox funktioniert im Container nicht und wird abgeschaltet — die Isolation übernimmt der Container.
  • Der gemeinsame Speicher muss vergrößert werden; mit den voreingestellten 64 MB stürzen Tabs ab.
  • Das Browser-Werkzeug muss ausdrücklich erlaubt werden, das voreingestellte Werkzeugprofil enthält es nicht.
Zur Sicherheit: Der Agent kann im Browser beliebiges JavaScript ausführen. Solange dieser Browser nirgends angemeldet ist, ist das unkritisch. Mit Anmeldungen wäre es ein direkter Weg für Prompt-Injection über manipulierte Seiten.

Eine eigene Mailadresse, aber mit Leine

Der Agent hat eine eigene Adresse auf dem eigenen Mailserver. Einen vollwertigen Mailclient bekommt er bewusst nicht. Stattdessen gibt es ein kleines Python-Werkzeug claw-mail mit genau vier Befehlen:

BefehlWas er darf
inbox Zeigt nur Mails von Absendern auf der Empfangs-Allowlist. Alles andere wandert ungelesen in einen Sammelordner; der Agent sieht es nie.
send Nur an Adressen auf der Sende-Allowlist.
invite Termineinladung an den Menschen.
count Zählt neue freigegebene Mails — für den Wächter, der ohne Modell auskommt.

Die Allowlist liegt als Datei außerhalb des Containers und ist dort nur lesend eingebunden. Der Agent kann sich also nicht selbst weitere Empfänger freischalten.

Der Grund für den Aufwand: Jede eingehende Mail ist fremder Text, den das Modell liest, also ein Angriffsweg für Prompt-Injection. Wer die Adresse kennt, könnte dem Agenten sonst Anweisungen schicken.

Die ehrliche Grenze dieser Lösung: Der Agent hat eine Shell im Container und könnte die Mail-Zugangsdaten theoretisch selbst verwenden. Das Werkzeug schützt gegen Manipulation von außen, nicht gegen einen absichtlich bösartigen Agenten. Eine harte Sperre ginge nur serverseitig.

Der Mail-Wächter prüft im Viertelstundentakt, ob überhaupt etwas da ist — ohne Modell. Nur wenn wirklich eine freigegebene Mail vorliegt, bekommt der Agent einen Auftrag. So bleibt die Grafikkarte ruhig.

„Ich sende die Mail jetzt" und dann nichts

Die technische Anbindung war der einfache Teil. Die interessanteren Erkenntnisse kamen aus dem Betrieb.

Der Agent sollte eine Mail verschicken. Er antwortete „Ich sende die Mail jetzt:" und beendete seinen Zug, ohne ein Werkzeug aufzurufen. Auf Nachfrage entschuldigte er sich und kündigte erneut an. Das ging zehnmal hintereinander. Später behauptete er, die Mail sei als Spam gefiltert worden. Sie war nie gesendet worden.

Die Analyse der Sitzungsdaten zeigte das Muster klar: In langen Gesprächen ahmt ein kleines Modell sein eigenes bisheriges Verhalten nach. Jeder gescheiterte Anlauf macht den nächsten wahrscheinlicher. In einer frischen Sitzung funktionierte derselbe Aufruf sofort.

Ein Detail daran war hausgemacht. Das Modell rief das Mail-Werkzeug mit einem Parameter auf, den es vom Kalender-Werkzeug kannte — unseres kannte ihn nicht. Statt dem Modell etwas abzugewöhnen, haben wir das Werkzeug angepasst.

Werkzeuge sollten die Konventionen sprechen, die das Modell ohnehin erwartet. Ein Agent, der sich an eine ungewohnte Aufrufform erinnern muss, vergisst sie genau dann, wenn es darauf ankommt.

Die Gegenmaßnahmen laufen alle auf dasselbe hinaus — Nachweis statt Vertrauen:

  • Jede gesendete Mail landet im Ordner „Gesendet" und in einem Versandprotokoll. Was dort nicht steht, ist nicht raus.
  • Ein täglicher Reset des Gesprächs, damit sich Schleifen nicht über Tage aufschaukeln.
  • Ein Aufpasser, der im Zwei-Minuten-Takt und ohne Modell die Sitzungsdaten liest. Endet eine Antwort mit einer Ankündigung ohne Werkzeugaufruf, stößt er den Agenten einmal an. Hilft das nicht, kommt eine Warnung. Behauptet der Agent „Gesendet an …" ohne Eintrag im Protokoll, ebenfalls.

Das Gedächtnis, das nie eines war

Nach dem ersten täglichen Reset kannte der Agent den Namen seines Nutzers nicht mehr. Die Ursache war ernüchternd: Er hatte sich nie etwas dauerhaft gemerkt.

Die Profildatei war noch die leere Vorlage. Die Gedächtnissuche war auf einen Cloud-Anbieter eingestellt, für den kein Zugang hinterlegt war — damit fand auch die nächtliche Konsolidierung nie Kandidaten. Und die Funktion, die vor einer Antwort in alten Gesprächen nachsieht, war abgeschaltet. Drei Einstellungen, von denen jede für sich harmlos aussieht.

Die Lösung kam ohne Cloud aus: Einbettungen über nomic-embed-text auf Ollama, das auf der CPU läuft und dem Sprachmodell keinen Grafikspeicher wegnimmt. Seitdem trägt der Agent ein „merk dir …" selbst ein.

Eine Falle beim Testen: Die Rückschau in alte Gespräche läuft nur bei echten Chat-Nachrichten, nicht bei Aufrufen über die Kommandozeile. Wer nur per Kommandozeile testet, hält die Funktion für kaputt.

Kleinere Stolpersteine

Nach einem Neubau des Containers hing das Gateway in einer Neustartschleife und meldete, ein anderer Besitzer halte noch die Sperre. Die Sperre des alten Containers läuft erst fünf Minuten nach dessen letztem Lebenszeichen ab, und der neue Container hatte zufällig dieselbe Prozessnummer. Die Lösung war, zu warten und nichts in der Datenbank zu löschen.

Automationen starten über eine Login-Shell, und die setzt den Suchpfad zurück. Werkzeuge im eigenen Verzeichnis werden dort nicht gefunden. In Skripten gehören deshalb vollständige Pfade hin.

Und noch einmal Mail: Eine Nachricht von der neuen Absenderadresse an einen großen Freemail-Anbieter ging laut Protokoll hinaus, kam beim Empfänger aber nicht an. Bei einer Adresse ohne Reputation und mit vielen Links landet so etwas gern im Spam-Ordner, trotz korrekt gesetztem SPF, DKIM, DMARC und Reverse-DNS. Zugestellt und gelesen sind zwei verschiedene Dinge.

Was trägt und was nicht

Tragfähig erwiesen haben sich vier Entscheidungen:

  • Nur lesende Rechte, ergänzt um Einladungen statt Schreibzugriff.
  • Allowlists, die der Agent selbst nicht ändern kann.
  • Wächter als reine Skripte, die das Modell nur bei Bedarf wecken.
  • Nachweise statt der Aussage des Modells.

Was ein kleines lokales Modell dagegen nicht leistet, ist das zuverlässige Zuendebringen von Aufgaben in langen Gesprächen. Für kurze, klar formulierte Aufträge in frischen Sitzungen reicht es. Für einen Assistenten, der über Tage hinweg „einfach macht", braucht es entweder ein stärkeres Modell oder ein Gerüst aus Resets, Wächtern und Nachweisen, das die Schwächen auffängt.

Die wichtigste Lehre: Einem Agenten nicht glauben, sondern nachprüfen können. Jede Aktion mit Außenwirkung sollte eine Spur hinterlassen, die unabhängig vom Modell ist. Ein Agent, der behauptet etwas getan zu haben, und ein Agent, der es getan hat, sind von außen nur dann unterscheidbar, wenn jemand anderes mitschreibt.

Offen bleibt die Frage, wie oft der Aufpasser eingreifen muss. Davon hängt ab, ob das Modell in dieser Rolle bleibt.