Start with a concrete case. A restaurant-booking agent talks with the same user for half a year: in January, the user lives in Beijing and usually chooses vegetarian restaurants with friends; in March, they say they have started eating seafood; in April, they move to Paris; in June, they ask, "Find a restaurant for this weekend, and remember my current situation"; in July, they ask, "What kind of place did I usually choose with friends before?" The agent cannot just replay chat history. It has to know which facts describe the current preference, which facts are historical, and which changes are worth preserving as changes.

Three direct designs are tempting. Put the whole half-year conversation into the prompt: it works briefly, then costs too much and lets old facts distract the answer. Maintain a single user profile and overwrite old values: the current state looks clean, but historical questions lose their evidence. Store every fact in a vector database and retrieve top-k: implementation is simple, but duplicates, conflicts, relationships, and time still need handling. Mem0 is interesting because it carries those direct designs one step further: memory becomes a separate layer that can extract searchable facts from conversation, then return a small set of relevant memories to the model before answering.

In an application architecture, Mem0 sits between the app and the model. The app sends conversation, user scope, and agent scope into Mem0; Mem0 turns durable information into memory records; when the model answers, the app retrieves a small set of relevant memories and places them back into context. That position explains why it became a central memory-layer option for agents: teams do not have to stuff all history into the prompt or rebuild extraction, deduplication, and retrieval logic from scratch. Several Mem0 posts circle the same boundary: the context window is closer to RAM than storage; a larger context window still does not replace persistent memory; and a memory file tends to mix evidence, preferences, temporary state, and retrieval policy. Together they point to one engineering stance: a memory layer is valuable because it separates preservation, retrieval, temporal interpretation, and ranking policy. Mem0 v3, Temporal Reasoning, and Memory Decay continue along that line.

Reading contract. By the end, you should be able to replay the restaurant example across four moves: how paper mem0 uses similar old memories and an LLM to decide ADD / UPDATE / DELETE / NOOP, why mem0g moves entity relations into a graph, why v3 makes writes thinner and shifts judgment into read, and how Temporal Reasoning and Memory Decay answer “which date frames this answer” and “which recently used memories should rank higher.”

Evidence boundary. This article separates four layers. The original Mem0 paper explains paper mem0 and mem0g. Mem0's official blogs and docs define the public Platform v3 contract. The public mem0ai/mem0 source explains the visible OSS and Python SDK paths. Platform service internals are not public, so Temporal Reasoning and Memory Decay are described as documented contracts, not as reconstructed server implementation.

The rest of the article keeps returning to this restaurant case. First, paper mem0 decides ADD, UPDATE, DELETE, or NOOP during write. Then mem0g explains why entities and relationships became a graph path. Then v3 moves toward ADD-only writes, entity indexing, and hybrid retrieval. Temporal Reasoning and Memory Decay follow naturally: one asks which date frames the history, and the other asks whether recently reused memories deserve a softer ranking boost.

1. One Problem, Three Memory Routes

Two naming lines are easy to mix together. The paper talks about mem0 and mem0g; product and SDK migration material talks about the older v2 surface and v3 behavior. This article avoids forcing them into an official "v1, v2, v3" sequence and reads them as algorithmic tradeoffs: if a user's preferences and location change over time, when should the system merge old facts, when should it preserve relationships explicitly, and when should it append new evidence first? Once that route is clear, ADD-only, Temporal Reasoning, and Memory Decay become layers answering the same problem rather than isolated feature names.

Name Better Reading Do Not Read It As
paper mem0 The base paper algorithm: extract new memories, then choose ADD/UPDATE/DELETE/NOOP against similar old memories. The official product "v1". The public materials do not name it that way.
paper mem0g The graph-augmented paper algorithm: explicitly extract entities and relationships, then use a graph database. The official product "v2". It is better read as a graph variant beside mem0.
v2 API / SDK The older product surface in the migration materials: add could return ADD/UPDATE/DELETE events, with graph-memory options such as enable_graph and graph_store. Only mem0g. v2 is a product/API/SDK behavior set, not the paper algorithm name.
mem0 v3 The April 2026 algorithm surface: single-pass ADD-only writes, built-in entity linking, hybrid retrieval, Temporal Reasoning, and Memory Decay. A rename of mem0g. v3 changes writes, relationship handling, and retrieval ranking together.
Mem0 algorithm evolution showing paper mem0 making write-time decisions on old memories, paper mem0g adding a graph sidecar beside text memory, and mem0 v3 using ADD-only writes, linked ids, and retrieval fusion to build a model view
Read the figure by where decisions happen: paper mem0 edits old memories during write, mem0g adds a graph sidecar, and v3 appends and links first before retrieval builds the current model view.

1.1 paper mem0: Clean Up Old Memory During Write

Focus on one event first: in March, the user says, "I have also started eating seafood." The store already has "the user usually chooses vegetarian restaurants with friends." Should the new fact be added as a separate memory? Should it update the old preference? Should the old preference remain because it is historical evidence? Paper mem0 answers by cleaning up old memory during write so the memory set stays compact.

Before Write New Conversation Question paper mem0 Tries to Answer
User usually chooses vegetarian restaurants with friends. User recently started eating seafood. Is this a new preference, an extension, or a replacement?
User lives in Beijing. User moved to Paris in April. Should the old location update, or remain as history?
User likes quiet small restaurants. User dislikes noisy bars. Is this the same preference made more specific, or a separate fact?

The base algorithm in the original paper has two main steps. First, the LLM extracts candidate memories from the latest conversation and recent context. Second, for each candidate, the system retrieves similar existing memories, then asks the LLM to compare old and new facts and choose ADD, UPDATE, DELETE, or NOOP. Similarity is not the final decision; it is how the system brings likely related old memories in front of the LLM. The paper describes this as retrieving the top-s semantically similar memories, and its experiments use s = 10. In other words, the LLM sees a small retrieved set for each candidate, not the whole memory store.

paper mem0 write path:

new conversation
  -> LLM extracts candidate memories
  -> embed each candidate
  -> vector search retrieves top-s similar existing memories
     (paper experiments use s = 10)
  -> LLM compares candidate with those retrieved old memories
  -> ADD / UPDATE / DELETE / NOOP changes the memory store

"Similar" has two responsibilities. Vector search handles recall: the candidate becomes an embedding, and vector similarity brings back old memories that may matter. Those recalled records are still only candidates; top-s is also a context-budget valve so the prompt does not receive every old memory. The LLM makes the actual decision: duplicate, more specific, contradictory, or new. The visible OSS DEFAULT_UPDATE_MEMORY_PROMPT follows the same shape: compare new facts with existing memory, then choose one of four operations. Its NONE is the same no-op idea this article calls NOOP.

DEFAULT_UPDATE_MEMORY_PROMPT, simplified:

new fact + similar existing memories
  -> ADD     the old memory does not contain the fact
  -> UPDATE  the same subject changed, or the new fact is richer
  -> DELETE  the new fact negates old memory, or the user asks to remove it
  -> NONE    already present, irrelevant, or not worth writing
Relation Between New Fact and Old Memory Operation Restaurant-case Reading
The old memory does not contain this fact ADD The old memory only has restaurant preference; the new fact is the move to Paris.
Same fact, different wording NOOP / NONE Old: likes quiet restaurants. New: prefers places that are not noisy.
Same subject or preference, but the new fact is richer or the value changed UPDATE Old: usually eats vegetarian. New: now also accepts seafood.
The new fact explicitly negates old memory, or the user asks to remove it DELETE Old: likes seafood. New: do not recommend seafood anymore.

The UPDATE row is important. Paper mem0 is trying to keep the memory set compact, so it may fold a preference extension or change into the same preference memory. v3 takes a different route: do not fold too early; preserve the change as new evidence first.

This naturally answers why a highly similar fact is not automatically a duplicate ADD. Paper mem0 first uses embeddings to retrieve similar old memories, then lets the LLM decide whether to add, update, delete, or do nothing. That is still not a database-level uniqueness guarantee. If vector search misses the right old memory, or if the LLM misreads a paraphrase as a new fact, duplicate ADD can happen. The value of s is part of the tradeoff: too small can miss the old memory that should be compared; too large increases LLM context, cost, and latency. The upside is a compact memory set. The cost is that write-time UPDATE and DELETE can weaken historical evidence. If the user later asks why they used to choose vegetarian restaurants more often, an overwritten or deleted version may no longer be available.

1.2 paper mem0g: Pull Relationships Out of Text Memories

Add a relationship problem to the same restaurant case. The user says, "I often eat out with Alice." Later they say, "Alice is allergic to shellfish." Later still they ask, "What should I avoid when booking dinner with Alice in Paris this weekend?" Text memories can store those sentences, and vector similarity can retrieve some nearby content. But it has no built-in object map: who Alice is, how she relates to the user, and how shellfish allergy should affect seafood recommendations are all buried in separate text records. Mem0g adds a structured path beside text memory: memory records still go into the vector store, while entities and relationships also go into a graph store. The graph is not a replacement for text memory; it is an index for "which objects are connected."

Step Text-memory Path Graph Path
Write Extract facts such as "user often eats with Alice" and "Alice is allergic to shellfish" into the vector memory store. Also extract entities such as Alice, shellfish allergy, and Paris, plus relationship edges.
Read Use semantic similarity to retrieve memory records related to the query. Start from entities in the query or recalled memories, then traverse graph neighbors and relationships.
Merge Place semantically relevant text facts into the candidate context. Add structured facts about Alice and dietary constraints to the same candidate context.
mem0g can be read as two parallel write paths:

message
  -> memory facts -----------------> vector memory store
  -> entities + relationships -----> graph store

search(query)
  -> vector memories
  -> graph neighbors around matched entities
  -> merged context for the model
Minimal graph example:

User  --eats_with----> Alice
Alice --allergic_to--> shellfish
User  --located_in---> Paris

In that design, entities and relationships really are graph-database objects. The paper represents mem0g memory as a directed labeled graph: entities are nodes, relationships are edges or triplets, and Neo4j is the underlying graph database. Read does not rely only on exact node-name lookup. It uses two paths: entity-centric retrieval finds key query entities, locates corresponding graph nodes, and traverses incoming and outgoing edges; semantic triplet retrieval embeds relationship triplets and matches them against the query embedding.

mem0g graph retrieval:

query: "What should I avoid for dinner with Alice in Paris?"

entity-centric:
  Alice / Paris -> graph nodes
  -> incoming + outgoing edges
  -> Alice --allergic_to--> shellfish
  -> User --located_in--> Paris

semantic triplet:
  query embedding
  -> match encoded triplets
  -> rank relevant relationships above threshold

This handles a different recall failure: two facts may not be close as sentences, but they should appear together because they share an entity or relationship path. "Alice is allergic to shellfish" and "the user wants a seafood restaurant in Paris" are not paraphrases, yet a recommendation should consider both. The graph route lets retrieval follow the Alice node to the allergy constraint and merge it with restaurant preference. It expands object connectivity; it does not replace vector similarity with keyword scoring. The later v3 entity linking path is different: it turns entity relationships into retrieval-ranking signals instead of exposing a full graph traversal surface.

What mem0g adds Problem it solves Cost it introduces
entity Gives people, organizations, projects, and places stable anchors. Requires entity extraction, alias merging, and cross-turn alignment.
relationship Makes "who is connected to whom, and how" explicit instead of buried in text. Relationships can change or conflict with old edges, so they need maintenance rules.
graph store Adds retrieval by entity adjacency, multi-hop paths, and semantic triplets. Adds storage, query paths, and result-merging logic.
vector + graph merge Lets semantic relevance and relationship connectivity both affect context selection. Needs deduplication, ranking, and conflict handling across two result types.

The point is not to replace text memory with a graph. It is to give relationship-heavy information its own index. That solves the "how do these objects connect?" problem, but it does not solve the historical-version problem. The graph can say Alice, shellfish allergy, and Paris restaurants are related; when the user later changes dinner companions, or when a relationship changes over time, the system still has to decide whether the old fact should be updated, deleted, or preserved as history.

1.3 mem0 v3: Make Writes Thin, Move Judgment Into Read

At this point, the strengths and pressure points of the first two routes are visible. Paper mem0 keeps the memory set compact, but write-time has to decide whether old facts should be preserved, updated, or deleted. Mem0g makes relationships explicit, but adds a graph-maintenance path beside text memory. Both treat write-time as the moment to tidy the store. That works well for short-term preferences. It becomes harder when a fact changes over time. If the user lived in Beijing in January and Paris in April, the system should preserve a chain of changing-state evidence, not collapse everything into one always-current address field.

The official v2-to-v3 migration docs and Token-Efficient Memory Algorithm post describe v3 as single-pass ADD-only. A useful reading is an ownership shift: write safely admits new evidence, and read decides which viewpoint this answer needs. The write path is reduced to three jobs: extract durable new facts, store them with deduplication, links, and entity indexes, then leave the current model view to retrieval and ranking.

Changing state, shape-level example:

t1 memory:
  text: "User lives in Beijing as of 2025-01-10"
  timestamp: "2025-01-10T09:00:00Z"

t2 memory:
  text: "User lives in Paris as of 2025-04-02"
  timestamp: "2025-04-02T11:00:00Z"
  linked_memory_ids: ["t1"]

search("Where does the user live now?", reference_date="2025-04-10")
  -> should rank Paris higher

search("Where did the user live before March?", reference_date="2025-03-01")
  -> can still recover Beijing

Treat this as a boundary sketch, not the exact public API response shape. Read it as four responsibilities: ADD-only preserves both Beijing and Paris as evidence; linked_memory_ids says the two records are about the same changing state; Platform timestamp records when a fact entered history; reference_date lets search say which date frames this answer. Beijing is not invalidated during write, and Paris does not erase the old evidence. Read-time ranking decides which record belongs in front for the current question.

The public OSS _add_to_vector_store() follows that evidence-ingestion shape. It gathers recent messages and similar existing memories, makes a single LLM extraction call, then batch-embeds, hash-deduplicates, inserts vectors, records history ADD events, and links entities. Existing memories are still consulted, but they help with deduplication, linking, and contextual judgment; old records are not UPDATEd or DELETEd in this path.

# _add_to_vector_store(), simplified shape
last_messages = db.get_last_messages(...)
existing_results = vector_store.search(..., top_k=10)

extracted = llm.generate_response(ADDITIVE_EXTRACTION_PROMPT, ...)
embeddings = embedding_model.embed_batch(memory_texts, "add")

for memory in extracted:
    if hash_seen(memory):
        continue
    vector_store.insert(memory)
    history.event = "ADD"
    link_entities(memory)
v3 choice Pressure it absorbs Constraint left for later
single-pass extraction Reduces repeated old-vs-new arbitration during write. Extraction quality depends more on one prompt, similar old memories, and recent context.
ADD-only Stores a state change as new evidence. Duplicates, conflicts, and stale facts move into retrieval.
linked_memory_ids Connects new and old facts so later retrieval can explain what changed. Retrieval and display need to understand those links, not only the single text record.
built-in entity linking Keeps part of mem0g's relationship cue while reducing external graph-store burden. Entity signals enter ranking; they do not provide the whole graph-query answer.
hybrid retrieval Moves "which memory should this answer see?" into read-time ranking. Model-view quality depends on signal fusion, not only on write-time cleanliness.

Seen this way, ADD-only is more than a token-saving trick. The ownership changes: write preserves evidence; read builds the current view. Write avoids irreversible judgment, while read combines semantic, keyword, entity, time, and access signals to decide the current model view. The tradeoff is clear: old facts remain in the store, storage grows, and retrieval must handle duplication, conflicts, and stale facts. Temporal Reasoning and Memory Decay answer the next question: once history is preserved, which few records should this turn see?

1.4 Separate Three Objects First: Window, Record, and Model View

ADD-only often raises a natural question: if history is preserved, does every old fact enter the prompt? The answer depends on three separate objects. The context window is the model's working area for the current request. A memory record is a durable fact that can be searched later. The model view is the small set of retrieved memories inserted back into the current request. ADD-only changes how memory records are written; Temporal Reasoning and Memory Decay change how the model view is selected. Neither one means the full history is pushed into the context window.

Context versus memory comparison showing current messages in a context window and durable memory retrieved into the model view
The context window answers "what can the model see now"; persistent memory answers "what can be found later"; retrieval answers "which few records should enter this model view."
Term Meaning Here Common Misread
context window The current request working area for short-term reasoning and tool results. Treating a larger window as durable memory while ignoring cost and lost-in-the-middle effects.
memory record A persisted fact extracted from conversation, with payload, metadata, embedding, and history. Treating the record as "current truth" instead of a fact that may have been true at a point in time.
model view The small result set shown to the model after retrieval, filtering, and ranking. Treating it as the whole memory store, so un-retrieved facts appear nonexistent.
ranking layer The layer that turns semantic, keyword, entity, time, and usage signals into result order. Treating ranking output as if old records had been overwritten.

2. Why Writes Are ADD-only: Preserve Evidence Before Judging Old Facts

Think of the write path as an archivist. When the user says, "I started eating seafood," the dangerous move is not missing one note. It is immediately rewriting "prefers vegetarian restaurants" as if that older fact never mattered. In June, the user may ask what kind of place they used to choose for dinner. At that point the older fact is not junk; it is evidence. v3 narrows the archivist's authority: write the new evidence first, and avoid deciding during write whether the old evidence should be overwritten, deleted, or treated as forever stale.

Mem0's Token-Efficient Memory Algorithm post frames v3 as single-pass ADD-only. The public OSS path has the same shape, and it is easier to read as an evidence-ingestion path: Memory.add() prepares inputs, filters, and metadata before calling _add_to_vector_store(). In that visible path, the LLM extracts new memories, then the runtime batch-embeds them, deduplicates by hash, inserts vectors, records history ADD events, and links entities. The function list matters less than the ownership change: the write path now protects new evidence instead of erasing old history.

ADD-only write path showing messages, existing memory, fact extraction, hash deduplication, vector insert, history ADD, and a blocked overwrite shortcut
ADD-only blocks in-place overwrite; it does not promise that duplicates can never exist.
Pressure in the Older Route ADD-only Handling Advantage
Write-time UPDATE / DELETE can be wrong New facts enter the memory store as new records. Irreversible overwrite is reduced, and evidence remains explainable later.
The same preference can have different answers at different times The transition itself can become a memory with time signal. "Now" and "then" no longer compete for one overwritten record.
Graph relations add storage and maintenance cost linked_memory_ids and built-in entity linking preserve relationships. Relationships remain available without forcing OSS users to operate an external graph store.
Longer write paths increase cost, latency, and failure surface Single-pass extraction, batch embedding, hash deduplication, then insert. The write path is shorter, and richer decisions move to retrieval.

2.1 LLM Extraction Links Evidence; It Is Not the Final State Judge

ADD-only does not mean the LLM leaves the write path. It means the LLM's role changes. The older route used it like a judge deciding whether an old memory should be UPDATEd or DELETEd. v3 uses it more like an evidence clerk: decide whether the new sentence is worth preserving, which old memory it relates to, and how relative time phrases such as "last week" or "yesterday" should be grounded. ADDITIVE_EXTRACTION_PROMPT encodes that job: both user and assistant messages can carry information; relative time must be grounded by the observation date; related prior memories are linked with linked_memory_ids, rather than directly rewriting old records.

New message:
  "I switched to oat milk because almond milk started causing sensitivity."

Write-time handling:
  1. Is this a durable new fact? Yes.
  2. Is it related to an old preference? Yes, link the old memory.
  3. Should it overwrite the old memory? Not decided during write.
{
  "memory": [
    {
      "id": "0",
      "text": "User switched from almond milk to oat milk lattes after developing an almond sensitivity",
      "linked_memory_ids": ["existing-memory-id"]
    }
  ]
}

The key point in this JSON is not the shape by itself. It is that the transition becomes a new traceable fact. The old preference is not silently deleted. Later retrieval can see both what used to be true and why it changed. Long-running agents need that replayable history; a single latest conclusion is not enough.

2.2 The OSS Path Keeps a Hard Boundary Around Platform-only Time Fields

Because later sections use Temporal Reasoning, it is worth setting the source boundary first. Once observation dates enter the discussion, it is tempting to treat Temporal Reasoning as part of the visible OSS path. That would blur the evidence boundary. Public OSS source shows ADD-only write, baseline retrieval, and the parameter boundary; Platform v3 exposes the full temporal fields. Passing timestamp to OSS Memory.add() raises a Platform-only error; passing reference_date to OSS Memory.search() does the same. This is an API boundary, not a missing local detail. Later sections can say that the official contract separates write time from search reference time, but the server-side ranking algorithm is not visible in the public source.

3. Read: Find Candidates First, Then Decide What the Model Sees

ADD-only keeps the history, so read has to be more careful. In the restaurant case, the query is "Find a restaurant for this weekend, and remember my current situation." "Beijing vegetarian," "Paris seafood," and "Alice is allergic to shellfish" may all matter, but they matter in different ways: old preference, current location, and companion constraint. The read layer is not only asking "which sentences look close to the query?" It is asking "which memories belong in this turn's model view?"

In OSS, search starts at Memory.search() and flows into _search_vector_store(). Read in source order, it does five things: normalize the query for keyword search and extract entities; embed the query; over-fetch semantic candidates; turn keyword-search results into BM25 scores when the store supports it; compute entity boosts through linked memories; then pass the signals into score_and_rank().

# _search_vector_store(), simplified shape
query_terms = lemmatize_for_bm25(query)
query_entities = extract_entities(query)
query_vector = embedding_model.embed(query, "search")

internal_limit = max(limit * 4, 60)
semantic_results = vector_store.search(..., top_k=internal_limit)
keyword_results = vector_store.keyword_search(query_terms, top_k=internal_limit)

bm25_scores = normalize_keyword_scores(keyword_results)
entity_boosts = compute_boosts_from_linked_entities(query_entities)

ranked = score_and_rank(
    semantic_results,
    bm25_scores=bm25_scores,
    entity_boosts=entity_boosts,
    threshold=threshold,
    top_k=limit,
)

That order clears up the common retrieval questions. BM25 is present, but in the visible OSS path it is a keyword score signal; the candidate pool first comes from semantic search. Entity search does not produce the final answer by itself either; it boosts memories linked to matched entities. The path is closer to additive score fusion: semantic candidates pass a gate, then BM25 and entity boost adjust their scores. Therefore, this is better read as adding scores over the same semantic candidate set, not as applying RRF to separate vector, BM25, and entity ranked lists. A compact reading is: semantic similarity keeps results on topic, keywords preserve exact terms, and entity signals keep related objects connected.

Hybrid retrieval showing query split into semantic, keyword, and entity signals, then candidate pool, threshold, score fusion, and ranked results
Mem0's visible OSS search forms a candidate pool and then combines semantic, keyword, and entity signals, going beyond a plain vector top-k call.

3.1 Semantic Relevance Gates Before Keyword and Entity Fusion

Ranking fails quickly when familiarity is mistaken for relevance. Suppose Alice appears in many memories and the query also mentions Alice, but this turn is about restaurant preference, not every older dinner plan involving Alice. If entity boost acted alone, many Alice-related records would look important. Fusion starts with a gate instead: candidates below the semantic threshold are removed before normalized BM25 and entity boost are added. Keyword and entity signals can change the order among relevant candidates, but they should not rescue a semantically unrelated one.

semantic candidates
  -> threshold gate
  -> add normalized BM25 when available
  -> add entity boost when available
  -> normalize by active signal budget
  -> return top_k
if semantic_score < threshold:
    drop(candidate)

combined = (semantic_score + bm25_score + entity_boost) / max_possible_score

In other words, the final score comes from semantic score, BM25 score, and entity boost added together. BM25 and entity signals can reorder relevant candidates, but they do not pull a memory into the final result if it never entered the semantic candidate pool. The design is conservative: it gives up some pure keyword recall in exchange for not letting familiar names, restaurant names, or frequent terms force unrelated memories into the model view.

3.2 "Still Applies Now" Needs Time and Usage Signals

Passing the semantic gate only means "related." It does not mean "the right fact for this moment." A user can like A, then switch to B. Both records should remain, because one answers what was true then and the other answers what is likely true now. Retrieval needs two more signals: Temporal Reasoning for the time point of the question, and Memory Decay for recently reinforced context. They do not replace semantic relevance; they continue the selection among already relevant candidates.

4. Temporal Reasoning: Let the Query Stand on a Date

"I live in Paris now" and "where did I live before March?" may involve the same residence state, but they should not produce the same answer. Temporal Reasoning gives the query a point of view in history instead of making the latest fact automatically win. It does not rewrite old records into new ones. It lets write record when a fact was observed and lets search state the date from which the answer should be framed. The Temporal Reasoning announcement and docs define the public contract: Platform v3 support, enabled by default; timestamp on add; reference_date on search; and unchanged search result shape.

Temporal layer showing timestamp on add, reference_date on search, a timeline, and ranked answer selection
The temporal layer lets search state which point in time should frame the answer, instead of relying on overwrite to handle change.

4.1 Request Shape: Observation Time and Search Time Are Separate

Return to the Beijing/Paris example. Saying in January that the user lives in Beijing, and in April that the user lives in Paris, are observation times for two facts. Asking in April "where do I live now?" or asking from March "where did I live then?" is the time perspective of the current search. They both look like "time," but they are different roles. If those roles collapse, the system has to guess whether the newest fact should overwrite the older one. Platform requests separate them: timestamp describes when the memory entered history, and reference_date describes the date from which this search should answer. The Python Platform client posts add and search requests to v3 endpoints: /v3/memories/add/ and /v3/memories/search/. The visible source shows request shape and parameter forwarding. The server-side details of temporal ranking are not public.

# Platform shape, simplified
client.add(
    messages=[{"role": "user", "content": "I currently live in Beijing."}],
    user_id="u1",
    timestamp="2025-01-10T09:00:00Z",
)

client.add(
    messages=[{"role": "user", "content": "I currently live in Paris."}],
    user_id="u1",
    timestamp="2025-04-02T11:00:00Z",
)

client.search(
    "Where do I live now?",
    filters={"user_id": "u1"},
    reference_date="2025-04-10",
)

The design point is not merely two extra fields. Write attaches time to the evidence; search tells the system which date to look back from. That keeps "lives in Paris now" and "used to live in Beijing" from competing for one overwritten record: a current query can prefer Paris, while a historical query can still recover Beijing.

4.2 Temporal Handles Contradictory History; It Does Not Replace Filters

Filters and Temporal are easy to conflate. Filters answer "which drawer should search open": user, agent, run, or business metadata. Temporal answers "once that drawer is open, from which date should this history be interpreted?" Inside the same user's history, February facts and March facts can coexist. Questions such as "what changed in the last three months" and "what did I prefer last year" need those records to coexist; one latest value is not enough.

5. Memory Decay: Recent Use Can Re-rank, but It Cannot Replace Relevance

A time view is still not enough. A long-running agent can collect many facts that are relevant and not obsolete: common projects, frequent contacts, preferred response styles, and tasks that keep coming back. All can be relevant, but the prompt can only hold a few results. If one kind of memory has been reinforced recently, it often deserves to rank higher. That should not be solved by deleting old memories, because those old records may still matter for other questions. Memory Decay and the Memory Decay docs place this at search-time soft re-ranking. It does not delete memories or rewrite facts on write; it gently adjusts ordering after basic relevance has been established.

Memory Decay showing candidate pool, threshold, access history, recent touch boost, stale dampening, and final top_k
Memory Decay sits late in search: first establish relevance, then use access history to influence the final top-k.

5.1 The Project Switch Changes Search-time Ranking

The switch itself tells you where Decay lives. It belongs to project-level search strategy, not to a deletion countdown on each memory record. The public SDK exposes the project-level switch: project.update(decay=True) adds decay to the project update payload. The source comment also frames it as search-time ranking: recently used memories are boosted, stale ones are gently dampened, and disabling the flag restores pre-decay behavior. The service-side access history store and multiplier calculation are not visible in OSS.

# Platform project setting, simplified
client.project.update(decay=True)

5.2 Key Boundary: Relevance First, Decay Second

The easiest misread is "whatever was asked recently should win." If that were true, repeated reimbursement questions could push reimbursement memories into a restaurant-preference query. The correct boundary is narrower: recent use changes the order among relevant candidates; it should not let unrelated facts cross the relevance gate. Decay's public boundaries are designed around that risk: it applies to Platform v3; it works over a candidate pool larger than final top-k; base relevance thresholding happens before the decay multiplier; public scores are clamped to the normal range; reinforcement is asynchronous. The docs also give scale boundaries: the pool expands to top_k * 3 with a floor of 50, and the multiplier is clamped between 0.3x and 1.5x. These details protect one invariant: decay is ranking bias, not fact deletion.

Boundary Meaning Misread Avoided
search-time Ranking is adjusted after candidates are retrieved. Not a write-time rewrite of old facts.
threshold first Basic semantic relevance gates the candidate before decay. Frequent but irrelevant memories do not automatically enter results.
soft multiplier Recent use moves records up; staleness moves them down. Not TTL, and not automatic deletion.
project opt-in The project setting enables decay. Not every Mem0 usage has decay automatically enabled.

6. Put the Layers Together: Old Facts Stay, New Questions Get a View

Return to the opening question: if a user's preference changes from A to B, what should the system remember? Keeping only B breaks historical explanation; pushing A, B, and every related fact into the prompt distracts the model. Mem0's route is layered instead of extreme: ADD-only preserves the evidence; hybrid retrieval builds the candidate pool; Temporal Reasoning supplies the time perspective; Memory Decay lets recent usage influence result order. The model sees neither the whole store nor one overwritten truth. It sees the model view for this question.

Mem0's BEAM benchmark post and memory simulation guide point in the same evaluation direction: production memory cannot stop at "was something retrieved?" It also has to handle stale facts, contradictions, time conditions, and retrieval drift. That is the model-view quality problem.

Memory rules map showing new fact routed through state changed, used often, must be exact, and stable fact paths
The practical rule is to decide which layer should handle which problem: evidence, present-day judgment, exact policy, and stable background should not collapse into one overwrite action.

6.1 When Mem0 Is Enough, and When the Application Must Own the Rule

That leaves a useful application boundary. Mem0 is a good fit for information that helps the model understand context: user preferences, common tools, long-running project background, and topics that keep reappearing. State that must execute exactly needs a stricter owner: payment addresses, permission state, compliance switches, or medical contraindications. Those should live in application databases or explicit policy systems. Mem0 can help retrieve and explain surrounding context; it should not authorize the final action.

Information State Best Mechanism Invariant Protected Failure Boundary
Preference changed ADD-only records the transition; temporal query supplies the time view. Both "then" and "now" remain explainable. Keeping only the latest value loses history; keeping only the old value answers current queries wrongly.
Recently reused context Memory Decay softly re-ranks search results. Frequently reinforced information is more likely to enter the current model view. Decay does not replace business priority or hard policy.
Must be exact Application database, permission system, or explicit rule. Probabilistic retrieval does not decide high-risk state. Authorization, payment, or medical decisions should not be owned by memory search.
Stable long-term background Ordinary memory record plus hybrid retrieval. Repeated questions decrease while personalization remains available. Without proper filters, user or agent boundaries can blur.

6.2 The Short Version of Mem0's Route

Mem0's useful lesson is not "make memory a bigger prompt" or "make the model reread all history." It is the separation of responsibilities: the write path preserves evidence, retrieval builds the candidate set, temporal reasoning frames history, decay expresses recent use, and the application owns hard-consistency rules. With that separation, an agent does not have to choose between overwriting old memory and showing all history forever. It also explains why v3's later mechanisms all circle the quality of the model view.

Sources