Forgetting mechanism¶
MemoRizz forgets the way Generative Agents (Park et al., 2023) do, with one addition. Memories are never deleted by the mechanism: they sink in the retrieval ranking as they age unused, and a governed, reversible suppression tier can hide records that would otherwise distort retrieval. Audit evidence, verified outcomes and pinned records are never touched.
Tier 0: ranking decay (always on)¶
Every candidate retrieved for a turn is scored before it enters the prompt:
recency = decay ** hours_since_last_access (decay 0.995 by default)
importance = stored 0..1 rating (default 0.5 when a record has none)
relevance = cosine to the query, or the provider score
score = (a_recency * recency + a_importance * importance + a_relevance * relevance) / (sum of weights)
Each term is min-max normalised over the candidate set, exactly as in the
paper, and the weights default to 1, 1, 1. Recency is anchored on the memory's
last access (last_accessed_at) and falls back to its creation time, so a
memory that keeps being recalled decays slowly and one that is never used
sinks. Diversity selection (maximal marginal relevance) runs afterwards.
Selected memories carry a scoring block with the normalised and raw
components, which retrieval diagnostics expose.
Global defaults come from environment settings (the Settings page writes
them): MEMORIZZ_RECENCY_DECAY_PER_HOUR, MEMORIZZ_RECENCY_ANCHOR
(last_accessed or created), MEMORIZZ_ALPHA_RECENCY,
MEMORIZZ_ALPHA_IMPORTANCE, MEMORIZZ_ALPHA_RELEVANCE,
MEMORIZZ_DEFAULT_IMPORTANCE and MEMORIZZ_SCORE_NORMALIZE. Per-agent
overrides live on the agent's retrieval policy under scoring and are edited
in the agent form's Forgetting (per-agent overrides) group; blank fields
inherit the global value.
from memorizz.memagent.utils.context_dedup import RetrievalScoring, dedupe_and_select
selected = dedupe_and_select(
candidates,
query_embedding=query_vector,
scoring=RetrievalScoring(alpha_importance=2.0, recency_anchor="last_accessed"),
)
Importance ratings¶
The paper rates each memory's "poignancy" from 1 to 10 at creation. MemoRizz
stores the rating as importance in [0, 1] and offers three rater modes
(MEMORIZZ_IMPORTANCE_RATER):
| Mode | Behaviour |
|---|---|
off |
Nothing is stored; retrieval uses the default importance. |
heuristic |
Deterministic and free: host-asserted facts and verified outcomes high, tool output low, decisions and corrections boosted. |
llm |
The paper's prompt, answered by a small or local model (MEMORIZZ_IMPORTANCE_MODEL), batched and run off the user path, with the heuristic as fallback. |
Ratings are immutable once stored; only a verified outcome raises one to 1.0.
from memorizz.memagent.utils.importance import ImportanceConfig, ImportanceRater
rater = ImportanceRater(ImportanceConfig(mode="llm", purpose="tracking a product release"), generate=model.generate_text)
importance, source = rater.rate("The release owner changed to Mira", role="user")
Reinforcement and reflection¶
Whenever a memory is selected into a prompt, the agent records the recall off
the user path (touch_many): last_accessed_at moves to now and
access_count increments, so recency is measured since last use as in the
paper. New conversation rows are rated at store time and their importance
accumulates per conversation; when MEMORIZZ_REFLECTION_ENABLED is on and the
sum passes MEMORIZZ_REFLECTION_THRESHOLD (default 15.0 on the 0 to 1 scale,
the paper's 150 on 1 to 10), the agent schedules one summarisation pass over
that conversation, the paper's reflection step, and records it in the turn
trace as Reflection scheduled. The pass runs after the turn returns, on the
agent's background model.
LLM importance ratings use the background model too.
Tier 1: governed suppression (plan, approve, restore)¶
A retention planner scores primary memory records (conversation, knowledge base, entities, summaries and workflows) with
retention = mean(importance, usage, recency)
usage = log1p(access_count) / log1p(max access_count in the set)
and lists the records whose retention stayed below min_retention for at
least grace_days since creation, unless they are pinned, verified, already
suppressed, or cited as a source by another record. The plan is a dry run with
a content-hashed id; a named approver applies it; each record receives
retention_state = "suppressed" with the plan id, reason and approver. Every
retrieval path honours that state, the memory history journal records the
change with the approver as writer and forgetting:<plan-id> as source, and
any suppression can be reversed.
Settings: MEMORIZZ_RETENTION_ENABLED, MEMORIZZ_RETENTION_MIN_SCORE,
MEMORIZZ_RETENTION_GRACE_DAYS, MEMORIZZ_RETENTION_PROTECT_VERIFIED,
MEMORIZZ_RETENTION_PROTECT_PINNED, MEMORIZZ_RETENTION_MAX_CANDIDATES and
MEMORIZZ_RETENTION_MEMORY_TYPES. Per-agent overrides sit on the retrieval
policy under retention.
plan = agent.plan_memory_retention(memory_id=memory_id) # dry run
applied = agent.apply_memory_retention(plan, approved_by="operator-7", reason="quarterly tidy")
agent.suppressed_memories(memory_id=memory_id) # review
agent.unsuppress_memory(record_id, "knowledge_base", approved_by="operator-7")
With the learning control plane enabled these calls go through
plane.plan_retention / plane.apply_retention and leave durable
forgetting_planned / memory_forgotten events; without it the agent runs the
planner directly against its provider. agent.retrieval_scoring and
agent.retention_config expose the resolved settings (global defaults overlaid
with the agent's retrieval_policy overrides).
From the command line:
memorizz learning retention-plan --agent-id AGENT --memory-id MEMORY --json
memorizz learning retention-apply PLAN_ID --agent-id AGENT --approved-by operator-7
memorizz learning suppressed --agent-id AGENT
memorizz learning unsuppress RECORD_ID --memory-type knowledge_base --agent-id AGENT --approved-by operator-7
In the UI, the Learning control plane page has Plan memory retention (dry run), an approval panel for the resulting plan, a Suppressed memories table and a Restore action per record. Hard deletion is unchanged: it stays an exact-scope, approval-gated lifecycle operation.
Semantic cache¶
The semantic cache keeps its own time-to-live (ttl_hours, default 24) and
per-domain freshness windows. Expired entries are never served, expired rows
are purged from the provider on preload and periodically on writes, and
re-caching the same query updates the existing row instead of duplicating it.
Provider parity¶
All fields (importance, last_accessed_at, access_count,
retention_state) are plain record fields, so the scoring and the planner
behave the same on the filesystem, MongoDB, Oracle and Notion providers. The
learning-plane store uses indexed observability queries where a provider
offers them and never scans the shared-memory partition on per-turn paths.