* feat(docs): mirror every docs/ page in all 65 locales Extends the documentation mirrors from the 22-page core set (#13940) to every Markdown page under docs/: 152 sources x 65 locales = 9,880 mirrors (6,208 new), language bars rewritten for the full locale list, state adopted so the blocking drift gate now covers all 152 pages. run-translation.mjs: an oversized block made only of table rows or list items (PROVIDER_REFERENCE.md 244-row table, FREE_TIERS.md 71-item list) is cut at item boundaries and rejoined without a blank line — the single 16-40 KB request outlived the backend socket for verbose scripts. 48 older mirrors whose tables had lost rows were retranslated with --force. * docs(i18n): refresh mirrors for the sources the base changed since the branch cut Section-level retranslation of the 29 docs (and README.md) whose source or mirrors moved on release/v3.8.51 during the run, then state adoption; the drift gate is green again on the merged tree.
71 KiB
Memory System (Nederlands)
🌐 Languages: 🇺🇸 English · 🇪🇹 am · 🇸🇦 ar · 🇦🇿 az · 🇧🇬 bg · 🇧🇩 bn · 🇨🇿 cs · 🇩🇰 da · 🇩🇪 de · 🇬🇷 el · 🇪🇸 es · 🇪🇪 et · 🇮🇷 fa · 🇫🇮 fi · 🇫🇷 fr · 🇮🇪 ga · 🇮🇳 gu · 🇳🇬 ha · 🇮🇱 he · 🇮🇳 hi · 🇭🇷 hr · 🇭🇺 hu · 🇦🇲 hy · 🇮🇩 id · 🇳🇬 ig · 🇮🇹 it · 🇯🇵 ja · 🇬🇪 ka · 🇰🇭 km · 🇮🇳 kn · 🇰🇷 ko · 🇱🇹 lt · 🇱🇻 lv · 🇮🇳 ml · 🇮🇳 mr · 🇲🇾 ms · 🇲🇹 mt · 🇲🇲 my · 🇳🇵 ne · 🇳🇴 no · 🇮🇳 or · 🇮🇳 pa · 🇵🇭 phi · 🇵🇱 pl · 🇵🇹 pt · 🇧🇷 pt-BR · 🇷🇴 ro · 🇷🇺 ru · 🇱🇰 si · 🇸🇰 sk · 🇸🇮 sl · 🇷🇸 sr · 🇸🇪 sv · 🇰🇪 sw · 🇮🇳 ta · 🇮🇳 te · 🇹🇭 th · 🇹🇷 tr · 🇺🇦 uk-UA · 🇵🇰 ur · 🇺🇿 uz · 🇻🇳 vi · 🇳🇬 yo · 🇨🇳 zh-CN · 🇹🇼 zh-TW
Bron van waarheid:
src/lib/memory/ensrc/app/api/memory/Laatst bijgewerkt: 2026-06-28 — v3.8.40 (standaard uitgeschakeld + inhaalslag voor int8-kwantisatie)
OmniRoute biedt permanent gespreksgeheugen, gekoppeld aan een API-sleutel (en optioneel een sessie-id). Herinneringen worden automatisch uit LLM-antwoorden geëxtraheerd via lichtgewicht patroonherkenning met reguliere expressies en opnieuw in volgende verzoeken geïnjecteerd als een voorafgaand systeembericht (of als eerste gebruikersbericht voor providers die de systeemrol weigeren).
Geheugen is standaard UITGESCHAKELD (v3.8.30+).
DEFAULT_MEMORY_SETTINGS.enabledis nufalse(src/lib/memory/settings.ts). Als geheugen wordt ingeschakeld, wordt maximaalmaxTokens(~2k) aan opgehaalde context in elk chatverzoek geïnjecteerd, wat in rekening wordt gebracht — een onverwachte kostenpost voor nieuwe installaties en voor clients die hun eigen context beheren. Schakel dit expliciet in via Instellingen → Geheugen (deMemorySkillsTabtoont een waarschuwing over tokenkosten wanneer geheugen is ingeschakeld). Een client kan geheugen voor één verzoek uitschakelen met de verzoekheaderx-omniroute-no-memory(true/1/yes) — zie de tabel met verzoekheaders in API_REFERENCE.md. Een verzoek zonder geheugen steltmemoryOwnerId = nullin, waardoor zowel geheugeninjectie als vaardigheidsinjectie voor dat verzoek wordt uitgeschakeld (open-sse/handlers/chatCore/headers.ts::isNoMemoryRequested).
Geheugen is afgebakend per API-sleutel, niet per gebruiker — elk verzoek dat
met dezelfde API-sleutel wordt geauthenticeerd, deelt dezelfde geheugenverzameling,
met optionele verdere afbakening via sessionId.
Architectuur
Client → /v1/chat/completions (apiKeyInfo eerder bepaald)
→ handleChatCore() [open-sse/handlers/chatCore.ts]
→ resolveMemoryOwnerId(apiKeyInfo) # haalt id op
→ getMemorySettings() # instellingen in cache
→ shouldInjectMemory(body, {enabled}) # controlepoort
→ retrieveMemories(apiKeyId, config) # SQL + FTS5 + optioneel vector
→ injectMemory(body, memories, provider) # systeem- of gebruikersbericht
→ aanroep van upstream-provider
→ bij antwoord: extractFacts(text, apiKeyId, sessionId) # niet-blokkerend
→ setImmediate → createMemory(fact) per overeenkomst
→ embed(content) + upsertVector(id, vec)
De aanroeplocaties voor injectie en extractie zijn gekoppeld in
open-sse/handlers/chatCore.ts (zoek naar retrieveMemories, injectMemory
en extractFacts).
Engine-architectuur (resolutie met 3 niveaus)
De Memory Engine bepaalt tijdens runtime het ophaalpad op basis van de beschikbare infrastructuur en instellingen. Er bestaan drie niveaus, toegepast in volgorde van prioriteit:
┌─────────────────────────────────────────────────────────────┐
│ NIVEAU 0 — Trefwoord (FTS5) │
│ Beschikbaarheid bepaald via een controle: FTS5 wanneer de │
│ SQLite-build dit ondersteunt (better-sqlite3 / node:sqlite /│
│ bun:sqlite); niet beschikbaar in builds zonder FTS5 (bijv. │
│ sql.js/WASM — "no such module: fts5"). Gebruikt wanneer │
│ strategy = "exact" of als terugvaloptie; de trefwoordstatus │
│ van de engine weerspiegelt de controle. │
└──────────────────────────────────┬──────────────────────────┘
│ strategy = semantic|hybrid?
▼
┌─────────────────────────────────────────────────────────────┐
│ NIVEAU 1 — Ingebedde vector (sqlite-vec) │
│ sqlite-vec v0.1.9 geladen via db.loadExtension(). │
│ KNN-brute-force over Float32-vectoren. Actief wanneer: │
│ • sqlite-vec loadExtension slaagt │
│ • Een bron voor embeddings beschikbaar is (remote | static│
│ | transformers) die een Float32Array kan produceren │
│ • De tabel vec_memories bestaat (aangemaakt bij de eerste │
│ ready()) │
└──────────────────────────────────┬──────────────────────────┘
│ qdrant.enabled?
▼
┌─────────────────────────────────────────────────────────────┐
│ NIVEAU 2 — Qdrant (optionele externe vectordatabase) │
│ Indien ingeschakeld, vervangt dit sqlite-vec voor semantic/ │
│ hybrid. Vereist een actieve Qdrant-instantie en een │
│ geconfigureerde host/poort. │
└─────────────────────────────────────────────────────────────┘
Degradatie verloopt automatisch en transparant:
- Als sqlite-vec niet kan worden geladen, is niveau 1 niet beschikbaar → er wordt teruggevallen op niveau 0.
- Als de bron voor embeddings een fout retourneert, valt niveau 1 terug op niveau 0.
- Als Qdrant niet goed functioneert, valt niveau 2 terug op niveau 1 (of op niveau 0 als niveau 1 eveneens niet beschikbaar is).
Embeddingbronnen
De embeddinglaag (src/lib/memory/embedding/) bepaalt welke bron moet worden gebruikt
op basis van MemorySettingsExtended.embeddingSource:
| Bron | Beschrijving | Sleutel vereist | Koude start |
|---|---|---|---|
remote |
Gebruikt de embedding-API van een geconfigureerde provider (OpenAI, Cohere, enz.) | Ja | Geen |
static |
Lokale embedding via een opzoektabel met potion-base-8M (WordPiece + mean pooling) |
Nee | ~200ms |
transformers |
Lokale ONNX-inferentie via @huggingface/transformers v4, all-MiniLM-L6-v2 |
Nee | ~3s + ~400MB RAM |
auto |
Resolutie tijdens runtime: remote (als sleutel bestaat) → static → transformers → null | Hangt ervan af | Hangt ervan af |
Resolutievolgorde voor auto:
- Zoek de eerste provider in
listEmbeddingProviders()waarvoorhasKey === true→remote. - Als
settings.staticEnabled === true→static. - Als
settings.transformersEnabled === true→transformers. - Anders →
null(valt terug op zoeken met FTS5-trefwoorden).
De embeddingcache (src/lib/memory/embedding/cache.ts) gebruikt een LRU-map in het geheugen,
met ${source}:${model}:${dim}:${sha256(text)} als sleutel, beperkt tot
MEMORY_EMBEDDING_CACHE_MAX vermeldingen (standaard 1000) met een TTL van
MEMORY_EMBEDDING_CACHE_TTL_MS (standaard 5 min). Deze wordt gedurende de levenscyclus
van het proces door alle aanroepers gedeeld.
Hybride RRF (k=60)
Wanneer strategy = "hybrid" en de vectoropslag beschikbaar is, gebruikt het ophalen
Reciprocal Rank Fusion om FTS5- en vectorresultaten samen te voegen:
RRF(d) = Σ 1 / (k + rank_i(d)) waarbij k = 60 (configureerbaar via MEMORY_RRF_K)
i
Concreet:
- Voer een FTS5-zoekopdracht uit → gerangschikte lijst
R_fts(positie 1..N). - Voer een KNN-vectorzoekopdracht uit → gerangschikte lijst
R_vec(positie 1..M). - Voor elke unieke
memoryId:
rrf_score = 1/(60 + fts_rank)+1/(60 + vec_rank)(0 indien niet in de lijst). - Sorteer op
rrf_scoreDESC en pas vervolgens de verwerking volgens het tokenbudget toe.
Van RRF is bekend dat het effectief is zonder dat scorenormalisatie tussen
heterogene ophaalsystemen nodig is. De standaardwaarde k=60 is afkomstig uit het
oorspronkelijke artikel van Cormack et al. en werkt goed voor kleine corpora (<10k herinneringen).
Backfill (lui + herindexering)
Wanneer het embeddingmodel verandert (gedetecteerd via embedding_signature), wordt de
vectoropslag opnieuw opgebouwd en worden alle bestaande herinneringen in de tabel
memories gemarkeerd met needs_reindex = 1.
Luie backfill: Bij de volgende ophaalactie wordt elke herinnering zonder vectorvermelding
geëmbed en vóór de zoekopdracht in vec_memories ingevoegd. Hierdoor worden de kosten
van de backfill over daadwerkelijke aanvragen verdeeld zonder het opstarten te blokkeren.
Expliciete herindexering: Het tabblad Engine in /dashboard/memory bevat een knop
"Nu herindexeren" die POST /api/memory/reindex aanroept. De handler roept
runReindexBatch() aan vanuit src/lib/memory/reindex.ts, waarmee per aanvraag maximaal
limit openstaande vermeldingen worden verwerkt. De voortgang kan worden opgevraagd via
GET /api/memory/engine-status (vectorStore.needsReindex).
De tabel memory_vec_meta (migratie 083_memory_vec.sql) slaat het volgende op:
active_dim— huidige vectordimensie (null = nog niet gekalibreerd).embedding_signature—${source}:${model}:${dim}, gebruikt om wijzigingen te detecteren.last_reset_at— tijdstempel van de laatste volledige reset.vec_loaded— 0/1-vlag die aangeeft of sqlite-vec succesvol is geladen.
Instellingenuitbreiding
Negen velden voor embeddings en vectoren zijn beschikbaar in MemorySettingsExtended in
src/shared/schemas/memory.ts en worden opgeslagen via src/lib/db/settings.ts:
| Veld | Type | Standaard | Beschrijving |
|---|---|---|---|
embeddingSource |
"remote" | "static" | "transformers" | "auto" |
"auto" |
Welke embeddingbron moet worden gebruikt |
embeddingProviderModel |
string | null |
null |
Provider/model in de notatie provider/model |
customBaseUrl |
string | null |
null |
Alleen voor geheugen bestemde OpenAI-compatibele basis-URL van het endpoint |
customModelId |
string | null |
null |
Model-ID dat naar het aangepaste endpoint wordt verzonden |
transformersEnabled |
boolean |
false |
Opt-in voor Transformers.js (MiniLM, ~400 MB) |
staticEnabled |
boolean |
false |
Opt-in voor het lokale statische model potion-base-8M |
rerankEnabled |
boolean |
false |
Herrangschikkingsstap inschakelen (voegt +200-500 ms/verzoek toe) |
rerankProviderModel |
string | null |
null |
Provider/model voor herrangschikking in de notatie provider/model |
vectorStore |
"sqlite-vec" | "qdrant" | "auto" |
"auto" |
Welke vectorbackend moet worden gebruikt |
Deze zijn beschikbaar via GET /PUT /api/settings/memory (schema MemorySettingsExtendedSchema).
Voor de bron remote accepteert Memory ook de optionele instellingen customBaseUrl en
customModelId. Samen selecteren ze een OpenAI-compatibel /embeddings-endpoint
en model zonder het globale embeddingregister te wijzigen. Het endpoint wordt vóór
gebruik genormaliseerd en gecontroleerd aan de hand van het beleid van de provider
voor uitgaande URL's: HTTP(S) is vereist, ingesloten aanmeldgegevens en querystrings
worden geweigerd en cloudmetadata-adressen blijven geblokkeerd. Lege waarden behouden
de geselecteerde registerprovider. Fouten die aan het dashboard worden geretourneerd,
worden opgeschoond en endpointaanmeldgegevens worden nooit gelogd.
TODO (D20): Het bereik
global(geheugens delen tussen alle API-sleutels) is niet geïmplementeerd in deze release. Hiervoor zijn schemawijzigingen en een globaal ophaalpad vereist. Houd dit afzonderlijk bij.
Opslaglagen
Primair: SQLite (memories-tabel)
Aangemaakt door migratie 015_create_memories.sql:
| Kolom | Type | Opmerkingen |
|---|---|---|
id |
TEXT PRIMARY KEY |
UUID gegenereerd via crypto.randomUUID() |
api_key_id |
TEXT NOT NULL |
API-sleutel van de eigenaar |
session_id |
TEXT |
Optioneel bereik per gesprek |
type |
TEXT NOT NULL |
Een van factual, episodic, procedural, semantic |
key |
TEXT |
Stabiele upsertsleutel, bijvoorbeeld preference:i_prefer_python |
content |
TEXT NOT NULL |
De daadwerkelijke tekst van het feit |
metadata |
TEXT |
JSON-blob (categorie, extractedAt, bron, ...) |
created_at / updated_at |
TEXT |
ISO 8601-tekenreeksen |
expires_at |
TEXT |
Optionele vervaldatum; NULL betekent permanent |
memory_id |
INTEGER UNIQUE |
Toegevoegd door 023_fix_memory_fts_uuid.sql om UUID's ↔ FTS5-rowids te koppelen |
Indexen: api_key_id, session_id, type, expires_at, plus de unieke
memory_id-index.
Upsert-semantiek: createMemory() zoekt naar een bestaande rij met dezelfde
(api_key_id, key) en werkt deze ter plaatse bij wanneer die wordt gevonden (waarbij
metadata via een oppervlakkige spread wordt samengevoegd). Dit voorkomt dat de tabel
onbegrensd groeit bij herhaalde voorkeursverklaringen.
Zoeken in volledige tekst (virtuele tabel memory_fts)
022_add_memory_fts5.sql maakt een virtuele FTS5-tabel aan voor content en
key. 023_fix_memory_fts_uuid.sql verhelpt een praktijkbug waarbij de primaire
UUID-sleutel niet kon worden gekoppeld aan de integer-rowid van FTS5 — de migratie voegt
de kolom memory_id toe, maakt de FTS-tabel opnieuw aan en configureert triggers
(memory_fts_ai, memory_fts_ad, memory_fts_au) die FTS gesynchroniseerd houden bij
INSERT, DELETE en UPDATE.
Wordt door retrieval.ts gebruikt voor de strategieën semantic en hybrid (zie hieronder).
De ophaalcode voert een controle uit met hasTable("memory_fts") en valt terug op
chronologische volgorde als de FTS-tabel ontbreekt of de FTS-query een fout veroorzaakt.
Optioneel: Qdrant (vectoropslag van niveau 2)
src/lib/memory/qdrant.ts implementeert een optionele Qdrant-integratie als vectoropslag
van niveau 2. Ophalen wordt alleen naar Qdrant gerouteerd wanneer de engine-selector
memoryVectorStore === "qdrant" is — de standaardwaarde "auto" (en "sqlite-vec")
selecteert Qdrant nooit. De schakelaar op het tabblad Engine stelt zowel
qdrantEnabled als memoryVectorStore gelijktijdig in: inschakelen maakt Qdrant de
primaire opslag, terwijl uitschakelen de instelling terugzet op "auto" (#5597 — vóór
die correctie had inschakelen geen effect omdat niets de engine-selector instelde).
Als Qdrant onbereikbaar is of niets retourneert, valt het ophalen terug op
sqlite-vec → FTS5.
upsertSemanticMemoryPoint()— embedtkey + contentmet het geconfigureerde embeddingmodel, zorgt dat de collectie bestaat (maakt bij het eerste gebruik vectoren met cosinusafstand aan) en voegt een punt toe of werkt het bij met payload{memoryId, apiKeyId, sessionId, key, content, metadata, createdAtUnix, expiresAtUnix}.searchSemanticMemory(query, topK, scope)— embedt de query, doorzoekt de collectie, gefilterd opkind = "omniroute_memory"en optioneel opapiKeyId/sessionId. BegrensdtopKtot[1, 20].deleteSemanticMemoryPoint(id)— verwijdert één punt. Wordt aangeroepen doordeleteMemory()nadat de SQLite-rij is verwijderd (D15).cleanupSemanticMemoryPoints({retentionDays})— verwijdert in bulk punten waarvanexpiresAtUnixin het verleden ligt of waarvancreatedAtUnixouder is dan de bewaarlimiet. Telt deze eerst, zodat het dashboard de werkelijke aantallen kan tonen.checkQdrantHealth()—GET /readyz-statuscontrole met latentie.
De instellingeninterface biedt Qdrant-configuratie, een statuscontrole, een semantische zoektest
en opschoning op het tabblad Engine van /dashboard/memory. De bijbehorende
routes onder src/app/api/settings/qdrant/ zijn allemaal aangesloten sinds v3.8.6:
| Route | Methode | Beschrijving |
|---|---|---|
/api/settings/qdrant |
GET / PUT |
Qdrant-instellingen lezen / bijwerken |
/api/settings/qdrant/health |
GET |
Beschikbaarheidscontrole + latentie |
/api/settings/qdrant/search |
POST |
Semantische zoektest |
/api/settings/qdrant/cleanup |
POST |
Verlopen / oude punten verwijderen |
/api/settings/qdrant/embedding-models |
GET |
Beschikbare embeddingmodellen weergeven |
Gedragsopmerkingen (wat u kunt verwachten):
- Engineselectie — wanneer Qdrant op het tabblad Engine wordt ingeschakeld, wordt
dit de primaire opslag (stelt
memoryVectorStore="qdrant"in); uitschakelen zet dit terug op"auto"(#5597). - Geen backfill — alleen geheugens die nadat Qdrant is ingeschakeld worden aangemaakt/bijgewerkt, worden ernaar geschreven (asynchrone dubbele schrijfbewerking). Reeds bestaande SQLite-geheugens worden niet gemigreerd; "Nu opnieuw indexeren" bouwt alleen de sqlite-vec-index opnieuw op, niet Qdrant.
- Vectordimensie wordt automatisch gedetecteerd aan de hand van de daadwerkelijke embedding bij het eerste gebruik — er hoeft geen dimensieveld te worden ingevuld. Het wijzigen van het embeddingmodel nadat een collectie bestaat, wordt niet automatisch afgehandeld: de bestaande collectie blijft ongewijzigd, schrijfbewerkingen/zoekopdrachten met niet-overeenkomende dimensies mislukken en vallen terug op sqlite-vec. Maak de collectie opnieuw aan (nieuwe naam, of verwijder deze in Qdrant) om van embedder te wisselen.
- Afstandsmetriek — altijd Cosine (hardgecodeerd bij het aanmaken van de collectie; niet configureerbaar).
- Authenticatie — alleen API-sleutel (verzonden als de
api-key-header; optioneel voor niet-geverifieerde lokale Docker). JWT/RBAC worden niet gebruikt. - Configuratievelden — de interface biedt
host,port,collection,embeddingModel,apiKey.vectorSize/hnswEfConstructzijn alleen beschikbaar via omgeving/database envectorSizewordt niet gebruikt bij het aanmaken van de collectie (de dimensie is afkomstig van de embedding).
Vectorkwantisatie (int8 — opt-in, beide backends)
Beide vectorbackends ondersteunen optionele int8-kwantisatie om de geheugenvoetafdruk van opgeslagen vectoren te verkleinen (~4× kleiner dan Float32), ten koste van een licht verlies aan recall. De standaardinstelling is voor beide uit — vectoren behouden hun volledige precisie, tenzij dit expliciet wordt ingeschakeld.
| Backend | Instelling | Type | Standaard | Waar uitgelezen |
|---|---|---|---|---|
| Qdrant | qdrantQuantization (DB-sleutel) |
"none" | "int8" | "binary" |
"none" |
src/lib/memory/qdrant.ts::normalizeQdrantConfig() |
| sqlite-vec | MEMORY_VEC_QUANTIZATION (omgeving) |
"none" | "int8" |
"none" |
src/lib/memory/vectorStore.ts::requestedVecQuantization() |
- Qdrant wordt per instantie geconfigureerd via de instellingssleutel
qdrantQuantization(beschikbaar als het veldquantizationbijPUT /api/settings/qdrant). Bij"int8"vraagtbuildQuantizationConfig()scalaire kwantisatie aan (always_ram, kwantiel0.99) en schakelen zoekopdrachtenrescore: truein, zodat de vectoren met volledige precisie de int8-kandidatenset verfijnen. - sqlite-vec-kwantisatie is alleen via de omgeving beschikbaar (geen
database-instelling): stel
MEMORY_VEC_QUANTIZATION=int8in om de lokale vectoren viavec_quantize_int8(?, 'unit')als eenint8[dim]-kolom op te slaan. De gekozen modus wordt opgenomen in deembedding_signature(een:int8-achtervoegsel), zodat het wisselen van modus een volledige herindexering van de tabelvec_memoriesactiveert — hetzelfde pad voor luie backfill dat wordt gebruikt wanneer het embeddingmodel verandert.
Geheugentypen
MemoryType (src/lib/memory/types.ts):
| Type | Gebruikt voor |
|---|---|
factual |
Voorkeuren, stabiele gebruikersfeiten, gedragspatronen |
episodic |
Beslissingen gekoppeld aan een specifiek moment ("Ik koos Postgres") |
procedural |
Workflow-/werkwijzegeheugen (gereserveerd; momenteel geen automatische extractor) |
semantic |
Gereserveerd voor vector-store-items |
De ophaalstrategie van MemoryConfig is exact, semantic of hybrid,
en het bereik is session, apiKey of global. Het standaardbereik van
getMemorySettings() is apiKey.
Feitenextractie (extraction.ts)
Extractie is gebaseerd op reguliere expressies, niet op een LLM — deze wordt in-process uitgevoerd met
setImmediate(), zodat de responsstream nooit wordt geblokkeerd:
- Voorkeurspatronen →
MemoryType.FACTUAL(bijv.I prefer …,I really like …,my favorite is …,I hate …) - Beslissingspatronen →
MemoryType.EPISODIC(bijv.I'll use …,I chose …,I went with …,I'm going to adopt …) - Gedragspatronen →
MemoryType.FACTUAL(bijv.I usually …,I always …,I tend to …)
Elke overeenkomst wordt opgeschoond (trim, witruimte samengevoegd, beperkt tot 500 tekens),
binnen de batch gededupliceerd via een stabiele factKey(category, content), en
opgeslagen via createMemory() met metadata
{category, extractedAt, source: "llm_response"}. Invoertekst is beperkt tot
64 KiB (MAX_EXTRACTION_TEXT_LENGTH) — wanneer de tekst langer is, wordt het einde van de tekst
gebruikt, zodat de meest recente assistentinhoud altijd wordt meegenomen.
extractFactsFromText(text) wordt geëxporteerd voor tests en retourneert de gestructureerde
feiten zonder ze op te slaan.
Ophalen (retrieval.ts)
retrieveMemories(apiKeyId, config) is het belangrijkste toegangspunt. De functie:
- Normaliseert en valideert de configuratie via
MemoryConfigSchema. - Retourneert onmiddellijk
[]wanneerenabledfalse is ofmaxTokens <= 0. - Begrensd
maxTokenstot[1, 8000]. - Detecteert of de moderne tabel
memoriesbestaat (in plaats van de verouderde tabelmemory), zodat oudere databases blijven werken. - Bouwt de basisquery met een vervalvoorwaarde
(
expires_at IS NULL OR datetime(expires_at) > datetime('now')), een optioneel sessiebereik en een optioneleretentionDays-grens. - Maakt een vertakking op basis van de strategie:
exact(standaard): chronologisch metORDER BY created_at DESC LIMIT 100.semantic: alsconfig.queryis opgegeven enmemory_ftsbestaat, wordtmemory_fts MATCH ?via een JOIN gekoppeld en op FTS-rangschikking gesorteerd; er wordt teruggevallen op chronologische resultaten wanneer FTS 0 rijen retourneert.hybrid: een combinatie van FTS-resultaten (hogere relevantie) en de chronologische set, gededupliceerd op id.
- Berekent een relevantiescore op basis van trefwoorden (
getRelevanceScore) voorcontent,keyen de JSON inmetadatawanneer een query is opgegeven. Rijen met een score van nul worden uitgefilterd. - Sorteert aflopend op score en vervolgens aflopend op
createdAt. - Doorloopt de gerangschikte lijst en accepteert items zolang een doorlopende
estimateTokens(content)(≈length / 4) binnen het budget blijft. Retourneert altijd ten minste één item wanneer er een overeenkomst is.
estimateTokens wordt geëxporteerd en gebruikt door de ophaalfunctie, de samenvattingsfunctie en de MCP-tool
omniroute_memory_search.
Injectie (injection.ts)
injectMemory(request, memories, provider):
- Voegt alle geheugeninhoud samen tot één tekenreeks
Memory context: …. - Kiest een strategie op basis van de providernaam:
- Systeembericht (standaard voor OpenAI, Anthropic, Gemini, …) — voegt
een
{role: "system", content: memoryText}toe vóór bestaande systeemberichten, zodat systeemprompts van gebruikers voorrang blijven houden. - Gebruikersbericht (terugvaloptie) — voor providers in
PROVIDERS_WITHOUT_SYSTEM_MESSAGE:o1,o1-mini,o1-preview,glm,glmt,glm-cn,zai,qianfan. Deze weigeren de systeemrol en zouden anders een 400-fout retourneren (zie issue #1701 voor GLM/Zhipu).
- Systeembericht (standaard voor OpenAI, Anthropic, Gemini, …) — voegt
een
- Logt het aantal, de strategie en het model onder
memory.injection.injected.
providerSupportsSystemMessage(provider) wordt geëxporteerd voor aanroepers die
zelf routeringsbeslissingen moeten nemen. Onbekende providers gebruiken voor de
veiligheid standaard true (systeemrol toegestaan).
Instellingen (settings.ts)
De geheugenconfiguratie wordt opgeslagen in de instellingentabel van de DB,
niet in omgevingsvariabelen. getMemorySettings() leest uit getSettings() en
cachet het resultaat binnen het proces; invalidateMemorySettingsCache() wordt
na schrijfbewerkingen aangeroepen door de PUT-route voor instellingen.
Verouderde velden (alle versies)
| DB-sleutel | Type | Standaardwaarde | UI-besturingselement |
|---|---|---|---|
memoryEnabled |
boolean | false (standaard uitgeschakeld sinds v3.8.30) |
Geheugen aan/uit |
memoryMaxTokens |
integer | 2000 (bereik 0–16000) |
Tokenbudget voor injectie |
memoryRetentionDays |
integer | 30 (bereik 1–365) |
Bewaarperiode |
memoryStrategy |
enum | "hybrid" (één van recent, semantic, hybrid) |
Ophaalstrategie |
skillsEnabled |
boolean | false |
Schakelt vaardigheidsinjectie per sleutel in/uit (zie SKILLS.md) |
Opmerking: de UI-strategie "recent" wordt via toMemoryRetrievalConfig()
toegewezen aan de interne ophaalstrategie "exact" (chronologische volgorde).
Nieuwe velden (v3.8.6, plan 21 D9)
Zie ook de sectie "Uitbreiding van instellingen" hierboven voor beschrijvingen van de velden.
| DB-sleutel | API-veld | Standaardwaarde |
|---|---|---|
memoryEmbeddingSource |
embeddingSource |
"auto" |
memoryEmbeddingModel |
embeddingProviderModel |
null |
memoryTransformersEnabled |
transformersEnabled |
false |
memoryStaticEnabled |
staticEnabled |
false |
memoryRerankEnabled |
rerankEnabled |
false |
memoryRerankModel |
rerankProviderModel |
null |
memoryVectorStore |
vectorStore |
"auto" |
Qdrant-gerelateerde DB-sleutels (qdrantEnabled, qdrantHost, qdrantPort,
qdrantApiKey, qdrantCollection met standaardwaarde "omniroute_memory",
qdrantEmbeddingModel met standaardwaarde "openai/text-embedding-3-small")
worden gelezen door normalizeQdrantConfig() in qdrant.ts.
Omgevingsvariabelen (v3.8.6)
Zes optionele omgevingsvariabelen stemmen het runtimegedrag van de engine af
(gedocumenteerd in .env.example):
| Variabele | Standaardwaarde | Beschrijving |
|---|---|---|
MEMORY_EMBEDDING_CACHE_TTL_MS |
300000 |
TTL van de embeddingcache (5 min) |
MEMORY_EMBEDDING_CACHE_MAX |
1000 |
Maximumaantal items in de LRU-cache voor embeddings |
MEMORY_TRANSFORMERS_MODEL |
Xenova/all-MiniLM-L6-v2 |
HF-repository voor het Transformers.js-model |
MEMORY_STATIC_MODEL |
minishlab/potion-base-8M |
HF-repository voor het statische potion-model |
MEMORY_STATIC_CACHE_DIR |
<DATA_DIR>/embeddings |
Locatie waar gedownloade modellen worden opgeslagen |
MEMORY_VEC_TOP_K |
20 |
Standaardwaarde voor top-K bij vectorzoekopdrachten |
MEMORY_RRF_K |
60 |
RRF-k-constante voor hybride zoekopdrachten |
MEMORY_VEC_QUANTIZATION |
none |
Stel in op int8 om lokale sqlite-vec-vectoren gekwantiseerd op te slaan (~4× kleiner; opt-in). Een moduswijziging dwingt herindexering af. |
Samenvatting (summarization.ts)
summarizeMemories(apiKeyId, sessionId?, maxTokens = 4000) comprimeert oudere
inhoud wanneer het totale aantal tokens van de geheugens van een sleutel het
budget overschrijdt. De functie doorloopt rijen aflopend op created_at, behoudt
rijen die passen en vervangt voor de overige rijen content ter plaatse door de
eerste drie zinnen van het origineel. tokensSaved is het verschil in
estimateTokens tussen de oude en nieuwe inhoud.
Deze routine is beschikbaar, maar wordt niet automatisch aangeroepen in de
huidige chatpipeline — roep deze aan vanuit een cron-taak, een beheerdersactie of
MemoryConfig.autoSummarize-koppelcode als doorlopende compressie nodig is. Het
gegevensverlies is onomkeerbaar: de oorspronkelijke tekst wordt overschreven.
REST-API
Alle endpoints vereisen beheerdersauthenticatie (requireManagementAuth).
Kernendpoints voor geheugen (bestaand + bijgewerkt)
| Methode | Pad | Beschrijving |
|---|---|---|
GET |
/api/memory |
Gepagineerde lijst met filters: apiKeyId, type, sessionId, q, limit, page, offset. Respons bevat stats.total, stats.tokensUsed, stats.hitRate, cacheStats |
POST |
/api/memory |
Item aanmaken (gevalideerd met Zod: content, key, optioneel type, sessionId, apiKeyId, metadata, expiresAt). Roept createMemory() aan, die een upsert uitvoert op (apiKeyId, key) |
GET |
/api/memory/[id] |
Eén item ophalen op basis van UUID |
PUT |
/api/memory/[id] |
Itemvelden bijwerken (type, key, content, metadata). Body: MemoryUpdatePutSchema. Synchroniseert ook de vector als een bron voor embeddings beschikbaar is. |
DELETE |
/api/memory/[id] |
Een item verwijderen; verwijdert dit ook uit vec_memories (D15) en waar mogelijk uit Qdrant. Retourneert 404 wanneer het item ontbreekt. |
GET |
/api/memory/health |
Voert verifyExtractionPipeline("health-check") uit — volledige cyclus aanmaken→weergeven→verwijderen. Retourneert {working, latencyMs, error?} |
Nieuwe endpoints voor de geheugenengine (plan 21)
| Methode | Pad | Beschrijving |
|---|---|---|
POST |
/api/memory/retrieve-preview |
Proefuitvoering van retrieveMemories — retourneert gerangschikte resultaten met score, niveau en tokens. Body: RetrievePreviewSchema. Injecteert of wijzigt GEEN geheugens. |
GET |
/api/memory/embedding-providers |
Geeft providers met embeddingmodellen weer en vermeldt voor welke een API-sleutel is geconfigureerd. |
GET |
/api/memory/engine-status |
Retourneert de volledige enginestatus: trefwoordniveau, embeddingresolutie, vectoropslagstatistieken, Qdrant-status en configuratie voor herrangschikking. Vorm: MemoryEngineStatusSchema. |
POST |
/api/memory/summarize |
Handmatig geheugencompressie starten. Body: MemorySummarizeSchema (olderThanDays, apiKeyId?, dryRun). Retourneert {candidates, tokensSaved}. |
POST |
/api/memory/reindex |
Vectorherindexering starten voor geheugens met needs_reindex=1. Body: MemoryReindexSchema (force). Retourneert {started, pending}. |
Instellingenendpoints
| Methode | Pad | Beschrijving |
|---|---|---|
GET |
/api/settings/memory |
Huidige genormaliseerde MemorySettingsExtended (7 nieuwe velden + verouderde velden) |
PUT |
/api/settings/memory |
Elk veld uit MemorySettingsExtendedSchema bijwerken (in totaal 12 velden) |
GET |
/api/settings/qdrant |
Huidige Qdrant-instellingen (QdrantSettingsSchema) |
PUT |
/api/settings/qdrant |
Qdrant-instellingen bijwerken. Body: QdrantSettingsUpdateSchema. apiKey = lege tekenreeks verwijdert de sleutel. |
GET |
/api/settings/qdrant/health |
Beschikbaarheidscontrole van het geconfigureerde Qdrant-exemplaar. Retourneert QdrantHealthResultSchema. |
POST |
/api/settings/qdrant/search |
Semantische zoektest in Qdrant. Body: QdrantSearchSchema (query, topK). |
POST |
/api/settings/qdrant/cleanup |
Qdrant-punten voor verlopen/oude geheugens verwijderen. |
GET |
/api/settings/qdrant/embedding-models |
Embeddingmodellen weergeven die beschikbaar zijn voor Qdrant. |
De lijstquery van /api/memory ondersteunt paginering op basis van page
(parsePaginationParams) of een onbewerkte offset — wanneer offset
aanwezig is, heeft deze voorrang en wordt een afgeleide page berekend voor de
responsstructuur.
MCP-tools (open-sse/mcp-server/tools/memoryTools.ts)
Wanneer de MCP-server is ingeschakeld, worden drie geheugentools geregistreerd:
omniroute_memory_search—{apiKeyId, query?, type?, maxTokens?, limit?}→ omhultretrieveMemories(). Sinds v3.8.6 (D16) wordt destrategyuitgetMemorySettings()gelezen in plaats van hardgecodeerd als"exact". Alsqueryis opgegeven enstrategysemanticofhybridis, wordt indien beschikbaar de vectoropslag gebruikt.omniroute_memory_add—{apiKeyId, sessionId?, type, key, content, metadata?}→ omhultcreateMemory(). Accepteert alleen de 4 canonieke typen:factual,episodic,procedural,semantic(D17).omniroute_memory_clear—{apiKeyId, type?, olderThan?}→ geeft overeenkomende items weer, filtert optioneel op tijdstempel van vóór de aanmaakdatum en verwijdert vervolgens elk item viadeleteMemory()(waarmee ook vectoren uit sqlite-vec + Qdrant worden verwijderd).
Zie MCP-SERVER.md voor details over transport en bereik.
Dashboard (Memory Studio)
src/app/(dashboard)/dashboard/memory/page.tsx is nu een Studio met 3 tabbladen:
Tabblad: Geheugens
- Conceptkaart (inklapbare uitleg over "Hoe het werkt").
- Realtime lijst, zoeken en paginering (debounce van 300 ms).
- Typefilter (
factual/episodic/procedural/semantic/ alle). - Modaal venster om geheugen toe te voegen (sleutel, inhoud, type).
- Inline bewerken (potloodknop →
PUT /api/memory/[id]). - Verwijderen per rij (met bevestigingsvenster).
- JSON-export van de huidige pagina; JSON-import via bestandskiezer.
- Statistiekkaarten:
totalEntries,tokensUsed,hitRate. - Knop "Oude items comprimeren" →
POST /api/memory/summarize(een dry-run toont eerst het aantal kandidaten, waarna om bevestiging wordt gevraagd). - Een groene/rode gezondheidsindicator aangestuurd door
GET /api/memory/health.
Tabblad: Speeltuin
- Queryinvoer + strategie-selector (Exact / Semantisch / Hybride) + tokenbudget.
- "Simuleren" →
POST /api/memory/retrieve-preview— toont gerangschikte resultaten metscore,tier,tokens,vecScore,ftsScore. - Resolutiepaneel dat toont welke insluitingsbron/vectoropslag is gebruikt en of er een fallback heeft plaatsgevonden.
Tabblad: Engine
- Statuspaneel van de engine (keyword-FTS5-chip, insluitingschip, vectoropslagchip, Qdrant-gezondheidschip, rerank-chip).
- Knop "Nu opnieuw indexeren" →
POST /api/memory/reindex. - Selector voor insluitingsbron (auto / remote / static / transformers + schakelaars).
- Qdrant-configuratiekaart (inschakelschakelaar, host/poort/verzameling/sleutel, verbinding testen, semantische zoektest, opschonen).
- Rerank-configuratiekaart (inschakelschakelaar, provider-/modelselector).
Geheugen- en Qdrant-instellingen zijn ook beschikbaar onder
/dashboard/settings → Memory & Skills (MemorySkillsTab.tsx) voor
de oudere/globale instellingeninterface.
Caching
src/lib/memory/store.ts bevat een procesinterne LRU-achtige cache
(MEMORY_CACHE_TTL = 1 min, MEMORY_MAX_CACHE_SIZE = 500, met verwijdering
van de oudste 20 %) voor getMemory(id)-leesbewerkingen, plus een generieke
sleutel-/waardelaag memoryCache (src/lib/memory/cache.ts) met de methoden
get/set/invalidate, die wordt gebruikt door aanroepers die hun eigen
afgebakende cache willen (LRU met 1 000 items, standaard-TTL van 5 min).
Privacy en levenscyclus
- Het eigenaarschap van geheugenitems wordt bepaald door de API-sleutel-id (
resolveMemoryOwnerIdinchatCore.ts). Zonder eenapiKeyInfo.idworden ophalen, injecteren en extraheren niet uitgevoerd. - Items met een toekomstige
expires_atworden bij het ophalen uitgefilterd; oude items die ouder zijn danretentionDaysworden uitgesloten door decreated_at >= cutoff-clausule inretrieveMemories. - Gebruik voor definitieve verwijdering
DELETE /api/memory/[id]ofomniroute_memory_clear. - Extractie wordt via
setImmediategestart zonder erop te wachten; fouten worden gelogd ondermemory.extraction.background.faileden worden nooit aan de aanroeper doorgegeven. - Verificatierondgangen (
verifyExtractionPipeline) ruimen hun eigen testitems op in eenfinally-blok.
Zie ook
- SKILLS.md — de instelling
skillsEnabledinjecteert tooldefinities naast geheugenitems. - MCP-SERVER.md — MCP-transport / bereiken.
- API_REFERENCE.md — breder API-oppervlak.
- Bronmodules:
src/lib/memory/types.ts,schemas.tssrc/lib/memory/store.ts,retrieval.ts,injection.ts,reindex.tssrc/lib/memory/extraction.ts,summarization.ts,verify.tssrc/lib/memory/settings.ts,qdrant.ts,cache.tssrc/lib/memory/vectorStore.ts— sqlite-vec + hybride RRFsrc/lib/memory/embedding/index.ts— embeddinglaag met meerdere bronnensrc/lib/memory/embedding/types.ts,remote.ts,staticPotion.ts,transformersLocal.ts,cache.tssrc/shared/schemas/memory.ts— Zod-schema's voor alle bodies van de geheugen-APIsrc/shared/schemas/qdrant.ts— Zod-schema's voor Qdrant-instellingen/-bewerkingensrc/lib/db/memoryVec.ts— CRUD voormemory_vec_metasrc/lib/db/migrations/015_create_memories.sql,022_add_memory_fts5.sql,023_fix_memory_fts_uuid.sql,083_memory_vec.sqlsrc/app/api/memory/route.ts,[id]/route.ts,health/route.tssrc/app/api/memory/retrieve-preview/route.tssrc/app/api/memory/engine-status/route.tssrc/app/api/memory/embedding-providers/route.tssrc/app/api/memory/summarize/route.tssrc/app/api/memory/reindex/route.tssrc/app/api/settings/memory/route.tssrc/app/api/settings/qdrant/route.ts+ subroutessrc/app/(dashboard)/dashboard/memory/— Studio-UI (pagina + componenten + tabbladen + hooks)open-sse/handlers/chatCore.ts(koppeling voor injectie / extractie)open-sse/mcp-server/tools/memoryTools.ts
Een embeddingprovider kiezen (v3.8.16+)
De geheugenengine van OmniRoute ondersteunt vier embeddingbronnen (src/lib/memory/embedding/). Elke bron kent andere afwegingen op het gebied van latentie, kosten, modelkwaliteit en complexiteit van de configuratie.
De embeddingbronnen
| Provider | Bron | Latentie | Kosten | Kwaliteit | Configuratie |
|---|---|---|---|---|---|
transformers |
Lokaal ONNX-model (Xenova/all-MiniLM-L6-v2) | ~50-150ms (CPU) | Gratis | Goed | Alleen npm install |
static |
Vooraf berekende vectoren (gecachet) | <1ms | Gratis | N.v.t. (afhankelijk van cachetreffer) | Geen |
remote |
OpenAI- / Cohere- / Voyage-API | ~100-300ms | $0.02-0.10/1M tokens | Uitstekend | API-sleutel |
auto |
Kiest tijdens runtime de beste beschikbare bron | Hetzelfde als de gekozen bron | Gratis | Hetzelfde als de gekozen bron | Geen |
| (cache) | LRU-laag in het geheugen bovenop elke bron | <1ms (treffer), volledige latentie (misser) | Gratis | Hetzelfde als de onderliggende bron | Altijd ingeschakeld (geen selecteerbare bron) |
Beslisboom
Wat is je implementatiecontext?
│
┌───────────┼───────────┬──────────────┐
│ │ │ │
ONTW./TEST KLEINE PROD. GROTE PROD. EDGE / OFFLINE
│ │ │ │
▼ ▼ ▼ ▼
transformers transformers remote (Qdrant) transformers
(gratis, geen API) (beste kwaliteit) (geen internet)
│ │ │ │
└────────┬──┴───────────┴──────────────┘
│
▼
Voeg er ALTIJD een `cache`-laag bovenop toe
(LruCache verpakt elke provider)
Database- en API-configuratie
Opties voor geheugenembeddings worden geconfigureerd via de instellingen-API/UI, niet via omgevingsvariabelen. De relevante databasesleutels onder Instellingen (normalizeMemorySettings in src/lib/memory/settings.ts) zijn:
memoryEmbeddingSource:"transformers"(lokaal),"remote"(API-gebaseerd, bijvoorbeeld OpenAI),"static"(externe opslag) of"auto"memoryEmbeddingProviderModel: Model-ID voor externe/statische bronnen (bijvoorbeeld"text-embedding-3-small")memoryTransformersEnabled:true|falsememoryStaticEnabled:true|falsememoryVectorStore:"sqlite-vec","qdrant"of"auto"
Lokaal model (transformers)
Gebruikt intern transformers.js om lokale modellen uit te voeren:
# Omgevingsvariabelen die in de code worden gelezen (src/lib/memory/embedding/index.ts):
MEMORY_TRANSFORMERS_MODEL=Xenova/all-MiniLM-L6-v2 # HF-modelrepository
MEMORY_STATIC_MODEL=minishlab/potion-base-8M # Statisch HF Potion-model
MEMORY_STATIC_CACHE_DIR=<DATA_DIR>/embeddings # Cachemap
LRU-embeddingcache
De cache is standaard altijd ingeschakeld en wordt geconfigureerd via omgevingsvariabelen:
MEMORY_EMBEDDING_CACHE_MAX=1000 # Maximumaantal gecachete items
MEMORY_EMBEDDING_CACHE_TTL_MS=300000 # TTL (5 min)
Prestatiecijfers
Benchmark op een typische x86-server met 4 cores (teksten van elk ~100 tokens):
| Aanbieder | p50 | p95 | p99 | Kosten / 1 mln. embeddings |
|---|---|---|---|---|
transformers (CPU) |
80ms | 180ms | 350ms | Gratis |
remote (OpenAI) |
120ms | 220ms | 400ms | ~$0.02 (ada-002) / $0.13 (3-large) |
static (Qdrant) |
15ms | 30ms | 60ms | Afhankelijk van Qdrant-hosting |
cache (hit) |
<1ms | <1ms | 2ms | Gratis |
Patronen voor feitenextractie (v3.8.16+)
De module extraction.ts (src/lib/memory/extraction.ts) gebruikt patroonherkenning met reguliere expressies om gestructureerde feiten uit conversatieberichten te extraheren. Inzicht in deze patronen helpt je de extractiekwaliteit af te stemmen op jouw gebruikssituatie.
Standaardpatrooncategorieën
| Categorie | Voorbeeldpatroon | Legt vast |
|---|---|---|
| PREFERENCE_PATTERNS | "Ik geef de voorkeur aan <X>", "Ik vind <X> leuk", "Ik haat <X>" |
Gebruikersvoorkeuren |
| DECISION_PATTERNS | "Ik gebruik <X>", "Ik heb besloten om <X>", "Ik koos voor <X>" |
Gebruikersbeslissingen (episodisch) |
| PATTERN_PATTERNS | "Ik doe meestal <X>", "Ik doe altijd <X>", "Ik doe nooit <X>" |
Aanhoudende gedragspatronen |
Voorbeeldpatronen (vereenvoudigd)
// Uit src/lib/memory/extraction.ts
const 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];
Wat wordt geëxtraheerd
Wanneer een gebruiker zegt:
"Ik geef de voorkeur aan TypeScript. Ik gebruik Postgres voor dit project. Ik commit altijd voordat ik push. Ik vind Python niet leuk." Extractie levert 4 herinneringen op:
Sleutel Categorie Type Inhoud preference:typescriptvoorkeur feitelijk "TypeScript" decision:postgres_for_this_projectbeslissing episodisch "Postgres voor dit project" pattern:commit_before_pushingpatroon feitelijk "commit voordat ik push" preference:pythonvoorkeur feitelijk "Python"
Extractielimieten
Om ongebreidelde extractie te voorkomen, gelden de volgende limieten:
| Minimale inhoudslengte | 3 tekens | | Maximale inhoudslengte | 500 tekens |
Wanneer extractie moet worden uitgeschakeld
Extractie wordt automatisch uitgevoerd wanneer het geheugen is ingeschakeld; er is geen afzonderlijke
schakelaar uitsluitend voor extractie. Schakel het geheugen volledig uit (enabled: false
via PUT /api/settings/memory) om dit uit te zetten. Overweeg dit wanneer:
- Je een groot berichtenvolume hebt en de extractiekosten niet verwaarloosbaar zijn
- Je conversaties voornamelijk tijdelijk zijn (chat, foutopsporing) en geen langetermijnwaarde hebben
- Je context al vastlegt via aangepaste plug-ins
Hybride RRF-afstemming (v3.8.16+)
Het algoritme Reciprocal Rank Fusion (RRF) combineert resultaten van FTS5 (trefwoorden) en vectoren (semantisch). De parameter k bepaalt hoeveel gewicht wordt toegekend aan lager gerangschikte resultaten.
De formule
Voor elke kandidaat-herinnering is de RRF-score:
RRF(d) = Σ 1 / (k + rank_i(d))
Waarbij:
kde constante is (standaard 60)rank_i(d)de positie van documentdin het i-de zoeksysteem is (FTS, vector)- De som over alle zoeksystemen loopt
Hoe k de resultaten beïnvloedt
k-waarde |
Effect | Het meest geschikt voor |
|---|---|---|
k=0 |
Zuivere rangfusie (geen afvlakking) | Theoretische basislijn |
k=10-30 |
Geeft veel gewicht aan de beste resultaten; lage posities dragen nauwelijks bij | Wanneer de top 3-resultaten meestal correct zijn |
k=60 (standaard) |
Evenwichtig — alle top 10-resultaten dragen betekenisvol bij | Zoeken voor algemene doeleinden |
k=100+ |
Vlakker — zelfs laag gerangschikte resultaten kunnen domineren als ze in meerdere systemen voorkomen | Wanneer recall > precisie van cruciaal belang is |
k in de praktijk afstemmen
# Standaard
MEMORY_RRF_K=60
# Agressieve precisie (klein geheugen, weinig documenten)
MEMORY_RRF_K=20
# Maximale recall (groot geheugen, uiteenlopende zoekopdrachten)
MEMORY_RRF_K=120
Voorbeeld met k=20:
- FTS-positie 1 → bijdrage
1/21 = 0.048 - FTS-positie 10 → bijdrage
1/30 = 0.033 - Vectorpositie 1 → bijdrage
0.048 - Gecombineerd maximum:
0.096
Voorbeeld met k=60:
- FTS-positie 1 → bijdrage
1/61 = 0.016 - FTS-positie 10 → bijdrage
1/70 = 0.014 - Vectorpositie 1 → bijdrage
0.016 - Gecombineerd maximum:
0.033
Bij een hogere k is het relatieve verschil tussen de eerste en de tiende positie kleiner, waardoor het algoritme meer vertrouwt op consensus tussen zoeksystemen dan op de betrouwbaarheid van de hoogste positie.
Wanneer k moet worden gewijzigd
| Symptoom | Probeer |
|---|---|
| Het beste resultaat wint altijd, maar is onjuist | Verlaag k (bijv. 20) — betrouwbaarheid van de hoogste positie telt meer |
| Het juiste antwoord staat in de top 5, maar niet bovenaan | Verhoog k (bijv. 100) — vlakkere scores belonen consensus |
| Recall is hoog, maar precisie is laag | Verlaag k — maak de rangschikking scherper |
| Recall is laag (relevante documenten ontbreken) | Verhoog k — geef lager gerangschikte documenten een kans |
RRF-weging
Reciprocal rank fusion gebruikt gelijke gewichten voor de semantische vectorpositie en de positie in de zoekresultaten voor volledige tekst:
RRF(d) = 1/(k + rank_vector) + 1/(k + rank_fts)
Er zijn geen omgevingsvariabelen om de afzonderlijke gewichten aan te passen (MEMORY_RRF_VECTOR_WEIGHT/MEMORY_RRF_FTS_WEIGHT bestaan niet).
Samenvattingsstrategie (v3.8.16+)
De module summarization.ts (src/lib/memory/summarization.ts) comprimeert oudere herinneringen om de actieve set klein te houden en tegelijkertijd het herinneringsvermogen te behouden.
Wanneer samenvatting wordt geactiveerd
| Trigger | Drempelwaarde (standaard) |
|---|---|
| Handmatige trigger via API | n.v.t. |
Wat wordt samengevat
Er worden twee toegangspunten geëxporteerd vanuit summarization.ts:
summarizeMemories(apiKeyId, sessionId?, maxTokens = 4000)— comprimeert de herinneringen voor een sessie tot één samenvattingstekst die binnen een tokenbudget blijft.summarizeMemoriesOlderThan(apiKeyId, days, dryRun)— de op leeftijd gebaseerde compactie die door de API wordt gebruikt: deze selecteert elke herinnering die ouder is dandays, maakt hiervan één gecomprimeerde samenvattingsherinnering en verwijdert (wanneerdryRunfalseis) de originelen. GeefdryRun: truedoor om de kandidatenset en het totale aantal tokens vooraf te bekijken zonder iets te wijzigen.
Er is geen clusterstap op basis van tags/sleutels of score per herinnering voor "kern versus samenvatbaar" — de selectie is uitsluitend gebaseerd op de leeftijdsgrens en de samenvattingstekst is een gecomprimeerde, met het type voorafgegaan regel per kandidaat.
Samenvatting activeren
Samenvatting is handmatig / opt-in — de instelling autoSummarize is standaard
false, waardoor niets automatisch wordt gecomprimeerd. Activeer dit via de API:
curl -X POST http://localhost:20128/api/memory/summarize \
-H "Authorization: Bearer $OMNIROUTE_KEY"
Om dit uitgeschakeld te laten, behoudt u simpelweg de standaardwaarde (false) voor autoSummarize.
Tips voor de kwaliteit van samenvattingen
- Bekijk eerst een voorbeeld met
dryRun—summarizeMemoriesOlderThan(..., true)retourneert de kandidatenlijst en het totale aantal tokens, zodat u kunt controleren wat er zou worden samengevoegd voordat de originelen worden verwijderd. - Voer samenvatting uit tijdens uren met weinig verkeer als u een groot herinneringencorpus hebt — de LLM-aanroep is het langzaamste onderdeel
# Cron-stijl: vat dagelijks om 3 uur 's nachts samen
0 3 * * * curl -X POST http://localhost:20128/api/memory/summarize \
-H "Authorization: Bearer $OMNIROUTE_KEY"
MemoryBackend-providerpatroon
Gezaghebbende bron:
src/lib/memory/backend.ts,src/lib/memory/genericBackend.ts,src/lib/memory/manager.tsTests:src/lib/memory/__tests__/generic-backend.test.ts
Het MemoryBackend-providerpatroon introduceert een verwisselbare backend-abstractielaag bovenop de bestaande herinneringsengine. In plaats van gebonden te zijn aan één opslagimplementatie, ondersteunt het herinneringssysteem nu meerdere backends (SQLite, Obsidian, Notion, aangepaste HTTP-backends) met configureerbare primaire/fallback-routering.
Architectuur
┌──────────────────────────────────────────────────────────┐
│ API-routes │
│ (src/app/api/memory/route.ts) │
└──────────────────────┬───────────────────────────────────┘
│
┌──────────────────────▼───────────────────────────────────┐
│ MemoryManager │
│ Singleton-orchestrator (manager.ts) │
│ │
│ Primair ──► Backend A (bijv. SQLite) │
│ Fallback ─► Backend B (bijv. Obsidian) │
│ Backend C (bijv. Notion via GenericBackend) │
└──────────────────────┬───────────────────────────────────┘
│
┌──────────────┼──────────────┐
▼ ▼ ▼
┌────────────┐ ┌────────────┐ ┌──────────────────┐
│ SQLite │ │ Obsidian │ │ GenericMemory │
│ Backend │ │ Backend │ │ Backend (HTTP) │
└────────────┘ └────────────┘ └──────────────────┘
Kerninterface (backend.ts)
Elke backend moet de interface MemoryBackend implementeren:
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> }>;
// Zoeken
search(config: SearchConfig): Promise<Memory[]>;
// Status
health(): Promise<HealthCheckResult>;
// Levenscyclus (optioneel)
initialize?(): Promise<void>;
shutdown?(): Promise<void>;
}
MemoryManager (manager.ts)
Singleton-orchestrator die:
- backends registreert via
register(backend)— aangeroepen tijdens het opstarten vanuitindex.ts - de primaire backend en fallbacks configureert via
configure(primary, fallbacks) - CRUD-bewerkingen en zoekopdrachten routeert naar de primaire backend, met een fallback-keten bij fouten
- periodiek statuscontroles uitvoert op alle backends
Fallback-gedrag:
| Bewerking | Primair | Fallbacks |
|---|---|---|
create |
✅ Alleen primair | ❌ |
get |
✅ Probeer primair eerst | ✅ Fallback indien null |
update |
✅ Alleen primair | ✅ Asynchrone sync zonder wachten |
delete |
✅ Alleen primair | ✅ Asynchrone sync zonder wachten |
list |
✅ Alleen primair | ❌ |
search |
✅ Primair eerst | ✅ Fallback bij fout |
GenericMemoryBackend (genericBackend.ts)
Een generieke HTTP-connector die elke REST-API aanpast tot een MemoryBackend. Nuttig voor:
- Notion — maak verbinding via de Notion API
- Obsidian — maak verbinding via de Obsidian Local REST API
- Aangepaste backends — elke service die een RESTful herinnerings-API beschikbaar stelt
Configuratie:
interface GenericBackendConfig {
baseUrl: string; // Basis-URL van de backend-API
apiKey?: string; // Bearer-token voor authenticatie
headers?: Record<string, string>; // Aangepaste HTTP-headers
timeout?: number; // Time-out van verzoek (standaard: 30000ms)
backendType?: string; // Voor logboekregistratie
// Endpointoverschrijvingen (standaardwaarden gebruiken REST-conventies)
endpoints?: {
search?: string; // standaard: "/memories/search"
create?: string; // standaard: "/memories"
list?: string; // standaard: "/memories"
get?: string; // standaard: "/memories/{id}"
update?: string; // standaard: "/memories/{id}"
delete?: string; // standaard: "/memories/{id}"
health?: string; // standaard: "/health"
};
// Toewijzingen van queryparameternamen
queryParams?: {
query?/apiKeyId?/limit?/offset?/strategy?/maxTokens?/type?/sessionId?/orderBy?/orderDir?/options?
};
// Toewijzingen van padparameternamen
pathParams?: {
id?/memoryId?
};
}
Bekende backends zijn vooraf geconfigureerd in KNOWN_BACKENDS:
createKnownBackend("obsidian"); // → GenericMemoryBackend gericht op localhost:27123
createKnownBackend("notion"); // → GenericMemoryBackend gericht op api.notion.com/v1
Ingebouwde backends
SQLiteBackend (sqliteBackend.ts)
De standaard primaire backend. Omhult de bestaande SQLite-gebaseerde geheugenopslag via src/lib/memory/store.ts. Wordt automatisch geregistreerd bij het opstarten.
import { sqliteBackend } from "./sqliteBackend";
memoryManager.register(sqliteBackend);
ObsidianBackend (obsidianBackend.ts)
Omhult de bestaande Obsidian-integratie (src/lib/memory/obsidianBackend.ts). Maakt verbinding met een Obsidian-kluis via de Obsidian Local REST API.
Instellingen
Instellingen voor geheugenbackends worden opgeslagen in de tabel met app-instellingen en beheerd via src/lib/memory/settings.ts:
| Instelling | Omgevings-/configuratiesleutel | Standaard | Beschrijving |
|---|---|---|---|
| Primaire backend | memoryPrimaryBackend |
"sqlite" |
ID van de primaire backend |
| Fallback-backends | memoryFallbackBackends |
[] |
Geordende ID's van fallback-backends |
| Backendconfiguraties | memoryBackendConfigs |
{} |
Configuratieoverschrijvingen per backend |
Instellingen worden genormaliseerd via normalizeMemorySettings() en gecachet in getMemorySettings().
Initialisatieproces
Opstarten van app
→ imports van index.ts (neveneffect): registreert SQLiteBackend
→ initMemoryBackends() aangeroepen vanuit de levenscyclus van de app:
1. Instellingen laden (getMemorySettings)
2. Primaire backend + fallback-backends configureren
3. Alle backends initialiseren (statuscontrole)
4. Klaar voor verzoeken
Een nieuwe backend toevoegen
- Implementeer de interface
MemoryBackendinsrc/lib/memory/<name>Backend.ts - Exporteer vanuit
src/lib/memory/index.ts - Registreer bij het opstarten met
memoryManager.register(yourBackend) - Configureer via instellingen: stel
memoryPrimaryBackendin op de ID van uw backend - Test met
src/lib/memory/__tests__/generic-backend.test.tsals referentie
Voorbeeld: Brain-backend
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);
Verificatie
Eenheidstests
npx vitest run src/lib/memory/__tests__/generic-backend.test.ts --reporter=verbose
Verwachte uitvoer: 35 tests, allemaal geslaagd, die het volgende afdekken:
- Constructor (2)
- Statuscontrole (4) — succes, fout 500, netwerkfout, latentie
- Initialiseren (2) — succes, fout
- Aanmaken (2) — standaardendpoint, aangepast endpoint
- Ophalen (4) — succes, 404 → null, niet-404 veroorzaakt uitzondering, aangepaste padparameters
- Bijwerken (2) — succes, 404 → false
- Verwijderen (2) — succes, 404 → false
- Opsommen (2) — queryparameters, aangepaste parameternamen
- Zoeken (3) — queryparameters, aangepast endpoint, serialisatie van opties
- Authenticatieheaders (2) — Bearer-token, aangepaste headers
- Factory (1)
Typecontrole
npm run typecheck:core
Verwacht: 0 fouten.