Work with memories
A bot keeps what it learns as memories: one fact each, in a sentence that stands on its own, of at most 1,000 characters. Bots save their own during turns. Your app can list them, add its own, change them, and remove them, through fxbot.profiles.memories. How bots use them: Memories.
See what a bot remembers
const newest = await fxbot.profiles.memories.list(bot.id);
for (const m of newest) console.log(`mem:${m.id} ${m.savedBy} ${m.updatedAt.slice(0, 10)} ${m.text}`);The list is newest first, 200 by default, and limit takes up to 500. Each memory has its id, text, savedBy (bot or owner), and when it was made and last changed. A memory the bot saved also has messageId: the stored message of the turn it was saved in, which chat_history reads as msg:<id>. It isn't the messageId your app sent.
To search, give a query. The best matches come first, by words and, with vectors on, by meaning:
const found = await fxbot.profiles.memories.list(bot.id, { query: "coffee or tea" });Add, change, or remove one
const memory = await fxbot.profiles.memories.add(bot.id, "The owner drinks tea, not coffee.");
await fxbot.profiles.memories.update(bot.id, memory.id, "The owner drinks green tea, not coffee.");
await fxbot.profiles.memories.remove(bot.id, memory.id);- Adding text the bot already has, in other case or spacing, answers with the memory already there.
- A change that says what another memory says merges the two: that other memory stays, and comes back.
- An id works as
12or asmem:12. - The bot sees the change on its next turn. Its newest memories are in its instructions on every turn, and
memory_searchfinds the rest.
Text that is empty or over 1,000 characters fails with status 400, and a memory the bot doesn't have with 404.
Watch a bot save its own
During a turn, a bot calls memory_save, memory_search, and memory_forget. They show on the chat's stream as tool calls, with what the bot passed in input, such as the text that memory_save saves:
if (line.type === "tool_start" && line.name?.startsWith("memory_")) console.log(line.name, line.input);Reading the stream: Stream replies.
Choose where vectors come from
Search by meaning needs vectors from an embedding model. Pick where they come from in the settings:
await fxbot.settings.update({ embedder: "local" }); // the default: the embedder at embedUrl
await fxbot.settings.update({ embedder: "remote", embeddingProvider: openai.id, embeddingModel: "text-embedding-3-small" });
await fxbot.settings.update({ embedder: "off" }); // words onlylocaluses the embedder you gavecreateFxbotasembedUrl, such as the one that fxbot's compose file runs.remoteuses a provider's embedding model. The provider must serve embeddings: OpenAI, OpenRouter, AI Gateway, a Vercel account, Ollama, LM Studio, or another OpenAI-style server. The SDK embeds a test string first. If that fails,updatefails with status 400, and the old setting stays.messageVectorsis how many characters of each chat message get a vector, 8,000 by default.0gives vectors to memories only.
A new model means new vectors for everything. They fill in behind your app, memories first, then messages, newest first. Search keeps working meanwhile, with the vectors the model has made, and with words.
Check how far vectors have got
const status = await fxbot.settings.embedding();
console.log(`${status.model ?? "Off"}: ${status.memoriesWithVectors} of ${status.memories} memories, ${status.withVectors} of ${status.messages} messages`);
if (status.error) console.error(status.error);model is null when vectors are off, or when the local embedder isn't answering. error is the last thing that went wrong, until vectors fill in again.
From a page
The client has the same calls over fxbot's HTTP API: client.profiles.memories.list(id, query), add, update, and remove, then client.settings.update(...) and client.settings.embedding(). See Build a page on the client.
If it doesn't work
- The status says this fxbot has no local embedder.
createFxbotgot noembedUrl. Pass it, or pickremoteoroff. - The status says the local embedder isn't answering yet. On its first start, it downloads its model, 310 MB. Until it answers, search uses words alone, and the vectors wait.
updatesays the embedding model didn't answer. The test string failed. Check the model's id, and that the provider serves it. Ollama must have pulled it first, as withollama pull nomic-embed-text.updatesays that provider serves no embeddings. ChatGPT and Grok plans, and OpenCode keys, serve none. Pick another provider.updateasks for a provider and an embedding model.remoteneeds both.