mem0 OSS: custom_instructions in mem0.json is silently dropped (OSSBackend hardcodes the config key list)
What
OSSBackend passes a hardcoded key list to Memory.from_config, so custom_instructions set in a user's mem0.json is silently dropped:
# plugins/memory/mem0/_backend.py:143
config = {"vector_store": vector_store, "llm": _provider_block("llm", LLM_PROVIDERS),
"embedder": _provider_block("embedder", EMBEDDER_PROVIDERS), "version": "v1.1"}
MemoryConfig accepts custom_instructions (mem0's documented control for "define what actually matters to you for extraction"), and the ADDITIVE extraction path already consumes it:
# mem0/memory/main.py:897
custom_instr = prompt or self.custom_instructions
user_prompt = generate_additive_extraction_prompt(..., custom_instructions=custom_instr)
So the field is a supported mem0 feature that the OSS wiring never forwards. Adding it is one line:
config = {"vector_store": vector_store, "llm": _provider_block("llm", LLM_PROVIDERS),
"embedder": _provider_block("embedder", EMBEDDER_PROVIDERS), "version": "v1.1"}
if (ci := str(oss_config.get("custom_instructions") or "").strip()):
config["custom_instructions"] = ci
Why it matters (measured, not theoretical)
Without it, a mem0 OSS pool accumulates paraphrases of the same fact that exact-hash dedup cannot see, because the additive path is ADD-only:
"User confirmed the recommendation and will correct the Mem0 setup for all bots..."
"User accepted the recommendation and will correct the Mem0 setup for all bots..."
"User corrected the Mem0 setup for all bots..."
Measured on one fleet pool: 45 redundant paraphrase copies in 483 records, re-created within hours of each manual cleanup. There is no Dream on OSS, so nothing else merges them.
With custom_instructions set, the same comparison on the real 33,653-char extraction prompt, with a near-identical existing memory present, returns {"memory": []} instead of a new paraphrase — reproduced 3/3:
without custom_instructions -> writes the paraphrase (with linked_memory_ids)
with custom_instructions -> {"memory": []}
Same class of trap as sync_max_chars (also a documented field the OSS config does not forward): both silently do nothing, and the absence is indistinguishable from a user not having set a preference.
Repro
- Set
custom_instructionsin$HERMES_HOME/mem0.json(modeoss) python -c "from plugins.memory.mem0._backend import OSSBackend; ..."or simply run a turn- Inspect the built instance:
<_backend>._memory.config.custom_instructionsisNone
Workaround in place (a thin provider subclass that sets the attribute after construction) — I'll drop it the moment the field is forwarded, since a subclass for a one-line config gap is worse than the one line.
Source: NousResearch/hermes-agent