Personal assistants that remember the person, not just the chat
Preferences, plans and personal details survive the end of a conversation. Every fact comes back tagged none, low, pii or sensitive, so you choose what reaches a hosted model.
npm install actrone-memory · pip install actrone-memory
Every conversation starts cold
Users repeat themselves
Their language, their diet and how brief they like answers have to be restated in every new conversation.
Replaying history is expensive
Sending every past conversation to find one preference costs tokens on each request and still misses things.
Personal details need handling
An assistant hears about health, money and family. Treating that like any other text is how it ends up somewhere it should not.
Four steps, one scope per user
user:42. Conversations are sessions inside it: they come and go, and what you remembered about the user stays.Remember what the user tells you
injectMemory, in Python inject_memory
Keep a fact under the user’s scope with the sensitivity you judge it to have: a language preference is low, an allergy is sensitive.
Keep each conversation
storeTurn, in Python store_turn
Records each exchange in the conversation’s session, so the next reply in the same conversation sees it.
Close the conversation, keep the person
clearSession, in Python clear_session
Drops the conversation’s turns. Long-term facts stay for the next conversation.
Recall with tags attached
retrieveContext, in Python retrieve_context
Returns the relevant facts, each with its sensitivity, so you decide what a hosted model sees and what stays out.
The whole integration, in both languages
sensitive and that one user never sees another’s facts.import { MemoryManager, type Sensitivity } from "actrone-memory";
// One scope per user: everything below is recalled for this user only.
const scopeFor = (userId: string) => `user:${userId}`;
// Keep something the user told you, with the sensitivity you judge it to have.
export async function rememberAboutUser(
memory: MemoryManager,
userId: string,
fact: string,
sensitivity: Sensitivity = "low",
) {
return memory.injectMemory(scopeFor(userId), fact, 0.9, "profile", [], "user", sensitivity);
}
// Each conversation is a session: its turns feed the next reply in the same conversation.
export async function recordTurn(
memory: MemoryManager,
userId: string,
conversationId: string,
userMessage: string,
reply: string,
) {
await memory.storeTurn(scopeFor(userId), conversationId, userMessage, reply);
}
// Recall for a new message; each fact keeps its tag, so you decide what the model sees.
export async function recallFor(
memory: MemoryManager,
userId: string,
conversationId: string,
message: string,
) {
const context = await memory.retrieveContext(scopeFor(userId), conversationId, message, 1500);
return {
facts: context.episodicMemories.map((m) => ({ content: m.content, sensitivity: m.sensitivity })),
recentTurns: context.recentTurns.length,
};
}
// Closing a conversation drops its turns. What you remembered about the user stays.
export async function endConversation(memory: MemoryManager, userId: string, conversationId: string) {
await memory.clearSession(scopeFor(userId), conversationId);
}from dataclasses import dataclass
from actrone_memory import MemoryManager, Sensitivity
def scope_for(user_id: str) -> str:
"""One scope per user: everything below is recalled for this user only."""
return f"user:{user_id}"
async def remember_about_user(
memory: MemoryManager, user_id: str, fact: str, sensitivity: Sensitivity = "low"
) -> str:
"""Keep something the user told you, with the sensitivity you judge it to have."""
return await memory.inject_memory(
agent_id=scope_for(user_id),
content=fact,
importance=0.9,
session_id="profile",
source="user",
sensitivity=sensitivity,
)
async def record_turn(
memory: MemoryManager, user_id: str, conversation_id: str, user_message: str, reply: str
) -> None:
"""Each conversation is a session: its turns feed the next reply in the same conversation."""
await memory.store_turn(
agent_id=scope_for(user_id),
session_id=conversation_id,
user_message=user_message,
assistant_message=reply,
)
@dataclass
class Recall:
facts: list[tuple[str, Sensitivity]]
recent_turns: int
async def recall_for(
memory: MemoryManager, user_id: str, conversation_id: str, message: str
) -> Recall:
"""Recall for a new message; each fact keeps its tag, so you decide what the model sees."""
context = await memory.retrieve_context(
agent_id=scope_for(user_id), session_id=conversation_id, query=message, token_budget=1500
)
return Recall(
facts=[(m.content, m.sensitivity) for m in context.episodic_memories],
recent_turns=len(context.recent_turns),
)
async def end_conversation(memory: MemoryManager, user_id: str, conversation_id: str) -> None:
"""Closing a conversation drops its turns. What you remembered about the user stays."""
await memory.clear_session(scope_for(user_id), conversation_id)Create the manager once and pass it in: await MemoryManager.create() in TypeScript, or async with create_memory_manager() as memory: in Python.
What this does not do for a personal assistant
It does not decide what is sensitive
Tags come from you, or from fact extraction with a model you configure. A hosted extraction model sees the raw text.
It does not resolve contradictions
If a user moves city, both facts stay until you delete the old one with
deleteMemory. It stores and recalls; it does not reason about which fact is current.Recall matches shared words until you add a model
With no extra packages the embedder compares words, so “Write a short note in British English” finds the British English preference because the words overlap. Install
fastembedin TypeScript or theonnxextra in Python for recall by meaning, still on your machine.In-process until you add a store
The default store lives in your process, so a restart forgets everything. Pass the Redis, Postgres or Qdrant adapters when memory has to outlive a deploy. The calls in the code do not change.
It runs where your code runs
The libraries need Node.js 22 or newer, or Python 3.11 or newer. A mobile or browser assistant needs a backend to hold its memory.