Multi-tier memory with working memory, short-term, long-term, and shared tiers
Version: 1.0 | Last Updated: February 2026
The Memory System provides agents with persistent, searchable memory across multiple tiers. Working memory lives in Redis for fast access, short-term memory has TTL-based expiration, long-term memory uses pgvector for semantic search, and shared memory enables cross-agent knowledge exchange.
┌─────────────────────────────────────────────────────────┐
│ Memory Router │
│ Ai::Memory::RouterService │
└────────────┬──────────┬──────────┬──────────┬───────────┘
│ │ │ │
┌─────▼────┐ ┌───▼──────┐ ┌▼────────┐ ┌▼─────────┐
│ Working │ │ Short- │ │ Long- │ │ Shared │
│ Memory │ │ Term │ │ Term │ │ Memory │
│ (Redis) │ │ (DB+TTL) │ │ (pgvec) │ │ (Pools) │
└──────────┘ └──────────┘ └─────────┘ └──────────┘
TTL: mins TTL: hours Permanent Cross-agent
Key-value Structured Embeddings Collaborative
TTL-based short-term memory entries per agent session.
MEMORY_TYPES = %w[task_context conversation tool_result reflection observation]
DEFAULT_TTL = 3600 # 1 hour
belongs_to :account
belongs_to :agent, class_name: "Ai::Agent"Key fields: session_id, memory_key, memory_value (JSON), memory_type, ttl_seconds, expires_at, access_count, last_accessed_at
Key methods:
expired?— checks ifexpires_athas passedtouch_access!— incrementsaccess_count, updateslast_accessed_atrefresh_ttl!— extends expiration byttl_secondsself.cleanup_expired!— bulk deletes expired entries
Shared memory pools for cross-agent collaboration.
POOL_TYPES = %w[shared agent_private team_shared task_scoped]
SCOPES = %w[global account team agent task]
belongs_to :accountKey fields: pool_id (unique), pool_type, scope, data (JSON), access_control (JSON), version, expires_at
Key methods:
read_data/write_data/merge_data— data access with access controlaccessible_by?(agent)— checks access control rulesgrant_access(agent_id, level)/revoke_access(agent_id)— permission managementstatistics— entry count, size, access metrics
Long-lived context containers for agents and knowledge bases.
CONTEXT_TYPES = %w[agent_memory knowledge_base shared_context tool_cache project_context]
SCOPES = %w[agent account team global]
belongs_to :account
belongs_to :agent, optional: true
has_many :context_entries, dependent: :destroy
has_many :context_access_logs, dependent: :destroyKey methods:
create_snapshot/restore_from_snapshot— point-in-time backup and recoveryarchive!/unarchive!— lifecycle managementgrant_access(agent_id, level)— fine-grained access control
Individual entries within a PersistentContext, with pgvector embeddings for semantic search.
ENTRY_TYPES = %w[fact observation reflection decision plan tool_output conversation_summary]
SOURCE_TYPES = %w[agent user system tool workflow]
MEMORY_TYPES = %w[factual experiential working]
has_neighbors :embedding # pgvector integration
belongs_to :persistent_context
belongs_to :previous_version, optional: true
has_many :newer_versionsKey methods:
semantic_search(query_embedding, limit)— nearest neighbor search via pgvectorupdate_content(new_content, editor)— versioned content updatesboost_importance!/reduce_importance!— importance score adjustmenteffective_relevance_score— combines importance, confidence, and recency
Workflow-scoped shared memory for multi-node data exchange.
# Pool types: shared_memory, tool_cache, blackboard
belongs_to :workflow_runUsed within workflow executions for nodes to share state via a common data pool.
Routes memory operations to the appropriate tier.
router = Ai::Memory::RouterService.new(account: account, agent: agent)
# Read from specific tier (auto-routes if tier not specified)
value = router.read("session_context", session_id: "sess-123")
# Write to specific tier
router.write("task_result", { output: "..." }, tier: :short_term, session_id: "sess-123")
# Semantic search across long-term memory
results = router.semantic_search(query_embedding, limit: 10)
# Consolidate short-term → long-term (access_count >= 3)
router.consolidate!(session_id: "sess-123")
# Get stats across all tiers
stats = router.stats
# => { working_memory: {...}, short_term: {...}, long_term: {...}, shared: {...} }Redis-backed fast key-value store for active agent sessions.
wm = Ai::Memory::WorkingMemoryService.new(agent: agent, account: account, task: task)
# Basic operations
wm.store("key", value, ttl: 300)
wm.retrieve("key")
wm.exists?("key")
wm.remove("key")
wm.keys # List all keys
wm.clear # Remove all
# Specialized storage
wm.store_task_state(state_hash)
wm.store_intermediate_result(step_name, result)
wm.store_conversation_context(messages)
wm.append_to_conversation(message)
wm.store_tool_state(tool_name, state)
wm.store_scratch_pad(content)
wm.append_to_scratch_pad(content)
# Cross-agent sharing
wm.share_with_agent(target_agent_id, key, value)
shared_value = wm.retrieve_shared(source_agent_id, key)
# Workflow integration
wm.workflow_memory # Returns workflow-scoped memory accessor
# Persistence (save Redis state to database)
wm.persist_to_database
wm.load_from_databaseBackend storage with specialized concerns for different memory types.
Included concerns:
Experiential— episodic memory from agent experiencesFactual— structured facts and knowledgeSharedLearning— learnings shared across agentsMemoryPool— pool management operations
Automated maintenance runs via background jobs:
| Job | Schedule | Action |
|---|---|---|
| Memory consolidation | 4:00 AM daily | Promotes STM entries with access_count >= 3 to long-term |
| Deduplication | 4:00 AM daily | Merges entries with similarity >= 0.92 |
| Expired cleanup | Continuous | Deletes entries past expires_at |
| Context rot detection | 4:00 AM daily | Archives context entries with staleness >= 0.9 |
Short-Term Memory Long-Term Memory
(DB with TTL) (pgvector embeddings)
┌──────────────┐ ┌──────────────┐
│ Entry A │ │ │
│ access: 5 │──────────────▶ Promoted │
│ ttl: expired │ access >= 3 │ (embedded) │
└──────────────┘ └──────────────┘
┌──────────────┐
│ Entry B │
│ access: 1 │──── Deleted (expired, low access)
│ ttl: expired │
└──────────────┘
┌──────────────┐ ┌──────────────┐
│ Entry C │ │ Entry D │
│ sim: 0.95 │─────│ sim: 0.95 │──── Merged (deduplicated)
└──────────────┘ └──────────────┘
Memory access is controlled at multiple levels:
- Account scope — all memory is scoped to an account
- Agent scope — private memory is only accessible to the owning agent
- Pool access control — JSONB
access_controlfield with read/write permissions - Context access control — per-agent grants on PersistentContext
# Pool access control structure
{
"read" => ["agent-uuid-1", "agent-uuid-2"],
"write" => ["agent-uuid-1"],
"public_read" => false
}
# Grant/revoke
pool.grant_access("agent-uuid-3", :read)
pool.revoke_access("agent-uuid-3")
pool.accessible_by?(agent) # => true/false| File | Path |
|---|---|
| Short-Term Memory Model | server/app/models/ai/agent_short_term_memory.rb |
| Memory Pool Model | server/app/models/ai/memory_pool.rb |
| Persistent Context Model | server/app/models/ai/persistent_context.rb |
| Context Entry Model | server/app/models/ai/context_entry.rb |
| Context Access Log Model | server/app/models/ai/context_access_log.rb |
| Shared Context Pool Model | server/app/models/ai/shared_context_pool.rb |
| Router Service | server/app/services/ai/memory/router_service.rb |
| Working Memory Service | server/app/services/ai/memory/working_memory_service.rb |
| Storage Service | server/app/services/ai/memory/storage_service.rb |
| Maintenance Service | server/app/services/ai/memory/maintenance_service.rb |
| Embedding Service | server/app/services/ai/memory/embedding_service.rb |