Files
ajarbot/memory_workspace/MEMORY.md
Jordan Ramos a99799bf3d Initial commit: Ajarbot with optimizations
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>
2026-02-13 19:06:28 -07:00

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 & 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:

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:

  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:

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 added
  • memory:synced - After memory.sync()
  • agent:startup - Agent initialization
  • agent: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 triggered
  • event.context - Dict with event data
  • event.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:

  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:

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

# 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