Architecture
How the fishmem engine works inside — the write path, the read path, the bi-temporal model, and how the cloud layers on top.
The engine has two paths. The write path runs when you call add: it turns
conversation into durable facts and commits them coherently. The read path
runs when you call search: it recalls the relevant facts using four signals at
once. Underneath both is a bi-temporal model that keeps superseded facts
instead of overwriting them.
Write path
add does not append text. It extracts facts, reconciles them against what is
already stored, and persists the survivors to both stores.
messages / content
│
▼
┌───────────────┐ LLM extracts durable atomic facts
│ 1. EXTRACT │ (infer:false skips this — stores content as-is)
└───────┬───────┘
▼
┌───────────────┐ for each candidate fact, pull semantically
│ 2. RECONCILE │ close memories and decide one of:
│ │ ADD new fact
│ │ UPDATE refine an existing fact
│ │ INVALIDATE supersede a contradicted fact
│ │ NOOP already known
└───────┬───────┘
▼
┌───────────────┐ graph store: entities + relations
│ 3. PERSIST │ vector index: embeddings (text-embedding-3-small)
└───────────────┘Extract. A gpt-4o-mini-class model reads the messages and pulls out atomic
facts worth keeping, dropping chit-chat. With infer: false, this step is
skipped and the content is stored verbatim.
Reconcile. This is the step that keeps the store from rotting. Rather than blindly appending, the engine compares each candidate fact to the existing memories nearest to it and emits ADD, UPDATE, INVALIDATE, or NOOP. Contradicted facts are invalidated, not deleted.
Persist. Surviving facts are written to the graph store (entities and the relations between them) and embedded into the vector index. Keeping both is what lets recall combine meaning with structure.
For a narrative walk-through, see How memory works.
Read path
search does not run a single nearest-neighbor lookup. It blends four signals
and returns a ranked, scoped result set.
- Vector similarity — embeddings find facts that mean the same thing as the query, even with different words.
- Keyword match — exact terms (names, ids, codes) that embeddings can blur are matched directly.
- Temporal ordering — recency and valid-time order results so the current truth surfaces ahead of stale facts.
- Graph diffusion — relevance spreads across connected entities in the graph, so a hit on one fact pulls in related facts that a pure vector search would miss.
See Hybrid recall for how the signals combine.
Bi-temporal model
FishMem tracks two timelines for every fact:
- Valid-time — when the fact was true in the world.
- Record-time — when the engine recorded it.
When a fact is superseded, the old one is invalidated, not deleted — it stays queryable with its valid-time range closed off. This is what makes questions like "what did the user prefer last month?" answerable, and it is why the engine can correct itself without losing history. See Bi-temporal facts.
How FishMem Cloud layers on top
FishMem Cloud does not run different memory code. It runs this same engine and adds an operational layer around it:
- API keys authenticate requests to the mem0-compatible REST API.
- Workspace isolation tags every memory with a workspace marker
(
metadata.__ws) so tenants never see each other's data. - Usage metering and credits count operations for billing.
- Webhooks notify your systems on memory events.
- Billing and dashboard handle plans, payment, and a UI over the same data.
The graph store, vector index, extraction, reconciliation, and recall are identical to what you get self-hosting. The cloud sells operations, not a different engine.
Benchmarks
On a shared open harness against mem0 OSS (TS), 233 held-out questions:
| Metric | FishMem | mem0 OSS |
|---|---|---|
| Overall recall | 62.2% | 54.5% |
| Temporal | 69.8% | 28.3% |
| Multi-hop | 54.8% | 57.1% |
mem0 leads on multi-hop. Search p50 is ~420ms. The harness and the LLM judge are open-sourced so you can reproduce these numbers.