Advanced Memory System

Multi-tiered memory with quality scoring and graph support

Multi-tiered memory with short-term, long-term, entity, and user-specific storage — enhanced by quality scoring and automatic extraction.

python
from praisonaiagents import Agent, MemoryConfig

agent = Agent(
    name="memory-agent",
    instructions="Remember context across turns.",
    memory=MemoryConfig(use_long_term=True),
)
agent.start("What did we decide about the launch date?")

The user chats across turns; multi-tier memory stores and scores context for later retrieval.

Diagram

Storage Backends

Quality System

Memory Tiers

⚡ Short-term Memory

💾 Long-term Memory

👤 Entity Memory

🔐 User Memory

📊 Quality Score

✅ Completeness

🎯 Relevance

💡 Clarity

✓ Accuracy

🗃️ RAG/ChromaDB

☁️ Mem0

🧠 Dakera

🗄️ SQLite

🕸️ Graph DB

How It Works

Diagram
AdvancedMemorySystemAgentUserAdvancedMemorySystemAgentUserRequestProcessResultResponse

Key Features

Quick Start

  1. Simple — enable with True
    python
    from praisonaiagents import Agent
    
    agent = Agent(
        name="Assistant",
        instructions="You are a helpful assistant with memory.",
        memory=True
    )
    
    agent.start("Remember that I prefer Python for data science")
  2. With config
    python
    from praisonaiagents import Agent
    
    agent = Agent(
        name="Assistant",
        instructions="You are a helpful assistant with memory.",
        memory={
            "backend": "rag",
            "user_id": "user123",
            "use_embedding": True
        }
    )

Direct Memory Access

python
from praisonaiagents import Memory

# Initialise memory
memory = Memory(
    config={
        "provider": "rag",
        "use_embedding": True,
        "short_db": ".praison/short_term.db",
        "long_db": ".praison/long_term.db"
    }
)

# Store memories with quality
memory.store_long_term(
    text="The user prefers technical explanations",
    memory={"user_id": "user123"},
    metadata={"type": "preference"},
    quality=0.9
)

# Search memories — scope with the first-class user_id kwarg
results = memory.search_long_term(
    query="user preferences",
    user_id="user123",
    min_quality=0.7
)
python
from praisonaiagents import Memory

memory = Memory(config={"provider": "rag"})

memory.store_long_term(
    text="Quantum computing uses qubits for computation",
    completeness=0.95,
    relevance=0.90,
    clarity=0.88,
    accuracy=0.92,
    memory={"user_id": "user123"}
)

Memory Tiers

Short-term Memory (STM)

Short-term memory is cleared between sessions and used for immediate context.

python
# Store temporary context and capture the returned id
mid = memory.store_short_term(
    text="User is asking about Python programming",
    metadata={"user_id": "user123", "context": "current_conversation"}
)

# Search recent context
context = memory.search_short_term(
    query="Python",
    limit=5
)

# Targeted delete uses the id (returns True on success)
memory.delete_short_term(mid)

# Or wipe everything
memory.reset_short_term()

memory.reset_short_term() / .reset_long_term() now work on the default rag / chroma provider, not just sqlite / in_memory and dakera. On sqlite / in_memory providers, reset_ / delete_ delegate to the active adapter (mirroring search_short_term), so they operate on the adapter-created short_term_memory / long_term_memory tables.

Long-term Memory (LTM)

Long-term memory persists across sessions and stores important information.

python
# Store persistent knowledge
memory.store_long_term(
    text="User works as a data scientist at TechCorp",
    memory={"user_id": "user123"},
    quality=0.95,
    metadata={"type": "user_info", "category": "professional"}
)

# Retrieve with quality filter — user_id is a first-class kwarg
memories = memory.search_long_term(
    query="user profession",
    user_id="user123",
    min_quality=0.8
)

Metadata types (ChromaDB / RAG backend)

ChromaDB only stores primitives — PraisonAI sanitises your metadata dict before writing so Chroma writes never fail.

Diagram

str/int/float/bool

None

dict / list / other

metadata=…

🧹 sanitize_chroma_metadata

pass through

drop key

str value

ChromaDB

Value typeWhat gets stored
str, int, float, boolpassed through unchanged
Nonekey dropped entirely (does not round-trip)
dict, list, tuple, any other typecoerced via str(value) — stored as its repr-style string
python
from praisonaiagents import Memory

memory = Memory(config={"provider": "rag"})

memory.store_long_term(
    text="Project atlas kicked off",
    metadata={
        "project": "atlas",              # stored as "atlas"
        "priority": 3,                    # stored as 3
        "shipped": False,                 # stored as False
        "owner": None,                    # DROPPED — not stored
        "tags": ["backend", "urgent"],    # stored as the string "['backend', 'urgent']"
        "config": {"env": "prod"},        # stored as the string "{'env': 'prod'}"
    },
)

Entity Memory

Entity memory stores information about specific people, places, or things.

python
# Store entity information
memory.store_entity(
    name="TechCorp",
    text="TechCorp is a leading AI company founded in 2020",
    entity_type="organization",
    metadata={"industry": "technology", "size": "large"}
)

# Search entity information
entity_info = memory.search_entity(
    name="TechCorp",
    query="company details"
)

User Memory

User memory stores personalised information and preferences.

python
# Store user preferences
memory.store_user_memory(
    memory={"user_id": "user123"},
    text="Prefers concise explanations with code examples",
    extra={"preference_type": "communication_style"}
)

# Retrieve user context
user_context = memory.search_user_memory(
    memory={"user_id": "user123"},
    query="preferences",
    limit=3
)

Configuration Options

MemoryConfig SDK Reference

Full parameter reference for MemoryConfig

MemoryBackend options:

BackendValueDescription
File"file"JSON files (default, zero dependencies)
SQLite"sqlite"SQLite database
In-memory"in_memory"Lightweight in-process store (dev/testing)
Chroma"chroma"ChromaDB vector store
Mem0"mem0"Mem0 cloud service (requires mem0ai)
MongoDB"mongodb"MongoDB
Dakera"dakera"Self-hosted decay-weighted memory server (requires praisonaiagents[dakera])

MemoryConfig parameters:

OptionTypeDefaultDescription
backendstr"file"Storage backend (see above)
user_id`str \None`NoneScope memory to a specific user
session_id`str \None`NoneScope memory to a specific session
auto_memoryboolFalseAuto-extract and store key facts
claude_memoryboolFalseUse Claude’s native memory (Anthropic models)
config`dict \None`NoneProvider-specific configuration
learn`bool \LearnConfig \None`NoneEnable continuous learning
historyboolFalseAuto-inject session history into context
history_limitint10Max messages to inject from history
auto_save`str \None`NoneAuto-save session with this name

RAG Configuration (Default)

python
memory_config = {
    "provider": "rag",
    "use_embedding": True,
    "short_db": ".praison/short_term.db",
    "long_db": ".praison/long_term.db",
    "entity_db": ".praison/entity.db",
    "rag_db_path": ".praison/chroma_db",
    "embedding_model": "text-embedding-3-small"
}

Share one SQLite file across agents (db_path)

Set db_path to point two or more agents at a single SQLite file so they share long-term and short-term memory.

Diagram

Shared db_path

store

recall

🤖 Writer Agent

🗄️ one .db file

🤖 Reader Agent

A support team runs two assistants in the same process. Agent A captures what the user said; Agent B answers a follow-up five minutes later. Point both at the same db_path and Agent B recalls the fact Agent A stored — without extra wiring, user_id juggling, or a shared vector store.

python
# Shared memory across Agent instances using db_path
from praisonaiagents import Agent
from praisonaiagents.config.feature_configs import MemoryConfig

shared = "/tmp/team-memory.db"

writer = Agent(
    name="Writer",
    instructions="Remember what the user tells you.",
    memory=MemoryConfig(backend="sqlite", config={"db_path": shared}),
)
writer.start("Remember codename ORANGE-PANDA for the Q4 launch.")

reader = Agent(
    name="Reader",
    instructions="Answer questions using memory.",
    memory=MemoryConfig(backend="sqlite", config={"db_path": shared}),
)
reader.start("What is the codename for the Q4 launch?")

Precedence — explicit short_db / long_db win over db_path, which wins over the per-user default paths. This is per-tier: set short_db explicitly and let long_db fall back to db_path.

Explicit short_db / long_db  >  db_path  >  Per-user default paths
Diagram

short_db or long_db set

only db_path set

nothing set

Which SQLite file(s) will the store write?

Explicit path wins per-tier

db_path used for BOTH tiers

Per-user default paths
(short_term<suffix>.db / long_term<suffix>.db)

python
# Explicit short_db / long_db still win over db_path
from praisonaiagents import Agent
from praisonaiagents.config.feature_configs import MemoryConfig

agent = Agent(
    name="Writer",
    instructions="Keep short- and long-term memory in separate files.",
    memory=MemoryConfig(
        backend="sqlite",
        config={
            "db_path":  "/tmp/ignored.db",   # ignored because the explicit paths win
            "short_db": "/tmp/short.db",
            "long_db":  "/tmp/long.db",
        },
    ),
)

Mem0 Configuration

Requires the optional memory extras:

bash
> pip install "praisonaiagents[memory]"
> ```
> If the package is missing, configuring `"provider": "mem0"` raises:
> ```
> ImportError: mem0ai is not installed. Run: pip install 'praisonaiagents[memory]'
> ```

```python
memory_config = {
    "provider": "mem0",
    "config": {
        "api_key": "your-mem0-api-key",
        "org_id": "your-org-id",
        "project_id": "your-project-id"
    }
}

Dakera Configuration

Requires the optional Dakera extra:

bash
> pip install "praisonaiagents[dakera]"
> ```
> If the package is missing, configuring `"provider": "dakera"` raises:
> ```
> ImportError: dakera is required for the dakera adapter. Install with: pip install 'praisonaiagents[dakera]' (or: pip install dakera)
> ```

```python
memory_config = {
    "provider": "dakera",
    "config": {
        "url": "http://localhost:3000",      # or DAKERA_URL / DAKERA_API_URL env var
        "api_key": "dk-...",                 # or DAKERA_API_KEY env var
        "agent_id": "my-agent",             # or DAKERA_AGENT_ID env var
        "short_term_type": "working",
        "long_term_type": "episodic",
        "default_importance": 0.5,
    }
}

See Dakera Memory for the full guide including self-hosted deployment, tier mapping, and delete/reset operations.

Graph Memory Configuration

Requires the optional memory extras:

bash
> pip install "praisonaiagents[memory]"
> ```

```python
memory_config = {
    "provider": "mem0",
    "config": {
        "graph_store": {
            "provider": "neo4j",
            "config": {
                "url": "bolt://localhost:7687",
                "username": "neo4j",
                "password": "password"
            }
        },
        "vector_store": {
            "provider": "qdrant",
            "config": {
                "host": "localhost",
                "port": 6333
            }
        },
        "version": "v1.1"
    }
}

Unimplemented Backends Raise

Requesting a backend that has no registered adapter raises a ValueError — there is no silent fallback to a local file store.

Diagram

Yes

No

📋 MemoryConfig backend

🔍 Adapter registered?

💾 Memory adapter

❌ ValueError

🗃️ Storage

Configured backendBehaviorStore/search path
filenative (default)FileMemory (direct)
sqlitenative adaptermemory_adapter.* (direct)
chromaChromaDB adapter (aliases chromadb, rag)memory_adapter.*
mem0Mem0 clientdirect Mem0 client
mongodbMongoDB adaptermemory_adapter.*
dakeraDakera adaptermemory_adapter.*
in_memoryin-process adapter (alias none)memory_adapter.*
redis / postgres / valkeyraises ValueErroruse db(state_url=...) (Redis) or db(database_url=...) (Postgres)

All registered adapters route store_, search_, reset_, and delete_ through memory_adapter.*. See Custom Memory Adapters.

Keep using Redis or Postgres

Pass a live store via db(state_url=...) for Redis (or db(database_url=...) for Postgres):

python
from praisonaiagents import Agent, MemoryConfig, db

agent = Agent(
    name="Assistant",
    instructions="You remember user preferences.",
    memory=MemoryConfig(db=db(state_url="redis://localhost:6379/0"), user_id="user_123"),
)
agent.start("Remember I prefer dark mode")
Register a custom adapter (escape hatch)

Register an adapter for the name you want, then backend="redis" is accepted:

python
from praisonaiagents.memory.adapters import register_memory_adapter

register_memory_adapter("redis", MyRedisAdapter)
agent = Agent(memory=MemoryConfig(backend="redis"))  # now accepted

Quality Scoring System

Quality Metrics

Quality Calculation

python
# Default weights
weights = {
    "completeness": 0.25,
    "relevance": 0.35,
    "clarity": 0.20,
    "accuracy": 0.20
}

# Overall quality = weighted average
quality = (
    completeness * weights["completeness"] +
    relevance * weights["relevance"] +
    clarity * weights["clarity"] +
    accuracy * weights["accuracy"]
)

Advanced Features

Scoped search with user_id and metadata_filter

search_short_term and search_long_term accept user_id and metadata_filter as first-class kwargs to scope results per tenant.

python
from praisonaiagents import Agent, Memory

memory = Memory(config={"provider": "rag", "use_embedding": True})
agent = Agent(
    name="TenantAssistant",
    instructions="Answer scoped to the current tenant.",
    memory=True,
)

# Multi-tenant memory scoping
results = memory.search_long_term(
    query="project details",
    user_id="tenant_a",             # tenant isolation — always wins
    metadata_filter={"project": "atlas"},  # extra scoping
    limit=10,
)

user_id is enforced inside Memory.search_short_term / Memory.search_long_term as of PraisonAI PR #4819. The method auto-derives a user_id metadata filter, so adapters that accept user_id but ignore it in their own query (Chroma/RAG, SQLite, in-memory) still isolate per-tenant results. If a caller passes both an explicit user_id and a conflicting metadata_filter["user_id"], the explicit user_id always wins — a caller cannot widen scope to another tenant’s data.

Cache-Optimised Context

Memory results are deterministically ordered for prompt caching effectiveness. Use build_context_for_task() with explicit output control for manual prompt assembly.

python
from praisonaiagents import Memory

memory = Memory(config={"provider": "rag"})

context = memory.build_context_for_task(
    task_descr="Research the latest AI developments",
    user_id="user_123",
    max_items=3,
    include_in_output=True  # Force include for prompt assembly
)

# Construct cache-friendly prompt structure
system_prompt = f"System instructions and tools\n\n{context}\n\nUser message: {{user_input}}"

See Prompt Caching for the full guide.

Context Building

Build comprehensive context for tasks:

python
# Automatically merge relevant memories
context = memory.build_context_for_task(
    task_descr="Write a Python tutorial",
    memory={"user_id": "user123"},
    additional="Focus on data science applications",
    max_items=5
)

# Context includes:
# - Relevant long-term memories
# - User preferences
# - Recent short-term context

Task Output Finalisation

Store task results with quality assessment:

python
from praisonaiagents import TaskOutput

# Create task output
output = TaskOutput(
    raw="Tutorial content...",
    agent_name="Writer",
    task_description="Write Python tutorial"
)

# Finalise with quality scoring
memory.finalize_task_output(
    task_output=output,
    memory={"user_id": "user123"}
)

Memory Citations

Automatically cite memory sources:

python
# Memories include source information
result = memory.search_long_term("Python", user_id="user123")
# Returns: {
#     'text': 'Python is...',
#     'metadata': {'source': 'conversation_2024_01_15', ...}
# }

Memory Reranking

Enhance search results with intelligent reranking based on relevance scores:

python
from praisonaiagents import Agent, AgentTeam

# Create agent
agent = Agent(
    name="Researcher",
    role="Research assistant"
)

# Initialize with memory enabled
agents = AgentTeam(
    agents=[agent],
    tasks=[],
    memory=True
)

# Search with reranking for better relevance
if agents.shared_memory:
    results = agents.shared_memory.search_long_term(
        "quantum computing applications",
        limit=10,
        rerank=True,  # Enable reranking
        relevance_cutoff=0.7  # Minimum relevance score
    )

Reranking Features

  1. Semantic Reranking: Re-scores results based on semantic similarity
  2. Context-Aware Ranking: Considers current context when ranking
  3. Quality-Weighted Ranking: Combines relevance with quality scores
python
# Advanced reranking with multiple criteria
results = agents.shared_memory.search_long_term(
    query="machine learning frameworks",
    limit=20,  # Retrieve more initially
    rerank=True,  # Enable reranking
    relevance_cutoff=0.6,  # Minimum relevance
    rerank_config={
        "method": "semantic",  # or "hybrid", "quality-weighted"
        "top_k": 5,  # Return top 5 after reranking
        "consider_recency": True,  # Factor in timestamp
        "boost_user_memories": True  # Prioritize user-specific memories
    }
)

Custom Reranking Logic

Implement custom reranking strategies:

python
from datetime import datetime
from praisonaiagents import RerankStrategy

class DomainSpecificReranker(RerankStrategy):
    def rerank(self, results, query, context=None):
        """Custom reranking based on domain knowledge"""
        scored_results = []

        for result in results:
            score = result['relevance']

            # Boost technical content
            if 'technical' in result.get('metadata', {}).get('tags', []):
                score *= 1.2

            # Boost recent memories
            if result.get('timestamp'):
                days_old = (datetime.now() - result['timestamp']).days
                recency_boost = 1.0 + (0.1 * max(0, 30 - days_old) / 30)
                score *= recency_boost

            scored_results.append((score, result))

        # Sort by score and return top results
        scored_results.sort(key=lambda x: x[0], reverse=True)
        return [result for score, result in scored_results]

# Use custom reranker
# Note: Assuming 'agents' is defined from previous examples
if agents.shared_memory:
    agents.shared_memory.set_rerank_strategy(DomainSpecificReranker())

Reranking Performance

Optimize reranking for large result sets:

python
# Efficient two-stage retrieval and reranking
# Stage 1: Fast retrieval of candidates
candidates = agents.shared_memory.search_long_term(
    query="data analysis techniques",
    limit=50,  # Get more candidates
    rerank=False  # Skip reranking in first stage
)

# Stage 2: Precise reranking of top candidates
if len(candidates) > 10:
    reranked = agents.shared_memory.rerank_results(
        results=candidates[:20],  # Rerank top 20
        query="data analysis techniques",
        method="hybrid",
        relevance_cutoff=0.8
    )
else:
    reranked = candidates

Performance Optimisation

Batch Operations
# Batch store memories
memories = [
    ("Fact 1", 0.9),
    ("Fact 2", 0.85),
    ("Fact 3", 0.95)
]

for text, quality in memories:
    memory.store_long_term(
        text=text,
        quality=quality,
        memory={"user_id": "user123"}
    )
Quality Filtering
# Pre-filter by quality to reduce results
high_quality = memory.search_long_term(
    query="important facts",
    min_quality=0.8,  # Only high-quality memories
    limit=10
)
Scoped Search
# Scope search to improve performance
results = memory.search_long_term(
    query="project details",
    user_id="user123",
    agent_id="assistant",
    run_id="session_456"
)

Complete Example

Personal Assistant with Memory
from praisonaiagents import Agent, Task, AgentTeam
from praisonaiagents import Memory

# Configure memory with quality scoring
memory_config = {
    "provider": "rag",
    "use_embedding": True,
    "embedding_model": "text-embedding-3-small"
}

# Create memory instance
memory = Memory(memory_config)

# Create assistant agent
assistant = Agent(
    name="Personal Assistant",
    instructions="""You are a personal assistant with perfect memory.
    Remember user preferences, important information, and context.
    Always use your memory to provide personalised responses.""",
    memory={"user_id": "john_doe"}  # Memory with user isolation
)

# Create task with memory integration
task = Task(
    description="Help me plan my day based on my preferences",
    agent=assistant,
    expected_output="Personalised daily schedule"
)

# Run the system
agents = AgentTeam(
    agents=[assistant],
    tasks=[task],
    memory=True  # Enable shared memory
)

# Memory automatically:
# 1. Retrieves user preferences
# 2. Searches relevant past interactions
# 3. Builds context for the task
# 4. Stores the interaction with quality score
result = agents.start()

# Store user feedback as high-quality memory
memory.store_long_term(
    text="User preferred morning schedule with exercise first",
    memory={"user_id": "john_doe"},
    completeness=0.9,
    relevance=1.0,
    clarity=0.95,
    accuracy=1.0
)

Best Practices

Set quality thresholds for search

Use min_quality=0.8 for critical information retrieval. Use lower thresholds (0.5–0.7) for exploratory searches where recall is more important.

Scope memories per user and agent

Always pass user_id for user-specific memories, agent_id for agent-scoped shared knowledge, and run_id for ephemeral session context.

Enable embeddings for semantic search

Set use_embedding=True in the memory config for semantic (not just keyword) retrieval. Use text-embedding-3-small for cost efficiency.

Use AutoMemory for automatic extraction

Enable MemoryConfig(auto_memory=True) to automatically extract preferences, facts, and entities from conversations without manual store_* calls.

AutoMemory

AutoMemory automatically extracts structured information from conversations using configurable patterns — no manual store_* calls needed.

How It Works

Diagram
Advanced Memory SystemAgentUserAdvanced Memory SystemAgentUserRequestProcess requestResultResponse

AutoMemory wraps FileMemory with a pattern-based extraction engine. After each agent interaction, it scans the conversation for matching patterns (preferences, facts, dates, etc.) and stores them automatically.

Quick Start

python
from praisonaiagents import Agent, MemoryConfig

# Enable auto-memory extraction
agent = Agent(
    name="Assistant",
    instructions="You are a helpful assistant.",
    memory=MemoryConfig(auto_memory=True, user_id="user123")
)

# Patterns are extracted automatically from conversation
agent.start("I prefer Python for data science and I'm based in London")
# AutoMemory stores: preference="Python for data science", location="London"

Custom Patterns

python
from praisonaiagents import AutoMemory

# Create with custom patterns
auto_mem = AutoMemory(
    user_id="user123",
    patterns={
        "preference": r"(?:I prefer|I like|I love)\s+(.+)",
        "location": r"(?:I'm based in|I live in|I'm from)\s+(.+)",
        "name": r"(?:My name is|I'm called|call me)\s+(\w+)",
        "fact": r"(?:I work at|I study at|my job is)\s+(.+)",
    }
)

# Search extracted memories
results = auto_mem.search("user preferences")

MemoryConfig Integration

Use MemoryConfig.auto_memory to enable AutoMemory as part of the consolidated memory parameter:

python
from praisonaiagents import Agent, MemoryConfig

agent = Agent(
    instructions="You are a helpful assistant.",
    memory=MemoryConfig(
        backend="file",
        auto_memory=True,   # Enable pattern-based extraction
        user_id="user123",
        session_id="session456",
    )
)
ParameterTypeDefaultDescription
auto_memoryboolFalseEnable automatic memory extraction
backendstr"file"Storage backend (file, sqlite, chroma, mongodb, mem0, dakera, in_memory)
user_idstrNoneUser ID for scoping memories

Previews and retry semantics

A preview call extracts memories without consuming the dedup hash, so a later real store for the same text still writes, and a failed store also leaves dedup un-consumed so a retry after the backend recovers stores the extracted memories.

python
from praisonaiagents.memory import FileMemory, AutoMemory

base = FileMemory(user_id="user123")
auto = AutoMemory(base, enabled=True)

# Preview — extraction only, no state change
auto.process_interaction("I prefer dark mode.", store=False)

# Real store — writes because the preview did not consume dedup
auto.process_interaction("I prefer dark mode.", store=True)

# Repeated real store — deduplicated (returns [])
auto.process_interaction("I prefer dark mode.", store=True)
Diagram
💾 Base memory🔍 AutoMemory👤 Caller💾 Base memory🔍 AutoMemory👤 Callerprocess_interaction(text, store=False)extract (hash NOT recorded)memories (deepcopy)process_interaction(text, store=True)hash check → missstore memoriesokhash recordedmemoriesprocess_interaction(text, store=True)hash check → hit[] (deduped)

Retry resumes after a transient backend outage because dedup is recorded only after a successful write.

python
# A failed store leaves dedup un-consumed — retry after recovery stores
original_add_long_term = base.add_long_term
base.add_long_term = lambda *a, **kw: (_ for _ in ()).throw(OSError("disk full"))

try:
    auto.process_interaction("I like concise answers.", store=True)
except OSError:
    pass

base.add_long_term = original_add_long_term  # backend recovers

retried = auto.process_interaction("I like concise answers.", store=True)
assert retried != []   # stores on retry

Related