5-layer memory architecture for OpenClaw agents. Solves context bloat, the 48h fogging problem, and rule amnesia. Works for single-agent and multi-agent setu...
---
name: smart-memory
description: >
5-layer memory architecture for OpenClaw agents. Solves context bloat,
the 48h fogging problem, and rule amnesia. Works for single-agent and
multi-agent setups. Agent reads this file and sets up the full system.
metadata:
openclaw:
emoji: 🧠
---
# Smart Memory — Setup Skill
## Was dieses Skill tut
Standard-OpenClaw-Setups akkumulieren über Wochen unkontrolliert Wissen in MEMORY.md.
Das führt zu Kontextfenster-Überlastung, Widersprüchen und einem Agenten der wichtige
Regeln "vergisst" obwohl sie in MEMORY.md stehen — und zu hartem 48h-Fogging bei Daily Logs.
Dieses Skill richtet eine **5-Schichten-Memory-Architektur** ein die das löst.
---
## AGENT: Setup-Anleitung
Gehe die folgenden Schritte der Reihe nach durch.
`[MULTI-AGENT ONLY]`-Schritte können übersprungen werden wenn nur ein Agent im System ist.
### Schritt 1: Verzeichnisse anlegen
```bash
mkdir -p memory/projects
mkdir -p memory/agents # [MULTI-AGENT ONLY]
mkdir -p docs
```
### Schritt 2: MEMORY.md auf Kern reduzieren
Lese die bestehende MEMORY.md. Extrahiere:
- User-Profil (max. 3 Zeilen)
- Projekt-Headlines (1 Zeile pro Projekt) → Verweis auf CONTEXT.md
- Kritische Regeln (max. 5 Bullet Points)
- Agent-Routing-Tabelle [MULTI-AGENT ONLY]
Alles andere gehört in active-context.md oder die Projekt-/Agent-Kontext-Dateien (s.u.).
Schreibe das Ergebnis in MEMORY.md — Ziel: max. 35 Zeilen.
Füge am Ende hinzu:
```
## Memory-Architektur
- active-context.md → offene Themen + Kontext (täglich pflegen)
- memory/projects/<name>/CONTEXT.md → Projekthandbücher (on-demand injizieren)
- memory/agents/<typ>.md → Domain-Wissen (on-demand injizieren) [MULTI-AGENT ONLY]
- ⚠️ Schreib-Pflicht: Strukturelle Änderungen → active-context.md SOFORT updaten (Session kann jederzeit enden)
```
### Schritt 3: active-context.md erstellen
Erstelle `memory/active-context.md` mit folgendem Header:
```markdown
# Active Context
# Pflege-Regel: [DONE]-Einträge wöchentlich entfernen (automatisch via Cron/Heartbeat).
# Jeder Eintrag: Status + inhaltlicher Kontext (was der User erklärt hat).
# Nach Erledigung: gesamten Block löschen.
# Größenbremse: > 100 Zeilen = zu viele offene Themen oder zu geschwätzig → kürzen.
---
```
Befülle die Datei mit allem was aktuell offen ist (aus MEMORY.md, Tages-Logs, Kontext).
Format pro Eintrag:
```markdown
## [OPEN] <Thema> — <Kurzbeschreibung>
<Inhaltlicher Kontext: Was wurde erklärt, entschieden, vereinbart?>
<Relevante Regeln oder Hintergründe die der Agent kennen muss>
---
```
Besonders wichtig: Kritische Regeln die oft vergessen werden → als `[OPEN]`-Block aufnehmen
(auch wenn sie "immer" gelten — das ist der Punkt, sie bleiben sichtbar bis sie wirklich verinnerlicht sind).
### Schritt 4: Projekthandbücher anlegen
Für jedes aktive Projekt: `memory/projects/<projektname>/CONTEXT.md`
Extrahiere alle projektspezifischen Details aus MEMORY.md in diese Datei.
Template: `templates/projects/example/CONTEXT.md` in diesem Skill-Ordner.
**Wann updaten:**
> ⚠️ **Kritisch:** "Am Ende des Tasks" ist ein Anti-Pattern — Sessions können jederzeit durch
> Rate-Limits oder Crashes enden. Die korrekte Anweisung ist: **CONTEXT.md SOFORT nach der
> Aktion updaten, in derselben Antwort.** Kein nachgelagerter Schritt.
### Schritt 5: Agent-Domain-Wissen anlegen [MULTI-AGENT ONLY]
Für jeden Sub-Agent-Typ: `memory/agents/<typ>.md`
Enthält cross-project Wissen das für diesen Agent-Typ immer gilt.
Templates: `templates/agents/` in diesem Skill-Ordner.
**Abgrenzung:**
- `memory/agents/coding.md`: "GPT erfindet Swift-Typen → nie GPT für Swift-Code"
- `memory/projects/myapp/CONTEXT.md`: "myapp hat zwei .strings-Dateien die synchron bleiben müssen"
Wenn das Wissen beim nächsten Projekt auch gilt → agents/. Sonst → projects/.
**Task-Summary-Pflicht für Sub-Agents:**
Jeder Sub-Agent sendet nach Abschluss eine Summary an den Orchestrator:
```
sessions_send(sessionKey="agent:main", message="[SUMMARY] Projekt: X | Was: Y | Status: Z")
```
Zweck: Orchestrator-Heartbeat kann als Backup-Bestätigung prüfen ob CONTEXT.md-Update erfolgte.
### Schritt 6: AGENTS.md Startup-Sequenz updaten
Ersetze die "Every Session"-Sektion durch:
```markdown
## Every Session
Before doing anything else:
1. Read `SOUL.md` — this is who you are
2. Read `USER.md` — this is who you're helping
3. **If in MAIN SESSION:** Read `MEMORY.md` + `memory/active-context.md`
4. Read `memory/YYYY-MM-DD.md` (today + yesterday) for recent raw context
**Sub-Agent Spawns [MULTI-AGENT ONLY]:** Injiziere in den Task-Prompt:
- Immer: Inhalt von `memory/agents/<typ>.md`
- Bei Projektarbeit: Inhalt von `memory/projects/<name>/CONTEXT.md`
```
### Schritt 7: Automatischen Cleanup + Backup einrichten
**Option A — Heartbeat (einfach):**
Füge in `HEARTBEAT.md` ein:
```
# TASK: Memory-Hygiene (wöchentlich, jeden Sonntag)
# Prüfe ob seit letztem Cleanup > 6 Tage vergangen (heartbeat-state.json).
# 1. Entferne alle [DONE]-Blöcke komplett aus memory/active-context.md
# 2. [OPEN]-Blöcke älter 30 Tage ohne Aktivität → in Tages-Log, aus active-context entfernen
# 3. memory/projects/*/CONTEXT.md — erledigte Tasks kürzen
# 4. lastMemoryCleanup Timestamp in memory/heartbeat-state.json schreiben
# TASK: Sub-Agent Memory Backup (bei JEDEM Heartbeat) [MULTI-AGENT ONLY]
# Sicherheitsnetz falls Sub-Agent CONTEXT.md-Sofortschreiben vergessen hat.
# 1. Lese letzten Sub-Agent-Output-Channel auf Abschluss-Posts
# 2. Prüfe ob strukturelle Änderungen in CONTEXT.md reflektiert sind
# 3. Falls NICHT → kompakten Eintrag ergänzen (was, wann, Commit/Status)
# TASK: Daily-Log automatisch schreiben (einmal täglich, abends)
# Prüfe ob memory/YYYY-MM-DD.md für heute existiert.
# Wenn NICHT: Lese memory/chat-YYYY-MM-DD.md → destilliere strukturelle Änderungen.
# Schreibe Ergebnis nach memory/YYYY-MM-DD.md (max. 30 Zeilen).
```
**Option B — Cron-Job (präzise):**
```
Schedule: 0 7 * * 0 (Sonntag 07:00 Ortszeit)
Message: "Wöchentliche Memory-Hygiene: [Cleanup-Anweisungen wie oben]"
```
### Schritt 8: Abschlusskontrolle
Prüfe jedes Kriterium:
- [ ] MEMORY.md < 40 Zeilen
- [ ] active-context.md erstellt, mindestens ein [OPEN]-Eintrag
- [ ] Jeder [OPEN]-Eintrag hat inhaltlichen Kontext, nicht nur Statuszeile
- [ ] Keine projektspezifischen Details mehr in MEMORY.md
- [ ] Wöchentlicher Cleanup aktiv (Heartbeat oder Cron)
- [ ] AGENTS.md Startup-Sequenz updated
- [ ] Für jedes aktive Projekt: CONTEXT.md angelegt [MULTI-AGENT ONLY]
- [ ] Für jeden Sub-Agent-Typ: agents/<typ>.md angelegt [MULTI-AGENT ONLY]
Berichte dem User kurz: Was wurde eingerichtet, wie viele Zeilen hat MEMORY.md jetzt,
wie viele offene Themen sind in active-context.md.
---
## Session-Lifecycle: Graceful Termination (v1.3+)
> **Das Session-Bloat-Problem:** Channel-Sessions (Slack, Discord etc.) wachsen unbegrenzt.
> Ab ~500 Zeilen wird jede Antwort spürbar langsamer — die gesamte History wird bei jeder
> Nachricht in den Kontext geladen. Ein harter Trim mitten im Gespräch löscht aber Kontext,
> Sprache und Persona sofort. Beides ist lösbar — ohne Script, ohne sichtbaren Output im Chat.
**Goldene Regel 1 — Immer Memory schreiben lassen, bevor du trimmst oder killst:**
Bevor eine Session getrimmt oder gelöscht wird, muss der Agent die Möglichkeit haben,
seinen Kontext in das Memory-System zu schreiben. Das gilt für manuelle Aktionen genauso
wie für automatische Heartbeat-Tasks. Der Hash-Handshake-Flow (s.u.) ist die technische
Umsetzung dieser Regel.
**Goldene Regel 2:** Niemals eine Channel-Session direkt trimmen oder löschen.
Immer den Hash-Handshake-Flow verwenden. Kein separates Script nötig — der Heartbeat-LLM
führt den Flow direkt aus.
### Warum der Flow unsichtbar ist
Channel-Sessions (z.B. Slack) leiten jede Antwort an den Chat weiter. Sobald man aber
`NO_REPLY` als einzigen Inhalt der Antwort vorgibt, unterdrückt OpenClaw den Output
vollständig — nichts landet im Kanal. Der Hash in `active-context.md` dient als
Out-of-Band-Bestätigung ohne Chat-Output.
### Der Hash-Handshake-Flow
1. Heartbeat prüft `sessions.json` → alle Keys mit `:channel:` **ohne** `:thread:`
2. Für jede Session **> 500 Zeilen** UND **> 2h inaktiv** (`updatedAt`):
- Generiere einmaligen Hash: `TRIM_READY_<4 random chars>` (z.B. `TRIM_READY_a3f8`)
- `sessions_send(sessionKey=<key>, timeoutSeconds=90, message="[WARTUNG] Schreibe jetzt alle offenen Themen und wichtigen Kontext in memory/active-context.md. Füge danach exakt diese Zeile am Ende ein: TRIM_HASH: <hash>. Antworte ausschließlich mit NO_REPLY.")`
- Lese `memory/active-context.md` → suche nach `TRIM_HASH: <hash>`
- **Hash gefunden:** Sicher trimmen (s.u.) + `TRIM_HASH:`-Zeile entfernen + Log: `TRIMMED`
- **Hash nicht gefunden (Timeout):** Trotzdem trimmen + Log: `FORCE-TRIM`
> **Warum Force-Trim nach Timeout OK ist:** Eine Session die 90s nicht reagiert, ist
> faktisch eingefroren. Wichtige Infos stehen bereits in active-context.md aus
> früheren Saves. Der Schaden durch dauerhaft aufgeblähte Sessions überwiegt.
**⚠️ Thread-Sessions niemals trimmen:** Keys mit `:thread:` enthalten laufende
Unterhaltungs-Threads und müssen von der Trim-Logik ausgeschlossen werden.
### ⚠️ Sicheres Trimmen: Nie mitten in einem Tool-Call schneiden
`tail -60` kann mitten in einem Tool-Call-Block landen (toolUse ohne nachfolgendes
toolResult, oder toolResult ohne nachfolgende Assistenten-Antwort). Das hinterlässt
die Session in einem **ungültigen Zustand** — der Agent schweigt danach komplett.
**Pflicht: Immer auf einer vollständigen Assistenten-Antwort abschneiden.**
```python
import json
def safe_trim(session_file, keep_lines=60):
lines = open(session_file).readlines()
if len(lines) <= keep_lines:
return # Nichts zu tun
# Kandidaten-Startpunkte: nur Zeilen wo role=assistant und kein toolUse
candidates = []
for i, line in enumerate(lines):
try:
msg = json.loads(line).get('message', {})
if msg.get('role') != 'assistant':
continue
content = msg.get('content', [])
# Nur wenn keine toolCall-Blöcke in der Antwort
if not any(c.get('type') == 'toolCall' for c in content if isinstance(c, dict)):
candidates.append(i)
except:
continue
# Letzten Kandidaten der noch >= keep_lines vom Ende entfernt ist
cutpoint = None
for i in reversed(candidates):
if len(lines) - i >= keep_lines:
cutpoint = i
break
if cutpoint is None:
cutpoint = max(0, len(lines) - keep_lines)
with open(session_file, 'w') as f:
f.writelines(lines[cutpoint:])
```
Dieses Skript sicherstellen dass der Trim immer nach einer abgeschlossenen
Assistenten-Antwort beginnt — nie mitten in einem Tool-Austausch.
### Heartbeat-Task-Template (direkt in HEARTBEAT.md einfügen)
```
# TASK: Auto-Trim große Channel-Sessions (bei JEDEM Heartbeat)
# [INSTRUCTION for LLM]:
# 1. Lese <workspace>/.openclaw/agents/main/sessions/sessions.json
# 2. Finde alle Keys die ":channel:" enthalten UND KEIN ":thread:" haben
# 3. Für jeden solchen Key: prüfe Zeilenanzahl der sessionFile (wc -l)
# 4. Wenn > 500 Zeilen UND updatedAt > 2h her:
# a. Generiere Hash: TRIM_READY_<4 random chars>
# b. sessions_send(sessionKey=<key>, timeoutSeconds=90,
# message="[WARTUNG] Schreibe jetzt alle offenen Themen und wichtigen Kontext
# in memory/active-context.md. Füge danach exakt diese Zeile am Ende ein:
# TRIM_HASH: <hash>
# Antworte ausschließlich mit NO_REPLY.")
# c. Lese memory/active-context.md → suche nach "TRIM_HASH: <hash>"
# d. Hash gefunden:
# - tail -60 <sessionFile> > /tmp/trim.jsonl && mv /tmp/trim.jsonl <sessionFile>
# - Entferne TRIM_HASH-Zeile aus active-context.md
# - Log: "<ISO> | <key> | TRIMMED" → memory/trim-log.txt
# e. Hash nicht gefunden (Timeout):
# - tail -60 <sessionFile> > /tmp/trim.jsonl && mv /tmp/trim.jsonl <sessionFile>
# - Log: "<ISO> | <key> | FORCE-TRIM" → memory/trim-log.txt
```
### Pflicht-Ergänzung in AGENTS.md
```markdown
## Session-Management (Harte Regel, keine Ausnahmen)
NIEMALS eine Channel-Session direkt löschen oder trimmen.
Immer den Hash-Handshake-Flow via Heartbeat verwenden (siehe smart-memory Skill).
Thread-Sessions (:thread: im Key) sind grundsätzlich ausgenommen.
```
---
## Neue Agents: Memory-System als Standard
**Jeder neue Agent bekommt das Memory-System — mit einer Ausnahme:**
| Agent-Typ | Memory-System? |
|-----------|---------------|
| Haupt-Agent (Orchestrator) | ✅ Pflicht — vollständige 5-Schichten-Architektur |
| Persistente Sub-Agents (eigene Channels, lange Laufzeit) | ✅ Pflicht — mindestens active-context.md |
| Reine Task-Sub-Agents (begrenzte Aufgabe, Kontext aus Prompt) | ❌ Nicht nötig — Kontext kommt vom Orchestrator |
**Erkennungsmerkmal für "reine Task-Sub-Agents":**
- Werden für einen einzelnen, klar begrenzten Task gespawnt
- Erhalten ihren gesamten Kontext im Task-Prompt vom Haupt-Agent
- Haben keinen eigenen persistenten Channel
- Schreiben ihr Ergebnis zurück an den Orchestrator (Summary via sessions_send)
**Erkennungsmerkmal für "persistente Sub-Agents":**
- Haben einen eigenen Slack-/Discord-Channel
- Werden wiederholt für ähnliche Tasks genutzt
- Akkumulieren über Zeit domänenspezifisches Wissen
> Faustregel: Wenn ein Agent Gespräche führt oder wiederholtes Wissen aufbaut → Memory.
> Wenn ein Agent einen einzigen Task abarbeitet und dann fertig ist → kein Memory nötig.
---
## Tägliche Schreib-Regeln (nach Setup)
| Situation | Aktion |
|-----------|--------|
| User erklärt etwas Wichtiges | → `[OPEN]`-Block in active-context.md mit vollem Kontext |
| Strukturelle Änderung (Channel, Projekt, Regel) | → active-context.md **SOFORT in derselben Antwort** |
| Technische Erkenntnis bei Coding | → CONTEXT.md **SOFORT** (nicht am Task-Ende) |
| Cross-project Lesson | → memory/agents/<typ>.md updaten |
| Sub-Agent Task abgeschlossen [MULTI-AGENT] | → CONTEXT.md sofort + Summary an Orchestrator |
| Thema abgeschlossen | → `[DONE]` markieren (Cleanup entfernt Block) |
| MEMORY.md nähert sich 40 Zeilen | → destillieren: Was ist wirklich dauerhaft relevant? |
| active-context.md > 100 Zeilen | → Warnsignal: Einträge kürzen oder klären |
> **Goldene Regel:** Schreiben ist Teil der Aktion, nicht ein nachgelagerter Schritt.
> Sessions enden unvorhersehbar. Was nicht sofort geschrieben wird, ist verloren.
---
## Warum das funktioniert
**Das Fogging-Problem:** In Standard-Setups gibt es nur Daily Logs (heute + gestern).
Was vor 3 Tagen besprochen wurde, ist weg. Der User muss alles neu erklären.
**Die Lösung:** active-context.md schreibt Relevanz, nicht Zeit. Ein offenes Thema
bleibt im Kontext bis es erledigt ist — egal wie lange das dauert. Nach Erledigung
fliegt es raus. Kein unkontrolliertes Aufblähen, weil erledigte Themen aktiv entfernt werden.
**Das Rauschen-Problem:** MEMORY.md wächst über Monate auf hunderte Zeilen.
Der Agent sieht alles gleichzeitig — Swift-Lektionen neben Marketing-Strategie neben Cron-IDs.
Alles konkurriert um Aufmerksamkeit. Regeln werden "vergessen" weil sie im Rauschen untergehen.
**Die Lösung:** Jede Schicht hat einen klar begrenzten Scope. Projektdetails sind nicht
im Orchestrator-Kontext — sie werden on-demand in Sub-Agent-Prompts injiziert wenn
sie gebraucht werden. Kein Rauschen, keine Ablenkung.
**Das Session-Crash-Problem (neu in v1.1):** "Am Ende der Session schreiben" versagt
wenn Sessions durch Rate-Limits oder Crashes enden. Die Lösung: Schreiben ist Teil
der Aktion selbst — sofort, inline, bevor die Antwort gesendet wird. Der Heartbeat
ist das Backup-Netz, nicht der Primary-Trigger.
don't have the plugin yet? install it then click "run inline in claude" again.