# MEMORY - Project Context ## Project: ajarbot - AI Agent with Memory **Created**: 2026-02-12 **Inspired by**: OpenClaw memory system ## Complete System Architecture ### 1. Memory System (memory_system.py) **Storage**: SQLite + Markdown (source of truth) **Files Structure**: - `SOUL.md` - Agent personality/identity (auto-created) - `MEMORY.md` - Long-term curated facts (this file) - `users/*.md` - Per-user preferences & context - `memory/YYYY-MM-DD.md` - Daily activity logs - `HEARTBEAT.md` - Periodic check checklist - `memory_index.db` - SQLite FTS5 index **Features**: - Full-text search (FTS5) - keyword matching, 64-char snippets - File watching - auto-reindex on changes - Chunking - ~500 chars per chunk - Per-user search - `search_user(username, query)` - Task tracking - SQLite table for work items - Hooks integration - triggers events on sync/tasks **Key Methods**: ```python memory.sync() # Index all .md files memory.write_memory(text, daily=True/False) # Append to daily or MEMORY.md memory.update_soul(text, append=True) # Update personality memory.update_user(username, text, append=True) # User context memory.search(query, max_results=5) # FTS5 search memory.search_user(username, query) # User-specific search memory.add_task(title, desc, metadata) # Add task → triggers hook memory.update_task(id, status) # Update task memory.get_tasks(status="pending") # Query tasks ``` ### 2. LLM Integration (llm_interface.py) **Providers**: Claude (Anthropic API), GLM (z.ai) **Configuration**: - API Keys: `ANTHROPIC_API_KEY`, `GLM_API_KEY` (env vars) - Models: claude-sonnet-4-5-20250929, glm-4-plus - Switching: `llm = LLMInterface("claude")` or `"glm"` **Methods**: ```python llm.chat(messages, system=None, max_tokens=4096) # Returns str llm.set_model(model_name) # Change model ``` ### 3. Task System **Storage**: SQLite `tasks` table **Schema**: - id, title, description, status, created_at, updated_at, metadata **Statuses**: `pending`, `in_progress`, `completed` **Hooks**: Triggers `task:created` event when added ### 4. Heartbeat System (heartbeat.py) **Inspired by**: OpenClaw's periodic awareness checks **How it works**: 1. Background thread runs every N minutes (default: 30) 2. Only during active hours (default: 8am-10pm) 3. Reads `HEARTBEAT.md` checklist 4. Sends to LLM with context: SOUL, pending tasks, current time 5. Returns `HEARTBEAT_OK` if nothing needs attention 6. Calls `on_alert()` callback if action required 7. Logs alerts to daily memory **Configuration**: ```python heartbeat = Heartbeat(memory, llm, interval_minutes=30, active_hours=(8, 22) # 24h format ) heartbeat.on_alert = lambda msg: print(f"ALERT: {msg}") heartbeat.start() # Background thread heartbeat.check_now() # Immediate check heartbeat.stop() # Cleanup ``` **HEARTBEAT.md Example**: ```markdown # Heartbeat Checklist - Review pending tasks - Check tasks pending > 24 hours - Verify memory synced - Return HEARTBEAT_OK if nothing needs attention ``` ### 5. Hooks System (hooks.py) **Pattern**: Event-driven automation **Events**: - `task:created` - When task added - `memory:synced` - After memory.sync() - `agent:startup` - Agent initialization - `agent:shutdown` - Agent cleanup **Usage**: ```python hooks = HooksSystem() def my_hook(event: HookEvent): if event.type != "task": return print(f"Task: {event.context['title']}") event.messages.append("Logged") hooks.register("task:created", my_hook) hooks.trigger("task", "created", {"title": "Build X"}) ``` **HookEvent properties**: - `event.type` - Event type (task, memory, agent) - `event.action` - Action (created, synced, startup) - `event.timestamp` - When triggered - `event.context` - Dict with event data - `event.messages` - List to append messages ### 6. Agent Class (agent.py) **Main interface** - Combines all systems **Initialization**: ```python agent = Agent( provider="claude", # or "glm" workspace_dir="./memory_workspace", enable_heartbeat=False # Set True for background checks ) ``` **What happens on init**: 1. Creates MemorySystem, LLMInterface, HooksSystem 2. Syncs memory (indexes all .md files) 3. Triggers `agent:startup` hook 4. Optionally starts heartbeat thread 5. Creates SOUL.md, users/default.md, HEARTBEAT.md if missing **Methods**: ```python agent.chat(message, username="default") # Context-aware chat agent.switch_model("glm") # Change LLM provider agent.shutdown() # Stop heartbeat, close DB, trigger shutdown hook ``` **Chat Context Loading**: 1. SOUL.md (personality) 2. users/{username}.md (user preferences) 3. memory.search(message, max_results=3) (relevant context) 4. Last 5 conversation messages 5. Logs exchange to daily memory ## Complete File Structure ``` ajarbot/ ├── Core Implementation │ ├── memory_system.py # Memory (SQLite + Markdown) │ ├── llm_interface.py # Claude/GLM API integration │ ├── heartbeat.py # Periodic checks system │ ├── hooks.py # Event-driven automation │ └── agent.py # Main agent class (combines all) │ ├── Examples & Docs │ ├── example_usage.py # SOUL/User file examples │ ├── QUICKSTART.md # 30-second setup guide │ ├── README_MEMORY.md # Memory system docs │ ├── HEARTBEAT_HOOKS.md # Heartbeat/hooks guide │ └── requirements.txt # Dependencies │ └── memory_workspace/ ├── SOUL.md # Agent personality (auto-created) ├── MEMORY.md # This file - long-term memory ├── HEARTBEAT.md # Heartbeat checklist (auto-created) ├── users/ │ └── default.md # Default user template (auto-created) ├── memory/ │ └── 2026-02-12.md # Daily logs (auto-created) └── memory_index.db # SQLite FTS5 index ``` ## Quick Start ```python # Initialize from agent import Agent agent = Agent(provider="claude") # Chat with memory context response = agent.chat("Help me code", username="alice") # Switch models agent.switch_model("glm") # Add task task_id = agent.memory.add_task("Implement feature X", "Details...") agent.memory.update_task(task_id, status="completed") ``` ## Environment Setup ```bash export ANTHROPIC_API_KEY="sk-ant-..." export GLM_API_KEY="your-glm-key" pip install anthropic requests watchdog ``` ## Token Efficiency - Memory auto-indexes all files (no manual sync needed) - Search returns snippets only (64 chars), not full content - Task system tracks context without bloating prompts - User-specific search isolates context per user # System Architecture Decisions ## Memory System Design - **Date**: 2026-02-12 - **Decision**: Use SQLite + Markdown for memory - **Rationale**: Simple, fast, no external dependencies - **Files**: SOUL.md for personality, users/*.md for user context ## Search Strategy - FTS5 for keyword search (fast, built-in) - No vector embeddings (keep it simple) - Per-user search capability for privacy