diff --git a/site/src/config/navigation.yml b/site/src/config/navigation.yml index 52b0edae50..1080ad9d8f 100644 --- a/site/src/config/navigation.yml +++ b/site/src/config/navigation.yml @@ -87,6 +87,7 @@ sidebar: items: - label: "Overview" slug: docs/user-guide/concepts/memory/overview + - docs/user-guide/concepts/memory/test-memory-store - docs/user-guide/concepts/memory/bedrock-knowledge-base - docs/user-guide/concepts/agents/retry-strategies - label: Sandbox diff --git a/site/src/content/docs/user-guide/concepts/memory/overview.mdx b/site/src/content/docs/user-guide/concepts/memory/overview.mdx index d051b14bdc..94089a3fc6 100644 --- a/site/src/content/docs/user-guide/concepts/memory/overview.mdx +++ b/site/src/content/docs/user-guide/concepts/memory/overview.mdx @@ -15,7 +15,7 @@ sidebar: By default a Strands agent starts every conversation from zero: it cannot recall a user's preferences, past decisions, or anything it learned in an earlier session. The `MemoryManager` gives an agent long-term memory that persists across sessions. -It works through **memory stores**, the backends that hold the memories. A store can be a vector database, a managed service like [Amazon Bedrock Knowledge Bases](./bedrock-knowledge-base), or [your own implementation](#custom-stores). The manager handles three jobs across the stores you give it: +It works through **memory stores**, the backends that hold the memories. A store can be the built-in zero-setup [test store](./test-memory-store), a managed service like [Amazon Bedrock Knowledge Bases](./bedrock-knowledge-base), or [your own implementation](#custom-stores). The manager handles three jobs across the stores you give it: 1. **Recall** - the agent searches stored knowledge on demand through a tool. 2. **Injection** - the manager folds relevant knowledge into the prompt automatically, before the model runs. @@ -25,7 +25,7 @@ Recall and injection are enabled by default when you attach a store. Extraction ## Getting Started -Attach a memory manager to an agent through the parameter. The examples below use a `store`, a `MemoryStore` you provide; see [Bedrock Knowledge Base](./bedrock-knowledge-base) for a managed backend or [Custom Stores](#custom-stores) to create your own. +Attach a memory manager to an agent through the parameter. The [test store](./test-memory-store) below needs no cloud account and persists to disk by default, so this agent remembers across restarts with no setup. For a managed backend see [Bedrock Knowledge Base](./bedrock-knowledge-base), or [Custom Stores](#custom-stores) to create your own. @@ -33,6 +33,10 @@ Attach a memory manager to an agent through the + + +```python +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])) +``` + + + +```typescript +--8<-- "user-guide/concepts/memory/test-memory-store_imports.ts:basic_imports" + +--8<-- "user-guide/concepts/memory/test-memory-store.ts:basic" +``` + + + +The store is writable by default, unlike a read-only managed store. So the [`add_memory` +tool](./overview#memory-tools) and [automatic extraction](./overview#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](./bedrock-knowledge-base). + +## Persistence and location + +The store writes to `~/.strands/memory/.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: + + + + +```python +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") +``` + + + +```typescript +--8<-- "user-guide/concepts/memory/test-memory-store_imports.ts:persistence_imports" + +--8<-- "user-guide/concepts/memory/test-memory-store.ts:persistence" +``` + + + +Reach for `persist=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. + +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. + +## Configuration + +`TestMemoryStore` takes the [shared `MemoryStore` fields](./overview#stores) (`name`, +`description`, , `writable`, +`extraction`) plus two of its own: + +| Field | Purpose | +|-------|---------| +| `persist` | Whether to write entries to disk. `True` (default) flushes to `path`; `False` keeps entries in memory only, lost when the process exits. | +| `path` | Full path to the backing JSON file. Defaults to `~/.strands/memory/.json`. Ignored when `persist` is `False`. | + +`writable` defaults to `True` here. Construction rejects an empty `name`, an empty `path`, +or a below `1`. + +## Recall + +`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. + +:::note +Recall is keyword matching, not semantic search. A query word matches only the same word, +not a synonym, so "seat" does not find an entry about "chair." For embedding-based +semantic search over a managed vector store, use the [Bedrock Knowledge Base +store](./bedrock-knowledge-base). +::: + +## Writing memories + +`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. + + + + +```python +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("what seat does the user prefer?") +for entry in results: + print(entry.content, entry.metadata.get("_relevanceScore")) +``` + + + +```typescript +--8<-- "user-guide/concepts/memory/test-memory-store_imports.ts:search_and_add_imports" + +--8<-- "user-guide/concepts/memory/test-memory-store.ts:search_and_add" +``` + + + +Writing to a store constructed with , +or adding empty content, raises. When the backing directory is unreachable or not +writable, the write raises with the target path. + +## Extraction + +Enable [automatic extraction](./overview#automatic-extraction) to capture memories from +the conversation without the agent calling a tool: + + + + +```python +from strands.vended_memory_stores.test_memory_store import TestMemoryStore + +store = TestMemoryStore( + name="notes", + extraction=True, # distill facts from the conversation, every 5 turns +) +``` + + + +```typescript +--8<-- "user-guide/concepts/memory/test-memory-store_imports.ts:extraction_imports" + +--8<-- "user-guide/concepts/memory/test-memory-store.ts:extraction" +``` + + + +The store implements but not , 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](./overview#automatic-extraction) on the Memory page. + +## Scale and limitations + +The store keeps every entry in memory and rewrites the whole file on each `add`, using an +atomic write 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](./bedrock-knowledge-base). + +Writes within a single process are serialized, so concurrent `add` calls can't clobber one +another. Writers across separate processes are not coordinated, so avoid pointing two +processes at the same file. A corrupt or wrong-shaped backing file raises with the path so +the problem is visible; a missing file starts empty. + +## Related + +- [Memory](./overview) - the `MemoryManager` concept this store plugs into, including the + tools, extraction, and injection it enables. +- [Bedrock Knowledge Base store](./bedrock-knowledge-base) - the managed `MemoryStore` + with semantic search, for production workloads. diff --git a/site/src/content/docs/user-guide/concepts/memory/test-memory-store.ts b/site/src/content/docs/user-guide/concepts/memory/test-memory-store.ts new file mode 100644 index 0000000000..2b33a358cd --- /dev/null +++ b/site/src/content/docs/user-guide/concepts/memory/test-memory-store.ts @@ -0,0 +1,76 @@ +import { Agent, BedrockModel } from '@strands-agents/sdk' +import { TestMemoryStore } from '@strands-agents/sdk/vended-memory-stores/test-memory-store' + +// ===================== +// Basic: persists to disk by default +// ===================== + +function basic() { + // --8<-- [start:basic] + // Persists to ~/.strands/memory/notes.json by default. Survives restarts. + const store = new TestMemoryStore({ name: 'notes' }) + + const agent = new Agent({ + model: new BedrockModel(), + memoryManager: { stores: [store] }, + }) + // --8<-- [end:basic] + + void agent +} +void basic + +// ===================== +// Persistence: ephemeral and explicit path +// ===================== + +function persistence() { + // --8<-- [start:persistence] + // Ephemeral: nothing is written to disk, and a fresh instance forgets everything. + const scratch = new TestMemoryStore({ name: 'notes', persist: false }) + + // Explicit file location instead of the default under ~/.strands/memory/. + const project = new TestMemoryStore({ name: 'notes', path: './notes.json' }) + // --8<-- [end:persistence] + + void scratch + void project +} +void persistence + +// ===================== +// Search and add +// ===================== + +async function searchAndAdd() { + // --8<-- [start:search_and_add] + const store = new TestMemoryStore({ name: 'notes' }) + + // add returns the id of the stored (or already-present, on dedup) record. + const { id } = await store.add('User prefers aisle seats', { category: 'travel' }) + + const results = await store.search('what seat does the user prefer?') + for (const entry of results) { + console.log(entry.content, entry.metadata?._relevanceScore) + } + // --8<-- [end:search_and_add] + + void id +} +void searchAndAdd + +// ===================== +// Extraction +// ===================== + +function extraction() { + // --8<-- [start:extraction] + const store = new TestMemoryStore({ + name: 'notes', + extraction: true, // distill facts from the conversation, every 5 turns + }) + // --8<-- [end:extraction] + + void store +} +void extraction diff --git a/site/src/content/docs/user-guide/concepts/memory/test-memory-store_imports.ts b/site/src/content/docs/user-guide/concepts/memory/test-memory-store_imports.ts new file mode 100644 index 0000000000..8c62fe7d9d --- /dev/null +++ b/site/src/content/docs/user-guide/concepts/memory/test-memory-store_imports.ts @@ -0,0 +1,18 @@ +// @ts-nocheck + +// --8<-- [start:basic_imports] +import { Agent, BedrockModel } from '@strands-agents/sdk' +import { TestMemoryStore } from '@strands-agents/sdk/vended-memory-stores/test-memory-store' +// --8<-- [end:basic_imports] + +// --8<-- [start:persistence_imports] +import { TestMemoryStore } from '@strands-agents/sdk/vended-memory-stores/test-memory-store' +// --8<-- [end:persistence_imports] + +// --8<-- [start:search_and_add_imports] +import { TestMemoryStore } from '@strands-agents/sdk/vended-memory-stores/test-memory-store' +// --8<-- [end:search_and_add_imports] + +// --8<-- [start:extraction_imports] +import { TestMemoryStore } from '@strands-agents/sdk/vended-memory-stores/test-memory-store' +// --8<-- [end:extraction_imports]