← Back to Plugins
Tools

Memoria

Primo-Studio By Primo-Studio 👁 11 views ▲ 0 votes

Memoria โ€” Multi-layer persistent memory plugin for OpenClaw. SQLite + FTS5 + embeddings + knowledge graph + topics + adaptive budget. 100% local via Ollama.

GitHub

Configuration Example

{
  "autoRecall": true,
  "autoCapture": true,
  "recallLimit": 12,
  "captureMaxFacts": 8,
  "defaultAgent": "koda",
  "contextWindow": 200000,
  "workspacePath": "~/.openclaw/workspace",
  "syncMd": true,

  "llm": {
    "provider": "ollama",
    "baseUrl": "http://localhost:11434",
    "model": "gemma3:4b",
    "apiKey": "",
    "overrides": {
      "extract":       { "provider": "ollama", "model": "gemma3:4b" },
      "contradiction": { "provider": "openai", "model": "gpt-5.4-nano", "apiKey": "sk-..." },
      "graph":         { "provider": "ollama", "model": "gemma3:4b" },
      "topics":        { "provider": "lmstudio", "model": "glm-4.7-flash" },
    }
  },

  "embed": {
    "provider": "ollama",
    "baseUrl": "http://localhost:11434",
    "model": "nomic-embed-text-v2-moe",
    "dimensions": 768,
    "apiKey": ""
  },

  "fallback": [
    { "name": "ollama",   "type": "ollama",   "model": "gemma3:4b",     "baseUrl": "http://localhost:11434", "timeoutMs": 12000, "embedModel": "nomic-embed-text-v2-moe", "embedDimensions": 768 },
    { "name": "openai",   "type": "openai",   "model": "gpt-5.4-nano",  "baseUrl": "https://api.openai.com/v1", "apiKey": "sk-...", "timeoutMs": 15000 },
    { "name": "lmstudio", "type": "lmstudio", "model": "auto",          "baseUrl": "http://localhost:1234/v1", "timeoutMs": 12000 }
  ],

  "topics": {
    "emergenceThreshold": 3,
    "mergeOverlap": 0.7,
    "subtopicThreshold": 5,
    "decayDays": 30,
    "scanInterval": 15
  },

  "mdRegen": {
    "recentDays": 30,
    "maxFactsPerFile": 150,
    "archiveNotice": true
  }
}

README

# ๐Ÿง  Memoria v2.5.0 โ€” Multi-layer Memory Plugin for OpenClaw

Brain-inspired persistent memory for AI agents. SQLite-backed, fully local, zero cloud dependency.

## Architecture

```
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                      MEMORIA v2.5.0                          โ”‚
โ”‚                                                             โ”‚
โ”‚  Hooks: before_prompt_build โ”‚ agent_end โ”‚ after_compaction  โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                             โ”‚
โ”‚  RECALL PIPELINE (before_prompt_build):                     โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚
โ”‚  โ”‚๐Ÿ”ฅ Hot   โ”‚โ†’โ”‚ Hybrid   โ”‚โ†’โ”‚ Graph    โ”‚โ†’โ”‚ Topics   โ”‚   โ”‚
โ”‚  โ”‚ Tier     โ”‚  โ”‚ Search   โ”‚  โ”‚ Enrich   โ”‚  โ”‚ Enrich   โ”‚   โ”‚
โ”‚  โ”‚ accessโ‰ฅ5 โ”‚  โ”‚ FTS5+cos โ”‚  โ”‚ BFS 2hop โ”‚  โ”‚ keyword  โ”‚   โ”‚
โ”‚  โ”‚ always   โ”‚  โ”‚ +scoring โ”‚  โ”‚ hebbian  โ”‚  โ”‚ +cosine  โ”‚   โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ”‚       โ†“                                         โ†“           โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                              โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚
โ”‚  โ”‚ Context  โ”‚ โ† merge all โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ”‚ Adaptive โ”‚    โ”‚
โ”‚  โ”‚ Tree     โ”‚                              โ”‚ Budget   โ”‚    โ”‚
โ”‚  โ”‚ heuristicโ”‚                              โ”‚ 2-12     โ”‚    โ”‚
โ”‚  โ”‚ NO LLM   โ”‚                              โ”‚ facts    โ”‚    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                              โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚
โ”‚       โ†“                                                     โ”‚
โ”‚  formatRecall() โ†’ inject into system prompt                 โ”‚
โ”‚                                                             โ”‚
โ”‚  CAPTURE PIPELINE (agent_end / after_compaction):           โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚
โ”‚  โ”‚ Extract  โ”‚โ†’โ”‚ Selectiveโ”‚โ†’โ”‚ Store    โ”‚โ†’โ”‚ Post-    โ”‚   โ”‚
โ”‚  โ”‚ via LLM  โ”‚  โ”‚ Filter   โ”‚  โ”‚ to DB    โ”‚  โ”‚ process  โ”‚   โ”‚
โ”‚  โ”‚(extract  โ”‚  โ”‚ dedup+   โ”‚  โ”‚          โ”‚  โ”‚ embed+   โ”‚   โ”‚
โ”‚  โ”‚ Chain)   โ”‚  โ”‚contradictโ”‚  โ”‚          โ”‚  โ”‚ graph+   โ”‚   โ”‚
โ”‚  โ”‚          โ”‚  โ”‚(contradictโ”‚  โ”‚          โ”‚  โ”‚ topics+  โ”‚   โ”‚
โ”‚  โ”‚          โ”‚  โ”‚  Chain)  โ”‚  โ”‚          โ”‚  โ”‚ sync .md โ”‚   โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ”‚                                                             โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚  Per-layer LLM: extract โ”‚ contradiction โ”‚ graph โ”‚ topics    โ”‚
โ”‚  Default: FallbackChain (Ollama โ†’ OpenAI โ†’ LM Studio)      โ”‚
โ”‚  Override: llm.overrides.{layer} โ†’ provider/model au choix  โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚            SQLite memoria.db (FTS5 + vectors)                โ”‚
โ”‚  Tables: facts, facts_fts, embeddings, entities,            โ”‚
โ”‚          relations, topics, fact_topics                      โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
```

---

## Layers โ€” Dรฉtail par couche

### Layer 1: SQLite Core + FTS5 (`db.ts` ~446 lignes)
- **DB**: `~/.openclaw/workspace/memory/memoria.db` (WAL mode)
- **Tables**: `facts` (main), `facts_fts` (FTS5 virtual table), `embeddings`, `entities`, `relations`, `topics`, `fact_topics`
- **CRUD**: `storeFact()`, `getFact()`, `searchFacts()`, `recentFacts()`, `hotFacts()`, `supersedeFact()`, `enrichFact()`, `trackAccess()`
- **FTS5**: Index via triggers (INSERT/UPDATE/DELETE). Queries sanitisรฉes (hyphens, unicode-safe)
- **LLM**: Aucun
- **Provider**: Aucun
- **Fallback**: N/A

### Layer 2: Temporal Scoring + Hot Tier (`scoring.ts`)
- **Rรดle**: Score chaque fait par fraรฎcheur + catรฉgorie + frรฉquence d'accรจs
- **Formule**: `score = confidence ร— decayFactor ร— recencyBoost ร— accessBoost ร— freshnessBoost ร— stalePenalty`
  - Decay exponentiel: demi-vie par catรฉgorie (erreur=โˆž, savoir/preference=90j, outil=30j, chronologie=14j)
  - Access boost: **`0.3 ร— log(accessCount + 1)`** โ€” un fait accรฉdรฉ 50x score 2.2x plus (v2.5.0: 3x plus fort qu'avant)
  - Recency boost: <24h = ร—1.3, <7j = ร—1.1
  - Freshness bonus: mis ร  jour <48h = ร—1.2
  - Stale penalty: >90j + faible confiance = ร—0.7
- **Hot Tier** (NEW v2.5.0): faits accรฉdรฉs โ‰ฅ5x = **toujours injectรฉs** en recall, comme un numรฉro de tรฉlรฉphone appris par cล“ur
  - `getHotFacts()` โ†’ top 3 par access_count (configurable: `minAccessCount`, `maxHotFacts`, `staleAfterDays`)
  - Hot facts exclus du search normal pour รฉviter les doublons
  - Slots rรฉservรฉs : `searchLimit = recallLimit - hotCount`
- **API**: `scoreAndRank(facts)`, `scoreFact(fact)`, `getHotFacts(facts, config)`
- **LLM**: Aucun
- **Provider**: Aucun
- **Fallback**: N/A

### Layer 3: Selective Memory (`selective.ts` ~361 lignes)
- **Rรดle**: Filtre avant stockage โ€” dedup, contradiction, enrichment
- **Pipeline**:
  1. Longueur < 10 chars โ†’ skip (too_short)
  2. Noise patterns (salutations, confirmations) โ†’ skip
  3. Importance scoring (mots-clรฉs techniques, catรฉgorie) โ†’ threshold
  4. FTS5 candidates โ†’ Levenshtein > 0.85 โ†’ skip (duplicate)
  5. FTS5 candidates โ†’ Jaccard keyword overlap โ†’ skip (duplicate)
  6. Si similaire mais pas identique โ†’ **LLM contradiction check** โ†’ supersede/enrich
- **API**: `process(fact, category, confidence)`, `processAndApply(...)`
- **LLM**: โœ… Contradiction check uniquement
- **Provider**: `this.llm` = `contradictionLlm` (configurable via `llm.overrides.contradiction`)
- **Fallback**: Override provider โ†’ puis chain par dรฉfaut (Ollama โ†’ OpenAI โ†’ LM Studio)
- **Safety**: try/catch โ†’ si LLM fail, le fait est stockรฉ quand mรชme (conservative)

### Layer 4: Embeddings + Hybrid Search (`embeddings.ts` ~247 lignes)
- **Rรดle**: Vecteurs + recherche sรฉmantique
- **Modรจle embed**: configurable, dรฉfaut `nomic-embed-text-v2-moe` (768 dims)
- **Stockage**: Table `embeddings` (fact_id, vector BLOB, model, dimensions, created_at)
- **Hybrid search**: FTS5 score (60%) + cosine similarity (40%) + temporal scoring โ†’ merged ranking
- **API**: `hybridSearch(query, limit)`, `embedFact(factId)`, `embedAllMissing()`, `embedBatch()`, `embeddedCount()`
- **LLM**: Aucun
- **Provider**: `this.provider` = `EmbedProvider` (configurรฉ via `embed.provider`)
  - Ollama: POST `/api/embed` (`OllamaEmbed`)
  - LM Studio: POST `/embeddings` (`lmStudioEmbed`)
  - OpenAI: POST `/embeddings` (`openaiEmbed`)
  - OpenRouter: POST `/embeddings` (`openrouterEmbed`)
- **Fallback embed**: Aucun (single provider). Si embed fail โ†’ fait stockรฉ sans vecteur

### Layer 5: Knowledge Graph + Hebbian (`graph.ts` ~390 lignes)
- **Rรดle**: Entitรฉs extraites, relations pondรฉrรฉes, traversal BFS
- **Extraction**: LLM parse le fait โ†’ extrait entitรฉs (nom, type) + relations (source, target, relation)
- **Hebbian**: Co-accรจs renforce les poids des relations (weight += 0.1 par co-recall)
- **Traversal**: `getRelatedFacts(entityNames, maxHops=2, maxFacts=10)` โ€” BFS, fuzzy entity matching
- **Tables**: `entities` (id, name, type, attributes), `relations` (source_id, target_id, relation, weight, context)
- **API**: `extractAndStore(factId, factText)`, `getRelatedFacts()`, `findEntitiesInText()`, `hebbianReinforce()`, `stats()`
- **LLM**: โœ… Extraction entitรฉs/relations (1 appel par fait capturรฉ)
- **Provider**: `this.llm` = `graphLlm` (configurable via `llm.overrides.graph`)
- **Fallback**: Override provider โ†’ puis chain par dรฉfaut
- **Safety**: try/catch โ†’ si LLM fail, le fait est stockรฉ mais pas indexรฉ dans le graph

### Layer 6: Context Tree (`context-tree.ts` ~340 lignes)
- **Rรดle**: Organise les faits candidats en arbre hiรฉrarchique, pondรจre par query
- **Algorithme**:
  1. Cluster faits par catรฉgorie
  2. Sous-cluster par mots-clรฉs si > 5 faits
  3. Pondรฉrer chaque branche par overlap query โ†” labels
  4. Retourner les faits triรฉs par poids de branche
- **Extraction keywords**: โš ๏ธ **Heuristique locale** (regex + patterns), PAS de LLM
- **API**: `build(facts, query)` โ†’ `ContextTree`, `extractFacts(tree, limit)`, `renderTree(tree, depth)`
- **LLM**: โŒ Aucun โ€” extraction keywords = regex/heuristique locale
- **Provider**: Aucun
- **Fallback**: N/A

### Layer 7: Adaptive Budget (`budget.ts` ~121 lignes)
- **Rรดle**: Limite dynamique du nombre de faits injectรฉs selon l'espace contexte
- **Courbe quadratique** (v2.3.0):
  - Light (< 30%): 10 faits max
  - Medium (30-70%): 10 โ†’ 4 (courbe tยฒ โ€” lent au dรฉbut, rapide ร  la fin)
  - Heavy (70-85%): 4 โ†’ 2
  - Critical (> 85%): 2 faits (minimum)
- **Config**: contextWindow (200K dรฉfaut, nous=1M), maxFacts=12 (dรฉfaut), minFacts=2, thresholds configurables
- **API**: `compute(messagesTokenEstimate, systemTokenEstimate)` โ†’ `BudgetResult { limit, usage, zone }`
- **LLM**: Aucun
- **Provider**: Aucun
- **Fallback**: N/A

### Layer 8: Topics ร‰mergents (`topics.ts` ~688 lignes)
- **Rรดle**: Clustering automatique de faits par keywords partagรฉs
- **Processus**:
  1. **LLM** extrait 3-5 keywords par fait โ†’ stockรฉs dans `facts.tags` (JSON array)
  2. Scan orphans: si โ‰ฅ 3 faits partagent un keyword โ†’ crรฉer topic
  3. Si โ‰ฅ 5 faits partagent un keyword spรฉcifique dans un topic โ†’ crรฉer subtopic
  4. Topics avec > 70% overlap โ†’ fusionner
  5. Topic embedding = moyenne des embeddings des faits membres (via `this.embedder`)
  6. **LLM** nomme chaque topic (prompt โ†’ 1-3 mots)
- **Tables**: `topics` (id, name, keywords, parent_id, score, embedding), `fact_topics` (fact_id, topic_id)
- **Scoring**: score = fact_count ร— (1 + recency_boost), decay si inactif > 30j
- **API**: `findRelevantTopics(query, limit)`, `onFactCaptured(factId, factText, category)`, `scanAndEmerge()`, `stats()`
- **LLM**: โœ… 2 usages โ€” keyword extraction + topic naming
- **Provider**: `this.llm` = `topicsLlm` (configurable via `llm.overrides.topics`) + `this.embedder` (config `embed`)
- **Fallback**: Override provider โ†’ puis chain par dรฉfaut
- **Safety**: try/catch โ†’ si LLM fail, fait non taggรฉ (reste orphelin jusqu'au prochain scan)

### Layer 9: .md Sync + Regen (`sync.ts` ~258 lignes, `md-regen.ts` ~277 lignes)

**Sync** (`sync.ts`):
- Aprรจs capture, append nouveaux faits aux fichiers .md du workspace
- Mapping catรฉgorie โ†’ fichier :

  | Catรฉgorie | Fichier cible |
  |-----------|------

... (truncated)
tools

Comments

Sign in to leave a comment

Loading comments...