Overview
Memory Soda gives an LLM application durable memory about its users.
You send it conversations. It works out which statements are worth keeping, resolves them against what it already believes, and gives you back a prompt-ready block of text before your next model call.
┌──────────────┐ messages ┌──────────────┐ facts ┌──────────────┐│ Your app │ ────────────► │ Memory Soda │ ─────────► │ Postgres ││ │ ◄──────────── │ │ ◄───────── │ + pgvector │└──────────────┘ context └──────────────┘ retrieval └──────────────┘The problem it solves
Section titled “The problem it solves”An LLM has no memory between calls. The usual fixes both break down:
- Send the whole history every turn. Costs grow linearly, latency grows with them, and past a few thousand turns it stops fitting at all.
- Embed everything and do RAG over the transcript. You retrieve messages, not knowledge. “I drive a Honda Civic” and “I switched to a Model 3 last month” both come back with similar scores, and the model has to guess which is current.
Memory Soda stores claims, not transcripts. Each claim carries the window of time it is true for, so a contradiction supersedes rather than accumulates.
What you get
Section titled “What you get”A prompt-ready context block. Not embeddings, not JSON you have to format, a string you paste into your system prompt.
Known facts about the user, most relevant first.
# FACTS (format: fact (valid: from – to))- user is interested in toyota corolla hybrid (valid: 2026-08-15 – present)- user finds too big suvs (valid: 2026-08-15 – present)- user drives honda civic (valid: 2026-03-01 – present)
# ENTITIES- toyota corolla hybrid (PRODUCT)- honda civic (PRODUCT)Contradiction handling. When a user says something that conflicts with a stored fact, an LLM judge decides which one survives. The loser is marked superseded, not deleted, the history stays queryable.
Time-travel queries. asOf returns what the system believed at any past
instant. Useful for auditing, debugging and “why did the agent say that?”.
A dashboard you can actually debug with. Every extracted fact, every entity, every episode, and a playground that shows the real HTTP traffic of the pipeline as it runs.
What it is not
Section titled “What it is not”Be clear about this before adopting it.
| Not | Because |
|---|---|
| A vector database | It uses pgvector internally, but you don’t put documents in it and get chunks out. |
| A RAG pipeline over your docs | There is no document ingestion. The only input is conversation messages. |
| A hosted service | Self-host only. You run the Postgres and you bring the model API key. |
| A general knowledge store | Extraction is deliberately constrained to facts about the user. See below. |
| Multi-model | Gemini is currently wired in directly. See Known limitations. |
When to use it
Section titled “When to use it”Good fit
- A chat product where the assistant should remember a returning user
- A support agent that should not re-ask what it was told last week
- Anything where user preferences, constraints and decisions accumulate over many sessions
- Cases where you need to explain why the model knew something, provenance is stored for every fact
Poor fit today
- Multi-agent workflows that need to record what an agent learned, not what a user said, every extracted fact must have the user as its subject
- Anything needing sub-second write-to-readable latency; extraction is asynchronous and takes tens of seconds
- Knowledge-base search over documents
The three layers
Section titled “The three layers”Memory Soda derives three kinds of memory from the same message stream. You mostly interact with the first and third.
| Layer | Holds | Read with | Cost |
|---|---|---|---|
| Working | the live conversation window, auto-compacted | prepare() |
pure SQL, milliseconds |
| Episodic | a summary of each chunk of a thread | recall({ include: ['episodes'] }) |
one vector search |
| Semantic | durable subject–predicate–object facts | recall() |
embedding + three parallel queries |
Read How it works for the full flow.
Known limitations
Section titled “Known limitations”Documented honestly, because you will hit these.
No write API. There is no add() that takes a fact. Memory can only be
derived from messages appended to a thread. If you already know something
about a user, you cannot tell the system directly.
Every fact must be about the user. Extraction enforces that the subject of
every fact is the literal string user. Facts about a project, a codebase, a
task or an agent are dropped by design. This makes it a personal-memory store,
not a general agent-memory store.
Extraction is deferred. A statement becomes retrievable once its thread is
ended, superseded by a new thread for the same dataset, or idle for 30 minutes,
then an episode summarisation call, a graph extraction call and a
contradiction-judging call run. Call endThread() when you want it sooner.
Scope is (project, dataset) only. There is no notion of an agent or a run,
so there is nowhere to put memory belonging to a particular workflow execution.
No bulk delete. Facts can be soft-deleted one at a time. There is no
DELETE /datasets/:id for a full erasure request. See
Privacy and data deletion.
Gemini is hard-wired. GOOGLE_GENERATIVE_AI_API_KEY is required to boot at
all, the model IDs are constants, and the 768-dimension embedding size is baked
into the schema.
Memory never shrinks. There is no forgetting, decay or consolidation pass. Superseded facts stay in the table indefinitely.
- How it works, the write path and the read path in detail
- Installation, get it running