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
Inhalt
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>
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.
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:
| Befehl | Was 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.
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.
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.
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.
Offen bleibt die Frage, wie oft der Aufpasser eingreifen muss. Davon hängt ab, ob das Modell in dieser Rolle bleibt.