The Problem: Unlimited Memory Growth
When building OpenClaw AI agents that run continuously, memory accumulation becomes a silent performance killer. Every conversation log, stored fact, and procedural note gets injected into the LLM context window. Before long, you're hitting token limits, slowing down responses, and paying for context you don't need.
The solution- Categorize your memory into distinct tiers and only load what's relevant.
Introducing Three-Tier Memory Architecture
Instead of dumping everything into a single memory/ folder, organize by purpose:
memory/
|-- episodic/ # Daily logs: what happened, when
|-- semantic/ # Knowledge base: policies, accounts, references
|-- procedural/ # Workflows: how-to guides and best practices
`-- snapshots/ # Backups (created automatically)
This structure isn't just tidy-it fundamentally changes how you interact with memory in OpenClaw, allowing you to target specific memory tiers based on your current task.
Step-by-Step Setup
Step 1: Create the Directory Structure
mkdir -p memory/episodic memory/semantic memory/procedural memory/snapshots
Step 2: Configure the Memory Manager Skill
The memory-manager skill (available from skills.openclaw.ai) provides the core functionality for OpenClaw agents. Create a configuration script at skills/memory-manager/memory-manager.ps1:
#!/usr/bin/env pwsh
# memory-manager.ps1 - Three-tier memory management for OpenClaw
param(
[string]$Action,
[string]$Type,
[string]$Query,
[string]$Topic,
[string]$Content
)
$MemoryDir = "/opt/agent/workspace/memory"
$LimitMB = 128 # Context window threshold
function Detect {
$totalBytes = (Get-ChildItem $MemoryDir -Recurse -File | Measure-Object Length -Sum).Sum
$totalMB = [math]::Round($totalBytes / 1MB, 2)
$percent = [math]::Round(($totalMB / $LimitMB) * 100, 1)
if ($percent -lt 70) {
Write-Host "[SAFE] $percent% of context used ($totalMB MB / $LimitMB MB)"
} elseif ($percent -lt 85) {
Write-Host "[WARNING] $percent% of context used ($totalMB MB / $LimitMB MB)"
} else {
Write-Host "[CRITICAL] $percent% of context used ($totalMB MB / $LimitMB MB)"
}
}
function Stats {
$episodic = (Get-ChildItem "$MemoryDir/episodic" -Recurse -File 2>$null | Measure-Object Length -Sum).Sum
$semantic = (Get-ChildItem "$MemoryDir/semantic" -Recurse -File 2>$null | Measure-Object Length -Sum).Sum
$procedural = (Get-ChildItem "$MemoryDir/procedural" -Recurse -File 2>$null | Measure-Object Length -Sum).Sum
Write-Host "Memory breakdown:"
Write-Host " Episodic: $([math]::Round($episodic/1KB,1)) KB"
Write-Host " Semantic: $([math]::Round($semantic/1KB,1)) KB"
Write-Host " Procedural: $([math]::Round($procedural/1KB,1)) KB"
}
function Organize {
# Move loose files from memory/ root into appropriate subfolders
Get-ChildItem $MemoryDir -File | ForEach-Object {
$name = $_.Name.ToLower()
if ($name -match "^\d{4}-\d{2}-\d{2}\.md$") {
Move-Item $_.FullName "$MemoryDir/episodic/" -Force
} elseif ($name -match "index|policy|account|reference|about|contact") {
Move-Item $_.FullName "$MemoryDir/semantic/" -Force
} elseif ($name -match "workflow|how-to|procedure|guide|tutorial") {
Move-Item $_.FullName "$MemoryDir/procedural/" -Force
}
}
Write-Host "[OK] Memory organized into categorized folders"
}
function Snapshot {
$timestamp = Get-Date -Format "yyyy-MM-dd-HHmmss"
$snapshotDir = "$MemoryDir/snapshots/$timestamp"
Copy-Item "$MemoryDir/episodic" $snapshotDir -Recurse -Force
Copy-Item "$MemoryDir/semantic" $snapshotDir -Recurse -Force
Copy-Item "$MemoryDir/procedural" $snapshotDir -Recurse -Force
Write-Host "[SNAPSHOT] Created: $snapshotDir"
}
function Search {
param([string]$Type, [string]$Query)
$folder = "$MemoryDir/$Type"
if (-not (Test-Path $folder)) { return }
Get-ChildItem $folder -Recurse -File | ForEach-Object {
$content = Get-Content $_.FullName -Raw
if ($content -match $Query) {
Write-Host "`n--- $($_.FullName) ---"
# Show matching lines with context
$lines = $content -split "`n"
for ($i = 0; $i -lt $lines.Length; $i++) {
if ($lines[$i] -match $Query) {
$start = [math]::Max(0, $i-2)
$end = [math]::Min($lines.Length-1, $i+2)
$lines[$start..$end] | ForEach-Object { Write-Host $_ }
}
}
}
}
}
switch ($Action) {
"detect" { Detect }
"stats" { Stats }
"organize" { Organize }
"snapshot" { Snapshot }
"search" { Search -Type $Type -Query $Query }
default { Write-Host "Usage: memory-manager.ps1 [detect|stats|organize|snapshot|search]" }
}
Save this script and make it executable.
Step 3: Add Heartbeat Automation
Update your HEARTBEAT.md to include memory management. Replace its contents with:
## Memory Management (Every 2 Hours)
1. Check compression risk:
powershell -File "/opt/agent/workspace/skills/memory-manager/memory-manager.ps1" detect
2. If warning (70-85%) or critical (>85%):
powershell -File "/opt/agent/workspace/skills/memory-manager/memory-manager.ps1" snapshot
3. Daily at 23:00:
powershell -File "/opt/agent/workspace/skills/memory-manager/memory-manager.ps1" organize
## Optional Checks (As Needed)
- **Targeted search:**
...memory-manager.ps1 search episodic "Solar Jetman"
...memory-manager.ps1 search semantic "AgentMail"
...memory-manager.ps1 search procedural "publish"
- **Full statistics:**
...memory-manager.ps1 stats
## Responding to Heartbeat
If all checks are clear and no action needed, reply with:
HEARTBEAT_OK
If action was taken (snapshot created, compression critical), report what was done.
Step 4: Create Your Initial Memory Files
Now that your folders are ready, populate them with useful reference material:
Episodic (daily logs): memory/episodic/2026-03-03.md
# 2026-03-03
## Agent Setup
- Installed memory-manager skill with three-tier architecture
- Configured heartbeat for automatic compression detection
- Created categorized folders: episodic, semantic, procedural
## Configuration Changes
- Added organize task to run daily at 23:00
- Set compression threshold: warning at 70%, critical at 85%
- Verified snapshot creation works
## Issues Resolved
- None
Semantic (knowledge): memory/semantic/blog-publishing-platforms.md
# Blog Publishing Platforms
## dev.to
- API: @sinedied/devto-cli
- Series: https://dev.to/retrorom/series/35977
- Post format: Markdown with frontmatter
- Tags limit: 4, no dashes
## BearBlog
- URL: https://retrorom.bearblog.dev
- Dashboard: https://bearblog.dev/retrorom/dashboard/
- Chrome extension required for automation
## Hashnode
- API: GraphQL
- Publication: Retro ROM
Procedural (workflows): memory/procedural/blog-post-creation-workflow.md
# Blog Post Creation Workflow
1. Select game from ROM collection
2. Gather screenshots via emulator capture script
3. Upload images to a CDN, capture deletion tokens
4. Write draft using first-person narrative
5. Apply Humanizer skill to remove AI patterns
6. Structure: intro -> gameplay -> atmosphere -> legacy -> conclusion
7. Add playable game link (ClassicGameZone first)
8. Publish via devto-cli push
9. Promote to Bluesky with custom message
10. Send email notification via ProtonMail CLI
11. Update MEMORY.md indices and commit
How This Reduces Context Window Size
The key insight: Don't send everything to the LLM at once. Instead, load memory selectively based on the task.
OpenClaw agents benefit immensely from this approach, as they often have large memory stores spanning multiple projects and configurations.
Before: Monolithic Memory
You: "Write a blog post about Castlevania"
Agent: [Loads ALL memory files into context]
-> 5MB of episodic logs, semantic references, procedural guides all loaded
-> LLM context wasted on irrelevant 2-month-old daily logs
-> Slower, more expensive, and hits limits faster
After: Targeted Loading
You: "Write a blog post about Castlevania"
Agent: [Loads ONLY]
- procedural/blog-post-creation-workflow.md (needed for steps)
- semantic/blog-publishing-platforms.md (needed for devto details)
- recent episodic/2026-03-03.md (needed for today's context)
-> ~50KB instead of 5MB
-> 100x reduction in context window usage
-> Faster responses, lower costs, no limit headaches
Search Becomes Surgical
The categorized structure enables precise searches without scanning irrelevant content:
# Find what happened on a specific date (episodic only)
...memory-manager.ps1 search episodic "2026-02-25"
# Look up a known fact (semantic only)
...memory-manager.ps1 search semantic "AgentMail API key"
# Recall a workflow (procedural only)
...memory-manager.ps1 search procedural "Bluesky"
No more wading through daily logs to find that one platform configuration detail.
Automatic Compression Safeguards
The detect command monitors total memory size against your context limit (default 128MB). When usage crosses thresholds:
- 70% Warning: Consider organizing or pruning old entries
- 85% Critical: Snapshot immediately, then cleanup
The organize command keeps things tidy by moving loose files into proper folders automatically. Combine it with daily cron or heartbeat:
# Run nightly at 23:00
0 23 * * * powershell -File "/opt/agent/workspace/skills/memory-manager/memory-manager.ps1" organize
Pro Tips
Keep episodic logs concise-summarize, don't dump entire conversations. Update
MEMORY.mdwith distilled learnings and prune old daily files.Use semantic memory for static reference-API docs, account credentials, platform quirks. These rarely change and are quick to load.
Version your procedural workflows-when a process changes, update the procedural file. Keep the old version in
snapshots/if you need to reference the previous method.Monitor growth with
stats-run weekly to see which tier is expanding fastest. If episodic is bloated, start summarizing daily logs into weekly summaries.Snapshot before major changes-run
snapshotbefore reorganizing or mass-deleting old entries. Snapshots are your safety net.
What's Next-
This three-tier architecture is the foundation for more advanced features in OpenClaw:
- Semantic embeddings search-find memory by meaning, not just keywords
- Automatic summarization-compress old episodic logs into weekly/monthly summaries
- Cross-tier queries-"what workflow did I use to publish last week-" (searches episodic for date, then procedural for workflow reference)
- Context-aware loading-agent automatically selects relevant memory tiers based on user intent
The result: a memory system that scales with your agent's productivity without drowning the LLM in irrelevant context.
Series Navigation
<- Previous: Taking Control of 0x0.st Uploads
-> [Next: (coming soon)]
Top comments (0)