Memory System (Français)
Choisir un fournisseur d’embeddings (v3.8.16+)
Section intitulée « Choisir un fournisseur d’embeddings (v3.8.16+) »Le moteur de mémoire d’OmniRoute prend en charge quatre sources d’embeddings (src/lib/memory/embedding/). Chacune présente différents compromis en matière de latence, coût, qualité du modèle et complexité de configuration.
Les sources d’embeddings
Section intitulée « Les sources d’embeddings »| Fournisseur | Source | Latence | Coût | Qualité | Configuration |
|---|---|---|---|---|---|
transformers |
Modèle ONNX local (Xenova/all-MiniLM-L6-v2) | ~50-150ms (CPU) | Gratuit | Bonne | npm install uniquement |
static |
Vecteurs précalculés (mis en cache) | <1ms | Gratuit | S/O (dépend du cache) | Aucune |
remote |
API OpenAI / Cohere / Voyage | ~100-300ms | $0.02-0.10/1M tokens | Excellente | Clé API |
auto |
Sélectionne la meilleure source disponible à l’exécution | Identique à la source choisie | Gratuit | Identique à la source choisie | Aucune |
| (cache) | Couche LRU en mémoire sur n’importe quelle source | <1ms (succès), latence complète (échec) | Gratuit | Identique à la source sous-jacente | Toujours active (source non sélectionnable) |
Arbre de décision
Section intitulée « Arbre de décision » Quel est votre contexte de déploiement ? │ ┌───────────┼───────────┬──────────────┐ │ │ │ │ DÉV/TEST PETITE PROD GRANDE PROD EDGE / HORS LIGNE │ │ │ │ ▼ ▼ ▼ ▼ transformers transformers remote (Qdrant) transformers (gratuit, sans API) (qualité optimale) (sans Internet) │ │ │ │ └────────┬──┴───────────┴──────────────┘ │ ▼ TOUJOURS ajouter la couche `cache` par-dessus (`LruCache` encapsule n’importe quel fournisseur)Configuration de la base de données et de l’API
Section intitulée « Configuration de la base de données et de l’API »Les options d’embedding de la mémoire sont configurées via l’API/l’interface utilisateur des paramètres, et non via des variables d’environnement. Les clés de base de données pertinentes sous Paramètres (normalizeMemorySettings dans src/lib/memory/settings.ts) sont :
memoryEmbeddingSource:"transformers"(local),"remote"(basé sur une API, par ex. OpenAI),"static"(stockage externe) ou"auto"memoryEmbeddingProviderModel: identifiant du modèle pour les sources distantes/statiques (par ex.,"text-embedding-3-small")memoryTransformersEnabled:true|falsememoryStaticEnabled:true|falsememoryVectorStore:"sqlite-vec","qdrant"ou"auto"
Modèle local (transformers)
Section intitulée « Modèle local (transformers) »Utilise transformers.js en interne pour exécuter des modèles locaux :
# Variables d’environnement lues dans le code (src/lib/memory/embedding/index.ts) :MEMORY_TRANSFORMERS_MODEL=Xenova/all-MiniLM-L6-v2 # Dépôt du modèle HFMEMORY_STATIC_MODEL=minishlab/potion-base-8M # Modèle potion statique HFMEMORY_STATIC_CACHE_DIR=<DATA_DIR>/embeddings # Répertoire du cacheCache LRU des embeddings
Section intitulée « Cache LRU des embeddings »Le cache est toujours activé par défaut et configuré via des variables d’environnement :
MEMORY_EMBEDDING_CACHE_MAX=1000 # Nombre maximal d’éléments mis en cacheMEMORY_EMBEDDING_CACHE_TTL_MS=300000 # TTL (5 min)Mesures de performances
Section intitulée « Mesures de performances »Benchmark sur un serveur x86 classique à 4 cœurs (textes d’environ 100 tokens chacun) :
| Fournisseur | p50 | p95 | p99 | Coût / 1 M de représentations vectorielles |
|---|---|---|---|---|
transformers (CPU) |
80ms | 180ms | 350ms | Gratuit |
remote (OpenAI) |
120ms | 220ms | 400ms | ~$0.02 (ada-002) / $0.13 (3-large) |
static (Qdrant) |
15ms | 30ms | 60ms | Dépend de l’hébergement de Qdrant |
cache (succès) |
<1ms | <1ms | 2ms | Gratuit |
Modèles d’extraction de faits (v3.8.16+)
Section intitulée « Modèles d’extraction de faits (v3.8.16+) »Le module extraction.ts (src/lib/memory/extraction.ts) utilise une correspondance par expressions régulières pour extraire des faits structurés des messages de conversation. Comprendre ces modèles vous aide à ajuster la qualité de l’extraction à votre cas d’utilisation.
Catégories de modèles par défaut
Section intitulée « Catégories de modèles par défaut »| Catégorie | Exemple de modèle | Éléments capturés |
|---|---|---|
| PREFERENCE_PATTERNS | "Je préfère <X>", "J’aime <X>", "Je déteste <X>" |
Préférences de l’utilisateur |
| DECISION_PATTERNS | "J’utiliserai <X>", "J’ai décidé de <X>", "J’ai choisi <X>" |
Décisions de l’utilisateur (épisodiques) |
| PATTERN_PATTERNS | "D’habitude, je <X>", "Je <X> toujours", "Je ne <X> jamais" |
Modèles comportementaux persistants |
Exemples de modèles (simplifiés)
Section intitulée « Exemples de modèles (simplifiés) »// Extrait de src/lib/memory/extraction.tsconst PREFERENCE_PATTERNS = [ /\bI\s+(?:really\s+)?prefer\s+([^.,\n]+)/gi, /\bI\s+(?:really\s+)?like\s+([^.,\n]+)/gi, /\bI\s+(?:hate|dislike|avoid)\s+([^.,\n]+)/gi,];const DECISION_PATTERNS = [ /\bI'?(?:ll|will)\s+use\s+([^.,\n]+)/gi, /\bI\s+(?:have\s+)?decided\s+(?:to\s+)?([^.,\n]+)/gi,];const PATTERN_PATTERNS = [/\bI\s+usually\s+([^.,\n]+)/gi, /\bI\s+always\s+([^.,\n]+)/gi];Éléments extraits
Section intitulée « Éléments extraits »Lorsqu’un utilisateur dit :
« Je préfère TypeScript. J’utiliserai Postgres pour ce projet. Je fais toujours un commit avant de pousser. Je n’aime pas Python. » L’extraction produit 4 souvenirs :
Clé Catégorie Type Contenu preference:typescriptpreference factual « TypeScript » decision:postgres_for_this_projectdecision episodic « Postgres pour ce projet » pattern:commit_before_pushingpattern factual « faire un commit avant de pousser » preference:pythonpreference factual « Python »
Limites de l’extraction
Section intitulée « Limites de l’extraction »Pour éviter une extraction incontrôlée, les limites suivantes s’appliquent :
| Longueur minimale du contenu | 3 caractères | | Longueur maximale du contenu | 500 caractères |
Quand désactiver l’extraction
Section intitulée « Quand désactiver l’extraction »L’extraction s’exécute automatiquement chaque fois que la mémoire est activée ; il n’existe pas d’option distincte permettant d’activer ou de désactiver uniquement l’extraction. Pour la désactiver, désactivez entièrement la mémoire (enabled: false
via PUT /api/settings/memory). Envisagez de le faire dans les cas suivants :
- Vous traitez un volume élevé de messages et le coût de l’extraction n’est pas négligeable
- Vos conversations sont principalement temporaires (discussion, débogage) et n’ont aucune valeur à long terme
- Vous capturez déjà le contexte au moyen de plugins personnalisés
Ajustement du RRF hybride (v3.8.16+)
Section intitulée « Ajustement du RRF hybride (v3.8.16+) »L’algorithme Reciprocal Rank Fusion (RRF) combine les résultats de FTS5 (mots-clés) et vectoriels (sémantiques). Le paramètre k contrôle le poids accordé aux résultats moins bien classés.
La formule
Section intitulée « La formule »Pour chaque souvenir candidat, le score RRF est :
RRF(d) = Σ 1 / (k + rank_i(d))Où :
kest la constante (60 par défaut)rank_i(d)est le rang du documentddans le iᵉ système de recherche (FTS, vectoriel)- La somme porte sur tous les systèmes de recherche
Influence de k sur les résultats
Section intitulée « Influence de k sur les résultats »Valeur de k |
Effet | Idéal pour |
|---|---|---|
k=0 |
Fusion pure des rangs (sans lissage) | Référence théorique |
k=10-30 |
Accorde beaucoup de poids aux premiers résultats ; les rangs faibles contribuent à peine | Lorsque les 3 premiers résultats sont généralement corrects |
k=60 (par défaut) |
Équilibré — les 10 premiers résultats contribuent tous de manière significative | Recherche généraliste |
k=100+ |
Plus uniforme — même les résultats de rang faible peuvent dominer s’ils apparaissent dans plusieurs systèmes | Lorsque le rappel est plus important que la précision |
Ajustement de k en pratique
Section intitulée « Ajustement de k en pratique »# Valeur par défautMEMORY_RRF_K=60
# Précision élevée (petite mémoire, peu de documents)MEMORY_RRF_K=20
# Rappel maximal (grande mémoire, requêtes variées)MEMORY_RRF_K=120Exemple avec k=20 :
- Rang FTS 1 → contribution
1/21 = 0.048 - Rang FTS 10 → contribution
1/30 = 0.033 - Rang vectoriel 1 → contribution
0.048 - Maximum combiné :
0.096
Exemple avec k=60 :
- Rang FTS 1 → contribution
1/61 = 0.016 - Rang FTS 10 → contribution
1/70 = 0.014 - Rang vectoriel 1 → contribution
0.016 - Maximum combiné :
0.033
Avec une valeur de k plus élevée, la différence relative entre le premier résultat et celui de rang 10 est plus faible ; l’algorithme s’appuie donc davantage sur le consensus entre les systèmes de recherche que sur le niveau de confiance associé au premier rang.
Quand modifier k
Section intitulée « Quand modifier k »| Symptôme | Essai recommandé |
|---|---|
| Le premier résultat gagne toujours, mais il est incorrect | Réduire k (p. ex., 20) — le niveau de confiance du premier rang compte davantage |
| La bonne réponse figure dans les 5 premiers résultats, mais pas en première position | Augmenter k (p. ex., 100) — un score plus uniforme favorise le consensus |
| Le rappel est élevé, mais la précision est faible | Réduire k — affiner le classement |
| Le rappel est faible (des documents pertinents manquent) | Augmenter k — donner une chance aux documents moins bien classés |
Pondération RRF
Section intitulée « Pondération RRF »La fusion réciproque des rangs utilise des poids égaux pour le rang vectoriel sémantique et le rang de recherche en texte intégral :
RRF(d) = 1/(k + rank_vector) + 1/(k + rank_fts)Il n’existe aucune variable d’environnement permettant d’ajuster les poids individuels (MEMORY_RRF_VECTOR_WEIGHT/MEMORY_RRF_FTS_WEIGHT n’existent pas).
Stratégie de synthèse (v3.8.16+)
Section intitulée « Stratégie de synthèse (v3.8.16+) »Le module summarization.ts (src/lib/memory/summarization.ts) compresse les souvenirs plus anciens afin de limiter la taille de l’ensemble actif tout en préservant les capacités de rappel.
Quand la synthèse se déclenche
Section intitulée « Quand la synthèse se déclenche »| Déclencheur | Seuil (par défaut) |
|---|---|
| Déclenchement manuel via l’API | s/o |
Ce qui est synthétisé
Section intitulée « Ce qui est synthétisé »Deux points d’entrée sont exportés depuis summarization.ts :
summarizeMemories(apiKeyId, sessionId?, maxTokens = 4000)— condense les souvenirs d’une session en un seul texte de synthèse respectant un budget de jetons.summarizeMemoriesOlderThan(apiKeyId, days, dryRun)— la compaction basée sur l’âge utilisée par l’API : elle sélectionne chaque souvenir antérieur àdays, crée à partir de ceux-ci un unique souvenir de synthèse condensé et, lorsquedryRunvautfalse, supprime les originaux. TransmettezdryRun: truepour prévisualiser l’ensemble des candidats et le nombre total de jetons sans rien modifier.
Il n’existe aucune étape de regroupement par étiquette/clé ni aucune notation « essentiel ou synthétisable » par souvenir — la sélection repose uniquement sur la limite d’âge, et le texte de synthèse est constitué d’une ligne condensée préfixée par le type pour chaque candidat.
Déclencher la synthèse
Section intitulée « Déclencher la synthèse »La synthèse est manuelle / facultative — le paramètre autoSummarize vaut
false par défaut, de sorte que rien n’est compacté automatiquement. Déclenchez-la via l’API :
curl -X POST http://localhost:20128/api/memory/summarize \ -H "Authorization: Bearer $OMNIROUTE_KEY"Pour la laisser désactivée, conservez simplement la valeur par défaut de autoSummarize (false).
Conseils pour améliorer la qualité de la synthèse
Section intitulée « Conseils pour améliorer la qualité de la synthèse »- Commencez par une prévisualisation avec
dryRun—summarizeMemoriesOlderThan(..., true)renvoie la liste des candidats et le nombre total de jetons afin que vous puissiez vérifier ce qui serait fusionné avant de supprimer les originaux. - Exécutez la synthèse pendant les heures de faible trafic si vous disposez d’un corpus de souvenirs volumineux — l’appel au LLM est l’étape la plus lente
# Style cron : effectuer la synthèse chaque jour à 3 h0 3 * * * curl -X POST http://localhost:20128/api/memory/summarize \ -H "Authorization: Bearer $OMNIROUTE_KEY"Modèle de fournisseur MemoryBackend
Section intitulée « Modèle de fournisseur MemoryBackend »Source de référence :
src/lib/memory/backend.ts,src/lib/memory/genericBackend.ts,src/lib/memory/manager.tsTests :src/lib/memory/__tests__/generic-backend.test.ts
Le modèle de fournisseur MemoryBackend introduit une couche d’abstraction de backend extensible au-dessus du moteur de mémoire existant. Au lieu d’être lié à une seule implémentation de stockage, le système de mémoire prend désormais en charge plusieurs backends (SQLite, Obsidian, Notion, backends HTTP personnalisés) avec un routage configurable entre le backend principal et les backends de secours.
Architecture
Section intitulée « Architecture »┌──────────────────────────────────────────────────────────┐│ Routes API ││ (src/app/api/memory/route.ts) │└──────────────────────┬───────────────────────────────────┘ │┌──────────────────────▼───────────────────────────────────┐│ MemoryManager ││ Orchestrateur singleton (manager.ts) ││ ││ Principal ─► Backend A (p. ex. SQLite) ││ Secours ───► Backend B (p. ex. Obsidian) ││ Backend C (p. ex. Notion via GenericBackend)│└──────────────────────┬───────────────────────────────────┘ │ ┌──────────────┼──────────────┐ ▼ ▼ ▼┌────────────┐ ┌────────────┐ ┌──────────────────┐│ Backend │ │ Backend │ │ Backend mémoire ││ SQLite │ │ Obsidian │ │ générique (HTTP) │└────────────┘ └────────────┘ └──────────────────┘Interface principale (backend.ts)
Section intitulée « Interface principale (backend.ts) »Chaque backend doit implémenter l’interface MemoryBackend :
interface MemoryBackend { readonly id: string; readonly displayName: string;
// CRUD create(input: CreateMemoryInput): Promise<Memory>; get(id: string): Promise<Memory | null>; update(id: string, updates: Partial<...>): Promise<boolean>; delete(id: string): Promise<boolean>; list(filter: MemoryFilter): Promise<{ data: Memory[]; total: number; byType: Record<string, number> }>;
// Recherche search(config: SearchConfig): Promise<Memory[]>;
// État health(): Promise<HealthCheckResult>;
// Cycle de vie (facultatif) initialize?(): Promise<void>; shutdown?(): Promise<void>;}MemoryManager (manager.ts)
Section intitulée « MemoryManager (manager.ts) »Orchestrateur singleton qui :
- Enregistre les backends via
register(backend)— appelé au démarrage depuisindex.ts - Configure le backend principal et les backends de secours via
configure(primary, fallbacks) - Achemine les opérations CRUD et les recherches vers le backend principal, avec une chaîne de secours en cas d’échec
- Vérifie l’état de tous les backends périodiquement
Comportement de secours :
| Opération | Principal | Backends de secours |
|---|---|---|
create |
✅ Principal uniquement | ❌ |
get |
✅ Essayer d’abord le principal | ✅ Secours si la valeur est nulle |
update |
✅ Principal uniquement | ✅ Synchronisation sans attente de résultat |
delete |
✅ Principal uniquement | ✅ Synchronisation sans attente de résultat |
list |
✅ Principal uniquement | ❌ |
search |
✅ Principal d’abord | ✅ Secours en cas d’erreur |
GenericMemoryBackend (genericBackend.ts)
Section intitulée « GenericMemoryBackend (genericBackend.ts) »Un connecteur HTTP générique qui adapte n’importe quelle API REST en MemoryBackend. Utile pour :
- Notion — connexion via l’API Notion
- Obsidian — connexion via l’API REST locale d’Obsidian
- Backends personnalisés — tout service exposant une API RESTful de mémoire
Configuration :
interface GenericBackendConfig { baseUrl: string; // URL de base de l’API du backend apiKey?: string; // Jeton Bearer pour l’authentification headers?: Record<string, string>; // En-têtes HTTP personnalisés timeout?: number; // Délai d’expiration de la requête (par défaut : 30000ms) backendType?: string; // Pour la journalisation
// Remplacements des points de terminaison (les valeurs par défaut suivent les conventions REST) endpoints?: { search?: string; // par défaut : "/memories/search" create?: string; // par défaut : "/memories" list?: string; // par défaut : "/memories" get?: string; // par défaut : "/memories/{id}" update?: string; // par défaut : "/memories/{id}" delete?: string; // par défaut : "/memories/{id}" health?: string; // par défaut : "/health" };
// Correspondances des noms de paramètres de requête queryParams?: { query?/apiKeyId?/limit?/offset?/strategy?/maxTokens?/type?/sessionId?/orderBy?/orderDir?/options? };
// Correspondances des noms de paramètres de chemin pathParams?: { id?/memoryId? };}Les backends connus sont préconfigurés dans KNOWN_BACKENDS :
createKnownBackend("obsidian"); // → GenericMemoryBackend pointant vers localhost:27123createKnownBackend("notion"); // → GenericMemoryBackend pointant vers api.notion.com/v1Backends intégrés
Section intitulée « Backends intégrés »SQLiteBackend (sqliteBackend.ts)
Section intitulée « SQLiteBackend (sqliteBackend.ts) »Le backend principal par défaut. Encapsule le stockage de mémoire SQLite existant à l’aide de src/lib/memory/store.ts. Il est automatiquement enregistré au démarrage.
import { sqliteBackend } from "./sqliteBackend";memoryManager.register(sqliteBackend);ObsidianBackend (obsidianBackend.ts)
Section intitulée « ObsidianBackend (obsidianBackend.ts) »Encapsule l’intégration Obsidian existante (src/lib/memory/obsidianBackend.ts). Se connecte à un coffre Obsidian via l’API REST locale d’Obsidian.
Paramètres
Section intitulée « Paramètres »Les paramètres des backends de mémoire sont stockés dans la table des paramètres de l’application et gérés via src/lib/memory/settings.ts :
| Paramètre | Clé d’environnement/configuration | Valeur par défaut | Description |
|---|---|---|---|
| Backend principal | memoryPrimaryBackend |
"sqlite" |
ID du backend principal |
| Backends de repli | memoryFallbackBackends |
[] |
ID ordonnés des backends de repli |
| Configurations des backends | memoryBackendConfigs |
{} |
Remplacements de configuration par backend |
Les paramètres sont normalisés via normalizeMemorySettings() et mis en cache dans getMemorySettings().
Flux d’initialisation
Section intitulée « Flux d’initialisation »Amorçage de l’application → Importations de index.ts (effet secondaire) : enregistre SQLiteBackend → initMemoryBackends() appelé depuis le cycle de vie de l’application : 1. Charger les paramètres (getMemorySettings) 2. Configurer le backend principal et les backends de repli 3. Initialiser tous les backends (vérification de l’état) 4. Prêt à traiter les requêtesAjout d’un nouveau backend
Section intitulée « Ajout d’un nouveau backend »- Implémenter l’interface
MemoryBackenddanssrc/lib/memory/<name>Backend.ts - Exporter depuis
src/lib/memory/index.ts - Enregistrer avec
memoryManager.register(yourBackend)au démarrage - Configurer via les paramètres : définir
memoryPrimaryBackendsur l’ID de votre backend - Tester en prenant
src/lib/memory/__tests__/generic-backend.test.tscomme référence
Exemple : backend Brain
Section intitulée « Exemple : backend Brain »import { createGenericMemoryBackend } from "./genericBackend";
const brainBackend = createGenericMemoryBackend("brain", "BK-Brain", { baseUrl: process.env.BRAIN_API_URL || "http://localhost:9099", apiKey: process.env.BRAIN_API_KEY, endpoints: { search: "/api/memory/search", create: "/api/memory", health: "/api/health", },});
memoryManager.register(brainBackend);Vérification
Section intitulée « Vérification »Tests unitaires
Section intitulée « Tests unitaires »npx vitest run src/lib/memory/__tests__/generic-backend.test.ts --reporter=verboseRésultat attendu : 35 tests, tous réussis, couvrant :
- Constructeur (2)
- Vérification de l’état (4) — réussite, échec 500, erreur réseau, latence
- Initialisation (2) — réussite, échec
- Création (2) — point de terminaison par défaut, point de terminaison personnalisé
- Récupération (4) — réussite, 404 → null, exception pour un code autre que 404, paramètres de chemin personnalisés
- Mise à jour (2) — réussite, 404 → false
- Suppression (2) — réussite, 404 → false
- Liste (2) — paramètres de requête, noms de paramètres personnalisés
- Recherche (3) — paramètres de requête, point de terminaison personnalisé, sérialisation des options
- En-têtes d’authentification (2) — jeton Bearer, en-têtes personnalisés
- Fabrique (1)
Vérification des types
Section intitulée « Vérification des types »npm run typecheck:coreRésultat attendu : 0 erreur.
HagiCode
HagiCode est un espace de développement agentique qui associe workflows structurés, exécution multi-agent et vues Hero Dungeon.
Transformez vos idées en logiciels utiles grâce à un workflow agentique plus intelligent, rapide et agréable.

- SmartDes workflows structurés transforment une intention en parcours exécutable, de l’idée à la livraison.
- EfficientLes workflows multi-agents font avancer recherche, réalisation et revue en parallèle.
- FunHero Dungeon rend les longues sessions de code plus visuelles et collaboratives.