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.
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.
How It Works
Key Features
Separate short-term and long-term memory systems
Card4-metric quality assessment for stored memories
CardUser, agent, and run-specific memory scoping
CardAutomatic entity extraction and storage
CardOptional Neo4j/Memgraph for relationships
CardQuality-based filtering and relevance ranking
Quick Start
- 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") - 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
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
)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.
# 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.
# 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.
| Value type | What gets stored |
|---|---|
str, int, float, bool | passed through unchanged |
None | key dropped entirely (does not round-trip) |
dict, list, tuple, any other type | coerced via str(value) — stored as its repr-style string |
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.
# 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.
# 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 ReferenceFull parameter reference for MemoryConfig
MemoryBackend options:
| Backend | Value | Description |
|---|---|---|
| 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:
| Option | Type | Default | Description | ||
|---|---|---|---|---|---|
backend | str | "file" | Storage backend (see above) | ||
user_id | `str \ | None` | None | Scope memory to a specific user | |
session_id | `str \ | None` | None | Scope memory to a specific session | |
auto_memory | bool | False | Auto-extract and store key facts | ||
claude_memory | bool | False | Use Claude’s native memory (Anthropic models) | ||
config | `dict \ | None` | None | Provider-specific configuration | |
learn | `bool \ | LearnConfig \ | None` | None | Enable continuous learning |
history | bool | False | Auto-inject session history into context | ||
history_limit | int | 10 | Max messages to inject from history | ||
auto_save | `str \ | None` | None | Auto-save session with this name |
RAG Configuration (Default)
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.
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.
# 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# 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:
> 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:
> 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:
> 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.
| Configured backend | Behavior | Store/search path |
|---|---|---|
file | native (default) | FileMemory (direct) |
sqlite | native adapter | memory_adapter.* (direct) |
chroma | ChromaDB adapter (aliases chromadb, rag) | memory_adapter.* |
mem0 | Mem0 client | direct Mem0 client |
mongodb | MongoDB adapter | memory_adapter.* |
dakera | Dakera adapter | memory_adapter.* |
in_memory | in-process adapter (alias none) | memory_adapter.* |
redis / postgres / valkey | raises ValueError | use 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):
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:
from praisonaiagents.memory.adapters import register_memory_adapter
register_memory_adapter("redis", MyRedisAdapter)
agent = Agent(memory=MemoryConfig(backend="redis")) # now acceptedQuality Scoring System
Quality Metrics
Measures how complete and comprehensive the information is
RelevanceMeasures how relevant the information is to the context
ClarityMeasures how clear and understandable the information is
AccuracyMeasures the factual accuracy of the information
Quality Calculation
# 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.
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.
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:
# 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 contextTask Output Finalisation
Store task results with quality assessment:
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:
# 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:
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
- Semantic Reranking: Re-scores results based on semantic similarity
- Context-Aware Ranking: Considers current context when ranking
- Quality-Weighted Ranking: Combines relevance with quality scores
# 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:
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:
# 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 = candidatesPerformance Optimisation
# 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"}
)# 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
)# 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
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
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
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
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:
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",
)
)| Parameter | Type | Default | Description |
|---|---|---|---|
auto_memory | bool | False | Enable automatic memory extraction |
backend | str | "file" | Storage backend (file, sqlite, chroma, mongodb, mem0, dakera, in_memory) |
user_id | str | None | User 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.
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)Retry resumes after a transient backend outage because dedup is recorded only after a successful write.
# 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