Memory
Das OpenClaw Memory besteht aus einfachen Markdown-Dateien im Agent Workspace. Diese Dateien sind die einzige Wahrheit — das Modell „erinnert” sich nur an das, was auf die Festplatte geschrieben wird.
Memory-Suchtools werden vom aktiven Memory-Plugin bereitgestellt (Standard: memory-core). Du kannst Memory-Plugins mit plugins.slots.memory = "none" deaktivieren.
Memory-Dateien (Markdown)
Das Standard-Workspace-Layout verwendet zwei Memory-Ebenen:
memory/YYYY-MM-DD.md- Tägliches Log (nur anhängen).
- Liest heute + gestern beim Session-Start.
MEMORY.md(optional)- Kuratiertes Langzeitgedächtnis.
- Wird nur in der privaten Hauptsession geladen (nie in Gruppenkontext).
Diese Dateien liegen im Workspace (agents.defaults.workspace, Standard: ~/.openclaw/workspace). Siehe Agent Workspace für das vollständige Layout.
Wann Memory schreiben
- Entscheidungen, Präferenzen und dauerhafte Fakten gehören in
MEMORY.md. - Tägliche Notizen und laufender Kontext gehören in
memory/YYYY-MM-DD.md. - Wenn jemand sagt „merk dir das”, schreib es auf (nicht im RAM behalten).
- Dieser Bereich entwickelt sich noch. Es hilft, das Modell daran zu erinnern, Erinnerungen zu speichern — es weiß dann, was zu tun ist.
- Wenn du willst, dass etwas bleibt, bitte den Bot, es ins Memory zu schreiben.
Automatischer Memory-Flush (Pre-Compaction Ping)
Wenn eine Session kurz vor der Auto-Compaction steht, löst OpenClaw einen stillen, agentischen Turn aus, der das Modell daran erinnert, dauerhafte Erinnerungen zu speichern, bevor der Context komprimiert wird. Die Standard-Prompts sagen explizit, dass das Modell antworten darf, aber normalerweise ist NO_REPLY die richtige Antwort, sodass der Nutzer diesen Turn nie sieht.
Das wird über agents.defaults.compaction.memoryFlush gesteuert:
{
agents: {
defaults: {
compaction: {
reserveTokensFloor: 20000,
memoryFlush: {
enabled: true,
softThresholdTokens: 4000,
systemPrompt: "Session nearing compaction. Store durable memories now.",
prompt: "Write any lasting notes to memory/YYYY-MM-DD.md; reply with NO_REPLY if nothing to store.",
},
},
},
},
}
Details:
- Soft Threshold: Der Flush wird ausgelöst, wenn die geschätzte Token-Anzahl der Session
contextWindow - reserveTokensFloor - softThresholdTokensüberschreitet. - Standardmäßig still: Prompts enthalten
NO_REPLY, sodass nichts ausgegeben wird. - Zwei Prompts: Ein User-Prompt plus ein System-Prompt fügen die Erinnerung hinzu.
- Ein Flush pro Compaction-Zyklus (wird in
sessions.jsongetrackt). - Workspace muss beschreibbar sein: Wenn die Session sandboxed mit
workspaceAccess: "ro"oder"none"läuft, wird der Flush übersprungen.
Für den vollständigen Compaction-Lebenszyklus siehe Session-Management + Compaction.
Vektor-Memory-Suche
OpenClaw kann einen kleinen Vektor-Index über MEMORY.md und memory/*.md (plus zusätzliche Verzeichnisse oder Dateien, die du hinzufügst) erstellen, sodass semantische Abfragen verwandte Notizen finden können, auch wenn die Formulierung unterschiedlich ist.
Standardeinstellungen:
- Standardmäßig aktiviert.
- Überwacht Memory-Dateien auf Änderungen (mit Debounce).
- Verwendet standardmäßig Remote-Embeddings. Wenn
memorySearch.providernicht gesetzt ist, wählt OpenClaw automatisch:local, wenn einmemorySearch.local.modelPathkonfiguriert ist und die Datei existiert.openai, wenn ein OpenAI-Key aufgelöst werden kann.gemini, wenn ein Gemini-Key aufgelöst werden kann.- Andernfalls bleibt die Memory-Suche deaktiviert, bis sie konfiguriert wird.
- Der lokale Modus verwendet node-llama-cpp und erfordert möglicherweise
pnpm approve-builds. - Verwendet sqlite-vec (wenn verfügbar), um die Vektorsuche in SQLite zu beschleunigen.
Remote-Embeddings erfordern einen API-Key für den Embedding-Provider. OpenClaw löst Keys aus Auth-Profilen, models.providers.*.apiKey oder Umgebungsvariablen auf. Codex OAuth deckt nur Chat/Completions ab und erfüllt nicht die Anforderungen für Embeddings bei der Memory-Suche. Für Gemini verwende GEMINI_API_KEY oder models.providers.google.apiKey. Bei einem benutzerdefinierten OpenAI-kompatiblen Endpoint setze memorySearch.remote.apiKey (und optional memorySearch.remote.headers).
Zusätzliche Memory-Pfade
Wenn du Markdown-Dateien außerhalb des Standard-Workspace-Layouts indexieren willst, füge explizite Pfade hinzu:
agents: {
defaults: {
memorySearch: {
extraPaths: ["../team-docs", "/srv/shared-notes/overview.md"]
}
}
}
Hinweise:
- Pfade können absolut oder workspace-relativ sein.
- Verzeichnisse werden rekursiv nach
.md-Dateien durchsucht. - Nur Markdown-Dateien werden indexiert.
- Symlinks werden ignoriert (Dateien oder Verzeichnisse).
Gemini Embeddings (nativ)
Setze den Provider auf gemini, um die Gemini Embeddings API direkt zu verwenden:
agents: {
defaults: {
memorySearch: {
provider: "gemini",
model: "gemini-embedding-001",
remote: {
apiKey: "YOUR_GEMINI_API_KEY"
}
}
}
}
Hinweise:
remote.baseUrlist optional (Standard ist die Gemini API Base-URL).- Mit
remote.headerskannst du bei Bedarf zusätzliche Header hinzufügen. - Standard-Modell:
gemini-embedding-001.
Wenn du einen benutzerdefinierten OpenAI-kompatiblen Endpoint (OpenRouter, vLLM oder einen Proxy) verwenden willst, kannst du die remote-Konfiguration mit dem OpenAI-Provider nutzen:
agents: {
defaults: {
memorySearch: {
provider: "openai",
model: "text-embedding-3-small",
remote: {
baseUrl: "https://api.example.com/v1/",
apiKey: "YOUR_OPENAI_COMPAT_API_KEY",
headers: { "X-Custom-Header": "value" }
}
}
}
}
Wenn du keinen API-Key setzen willst, verwende memorySearch.provider = "local" oder setze memorySearch.fallback = "none".
Fallbacks:
memorySearch.fallbackkannopenai,gemini,localodernonesein.- Der Fallback-Provider wird nur verwendet, wenn der primäre Embedding-Provider fehlschlägt.
Batch-Indexierung (OpenAI + Gemini):
- Standardmäßig für OpenAI- und Gemini-Embeddings aktiviert. Setze
agents.defaults.memorySearch.remote.batch.enabled = falsezum Deaktivieren. - Das Standardverhalten wartet auf den Batch-Abschluss; passe
remote.batch.wait,remote.batch.pollIntervalMsundremote.batch.timeoutMinutesbei Bedarf an. - Setze
remote.batch.concurrency, um zu steuern, wie viele Batch-Jobs parallel eingereicht werden (Standard: 2). - Der Batch-Modus gilt, wenn
memorySearch.provider = "openai"oder"gemini"ist und verwendet den entsprechenden API-Key. - Gemini-Batch-Jobs verwenden den asynchronen Embeddings-Batch-Endpoint und erfordern die Verfügbarkeit der Gemini Batch API.
Warum OpenAI Batch schnell + günstig ist:
- Für große Backfills ist OpenAI typischerweise die schnellste Option, die wir unterstützen, weil wir viele Embedding-Anfragen in einem einzigen Batch-Job einreichen und OpenAI sie asynchron verarbeiten lassen können.
- OpenAI bietet vergünstigte Preise für Batch-API-Workloads, sodass große Indexierungsläufe normalerweise günstiger sind als das synchrone Senden derselben Anfragen.
- Siehe die OpenAI Batch API-Dokumentation und Preise für Details:
Konfigurationsbeispiel:
agents: {
defaults: {
memorySearch: {
provider: "openai",
model: "text-embedding-3-small",
fallback: "openai",
remote: {
batch: { enabled: true, concurrency: 2 }
},
sync: { watch: true }
}
}
}
Tools:
memory_search— gibt Snippets mit Datei + Zeilenbereichen zurück.memory_get— liest Memory-Dateiinhalt nach Pfad.
Lokaler Modus:
- Setze
agents.defaults.memorySearch.provider = "local". - Gib
agents.defaults.memorySearch.local.modelPathan (GGUF oderhf:-URI). - Optional: Setze
agents.defaults.memorySearch.fallback = "none", um Remote-Fallback zu vermeiden.
Wie die Memory-Tools funktionieren
memory_searchdurchsucht semantisch Markdown-Chunks (~400 Token Ziel, 80-Token Überlappung) ausMEMORY.md+memory/**/*.md. Es gibt Snippet-Text (max. ~700 Zeichen), Dateipfad, Zeilenbereich, Score, Provider/Modell und ob wir von lokal auf Remote-Embeddings zurückgefallen sind zurück. Es wird kein vollständiger Dateiinhalt zurückgegeben.memory_getliest eine bestimmte Memory-Markdown-Datei (workspace-relativ), optional ab einer Startzeile und für N Zeilen. Pfade außerhalb vonMEMORY.md/memory/sind nur erlaubt, wenn sie explizit inmemorySearch.extraPathsaufgeführt sind.- Beide Tools sind nur aktiviert, wenn
memorySearch.enabledfür den Agent true ergibt.
Was indexiert wird (und wann)
- Dateityp: Nur Markdown (
MEMORY.md,memory/**/*.md, plus alle.md-Dateien untermemorySearch.extraPaths). - Index-Speicherung: Pro-Agent SQLite unter
~/.openclaw/memory/<agentId>.sqlite(konfigurierbar überagents.defaults.memorySearch.store.path, unterstützt{agentId}-Token). - Aktualität: Watcher auf
MEMORY.md,memory/undmemorySearch.extraPathsmarkiert den Index als dirty (Debounce 1,5s). Sync wird beim Session-Start, bei der Suche oder in einem Intervall geplant und läuft asynchron. Session-Transkripte verwenden Delta-Schwellenwerte, um Hintergrund-Sync auszulösen. - Reindex-Auslöser: Der Index speichert den Embedding-Provider/Modell + Endpoint-Fingerprint + Chunking-Parameter. Wenn sich einer davon ändert, setzt OpenClaw automatisch zurück und reindexiert den gesamten Store.
Hybride Suche (BM25 + Vektor)
Wenn aktiviert, kombiniert OpenClaw:
- Vektor-Ähnlichkeit (semantischer Match, Formulierung kann unterschiedlich sein)
- BM25-Keyword-Relevanz (exakte Tokens wie IDs, Umgebungsvariablen, Code-Symbole)
Wenn Volltextsuche auf deiner Plattform nicht verfügbar ist, fällt OpenClaw auf reine Vektorsuche zurück.
Warum hybrid?
Vektorsuche ist großartig bei „das bedeutet dasselbe”:
- „Mac Studio Gateway Host” vs. „die Maschine, auf der das Gateway läuft”
- „Dateiupdates debouncen” vs. „nicht bei jedem Schreiben indexieren”
Aber sie kann schwach bei exakten, signalstarken Tokens sein:
- IDs (
a828e60,b3b9895a…) - Code-Symbole (
memorySearch.query.hybrid) - Fehlerstrings („sqlite-vec unavailable”)
BM25 (Volltext) ist das Gegenteil: stark bei exakten Tokens, schwächer bei Umschreibungen. Hybride Suche ist der pragmatische Mittelweg: beide Retrieval-Signale nutzen, sodass du gute Ergebnisse sowohl für „natürliche Sprache”-Abfragen als auch für „Nadel im Heuhaufen”-Abfragen bekommst.
Wie wir Ergebnisse zusammenführen (aktuelles Design)
Implementierungsskizze:
- Kandidatenpool von beiden Seiten abrufen:
- Vektor: Top
maxResults * candidateMultipliernach Kosinus-Ähnlichkeit. - BM25: Top
maxResults * candidateMultipliernach FTS5 BM25-Rang (niedriger ist besser).
- BM25-Rang in einen 0..1-ähnlichen Score umwandeln:
textScore = 1 / (1 + max(0, bm25Rank))
- Kandidaten nach Chunk-ID vereinigen und gewichteten Score berechnen:
finalScore = vectorWeight * vectorScore + textWeight * textScore
Hinweise:
vectorWeight+textWeightwird in der Config-Auflösung auf 1.0 normalisiert, sodass Gewichte sich wie Prozentsätze verhalten.- Wenn Embeddings nicht verfügbar sind (oder der Provider einen Null-Vektor zurückgibt), führen wir trotzdem BM25 aus und geben Keyword-Matches zurück.
- Wenn FTS5 nicht erstellt werden kann, behalten wir die reine Vektorsuche bei (kein harter Fehler).
Das ist nicht „IR-theoretisch perfekt”, aber es ist einfach, schnell und verbessert tendenziell Recall/Precision bei echten Notizen. Wenn wir später ausgefeilter werden wollen, sind gängige nächste Schritte Reciprocal Rank Fusion (RRF) oder Score-Normalisierung (min/max oder z-Score) vor dem Mischen.
Konfiguration:
agents: {
defaults: {
memorySearch: {
query: {
hybrid: {
enabled: true,
vectorWeight: 0.7,
textWeight: 0.3,
candidateMultiplier: 4
}
}
}
}
}
Embedding-Cache
OpenClaw kann Chunk-Embeddings in SQLite cachen, sodass Reindexierung und häufige Updates (besonders Session-Transkripte) unveränderten Text nicht neu embedden.
Konfiguration:
agents: {
defaults: {
memorySearch: {
cache: {
enabled: true,
maxEntries: 50000
}
}
}
}
Session-Memory-Suche (experimentell)
Du kannst optional Session-Transkripte indexieren und sie über memory_search auffindbar machen.
Das ist hinter einem experimentellen Flag versteckt.
agents: {
defaults: {
memorySearch: {
experimental: { sessionMemory: true },
sources: ["memory", "sessions"]
}
}
}
Hinweise:
- Session-Indexierung ist Opt-in (standardmäßig aus).
- Session-Updates werden gedebounct und asynchron indexiert, sobald sie Delta-Schwellenwerte überschreiten (Best-Effort).
memory_searchblockiert nie auf Indexierung; Ergebnisse können leicht veraltet sein, bis der Hintergrund-Sync abgeschlossen ist.- Ergebnisse enthalten weiterhin nur Snippets;
memory_getbleibt auf Memory-Dateien beschränkt. - Session-Indexierung ist pro Agent isoliert (nur die Session-Logs dieses Agents werden indexiert).
- Session-Logs liegen auf der Festplatte (
~/.openclaw/agents/<agentId>/sessions/*.jsonl). Jeder Prozess/Benutzer mit Dateisystemzugriff kann sie lesen, also behandle Festplattenzugriff als Vertrauensgrenze. Für strengere Isolation führe Agents unter separaten OS-Benutzern oder Hosts aus.
Delta-Schwellenwerte (Standardwerte gezeigt):
agents: {
defaults: {
memorySearch: {
sync: {
sessions: {
deltaBytes: 100000, // ~100 KB
deltaMessages: 50 // JSONL-Zeilen
}
}
}
}
}
SQLite-Vektorbeschleunigung (sqlite-vec)
Wenn die sqlite-vec-Erweiterung verfügbar ist, speichert OpenClaw Embeddings in einer SQLite Virtual Table (vec0) und führt Vektor-Distanz-Abfragen in der Datenbank durch. Das hält die Suche schnell, ohne jedes Embedding in JS laden zu müssen.
Konfiguration (optional):
agents: {
defaults: {
memorySearch: {
store: {
vector: {
enabled: true,
extensionPath: "/path/to/sqlite-vec"
}
}
}
}
}
Hinweise:
enabledist standardmäßig true; wenn deaktiviert, fällt die Suche auf In-Process-Kosinus-Ähnlichkeit über gespeicherte Embeddings zurück.- Wenn die sqlite-vec-Erweiterung fehlt oder nicht geladen werden kann, loggt OpenClaw den Fehler und fährt mit dem JS-Fallback fort (keine Vektor-Tabelle).
extensionPathüberschreibt den gebündelten sqlite-vec-Pfad (nützlich für Custom Builds oder nicht-standardmäßige Installationsorte).
Lokales Embedding Auto-Download
- Standard lokales Embedding-Modell:
hf:ggml-org/embeddinggemma-300M-GGUF/embeddinggemma-300M-Q8_0.gguf(~0,6 GB). - Wenn
memorySearch.provider = "local", löstnode-llama-cppdenmodelPathauf; wenn das GGUF fehlt, wird es automatisch heruntergeladen in den Cache (oderlocal.modelCacheDirfalls gesetzt), dann geladen. Downloads werden bei Retry fortgesetzt. - Native Build-Anforderung: Führe
pnpm approve-buildsaus, wählenode-llama-cpp, dannpnpm rebuild node-llama-cpp. - Fallback: Wenn das lokale Setup fehlschlägt und
memorySearch.fallback = "openai", wechseln wir automatisch zu Remote-Embeddings (openai/text-embedding-3-smallfalls nicht überschrieben) und protokollieren den Grund.
Beispiel für benutzerdefinierten OpenAI-kompatiblen Endpoint
agents: {
defaults: {
memorySearch: {
provider: "openai",
model: "text-embedding-3-small",
remote: {
baseUrl: "https://api.example.com/v1/",
apiKey: "YOUR_REMOTE_API_KEY",
headers: {
"X-Organization": "org-id",
"X-Project": "project-id"
}
}
}
}
}
Hinweise:
remote.*hat Vorrang vormodels.providers.openai.*.remote.headerswerden mit OpenAI-Headern zusammengeführt; remote gewinnt bei Key-Konflikten. Lassremote.headersweg, um die OpenAI-Standardwerte zu verwenden.