Memory
Memory holds an agent’s conversational state: episodes (the past
conversations), working blocks (per-user scratch data), and Facts
(structured (subject, predicate, object) triples extracted from
chats). One Agent + one Memory partitions automatically by
user_id. No app-side namespace plumbing.
from loomflow import Agent
# In-memory (default; lost on restart)
agent = Agent("...", memory="inmemory")
# Single-file SQLite (persistent, no server)
agent = Agent("...", memory="sqlite:./bot.db")
# Chroma (ephemeral / persistent)
agent = Agent("...", memory="chroma")
agent = Agent("...", memory="chroma:./chroma-db")
# Postgres + pgvector
agent = Agent("...", memory="postgres://user:pw@localhost/jeeves")
# Redis (with optional RediSearch HNSW vector index)
agent = Agent("...", memory="redis://localhost:6379/0")Pass user_id= to agent.run and the framework partitions every
recall and write automatically:
await agent.run("Hi, I'm Alice and I live in Tokyo.", user_id="alice")
await agent.run("Hi, I'm Bob.", user_id="bob")
# Alice's lives_in fact never surfaces in Bob's recall.What ships by default
- Auto fact extraction. Every
agent.run()against a real model auto-pulls structured(subject, predicate, object)claims into the bi-temporal fact store. On by default for Anthropic / OpenAI / LiteLLM models. - Auto-attached fact store. The resolver wires the fact store
for every backend automatically (pass
with_facts=Falseto skip). - Auto-picked embedder,
OpenAIEmbedder("text-embedding-3-small")ifOPENAI_API_KEYis set,HashEmbedder()otherwise. user_idpartition. Every backend honours the multi-tenant contract. One shared file or pool serves N users.- Lazy connect for async backends. Postgres / Redis URLs return
a
LazyMemoryproxy; the connection opens on the firstagent.run, soAgent(...)stays synchronous.
Read more
Embedder protocol for custom backends.Embeddersrecall_scored(). BM25 + vector + RRF with score breakdown. Native on InMemory + VectorMemory; protocol-coherent across all backends.Hybrid recallHow supersession works. Querying historical state at a past valid_at.Bi-temporal factsprofile() / forget() / export(). Right-to-be-forgotten + DSAR by default.GDPR opsThree ways to construct memory
1. URL string. The resolver picks defaults
agent = Agent("...", memory="postgres://localhost/jeeves")2. Dict form for tweaks
agent = Agent("...", memory={
"backend": "chroma",
"path": "./chroma-db",
"namespace": "tenant_a",
"embedder": "openai-large",
"with_facts": True,
})3. Explicit instance for full control
from loomflow.memory.chroma import ChromaMemory
from loomflow.memory.embedder import OpenAIEmbedder
memory = ChromaMemory.local(
"./chroma-db", with_facts=True, embedder=OpenAIEmbedder()
)
agent = Agent("...", memory=memory)Tools see the active scope automatically
Inside any tool, get_run_context() returns the live RunContext:
from loomflow import get_run_context, tool
@tool
async def my_orders() -> list[dict]:
ctx = get_run_context()
return await db.query("orders", user_id=ctx.user_id)You don’t need to thread user_id through every tool signature.
Memory vs Vector stores. Memory is for conversational state ,
episodes, working blocks, and Facts built up over time. VectorStore
(see RAG) is for arbitrary document corpora. What your
agent retrieves from but doesn’t build up. Different primitives,
different backends.