Memory for an AI agent

TeselaDB

TeselaDB

TeselaDB team

25 minIntermediate

Store what an agent learns about each user with its vector and recall the closest memories to the current conversation.

An agent that remembers each user stores short sentences, such as a preference or a fact about their project, and in each conversation recalls the ones closest to what is being discussed. In TeselaDB, those memories are documents with their vector, a vector index with a filter by user and a TTL index that forgets them after a while.

What you will build

A memories collection in the agent database with a vector index filtered by userId and a query that returns a user's two memories closest to the conversation. Memories older than 90 days are deleted on their own.

Requirements

  • mongosh and a user with readWrite on the agent database.
  • An embedding model for your texts. The examples use three-dimension vectors written by hand so you see the exact result; with a real model, numDimensions is the size of its vectors.

1. Store the memories

Each memory carries the user, the text, its vector and the date. Two of them belong to Ana and one to Luis, whose vector is close to one of Ana's, so later you can check that a search for one user never returns the memories of the other:

JavaScript
db.memories.insertMany([
  { userId: "ana", text: "Prefers answers in Spanish", embedding: [0.9, 0.1, 0.0], createdAt: new Date() },
  { userId: "ana", text: "Works with a three-node cluster", embedding: [0.1, 0.9, 0.1], createdAt: new Date() },
  { userId: "luis", text: "Prefers short answers", embedding: [0.8, 0.2, 0.1], createdAt: new Date() },
]);

2. Create the vector index

The userId field goes in as a filter, so a search never mixes the memories of two users. The loop waits until the index can be queried:

JavaScript
db.memories.createSearchIndex({
  name: "memory",
  type: "vectorSearch",
  definition: {
    fields: [
      { type: "vector", path: "embedding", numDimensions: 3, similarity: "cosine" },
      { type: "filter", path: "userId" },
    ],
  },
});
while (!db.memories.getSearchIndexes("memory")[0]?.queryable) sleep(100);

3. Forget what is old

A TTL index on createdAt deletes each memory 90 days after it was stored:

JavaScript
db.memories.createIndex({ createdAt: 1 }, { expireAfterSeconds: 60 * 60 * 24 * 90 });

Try it

Look for Ana's two memories closest to the conversation's vector, [1, 0, 0] in this example. With exact: true the search compares with every vector and gives the same result on any replica. The filter keeps it inside Ana's memories:

JavaScript
db.memories.aggregate([
  {
    $vectorSearch: {
      index: "memory",
      path: "embedding",
      queryVector: [1, 0, 0],
      exact: true,
      limit: 2,
      filter: { userId: "ana" },
    },
  },
  { $project: { _id: 0, text: 1 } },
]);
[
  { text: 'Prefers answers in Spanish' },
  { text: 'Works with a three-node cluster' }
]

Luis's memories do not show up even though his vector is close, because the filter leaves the other users out. If your agent uses MCP, the aggregate tool of the MCP server runs this same query.

Clean up

JavaScript
db.memories.drop();

Dropping the test collection also drops its two indexes, the vector one and the TTL one.

Next steps