Features: - Multi-platform bot (Slack, Telegram) - Memory system with SQLite FTS - Tool use capabilities (file ops, commands) - Scheduled tasks system - Dynamic model switching (/sonnet, /haiku) - Prompt caching for cost optimization Optimizations: - Default to Haiku 4.5 (12x cheaper) - Reduced context: 3 messages, 2 memory results - Optimized SOUL.md (48% smaller) - Automatic caching when using Sonnet (90% savings) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
7.3 KiB
7.3 KiB
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 & contextmemory/YYYY-MM-DD.md- Daily activity logsHEARTBEAT.md- Periodic check checklistmemory_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:
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:
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:
- Background thread runs every N minutes (default: 30)
- Only during active hours (default: 8am-10pm)
- Reads
HEARTBEAT.mdchecklist - Sends to LLM with context: SOUL, pending tasks, current time
- Returns
HEARTBEAT_OKif nothing needs attention - Calls
on_alert()callback if action required - Logs alerts to daily memory
Configuration:
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:
# 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 addedmemory:synced- After memory.sync()agent:startup- Agent initializationagent:shutdown- Agent cleanup
Usage:
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 triggeredevent.context- Dict with event dataevent.messages- List to append messages
6. Agent Class (agent.py)
Main interface - Combines all systems
Initialization:
agent = Agent(
provider="claude", # or "glm"
workspace_dir="./memory_workspace",
enable_heartbeat=False # Set True for background checks
)
What happens on init:
- Creates MemorySystem, LLMInterface, HooksSystem
- Syncs memory (indexes all .md files)
- Triggers
agent:startuphook - Optionally starts heartbeat thread
- Creates SOUL.md, users/default.md, HEARTBEAT.md if missing
Methods:
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:
- SOUL.md (personality)
- users/{username}.md (user preferences)
- memory.search(message, max_results=3) (relevant context)
- Last 5 conversation messages
- 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
# 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
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