Skip to content

Test Memory Store

TestMemoryStore is a MemoryStore backed by a JSON file. It requires no setup and no provisioned resources, so you can give an agent memory in one line and try recall, injection, and extraction for prototyping or writing tests.

It persists to disk by default, so an agent remembers across restarts with no setup. Point it at a store name and go:

from strands import Agent
from strands.memory import MemoryManager
from strands.vended_memory_stores.test_memory_store import TestMemoryStore
# Persists to ~/.strands/memory/notes.json by default. Survives restarts.
store = TestMemoryStore(name="notes")
agent = Agent(memory_manager=MemoryManager(stores=[store]))

The store is writable by default, unlike a read-only managed store. So the add_memory tool and automatic extraction work as soon as you enable them, with no data source to set up first.

It suits prototyping and tests, not a production corpus: each write rewrites the whole file and recall is keyword matching rather than semantic. For a production backend, use the Bedrock Knowledge Base store.

The store writes to ~/.strands/memory/<store-name>.json, deriving the filename from name. Give it an explicit path to control where the file lands, or turn persistence off for a store that lives only in memory:

from strands.vended_memory_stores.test_memory_store import TestMemoryStore
# Ephemeral: nothing is written to disk, and a fresh instance forgets everything.
scratch = TestMemoryStore(name="notes", persist=False)
# Explicit file location instead of the default under ~/.strands/memory/.
project = TestMemoryStore(name="notes", path="./notes.json")

Reach for persist=Falsepersist: false in tests and throwaway runs where you want recall and ranking without leaving a file behind. The default (persist to disk) is what demonstrates the feature: memory that survives a restart.

Persistence runs on the SDK’s Storage interface: persist=Falsepersist: false uses an in-memory backend, and persisting to disk uses a local-file backend. The config above is all you configure; the store selects the backend for you.

The on-disk format is shared between the Python and TypeScript SDKs. Records use the same keys and timestamp shape, so a file written by one SDK reads in the other.

TestMemoryStore takes the shared MemoryStore fields (name, description, max_search_resultsmaxSearchResults, writable, extraction) plus two of its own:

FieldPurpose
persistWhether to write entries to disk. On by default: flushes to path. When off, entries stay in memory only and are lost when the process exits.
pathFull path to the backing JSON file. Defaults to ~/.strands/memory/<store-name>.json. Ignored when persistence is off.

writable is on by default here. Construction rejects an empty name, an empty path, or a max_search_resultsmaxSearchResults below 1.

search ranks entries by lexical overlap: it counts how many distinct words from the query appear in each entry’s content, and returns the highest scorers first, breaking ties toward the most recent entry. Each result carries the count under a reserved _relevanceScore metadata key. A query with no usable words returns nothing.

add stores a single piece of content and returns its id. Identical content is deduplicated: a repeat write returns the existing record’s id instead of storing a second copy, so the at-least-once retries that extraction may perform never accumulate duplicates.

from strands.vended_memory_stores.test_memory_store import TestMemoryStore
store = TestMemoryStore(name="notes")
# add returns the id of the stored (or already-present, on dedup) record.
result = await store.add("User prefers aisle seats", {"category": "travel"})
print(result.id)
results = await store.search("which seats does the user prefer?")
for entry in results:
print(entry.content, entry.metadata.get("_relevanceScore"))

Writing to a store constructed with writable=Falsewritable: false, or adding empty content, raises. When the backing directory is unreachable or not writable, the write raises, naming the backing file.

Enable automatic extraction to capture memories from the conversation without the agent calling a tool:

from strands.vended_memory_stores.test_memory_store import TestMemoryStore
store = TestMemoryStore(
name="notes",
extraction=True, # distill facts from the conversation, every 5 turns
)

The store implements addadd but not add_messagesaddMessages, so extraction runs client-side: a ModelExtractor distills facts from the conversation and writes each through add. To change the cadence or swap the extractor, see Automatic Extraction on the Memory page.

Each add reads the current file and rewrites it in full, replacing it atomically so a crash mid-write never leaves a partial file. That design suits prototyping and personal memory, hundreds to low thousands of entries, not a production corpus. For large or high-throughput workloads, use a managed store like the Bedrock Knowledge Base store.

Concurrent add calls on one store instance are serialized, so they can’t clobber one another. Separate instances or processes writing the same file are not coordinated, so avoid pointing two at the same file: the last write wins. A corrupt or wrong-shaped backing file raises rather than returning bad data; a missing file starts empty.

  • Memory - the MemoryManager concept this store plugs into, including the tools, extraction, and injection it enables.
  • Bedrock Knowledge Base store - the managed MemoryStore with semantic search, for production workloads.