Tools
Memoria
Memoria โ Multi-layer persistent memory plugin for OpenClaw. SQLite + FTS5 + embeddings + knowledge graph + topics + adaptive budget. 100% local via Ollama.
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