Memory
Give agents persistent memory across sessions with pluggable backends
Memory lets agents remember past conversations, user preferences, and context across sessions.
Grow memory autonomously
With self_improve="background" and memory=True, the same guarded post-turn review that autonomously grows skills can also autonomously persist durable facts and preferences via store_memory — off the hot path, with zero cost to reply latency and zero context pollution during the live turn. See Self-Improving Agents.
from praisonaiagents import Agent
agent = Agent(
name="Assistant",
instructions="You are a helpful personal assistant.",
memory=True, # memory is isolated per instance — pass user_id="…" to share/persist across runs
)
agent.start("My name is Alice and I prefer concise answers.")The user shares preferences in chat; the agent recalls them in later sessions via the configured memory backend.
Quick Start
- Level 1 — Bool (simplest)
Turn on memory with a single flag — the agent remembers across turns using the default file backend.
python from praisonaiagents import Agent agent = Agent( name="Assistant", instructions="You are a helpful assistant.", memory=True, # memory is isolated per instance — pass user_id="…" to share/persist across runs ) agent.start("Remember that I work in software engineering.") - Level 2 — String (pick a backend)
Pass a backend name to choose where memories are stored.
python from praisonaiagents import Agent agent = Agent( name="Assistant", instructions="You are a helpful assistant.", memory="sqlite", ) agent.start("Remember that I prefer concise answers.") - Level 3 — Config class (full control)
Use
MemoryConfigto scope memory per user and auto-extract facts.python from praisonaiagents import Agent, MemoryConfig agent = Agent( name="Assistant", instructions="You are a helpful assistant.", memory=MemoryConfig( backend="sqlite", user_id="alice", auto_memory=True, ), ) agent.start("What do you know about my preferences?") - Level 4 — Config with continuous learning
Add
LearnConfigto build a long-term persona alongside session memory.python from praisonaiagents import Agent, MemoryConfig, LearnConfig agent = Agent( name="Assistant", instructions="You are a helpful assistant.", memory=MemoryConfig( backend="sqlite", user_id="alice", learn=LearnConfig(persona=True, insights=True, mode="agentic"), ), ) agent.start("I always want answers formatted as bullet points.")
How It Works
| Phase | What happens |
|---|---|
| 1. Store | Each conversation turn is saved to the memory backend |
| 2. Retrieve | Relevant past context is recalled before responding |
| 3. Respond | Agent answers with full historical context available |
Which Backend to Choose?
graph TB
Q{What's your setup?}
Q -->|Prototype / single server| F[file backend\ndefault, zero deps]
Q -->|Single server, persistent| S[sqlite backend]
Q -->|Vector search / RAG| C2[chroma backend]
Q -->|Multi-agent shared memory| M[mem0 or mongodb]
Q -->|Self-hosted decay-weighted memory| D[dakera]
Q -->|Redis / Postgres| DB[use db\(url=...\)]
Q -->|Anthropic model with long context| C[claude_memory=True]
classDef decision fill:#F59E0B,stroke:#7C90A0,color:#fff
classDef backend fill:#6366F1,stroke:#7C90A0,color:#fff
class Q decision
class F,S,C2,M,D,DB,C backendConfiguration Options
CardFull list of options, types, and defaults — MemoryConfig
The most common options at a glance:
| Option | Type | Default | Description | ||
|---|---|---|---|---|---|
backend | str | "file" | Storage backend: file, sqlite, chroma, mem0, mongodb, dakera, in_memory. Aliases: chromadb/rag → chroma, none → in_memory. | ||
db | `DbAdapter \ | None` | None | Live database adapter (e.g. db(database_url="sqlite:///path/to.db") or db.SQLiteDB(path=...)). Must implement on_agent_start / on_user_message / on_agent_message — a bare path string raises TypeError at construct. For file paths without an adapter, use backend="sqlite" with config={"short_db": "…", "long_db": "…"} instead. | |
user_id | `str \ | None` | None | User identifier for scoped memory. Leaving it None now yields a per-instance agent-<uuid> id (not a shared "default") — set it to share/persist across runs | |
auto_memory | bool | False | Auto-extract and store key facts | ||
learn | `bool \ | LearnConfig \ | None` | None | Enable continuous learning |
history | bool | False | Auto-inject session history into context | ||
prefetch | Optional[bool] | None (auto) | Auto: on for query-based backends (sqlite, chroma, mem0), off for FileMemory. True / False force it on/off. See Memory Prefetch. | ||
prefetch_limit | int | 5 | Max number of memories injected per turn | ||
prefetch_token_budget | int | 512 | Estimated-token cap for the injected memory block (truncated with … if exceeded) |
Combining backend and config
Pass a config= dict alongside backend= to hand provider-specific settings to the store — the outer backend= still decides which store is built.
from praisonaiagents import Agent
from praisonaiagents.config import MemoryConfig
agent = Agent(
name="Assistant",
instructions="Remember facts across turns.",
memory=MemoryConfig(
backend="sqlite",
user_id="alice",
config={"short_db": "./mem/short.db"},
),
)
agent.start("My favourite colour is teal.")An explicit provider or backend key inside config= wins; otherwise the outer backend= fills in.
| What you wrote | Backend actually used |
|---|---|
MemoryConfig(backend="sqlite") | sqlite |
MemoryConfig(backend="sqlite", config={"short_db": "…"}) | sqlite |
MemoryConfig(backend="sqlite", config={"provider": "file"}) | file (inner wins) |
MemoryConfig(backend="sqlite", config={"backend": "file"}) | file (inner wins) |
MemoryConfig(backend="file") | file |
Common Patterns
Pattern 1 — User-scoped memory
from praisonaiagents import Agent, MemoryConfig
agent = Agent(
instructions="You are a personal assistant.",
memory=MemoryConfig(user_id="user_alice", backend="sqlite"),
)
response = agent.start("What have I told you about my work preferences?")
print(response)Pattern 2 — History injection for conversation continuity
from praisonaiagents import Agent, MemoryConfig
agent = Agent(
instructions="You are a support agent.",
memory=MemoryConfig(history=True, history_limit=5),
)
agent.start("Continue from where we left off.")Pattern 3 — Recalled context at turn start
from praisonaiagents import Agent, MemoryConfig
writer = Agent(
name="writer",
instructions="Store user facts.",
memory=MemoryConfig(backend="file", user_id="alice"),
)
writer.memory.remember("User prefers metric units and dark mode.")
reader = Agent(
name="reader",
instructions="Answer using recalled context.",
memory=MemoryConfig(
backend="file",
user_id="alice",
prefetch=True, # required on file; sqlite/chroma/mem0 prefetch automatically
prefetch_limit=5,
prefetch_token_budget=512,
),
)
reader.start("What are my UI preferences?")The agent sees a ## Recalled memories block in its system prompt before the first model call — no extra tool call needed.
Quick API — remember, recall, forget
Three verbs give you explicit control over long-term facts — store, look up, and delete.
An agent with memory=True exposes its memory instance directly, so you can store and recall facts on demand.
from praisonaiagents import Agent
agent = Agent(
name="Note Taker",
instructions="Save and recall project facts on request.",
memory=True,
)
# The agent's memory instance is available directly
agent.memory.remember("Sprint 42 ships on Friday")
matches = agent.memory.recall("When does sprint 42 ship?")Use Memory() standalone in a plain script — no config, no agent required.
from praisonaiagents.memory import Memory
mem = Memory() # no config needed — uses local SQLite
mem.remember("API rate limit is 1000 req/min")
for m in mem.recall("rate limit"):
print(m["text"])
mem.forget(query="rate limit")Prompt-ready context — get_context
get_context(query=...) returns retrieved memory as a single prompt-ready string for a given query — the same string Agent.get_memory_context() injects into the system prompt.
from praisonaiagents import Agent
agent = Agent(name="Assistant", instructions="Help the user.", memory=True)
agent.memory.remember("User is working on the climate dashboard project.")
ctx = agent.memory.get_context(query="What projects am I working on?")
print(ctx)An empty or whitespace-only query intentionally returns "" — this avoids a LIKE "%%" scan that would pull in unrelated (and possibly other users’) records.
agent.memory.get_context(query="") # -> "" (nothing leaked)Pass user_id="…" (or configure it on MemoryConfig) so user-scoped memories stay isolated when a single Memory instance is shared across users.
Parameters
| Method | Parameter | Type | Default | Description | |
|---|---|---|---|---|---|
Memory() | config | `dict \ | None` | None | Backend config. Optional — omit for the default local backend. |
Memory() | verbose | int | 0 | Verbosity level. | |
remember | content | str | (required) | Fact to store in long-term memory. | |
remember | metadata | `dict \ | None` | None | Keyword-only. Extra metadata stored with the record. |
remember (returns) | — | str | — | The generated memory id. | |
recall | query | str | (required) | Search string. | |
recall | limit | int | 5 | Keyword-only. Max results returned. | |
recall (returns) | — | list[dict] | — | Matching records, each with at least a text field. | |
forget | memory_id | `str \ | None` | None | Keyword-only. Delete by exact id. When no memory_type is passed, deletes from both tiers — short-term and long-term ids can collide because each tier has its own AUTOINCREMENT sequence, and the returned count reflects rows removed from both. Pass memory_type="short_term" or "long_term" via delete_memory() to scope the delete to a single tier. |
forget | query | `str \ | None` | None | Keyword-only. Delete long-term records matching this query. |
forget (returns) | — | int | — | Count of records deleted across both tiers when memory_id is used without a tier. | |
get_context | query | `str \ | None` | None | Search string. Empty/whitespace returns "". |
get_context | user_id | `str \ | None` | None | Keyword arg. Falls back to the configured user_id. |
get_context (returns) | — | str | — | Prompt-ready context string ("" when nothing matches). |
agent.memory.get_all_memories() returns every stored short-term and long-term record (each with a public type field of "short_term" or "long_term"). When a memory adapter is configured, it now correctly delegates to the adapter — earlier releases returned [] for every provider.
Which memory API?
Interaction flow
Best Practices
Always set user_id when you want memory to persist or be shared
As of PraisonAI PR #3614, Agent(memory=True) without a user_id generates a fresh per-instance identifier (agent-<uuid>) and logs a warning. Memory is isolated per Agent object by default — safe, but not what you want if you re-create the agent between requests and expect it to remember the last conversation.
Pass user_id="alice" (or "tenant-42", "project-climate", etc.) to opt into a shared / persistent store that survives across processes.
As of PraisonAI #4203, this isolation now covers every backend — sqlite, chroma, mongodb, mem0, and learn derive per-agent default store paths and collection names from the same agent-<uuid> id, so two agents in one process no longer silently share one short_term.db / long_term.db / memory_store. Explicit user_id, short_db, long_db, rag_db_path, or collection_name still win. See Per-agent isolation across backends.
Use auto_memory for fact extraction
Enable auto_memory=True to have the agent automatically identify and store important facts from each conversation — names, preferences, decisions — without extra code.
auto_memory=True runs the pattern extractor and persists the verbatim conversation turn to long-term memory, stamped with the configured user_id (and session_id). The raw turn is saved alongside the extracted facts, so arbitrary details the extractor would miss — a one-off codename, a date, an ID — still survive.
A brand-new Agent(memory=MemoryConfig(backend="sqlite", user_id="alice")) therefore recalls facts a previous instance stored under the same user_id — no prefetch=True and no explicit store_memory() or recall() call required. See Memory Prefetch → Automatic cross-instance recall.
Combine memory with learning
Set learn=True inside MemoryConfig to enable continuous learning alongside session memory. This gives agents both short-term context (memory) and long-term pattern recognition (learn).
Backend scaling path
Start with file, move to sqlite when you need durability, then mongodb (or chroma for vector search) when you deploy multiple agent instances that share memory. For Redis or Postgres, use memory=MemoryConfig(db=db(state_url="redis://...")) or db(database_url="postgresql://...") — see Redis persistence.
Choose remember/recall for facts, memory=True for conversation history
Agent(memory=True) auto-captures every conversation turn. remember() / recall() / forget() give you explicit control for fact storage — use them for structured knowledge you want to look up later. Both share the same long-term store when the agent’s memory instance is used.
Async safety
Task-callback memory writes (store_in_memory in Task.execute_callback) run on a worker thread on the async path. Parallel tasks batched into a single asyncio.gather(...) no longer stall the event loop on one slow embedding/DB write.
The built-in in-memory adapter is thread-safe: concurrent async writes from multiple parallel tasks cannot produce duplicate ids or lose entries.
No user-facing change: the async path automatically dispatches the write via asyncio.to_thread.
Session auto-save writes now run on a worker thread on the async path too. Agent(memory=MemoryConfig(auto_save="…")) persists via asyncio.to_thread on astart, _adelegate_to_backend, and each iteration of run_autonomous_async, so concurrent async agents sharing the same session store no longer serialise on one another’s file-lock + JSON write (PR #5258). An internal lock also serialises the shared save index, so two async runs sharing one Agent never double-persist the same turns.
Related
Learn — continuous learning from conversations
KnowledgeKnowledge — add documents and URLs as agent knowledge
Memory Flush on CompactionPreserve facts before compaction — save durable facts before older messages are dropped
Async SafetyOffloaded memory writes and the thread-safe in-memory adapter