Skip to Content
DocsMemoryBackends

Memory backends

Five backends, all behind the same Memory protocol. The memory= URL resolver picks one for you; the underlying classes are exported for direct construction.

Feature matrix

BackendPersistenceRecallHybrid recall_scoreduser_idAsync networkFact store
InMemoryMemorynonecosine over Python listnative BM25yesn/aInMemoryFactStore
VectorMemorynonecosine over Python listnative BM25 + cosine + RRFyesn/a(no facts)
SqliteMemorysingle filecosine; full table scanshim (neutral scores)yesnoSqliteFactStore
ChromaMemoryon-disk or hostedChroma’s HNSWshim (neutral scores)yesdepends on backendChromaFactStore
PostgresMemoryPostgres + pgvectorpgvector ANNshim (neutral scores)yesyes (asyncpg)PostgresFactStore
RedisMemoryRedis (optional RediSearch)RediSearch HNSW (optional)shim (neutral scores)yesyes (redis.asyncio)RedisFactStore

Native = the backend computes BM25 + vector cosine + RRF internally and populates EpisodeMatch.bm25_score / vector_score. Shim = recall_scored wraps the existing recall() results with a neutral score=1.0 via default_recall_scored. Native plumbing for shim backends can land later as additive changes. See Hybrid recall.

InMemoryMemory. Default

Zero deps, zero setup. Lost on process exit.

from loomflow import Agent, InMemoryMemory memory = InMemoryMemory() agent = Agent("...", memory=memory) # Or just by URL: agent = Agent("...", memory="inmemory")

Bounded by default to 100k users with a 24h idle TTL. See Bounded in-process state.

SqliteMemory. Persistent, no server

Single file, no infrastructure. Great for prototypes, single-instance bots, and demos.

agent = Agent("...", memory="sqlite:./bot.db") # Or explicit: from loomflow.memory.sqlite import SqliteMemory memory = await SqliteMemory.connect("./bot.db", with_facts=True) agent = Agent("...", memory=memory)

Uses cosine similarity over an in-memory vector cache backed by SQLite storage. Recall is fast up to ~50K episodes; for larger working sets move to Postgres or Chroma.

ChromaMemory. Ephemeral or persistent

Lazy chromadb import. Set path= to persist on disk; omit for ephemeral.

agent = Agent("...", memory="chroma") # ephemeral agent = Agent("...", memory="chroma:./chroma-db") # persistent # Explicit: from loomflow.memory.chroma import ChromaMemory from loomflow.memory.embedder import OpenAIEmbedder memory = ChromaMemory.local( "./chroma-db", with_facts=True, embedder=OpenAIEmbedder(), )

Install: pip install 'loomflow[chroma]'.

PostgresMemory. Production durable

Postgres + pgvector. Async via asyncpg. Schema migrations are idempotent, init_schema() runs once on first connect and is safe to re-run.

agent = Agent("...", memory="postgres://user:pw@host/db") # Explicit: from loomflow.memory.embedder import OpenAIEmbedder from loomflow.memory.postgres import PostgresMemory memory = await PostgresMemory.connect( dsn="postgres://user:pw@host/db", embedder=OpenAIEmbedder("text-embedding-3-small"), with_facts=True, )

Install: pip install 'loomflow[postgres]'.

The schema lives in three tables, episodes, memory_blocks, and facts (when with_facts=True). All carry a user_id column with a default for the anonymous bucket and indexes for the multi-tenant queries.

RedisMemory. Fast multi-instance

Redis with optional RediSearch HNSW vector index. Async via redis.asyncio.

agent = Agent("...", memory="redis://localhost:6379/0") # Explicit: from loomflow.memory.embedder import OpenAIEmbedder from loomflow.memory.redis import RedisMemory memory = await RedisMemory.connect( url="redis://localhost:6379/0", embedder=OpenAIEmbedder(), with_facts=True, use_redisearch=True, # opt-in HNSW )

Install: pip install 'loomflow[redis]'.

Without RediSearch the backend falls back to brute-force cosine; with it, vector recall scales to millions of chunks with sub-10ms latency.

LazyMemory. What memory="postgres://..." actually returns

Agent(...) is synchronous, but PostgresMemory.connect() and RedisMemory.connect() are async. The resolver returns a LazyMemory proxy that opens the connection on the first agent.run:

agent = Agent("...", memory="postgres://...") # no connection yet await agent.run("hi", user_id="alice") # connection opens here

LazyMemory forwards every call to the inner Memory once materialized, so it’s transparent.

Pick a backend by your durability + scale story. Single instance, ephemeral data → InMemory. Single instance, durable → SQLite. Multi-instance or large vector recall → Postgres / Redis. Want hosted vector ANN with metadata filters → Chroma. The agent behaviour is identical across all five.

Last updated on