3 Architecture Memory and Sleep
Blightbow edited this page 2025-12-09 04:34:35 -05:00

Architecture: Memory and Sleep

Layer 2 - Three-Tier Memory, Sleep Operations, and Consolidation


Overview

The memory and sleep system provides persistent knowledge storage and offline processing:

  • rag_memory.py - Memory operations and sleep tick orchestration
  • operating_mode.py - Mode transitions and wake conditions
  • helpers/entity_profiles.py - Entity memory (O-Mem style)
  • helpers/episodic_index.py - Journal scoring and search
  • helpers/entity_context.py - Entity consolidation

1. Three-Tier Memory Architecture

Memory is organized in three tiers with different lifespans and purposes.

Tier Summary

Tier Storage Lifespan Purpose
Semantic (Mem0) External vector DB Permanent Long-term world facts
Entity Profiles (db.*) Django DB Permanent Per-entity knowledge (O-Mem)
Working Memory (ndb.*) In-memory Session Active conversation state

Tier 1: Semantic Memory (Mem0)

Long-term factual storage with vector similarity search.

# Retrieval with two-stage filtering
memories = yield retrieve_relevant_memories(
    script, events, goals,
    min_source_trust=0.5  # O-Mem style trust filtering
)

# Stage 1: Semantic relevance (score > 0.7)
# Stage 2: Source trust threshold (metadata.source_trust >= min)

Persona Protection Prompt (MIRIX-inspired):

ASSISTANT_FACT_EXTRACTION_PROMPT = """
Extract only objective facts about the world, users, and events.
Use third-person, factual language focused on WORLD STATE, not assistant behavior.

NEVER extract:
- Assistant actions ("The assistant helped", "I did X")
- Assistant personality or identity information

ALWAYS extract:
- World state facts ("The tavern is in the north wing")
- User/player information ("Player Alice prefers formal address")
"""

Tier 2: Entity Profiles (O-Mem Inspired)

Per-entity knowledge stored on character.db.entity_profiles:

profile = {
    "entity_id": "#123",
    "entity_type": "player",
    "attributes": {},     # Pa - Consolidated facts (stable)
    "observations": [],   # Pf - Raw observations (recent)
    "relationship": {
        "state": "acquaintance",  # stranger/acquaintance/friend/ally
        "favorability": 0.45,     # Dunbar-based thresholds
        "interaction_count": 12,
    },
}

Relationship States (Dunbar thresholds):

State Favorability Meaning
stranger < 0.25 No relationship
acquaintance 0.25 - 0.50 Casual familiarity
friend 0.50 - 0.75 Positive relationship
ally >= 0.75 Strong trust

Tier 3: Working Memory

Active conversation state in character.db.working_memory:

working_memory = {
    "#123": {
        "topic": "quest_help",
        "started_at": "2025-12-06T10:00:00Z",
        "messages": [...],
        "pending_actions": [...],
    },
}

2. Sleep Mode Architecture

Sleep mode enables offline memory processing with reduced tick rate.

Operating Modes

Mode Tick Rate Tools Purpose
awake 5s (default) All Normal operation
sleep 60s (default) Limited Memory consolidation

Sleep Phases

+------------------+     consolidation      +------------------+
|   COMPACTING     |  ─────complete────────>|    DREAMING      |
|                  |                        |                  |
| Journal → Mem0   |                        | Link generation  |
| Batch scoring    |                        | Entity consolidation |
| Wake deferred    |                        | Reflection       |
+------------------+                        | Episodic pruning |
                                            | Context compaction |
                                            +------------------+

Sleep Mode Tools

SLEEP_MODE_TOOLS = [
    "noop",
    "add_journal_entry",
    "review_journal",
    "recall_memories",
    "store_memory",
]

3. Sleep Tick Operations

The run_sleep_tick() function orchestrates 8+ operations per dreaming tick.

Compacting Phase Operations

# Phase: compacting
# 1. Importance scoring - upgrade heuristic scores to LLM scores
scoring_result = yield score_pending_entries(script, character)

# 2. Memory consolidation - journal entries → Mem0
consolidation_result = yield run_sleep_consolidation(script, character)

# 3. Phase transition when complete
if consolidation_result.get("completed"):
    script.db.sleep_phase = "dreaming"

Dreaming Phase Operations

# Phase: dreaming
# 1. A-MEM link generation - find semantic connections
dreaming_result = yield run_dreaming_tick(script, character)

# 2. Memory link cleanup - remove orphaned references
link_cleanup_result = yield prune_orphaned_memory_links(script)

# 3. Entity consolidation - observations → attributes (O-Mem)
# helpers/entity_context.py::run_entity_consolidation_batch()
entity_result = yield run_entity_consolidation_batch(script, character)

# 4. Generative reflection - triggered at importance threshold
if check_reflection_trigger(script):  # cumulative >= 150
    reflection_result = yield run_reflection(script, character)

# 5. Episodic pruning - remove old low-importance entries
# helpers/episodic_index.py::prune_low_importance_entries()
prune_result = prune_low_importance_entries(character)

# 6. Stale conversation cleanup - working memory maintenance
# helpers/working_memory.py::clear_stale_conversations()
stale_cleared = clear_stale_conversations(character)

# 7. Pre-compaction extraction - journal facts before compaction
pre_compact_result = yield run_pre_compaction_extraction(script, character)

# 8. Context compaction - summarize old conversation history
compaction_result = yield compact_conversation_history(script, character)

4. Memory Consolidation

Journal → Mem0 Flow

@inlineCallbacks
def run_sleep_consolidation(script, character, max_entries_per_tick=5):
    """
    Process journal entries into semantic memory.

    1. Find unconsolidated entries (not synthesis, not already processed)
    2. Call mem0.add() with source trust metadata
    3. Mark entries as consolidated
    4. Track progress for atomic completion
    """

Consolidation Metadata:

result = yield memory_client.add(
    content=content,
    user_id=script.db.assistant_key,
    metadata={
        "source": "journal",
        "entry_id": entry.get("id"),
        "tags": entry.get("tags", []),
        # O-MEM source trust metadata
        "source_type": source_type,      # observation, direct, etc.
        "source_trust": source_trust,    # 0.0-1.0
        "source_entity": source_entity,  # Who said it
        # Generative Agents importance
        "importance": entry.get("importance"),
        "importance_method": entry.get("importance_method"),  # llm/heuristic
    },
)
@inlineCallbacks
def run_dreaming_tick(script, character, batch_size=5):
    """
    Discover semantic connections between memories.

    1. Find memories not yet processed for links
    2. Search for similar memories (similarity >= 0.7)
    3. Store connections in script.db.memory_links
    """

Memory Link Structure:

script.db.memory_links = {
    "mem_123": {
        "linked_to": [
            {"id": "mem_456", "score": 0.85},
            {"id": "mem_789", "score": 0.73},
        ],
        "processed_at": "2025-12-06T10:00:00Z",
    },
}

5. Context Compaction

Two Trigger Modes

Mode Threshold Trigger
Sleep-based 70% compact_sleep_threshold during sleep
Emergency 80% compact_emergency_threshold during awake

Compaction Flow

@inlineCallbacks
def compact_conversation_history(script, character, force=False):
    """
    1. Check token usage against threshold
    2. Split: messages_to_compact | messages_to_preserve
    3. Generate LLM summary of old messages
    4. Store synthesis in journal
    5. Replace history with [summary_marker] + preserved
    """

Pre-Compaction Extraction:

@inlineCallbacks
def run_pre_compaction_extraction(script, character):
    """
    Give assistant one final chance to journal facts.

    Uses pre_compaction context type with journal-only tools.
    Runs ReAct loop before compaction destroys full context.
    """

6. Mode Transitions

Transition Mechanics

def transition_mode(script, target_mode, reason):
    """
    1. Remove old ticker (TICKER_HANDLER.remove)
    2. Add new ticker with adjusted rate
    3. Update script.db.operating_mode
    4. Log transition reason
    5. Notify character if available
    """

Wake Conditions

def check_wake_conditions(script):
    """
    Returns: (should_wake, reason, deferred)

    Wake triggers:
    1. Scheduled wake time reached
    2. Urgent events (light sleep only)

    Deferred when phase == "compacting" (atomic completion)
    """

Urgent Events (wake light sleepers):

  • Direct messages
  • metadata.urgent = True
  • metadata.priority >= 8
  • Classification: direct_addressing, direct_message

Canonical Wake Transition

def perform_wake_transition(script, reason):
    """
    1. Set cooldown timer (default: 60 minutes)
    2. Reset activity counter
    3. Clear sleep state attributes
    4. Restore scheduled sleep if tool-disabled
    5. Transition to awake mode
    """

7. Sleep Schedule

Automatic mode transitions based on time of day.

db.sleep_schedule = {
    "enabled": False,
    "sleep_start_hour": 2,      # 2:00 AM
    "sleep_duration_hours": 4,  # Until 6:00 AM
    "tick_rate_sleep": 60,      # Slower ticks during sleep
    "min_awake_ticks": 10,      # Minimum before auto-sleep
}

Schedule Check Flow

def check_sleep_schedule(script):
    """
    Returns: (should_transition, target_mode, reason)

    1. Calculate if current_hour is in sleep window
    2. Handle wrap-around (e.g., 22:00-02:00)
    3. Check min_awake_ticks before transitioning
    """

8. Reflection System

Generative Agents-inspired periodic self-reflection.

Trigger Condition

# Reflection triggers when cumulative importance >= 150
def check_reflection_trigger(script):
    state = script.db.reflection_state or {}
    cumulative = state.get("cumulative_importance", 0)
    threshold = state.get("threshold", 150)
    return cumulative >= threshold

Reflection Flow

@inlineCallbacks
def run_reflection(script, character):
    """
    1. Generate reflection questions from recent high-importance entries
    2. Call LLM to produce insights
    3. Store insights as high-importance journal entries
    4. Reset cumulative importance counter
    """

Key Files

File Key Functions Purpose
rag_memory.py ASSISTANT_FACT_EXTRACTION_PROMPT Persona-safe extraction prompt
rag_memory.py retrieve_relevant_memories() Two-stage memory retrieval
rag_memory.py run_sleep_consolidation() Journal to Mem0 transfer
rag_memory.py run_dreaming_tick() A-MEM link generation
rag_memory.py compact_conversation_history() Context compaction
rag_memory.py run_pre_compaction_extraction() Pre-compaction journaling
rag_memory.py run_sleep_tick() Sleep tick orchestrator
operating_mode.py transition_mode() Mode switching
operating_mode.py check_wake_conditions() Wake trigger evaluation
operating_mode.py perform_wake_transition() Wake transition logic
helpers/entity_profiles.py create_entity_profile() O-Mem profile creation
helpers/entity_context.py run_entity_consolidation_batch() Entity Pf → Pa synthesis
helpers/episodic_index.py search_episodic_memory() Hybrid-scored journal search
helpers/episodic_index.py prune_low_importance_entries() Journal cleanup
helpers/working_memory.py clear_stale_conversations() Working memory maintenance

See also: Architecture-Overview | Architecture-Context-System | Architecture-Tool-System

Updated: 2025-12-09 - Added helpers/ package references, converted to semantic refs