Skip to content

[Feature] Write-path dedup: reject identical memory content within a time window (server-side) #2141

Description

@larryluozhang

Motivation

MemOS has no server-side dedup on the write path. Any client that re-sends identical memory content (retry loops, replayed events, buggy integrations) creates duplicate nodes — duplicates that then pollute search results, graph structure, and reorganization.

We hit this in production via the OpenClaw cloud plugin (see MemTensor/MemOS-Cloud-OpenClaw-Plugin#145): a replay loop wrote 6,076 memories in 24h, ~90% duplicates (583 unique contents × 30-40 copies). Client-side dedup helps that specific client, but the server is the right place for a last-line defense that protects all writers.

Proposal

Content-hash dedup at MemoryManager.add:

  1. Compute sha1(memory_text) for each incoming item
  2. Skip items whose hash already exists for the same user_name within a configurable window (default 7 days)
  3. Also dedup within the batch itself
  4. Persist the hash as a content_hash node property with a Neo4j index (memory_content_hash_index) for O(1) lookup
  5. Fail-open: if the dedup check errors, the write proceeds (availability over strictness)
  6. Toggles: MOS_DEDUP_ENABLED=0 to disable, MOS_DEDUP_WINDOW_DAYS to adjust the window

Sketch of what we deployed (self-hosted patch, working in production):

def _dedup_by_content_hash(self, memories, user_name):
    kept, seen, skipped = [], set(), 0
    for m in memories:
        h = hashlib.sha1(m.memory.encode("utf-8")).hexdigest()
        if h in seen or self.graph_store.exists_recent_by_hash(user_name, h, days=days):
            skipped += 1
            continue
        m.metadata.content_hash = h  # extra="allow" → flows to node property
        seen.add(h)
        kept.append(m)
    return kept

with a matching graph-store query:

MATCH (n:Memory {content_hash: $h})
WHERE n.created_at >= datetime() - duration({days: $days})
  AND n.user_name = $user_name
RETURN n.id LIMIT 1

Notes

  • Window-based (not permanent) dedup intentionally allows re-learning: the same fact re-stated weeks later is a valid new memory
  • The hash doubles as a cheap idempotency key for retried writes
  • Happy to contribute this as a PR if the approach looks reasonable

Metadata

Metadata

Labels

area:memory记忆存储、检索、更新、召回逻辑status:needs-triageNeeds initial triage | 需要初步判断 & 问题复现types:enhancementNew feature or improvement | 新功能或改进

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions