How It Works
Memory lifecycle
memory_add(text)
│
▼
1. Hash dedup ──── duplicate? ──► skip
│
▼
2. Note Construction (LLM)
├── keywords (3–7 key terms)
├── tags (2–4 category tags)
├── context (one-sentence summary)
└── category (Technical / Business / Personal / ...)
│
▼
3. Link Generation
├── retrieve top-6 candidates (embedding similarity ≥ 0.3)
└── LLM verifies each: link bidirectionally if relevant
│
▼
4. Memory Evolution
├── up to 3 linked notes get attributes updated
└── may trigger additional link candidates
│
▼
5. Save to QdrantRetrieval pipeline
memory_search(query)
│
▼
1. Embed query (local ONNX)
│
├──► BM25 ranking (Jieba for Chinese; see note on ja/ko)
│ only notes sharing a term with the query
└──► Dense vector cosine similarity
│
▼
2. RRF fusion (k=60)
Final Score = BM25_rank⁻¹ + Vector_rank⁻¹
│
▼
3. Heat boost
Score × (1 + 0.05 × ln(1 + retrieval_count) / (age_days + 1))
│
▼
4. 2-hop BFS expansion
├── Walk link graph up to 2 hops from top-K anchors
├── Admit only nodes with cos-sim ≥ 0.25 vs query
└── Returned as via: "link" — appended after the matches, never ranked
│
▼
5. Return merged, deduplicated resultsTemporal invalidation
When a memory is updated or contradicted, amem marks the old note is_active: false and excludes it from all future searches via Qdrant payload filtering. amem never hard-deletes data. amem preserves the full history.
Daily consolidation
At 02:30 AM (in-process scheduler), the plugin:
- For each agent that wrote since the last run, resolves its
pending_mergenotes and merges near-duplicates it wrote since then (cosine ≥ 0.80) - Groups active episodic notes by
category— knowledge notes are skipped - Within each group, finds pairs with cosine similarity ≥ 0.75
- Merges duplicates into a single unified note
- Cascades all link references from soft-deleted notes to the merged note
This process prevents memory bloat from semantically redundant facts that accumulate over days.
The job does not run beside your own runs. It starts a step only when no run, compaction or agent_end write is in flight and nothing has happened for 60 s. If anything happens during a step, the step stops at its next LLM call. It runs again from the start when the gateway is idle. If the same step stops three times, or the job waits 60 minutes in one night, the job stops. The next night goes on with the agents it did not finish. The job cannot see runs in another process, such as openclaw agent --local.
Dedup layers
Every memory_add call passes through three dedup layers before reaching Qdrant:
| Layer | Mechanism | Threshold | Action |
|---|---|---|---|
| L1 | MD5 hash | Exact match | Skip (return existing ID) |
| L2 | Vector similarity | ≥ 0.85 | UPDATE existing note |
| L2.5 | Vector similarity | 0.72 ≤ s < 0.85 | Write + flag pending_merge=true |
The 02:30 job resolves pending_merge notes through LLM evolution judgment. See Evolution & Quality for details.
Agent isolation
openclaw-amem enforces per-agent memory namespacing. Every note carries owner, readers, and writers fields.
- Private (default):
readers = [agentId]— only the writing agent can retrieve this note. - Shared:
agent_id = "shared",readers = ["*"]— all agents see this note in search results.
amem scopes consolidation per agent. The dev consolidation pass considers only agent_id = "dev" notes. amem never modifies shared notes or other agents' private notes.
See Agent Isolation for full details.
Hook self-check (Story 34)
If the agent_end hook is silently blocked by OpenClaw's security policy (missing allowConversationAccess=true), the plugin detects this automatically:
- The plugin reads the hook configuration at startup. If write-back is blocked, the plugin warns immediately. It makes this decision from the configuration, not by waiting to see whether the hook ever fires.
- Every
memory_searchresult includes a visible warning notice in the agent's replies.
This check prevents the silent failure mode where automatic write-back stops working without any visible indication.
