Skip to Content
DocsMemoryOverview

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=False to skip).
  • Auto-picked embedder, OpenAIEmbedder("text-embedding-3-small") if OPENAI_API_KEY is set, HashEmbedder() otherwise.
  • user_id partition. 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 LazyMemory proxy; the connection opens on the first agent.run, so Agent(...) stays synchronous.

Read more

Three 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.

Last updated on