Skip to content

Memory

Concept

Context is what the model sees this turn. Memory is what survives between turns and between runs. Conflating them produces a system that either forgets everything or drags its entire history into every prompt.

Rewyn splits memory by what it is for. Working and short-term memory hold the current exchange. Episodic memory records what happened and whether it worked. Semantic memory holds facts. Procedural memory holds learned how-to. Each read emits MEMORY_READ and each write MEMORY_WRITE, so a recalled memory that steered an answer is visible in the run.

Minimal example

from rewyn.memory import Memory

memory = Memory()
await memory.remember("Acme prefers invoices over card payments", importance=0.8)

hits = await memory.recall("how does Acme pay?")
print(hits[0].item.content, hits[0].score)

Production example

from rewyn.memory import FileStore, Memory, MemoryKind

memory = Memory(
    name="account-memory",
    namespace=f"tenant:{tenant_id}",  # isolation between customers
    store=FileStore(),  # survives process restarts
)

await memory.remember(
    "Credit limit raised to 250k after the 2026 contract renewal",
    kind=MemoryKind.EPISODIC,
    importance=0.9,
    tags=["acme", "credit"],
)

agent = Agent(model=..., memory=memory)  # recall feeds the context automatically

Attaching memory to an agent adds it as a context source, so relevant items are retrieved and budgeted like any other context. The agent also writes back after each run unless you pass memory_autosave=False.

Namespaces

namespace is the tenant boundary and it is enforced, not advisory. Items are stamped with their namespace on write, and reads, deletes and clears refuse to cross it, so two tenants can share one store safely:

a = Memory(namespace="tenant:a", store=FileStore())
b = Memory(namespace="tenant:b", store=FileStore())

await a.remember("Acme's margin is 42%")
assert await b.recall("margin") == []

Stores that can isolate natively do. FileStore gives each namespace its own file and RedisMemoryStore its own key prefix; anything else is filtered, with the filter applied before the limit so a busy tenant cannot push a quiet one out of its own result page.

Set it from the start. Migrating later means rewriting keys.

API reference

rewyn/memory/memory.py for Memory, MemoryItem, MemoryHit and MemoryKind. rewyn/memory/providers/ for InMemoryStore and FileStore. rewyn/integrations/redis.py for RedisMemoryStore, which is what a fleet of workers needs: a file store is fine on a laptop and useless behind a load balancer. rewyn/memory/episodic.py for record_episode and similar_episodes. rewyn/memory/semantic.py for remember_fact and facts_about.

Failure modes

Memory grows without bound. Nothing expires by default. Set importance honestly and prune on a schedule; recall ranks by relevance and importance, so low-value writes dilute results rather than disappearing.

Recall returns nothing useful. The default scoring is lexical. For semantic recall over a large store, back memory with a retriever instead.

Memories leak across tenants. They should not: namespaces are enforced. If you are seeing crosstalk, check that both sides construct Memory with the namespace rather than reaching into the store directly.

The agent remembers something wrong. Autosave writes the exchange after every run, including the failed ones. Episodic items record success, so filter on it when recalling for guidance.

Nothing persists. The default store is in-memory. Pass store=FileStore() for anything that should outlive the process.