# How is memory and user context stored and retrieved?

> Open-source personal assistants — a good answer covers: Storage (DB, vector store, files); what is remembered; how it is injected into prompts; summarization.

Canonical page: https://llms-technical-reviews.com/personal-assistants/q/memory/

## Verdict

The two projects store memory very differently.

[OpenBot](/p/openbot/) keeps two kinds. Personal memories are free-text rows in Postgres, deduplicated by a content hash. Bots can propose observed facts that the person confirms or dismisses, and connected apps can be imported. `PersonalMemoryMiddleware`, an AG-UI middleware, injects them as a system message on every run. Conversation threads and learned skills are held by CopilotKit Intelligence, so a large part of the context is outside your database.

[Rakazo](/p/rakazo/) keeps everything in its own Postgres. Messages are stored per thread and old history is compacted into an LLM summary that is injected into later runs. A `MarkdownMemoryStore` holds versioned Markdown documents per user, bot and scope, written and read through `remember`/`recall_memory` tools. Optional semantic providers (Serenity, Supermemory) add vector recall of up to five items, but only once a thread has been compacted. Memory text is passed through secret redaction before it reaches the prompt.

Pick OpenBot if a human-reviewed list of facts is what you want and you accept a hosted thread store. Pick Rakazo for self-contained, portable memory and long threads that need summarisation.

More projects in this category are being researched.

## Per-project answers

### CopilotKit/OpenBot (answered)

**Storage — PostgreSQL.** Personal memories live in the `personalMemories` table in PostgreSQL, accessed through Drizzle ORM (`server/src/db/schema/memory`). The memory store (`server/src/memory/store.ts:16-80`) provides CRUD operations: `list` (up to 500, newest-first), `create`, `formMemory` (Bot-observed facts awaiting human review), `update`, `remove`. Deduplication uses a SHA-256 hash of lowercased content (deleted memories stay forgotten).

**Memory types.** Personal memories are free-text records with optional `sourceApp` and `sourceLink`. Bot-formed memories (`formMemory`) are observed facts the person can confirm or dismiss. A `MemorySource` records where imported data came from (a connected app). Memory import from connected apps is handled by `normalizeConnectorRecords` (`server/src/memory/ingestion.ts:16-80`) which parses JSON responses from external services (searching `items`, `files`, or `results` keys), normalizes records to text, and caps at 50 records or 256KB.

**Injection into prompts.** The `PersonalMemoryMiddleware` (`server/src/memory/tools.ts:29-70`) is an AG-UI `Middleware` that injects the person's memories as both a system message and a context entry on every agent run. The system message id is `"openbot:personal-memory"` — the middleware also deduplicates stale ones. Memory content is loaded asynchronously and injected before the run begins.

**Learning (skills)**. ``CopilotKit Intelligence's Learning system (`server/src/learning/runtime.ts:1-35`) provides learned skills — reusable capabilities published from observed patterns. The runtime exposes `copilotkit_load_skill` and `copilotkit_read_skill_file` tools to agents, backed by CopilotKit Intelligence. A `RemoteLearnedSkillsMiddleware` injects learned skill context into agent runs. Learning targets are per-agent or default, configured through admin settings (`server/src/learning/settings.ts`).


Citations: [server/src/memory/store.ts:16-80](https://github.com/CopilotKit/OpenBot/blob/f4bc60bf9b12c65eb3d7640173f1b3432f6b2a60/server/src/memory/store.ts#L16-L80) · [server/src/memory/tools.ts:29-70](https://github.com/CopilotKit/OpenBot/blob/f4bc60bf9b12c65eb3d7640173f1b3432f6b2a60/server/src/memory/tools.ts#L29-L70) · [server/src/memory/ingestion.ts:16-80](https://github.com/CopilotKit/OpenBot/blob/f4bc60bf9b12c65eb3d7640173f1b3432f6b2a60/server/src/memory/ingestion.ts#L16-L80) · [server/src/learning/runtime.ts:1-35](https://github.com/CopilotKit/OpenBot/blob/f4bc60bf9b12c65eb3d7640173f1b3432f6b2a60/server/src/learning/runtime.ts#L1-L35)

### elie222/rakazo (answered)

**Storage layers.** Rakazo has three memory systems:

1. **Conversation history** — stored as `Message` rows in PostgreSQL (`packages/db/src/messages.ts`), scoped to a `thread`. Each message has blocks (text, tool calls, images) and a sequential `seq`. To manage context windows, history is **compacted** asynchronously via `packages/adapters/src/history-compaction.ts`: messages beyond a sliding window of 50 are removed and replaced with an LLM-generated summary stored in `thread.historyCompactionSummary`. Compaction is triggered when the uncompacted window exceeds the batch size threshold (`shouldEnqueueCompaction`, line 20). The summary is injected as a `user`-role message in subsequent runs (line 6047-6054 of executor.ts).

2. **Markdown memory store** (`packages/memory/src/index.ts` — `MarkdownMemoryStore`): a user-and-bot-scoped key-value store for structured facts. Implements the `MemoryStore` interface from `@rakazo/adapter-kit`. Documents are stored in `memoryDocument` and `memoryRevision` tables. Supports `read` (list docs by scope/path), `commit` (upsert with optimistic concurrency via revision numbers), and `search` (simple substring match on content/path). The agent calls `remember`/`recall_memory` tools to write/read from this store. It uses Prisma transactions with `Serializable` isolation (line 117).

3. **Semantic (vector) memory** — optional providers accessed via `SpaceMemoryProviderResolver` (`packages/adapters/src/memory-provider-factory.ts`). Supports **Serenity** (a self-hostable vector memory service) and **Supermemory** (external SaaS). These providers expose `recall()`, `save()`, and `forget()` operations for semantic similarity search. The memory scope (`isolated` vs `shared`, resolved in `packages/db/src/memory-config.ts`) controls whether a bot sees only its own memories or the whole space's.

**Injection into prompts.** At run start, the executor calls `semanticMemory.recall()` (line 3459 of executor.ts) with the user's prompt as query, getting up to 5 relevant memories. The recalled text is formatted and injected into the prompt's `historicalContext` array (line 6056-6060). Similarly, compacted history summaries are injected as a synthetic user message. All memory content passes through `redactSecrets()` before injection.


Citations: [packages/memory/src/index.ts:28-120](https://github.com/elie222/rakazo/blob/4cdf3e23315c2633b6b3fde8977f197b986f06fa/packages/memory/src/index.ts#L28-L120) · [packages/adapters/src/history-compaction.ts:20-80](https://github.com/elie222/rakazo/blob/4cdf3e23315c2633b6b3fde8977f197b986f06fa/packages/adapters/src/history-compaction.ts#L20-L80) · [packages/adapters/src/executor.ts:3454-3500](https://github.com/elie222/rakazo/blob/4cdf3e23315c2633b6b3fde8977f197b986f06fa/packages/adapters/src/executor.ts#L3454-L3500) · [packages/adapters/src/memory-provider-factory.ts:1-80](https://github.com/elie222/rakazo/blob/4cdf3e23315c2633b6b3fde8977f197b986f06fa/packages/adapters/src/memory-provider-factory.ts#L1-L80)
