27 KiB
27 KiB
Memory System Architecture Diagram
Visual representation of how the Clawdbot memory system works.
High-Level Flow
┌─────────────────────────────────────────────────────────────┐
│ USER / AGENT CHAT │
│ "What did we decide about the API key strategy?" │
└──────────────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ AGENT REASONING LAYER │
│ "This is about prior decisions → must search memory" │
└──────────────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ memory_search(query) │
│ → Converts query to embedding vector │
│ → Searches SQLite vector store │
│ → Returns: snippets + file paths + line numbers │
└──────────────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ SEARCH RESULTS (snippets only) │
│ memory/2026-02-04.md (lines 42-58) │
│ "Decided to use manual API key signup batch..." │
│ Score: 0.87 │
└──────────────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ memory_get(path, from, lines) │
│ → Reads full context from markdown file │
│ → Returns: complete text from specified line range │
└──────────────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ AGENT RESPONSE │
│ "On Feb 4, we decided manual signup batch (~30 min) │
│ because CAPTCHA bypass violates ToS..." │
└─────────────────────────────────────────────────────────────┘
Storage Architecture
┌────────────────────────────────────────────────────────────────┐
│ WORKSPACE DIRECTORY │
│ ~/.clawdbot/workspace/ │
└────────────────────────┬───────────────────────────────────────┘
│
┌────────────────┴────────────────┐
│ │
▼ ▼
┌──────────────────┐ ┌──────────────────────┐
│ MARKDOWN FILES │ │ CONFIG FILES │
│ (Source Truth) │ │ (Identity/Rules) │
├──────────────────┤ ├──────────────────────┤
│ memory/ │ │ AGENTS.md │
│ ├─ 2026-02-09.md │ │ SOUL.md │
│ ├─ 2026-02-08.md │ │ USER.md │
│ ├─ research.md │ │ HEARTBEAT.md │
│ └─ TEMPLATE.md │ │ TOOLS.md │
└────────┬─────────┘ └──────────────────────┘
│
│ (watched by file watcher, 1.5s debounce)
│
▼
┌─────────────────────────────────────────────────────────┐
│ INDEXING PIPELINE │
│ 1. Detect file changes (watcher) │
│ 2. Chunk markdown (~400 tokens, 80 overlap) │
│ 3. Generate embeddings (OpenAI/Gemini/local) │
│ 4. Store in SQLite │
└────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ SQLITE DATABASE (Search Engine) │
│ ~/.clawdbot/memory/main.sqlite │
├─────────────────────────────────────────────────────────┤
│ Tables: │
│ ┌─────────────────────────────────────────────┐ │
│ │ files │ │
│ │ - path, hash, mtime, size │ │
│ └─────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────┐ │
│ │ chunks │ │
│ │ - id, file_id, text, start_line, end_line │ │
│ └─────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────┐ │
│ │ chunks_vec (vector table) │ │
│ │ - chunk_id, embedding (float32[1536]) │ │
│ │ - Accelerated by sqlite-vec extension │ │
│ └─────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────┐ │
│ │ chunks_fts (full-text search) │ │
│ │ - FTS5 index for BM25 keyword search │ │
│ └─────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────┐ │
│ │ embedding_cache │ │
│ │ - text_hash, embedding (dedup) │ │
│ └─────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
Hybrid Search Flow (Vector + BM25)
memory_search("API key strategy")
│
▼
┌──────────────────────┐
│ Query Embedding │
│ (via OpenAI API) │
└──────────┬───────────┘
│
┌─────────┴─────────┐
│ │
▼ ▼
┌──────────────┐ ┌──────────────────┐
│ Vector Search│ │ BM25 Keyword │
│ (semantic) │ │ Search (exact) │
├──────────────┤ ├──────────────────┤
│ Top 20 by │ │ Top 20 by FTS5 │
│ cosine sim │ │ BM25 rank │
└──────┬───────┘ └────────┬─────────┘
│ │
└──────────┬─────────┘
│
▼
┌──────────────────────┐
│ Merge & Rank │
│ (weighted score) │
│ 70% vector │
│ 30% keyword │
└──────────┬───────────┘
│
▼
┌──────────────────────┐
│ Top 5 Results │
│ (with snippets) │
└──────────────────────┘
Embedding Providers
┌──────────────────────────────────────────────────────┐
│ EMBEDDING PROVIDER OPTIONS │
└──────────────────────────────────────────────────────┘
Option 1: OpenAI (Recommended for Production)
┌────────────────────────────────────────────┐
│ Provider: openai │
│ Model: text-embedding-3-small │
│ Size: 1536 dimensions │
│ Cost: $0.02 / 1M tokens (Batch: $0.01) │
│ Speed: Fast (Batch API parallel) │
│ Setup: API key only │
└────────────────────────────────────────────┘
Option 2: Gemini
┌────────────────────────────────────────────┐
│ Provider: gemini │
│ Model: text-embedding-004 │
│ Size: 768 dimensions │
│ Cost: Free tier available │
│ Speed: Fast │
│ Setup: API key only │
└────────────────────────────────────────────┘
Option 3: Local (Offline)
┌────────────────────────────────────────────┐
│ Provider: local │
│ Model: embeddinggemma-300M-Q8_0.gguf │
│ Size: ~600 MB download │
│ Cost: Free (compute only) │
│ Speed: Slower (CPU/GPU dependent) │
│ Setup: Auto-download, rebuild required │
└────────────────────────────────────────────┘
Write Flow (Agent → Disk)
┌──────────────────────────────────────────────────────┐
│ AGENT DECISION: "Need to remember this" │
└──────────────────────┬───────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────┐
│ write / edit / append │
│ target: memory/2026-02-09.md │
│ content: "## Decisions Made\n- Chose X..." │
└──────────────────────┬───────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────┐
│ MARKDOWN FILE UPDATED │
│ memory/2026-02-09.md (on disk) │
└──────────────────────┬───────────────────────────────┘
│
▼ (file watcher triggers)
┌──────────────────────────────────────────────────────┐
│ INDEXING PIPELINE (Automatic) │
│ 1. Detects change │
│ 2. Re-chunks file │
│ 3. Generates embeddings (cached if unchanged) │
│ 4. Updates SQLite │
└──────────────────────┬───────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────┐
│ SEARCH INDEX UPDATED (background) │
│ New decision is now searchable │
└──────────────────────────────────────────────────────┘
Pre-Compaction Memory Flush
┌──────────────────────────────────────────────────────┐
│ SESSION TOKEN COUNT MONITOR │
│ Threshold: contextWindow - reserveFloor - 4000 │
└──────────────────────┬───────────────────────────────┘
│
▼ (threshold crossed)
┌──────────────────────────────────────────────────────┐
│ MEMORY FLUSH TRIGGER │
│ Silent agentic turn (user doesn't see) │
│ System: "Session nearing compaction. Store now." │
│ User: "Write lasting notes; reply NO_REPLY" │
└──────────────────────┬───────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────┐
│ AGENT REVIEWS CONTEXT │
│ Scans recent messages for: │
│ - Decisions made │
│ - Preferences stated │
│ - Important facts │
│ - TODOs assigned │
└──────────────────────┬───────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────┐
│ WRITES TO MEMORY (if needed) │
│ write("memory/2026-02-09.md", ...) │
│ → Durable memory stored │
└──────────────────────┬───────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────┐
│ REPLIES "NO_REPLY" │
│ (User never sees this turn) │
└──────────────────────┬───────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────┐
│ COMPACTION PROCEEDS SAFELY │
│ Old context removed, but memory is on disk │
└──────────────────────────────────────────────────────┘
Git Backup Flow
┌──────────────────────────────────────────────────────┐
│ END OF DAY / SESSION │
└──────────────────────┬───────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────┐
│ MANUAL OR CRON TRIGGER │
│ git add -A && git commit -m "..." && git push │
└──────────────────────┬───────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────┐
│ ALL MARKDOWN FILES STAGED │
│ memory/*.md, AGENTS.md, USER.md, etc. │
└──────────────────────┬───────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────┐
│ COMMITTED TO LOCAL GIT REPO │
│ SHA: abc123... "Daily backup: 2026-02-09" │
└──────────────────────┬───────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────┐
│ PUSHED TO REMOTE (GitHub/GitLab) │
│ Entire memory history is now backed up │
│ Recoverable even if local disk fails │
└──────────────────────────────────────────────────────┘
Data Flow Summary
HUMAN INPUT
↓
AGENT CHAT
↓
DECISION/FACT IDENTIFIED
↓
WRITE TO MARKDOWN ← (source of truth)
↓
FILE WATCHER DETECTS CHANGE
↓
INDEXING PIPELINE
├→ Chunk text
├→ Generate embeddings
└→ Store in SQLite ← (search engine)
↓
SEARCHABLE VIA memory_search
↓
AGENT CAN RECALL IN FUTURE SESSIONS
↓
GIT BACKUP (end of day)
↓
SAFE IN REMOTE REPO
Performance Characteristics
┌───────────────────────────────────────────────────────┐
│ OPERATION TIMINGS │
├───────────────────────────────────────────────────────┤
│ memory_search query: <100ms │
│ memory_get read: <10ms │
│ Index rebuild (35 files): ~2 seconds │
│ File watcher debounce: 1.5 seconds │
│ Embedding generation: ~50ms per chunk (OpenAI) │
│ SQLite vector search: <50ms (with sqlite-vec) │
│ BM25 search: <20ms (FTS5) │
└───────────────────────────────────────────────────────┘
┌───────────────────────────────────────────────────────┐
│ STORAGE FOOTPRINT │
├───────────────────────────────────────────────────────┤
│ 35 markdown files: ~500 KB │
│ SQLite database: ~15 MB │
│ Vector embeddings: ~12 MB (in SQLite) │
│ Metadata + indexes: ~3 MB │
└───────────────────────────────────────────────────────┘
┌───────────────────────────────────────────────────────┐
│ COST (OpenAI Batch API) │
├───────────────────────────────────────────────────────┤
│ Initial index (35 files): ~$0.10 │
│ Daily updates (~3 files): ~$0.01 │
│ Monthly cost: ~$0.50 │
└───────────────────────────────────────────────────────┘
Scaling Considerations
┌─────────────────────────────────────────────────────┐
│ FILES │ CHUNKS │ SEARCH TIME │
├─────────────────────────────────────────────────────┤
│ < 50 files │ < 200 │ < 100ms │
│ 50-200 files │ 200-1000 │ < 200ms │
│ 200-500 files │ 1000-2500 │ < 500ms │
│ 500-1000 files │ 2500-5000 │ < 1 second │
│ 1000+ files │ 5000+ │ Consider: │
│ │ │ - Partitioning│
│ │ │ - Archiving │
│ │ │ - Multi-index│
└─────────────────────────────────────────────────────┘
Recommendation: Archive old logs after 3-6 months
- Move to `memory/archive/YYYY/`
- Keep recent 90 days active
- Full archive remains searchable if needed
Failure Modes & Recovery
┌─────────────────────────────────────────────────────────┐
│ FAILURE SCENARIO │ RECOVERY │
├─────────────────────────────────────────────────────────┤
│ SQLite index corrupted │ Rebuild from MD │
│ Markdown files deleted │ Restore from git│
│ Embedding API down │ Fallback/local │
│ Disk full │ Archive old logs│
│ Config error │ Revert git │
│ Clawdbot crash mid-write │ Git diff check │
└─────────────────────────────────────────────────────────┘
Recovery command:
rm ~/.clawdbot/memory/main.sqlite && clawdbot memory index
Principle: Markdown files are source of truth —
SQLite is always regenerable.
END OF ARCHITECTURE DIAGRAM
ᕕ( ᐛ )ᕗ