accesspatterns.dev
← All models

ai

Agent Memory Store

One conversation log, read two ways: by recency and by meaning.

An assistant needs the last few messages every turn, and occasionally the one thing said three weeks ago that bears on the question. Those are different reads over the same items. The recency window is a Query on the session; relevance is a vector search partitioned by USER, which is what lets it reach back past the conversation you are already in.

The model

Message

One message in one session, with the vector of its text.

pk
SESSION#<sessionId>
sk
TS#<sentAtIso>

Attributes: userId (S), text (S), embedding (L)

Access patterns

  • QueryRecent messages in a session

    The last few turns of the conversation that is open now.

  • SearchVectors · by-userRelevant messages for a user

    Messages near a topic, across every session that user has had.

Design notes

Partitioned by user, not by sessionmodelling

The SearchSchema declares userId as its HASH. Partition by session instead and relevance can only ever reach inside the conversation you are already in, which is the one place the recency Query has already covered. Partitioning by user is what makes the second read path worth having.

The HASH is not a security boundarysecurity

Fine-grained access control does not apply to SearchVectors, so any principal holding dynamodb:SearchVectors on this index can search any user's partition by changing one string. Real isolation needs separate tables or indexes with separate IAM grants. A per-user partition key is a data-locality mechanism, not a permission.

Four dimensions here, hundreds in productionmodelling

These vectors are hand-authored at four dimensions so the values are readable on the page. A real embedding model emits 384 or more. Nothing about the index changes with dimensionality: it is fixed at creation, it is the only thing the index validates, and it is the only reason a query from the wrong model is ever refused.

Taught in the course

agent-memory

These rows are real and already here. Running an operation against them downloads a DynamoDB-compatible engine, about 850 KB, and executes it in this tab.

Try an example

The last few turns of the conversation that is open now.

PK(pk)
SK(sk)
embedding
text
userId
SESSION#s-101
TS#2026-07-31T00:00:00ZS
[0.91, 0.12, 0.05, 0.03]L
Roll the API out to Kubernetes rather than the VM fleetS
u-adaS
SESSION#s-101
TS#2026-07-31T02:24:00ZS
[0.88, 0.19, 0.07, 0.02]L
The rollout needs a readiness probe or traffic hits pods that are not upS
u-adaS
SESSION#s-102
TS#2026-08-14T00:00:00ZS
[0.08, 0.94, 0.11, 0.04]L
Invoice totals are off by a penny on multi-currency accountsS
u-adaS
SESSION#s-103
TS#2026-08-25T00:00:00ZS
[0.05, 0.09, 0.93, 0.06]L
The settings page collapses to one column below 640 pixelsS
u-adaS
SESSION#s-103
TS#2026-08-25T02:24:00ZS
[0.04, 0.12, 0.90, 0.08]L
Use a minmax track so it reflows instead of snappingS
u-adaS
SESSION#s-201
TS#2026-08-22T00:00:00ZS
[0.07, 0.10, 0.06, 0.95]L
Summarise last quarter's support volume by product areaS
u-linusS

Run an operation to see the raw engine response.

table agent-memorykeys PK / SKitems 6

Transactions, streams, tags and TTL are among the operations the in-browser engine doesn't implement yet.