* 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.
69 KiB
Memory System (Bahasa Indonesia)
🌐 Languages: 🇺🇸 English · 🇪🇹 am · 🇸🇦 ar · 🇦🇿 az · 🇧🇬 bg · 🇧🇩 bn · 🇨🇿 cs · 🇩🇰 da · 🇩🇪 de · 🇬🇷 el · 🇪🇸 es · 🇪🇪 et · 🇮🇷 fa · 🇫🇮 fi · 🇫🇷 fr · 🇮🇪 ga · 🇮🇳 gu · 🇳🇬 ha · 🇮🇱 he · 🇮🇳 hi · 🇭🇷 hr · 🇭🇺 hu · 🇦🇲 hy · 🇳🇬 ig · 🇮🇹 it · 🇯🇵 ja · 🇬🇪 ka · 🇰🇭 km · 🇮🇳 kn · 🇰🇷 ko · 🇱🇹 lt · 🇱🇻 lv · 🇮🇳 ml · 🇮🇳 mr · 🇲🇾 ms · 🇲🇹 mt · 🇲🇲 my · 🇳🇵 ne · 🇳🇱 nl · 🇳🇴 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
Sumber acuan:
src/lib/memory/dansrc/app/api/memory/Terakhir diperbarui: 2026-06-28 — v3.8.40 (dinonaktifkan secara default + penyesuaian kuantisasi int8)
OmniRoute menyediakan memori percakapan persisten yang diidentifikasi berdasarkan kunci API (dan secara opsional id sesi). Memori diekstraksi secara otomatis dari respons LLM melalui pencocokan pola regex ringan dan disuntikkan kembali ke permintaan berikutnya sebagai pesan sistem di awal (atau pesan pengguna pertama untuk penyedia yang menolak peran sistem).
Memori NONAKTIF secara default (v3.8.30+).
DEFAULT_MEMORY_SETTINGS.enabledsekarang bernilaifalse(src/lib/memory/settings.ts). Mengaktifkan memori akan menyuntikkan hinggamaxTokens(~2k) konteks yang diambil ke dalam setiap permintaan percakapan, yang dikenai biaya — biaya tak terduga bagi instalasi baru dan klien yang mengelola konteksnya sendiri. Aktifkan secara eksplisit di Settings → Memory (MemorySkillsTabmenampilkan peringatan biaya token saat memori diaktifkan). Klien dapat mengecualikan satu permintaan dengan header permintaanx-omniroute-no-memory(true/1/yes) — lihat tabel header permintaan di API_REFERENCE.md. Permintaan tanpa memori menetapkanmemoryOwnerId = null, yang menonaktifkan baik penyuntikan memori maupun keahlian untuk permintaan tersebut (open-sse/handlers/chatCore/headers.ts::isNoMemoryRequested).
Memori dicakup per kunci API, bukan per pengguna — setiap permintaan yang diautentikasi
dengan kunci API yang sama berbagi kumpulan memori yang sama, dengan pencakupan tambahan
opsional berdasarkan sessionId.
Arsitektur
Klien → /v1/chat/completions (apiKeyInfo diselesaikan sebelumnya)
→ handleChatCore() [open-sse/handlers/chatCore.ts]
→ resolveMemoryOwnerId(apiKeyInfo) # mengekstrak id
→ getMemorySettings() # pengaturan yang di-cache
→ shouldInjectMemory(body, {enabled}) # gerbang
→ retrieveMemories(apiKeyId, config) # SQL + FTS5 + vektor opsional
→ injectMemory(body, memories, provider) # pesan sistem atau pengguna
→ panggilan penyedia upstream
→ saat merespons: extractFacts(text, apiKeyId, sessionId) # tanpa pemblokiran
→ setImmediate → createMemory(fact) untuk setiap kecocokan
→ embed(content) + upsertVector(id, vec)
Lokasi pemanggilan penyuntikan dan ekstraksi terhubung di
open-sse/handlers/chatCore.ts (cari retrieveMemories, injectMemory,
dan extractFacts).
Arsitektur mesin (resolusi 3 tingkat)
Memory Engine menentukan jalur pengambilan saat runtime berdasarkan infrastruktur dan pengaturan yang tersedia. Terdapat tiga tingkat, yang diterapkan berdasarkan urutan prioritas:
┌─────────────────────────────────────────────────────────────┐
│ TINGKAT 0 — Kata kunci (FTS5) │
│ Ketersediaan berbasis pemeriksaan: FTS5 saat build SQLite │
│ mendukungnya (better-sqlite3 / node:sqlite / bun:sqlite); │
│ tidak tersedia pada build tanpa FTS5 (misalnya sql.js/WASM —│
│ "no such module: fts5"). Digunakan saat strategy = "exact" │
│ atau sebagai fallback; keyword status mesin mencerminkan │
│ hasil pemeriksaan. │
└──────────────────────────────────┬──────────────────────────┘
│ strategy = semantic|hybrid?
▼
┌─────────────────────────────────────────────────────────────┐
│ TINGKAT 1 — Vektor Tertanam (sqlite-vec) │
│ sqlite-vec v0.1.9 dimuat melalui db.loadExtension(). │
│ KNN brute-force pada vektor Float32. Aktif ketika: │
│ • sqlite-vec loadExtension berhasil │
│ • Sumber embedding tersedia (remote | static | │
│ transformers) yang dapat menghasilkan Float32Array │
│ • Tabel vec_memories tersedia (dibuat saat ready() pertama)│
└──────────────────────────────────┬──────────────────────────┘
│ qdrant.enabled?
▼
┌─────────────────────────────────────────────────────────────┐
│ TINGKAT 2 — Qdrant (basis data vektor eksternal opsional) │
│ Saat diaktifkan, menggantikan sqlite-vec untuk semantic/ │
│ hybrid. Memerlukan instans Qdrant yang berjalan serta host/ │
│ port yang dikonfigurasi. │
└─────────────────────────────────────────────────────────────┘
Degradasi berlangsung secara otomatis dan transparan:
- Jika sqlite-vec gagal dimuat, tingkat 1 tidak tersedia → kembali ke tingkat 0.
- Jika sumber embedding mengembalikan kesalahan, tingkat 1 kembali ke tingkat 0.
- Jika Qdrant tidak sehat, tingkat 2 kembali ke tingkat 1 (atau tingkat 0 jika tingkat 1 juga tidak tersedia).
Sumber embedding
Lapisan embedding (src/lib/memory/embedding/) menentukan sumber yang akan digunakan
berdasarkan MemorySettingsExtended.embeddingSource:
| Sumber | Deskripsi | Kunci diperlukan | Cold start |
|---|---|---|---|
remote |
Menggunakan API embedding penyedia yang dikonfigurasi (OpenAI, Cohere, dll.) | Ya | Tidak ada |
static |
Embedding tabel pencarian lokal melalui potion-base-8M (WordPiece + mean pooling) |
Tidak | ~200ms |
transformers |
Inferensi ONNX lokal melalui @huggingface/transformers v4, all-MiniLM-L6-v2 |
Tidak | ~3s + ~400MB RAM |
auto |
Resolusi runtime: remote (jika kunci tersedia) → static → transformers → null | Bergantung | Bergantung |
Urutan resolusi untuk auto:
- Temukan penyedia pertama dalam
listEmbeddingProviders()denganhasKey === true→remote. - Jika
settings.staticEnabled === true→static. - Jika
settings.transformersEnabled === true→transformers. - Jika tidak →
null(beralih ke pencarian kata kunci FTS5).
Cache embedding (src/lib/memory/embedding/cache.ts) menggunakan
peta LRU dalam memori dengan kunci ${source}:${model}:${dim}:${sha256(text)}, yang dibatasi hingga
MEMORY_EMBEDDING_CACHE_MAX entri (default 1000) dengan TTL
MEMORY_EMBEDDING_CACHE_TTL_MS (default 5 menit). Cache ini digunakan bersama oleh semua pemanggil
selama siklus hidup proses.
RRF Hibrida (k=60)
Ketika strategy = "hybrid" dan penyimpanan vektor tersedia, pengambilan menggunakan
Reciprocal Rank Fusion untuk menggabungkan hasil FTS5 dan vektor:
RRF(d) = Σ 1 / (k + rank_i(d)) dengan k = 60 (dapat dikonfigurasi melalui MEMORY_RRF_K)
i
Secara konkret:
- Jalankan pencarian FTS5 → daftar berperingkat
R_fts(posisi 1..N). - Jalankan pencarian vektor KNN → daftar berperingkat
R_vec(posisi 1..M). - Untuk setiap
memoryIdunik:
rrf_score = 1/(60 + fts_rank)+1/(60 + vec_rank)(0 jika tidak ada dalam daftar). - Urutkan berdasarkan
rrf_scoreDESC, lalu terapkan penelusuran anggaran token.
RRF dikenal efektif tanpa memerlukan normalisasi skor di antara
sistem pengambilan yang heterogen. Nilai default k=60 berasal dari makalah asli
Cormack dkk. dan berfungsi dengan baik untuk korpus kecil (<10 ribu memori).
Backfill (lazy + reindex)
Ketika model embedding berubah (terdeteksi melalui embedding_signature),
penyimpanan vektor dibangun ulang dan semua memori yang sudah ada ditandai
needs_reindex = 1 dalam tabel memories.
Backfill lazy: Pada pengambilan berikutnya, setiap memori yang tidak memiliki entri vektor akan
dibuatkan embedding dan dimasukkan ke dalam vec_memories sebelum pencarian dijalankan. Hal ini
mengamortisasi biaya backfill di seluruh permintaan nyata tanpa memblokir startup.
Reindex eksplisit: Tab Engine di /dashboard/memory menyediakan tombol
"Indeks Ulang Sekarang" yang memanggil POST /api/memory/reindex. Handler tersebut memanggil
runReindexBatch() dari src/lib/memory/reindex.ts, yang memproses hingga
limit entri tertunda per permintaan. Progres dapat dipantau melalui
GET /api/memory/engine-status (vectorStore.needsReindex).
Tabel memory_vec_meta (migrasi 083_memory_vec.sql) menyimpan:
active_dim— dimensi vektor saat ini (null = belum dikalibrasi).embedding_signature—${source}:${model}:${dim}yang digunakan untuk mendeteksi perubahan.last_reset_at— stempel waktu reset penuh terakhir.vec_loaded— flag 0/1 yang menunjukkan apakah sqlite-vec berhasil dimuat.
Ekstensi pengaturan
Sembilan bidang embedding dan vektor tersedia di MemorySettingsExtended dalam
src/shared/schemas/memory.ts, yang dipersistenkan melalui src/lib/db/settings.ts:
| Bidang | Tipe | Default | Deskripsi |
|---|---|---|---|
embeddingSource |
"remote" | "static" | "transformers" | "auto" |
"auto" |
Sumber embedding yang akan digunakan |
embeddingProviderModel |
string | null |
null |
Penyedia/model dalam format provider/model |
customBaseUrl |
string | null |
null |
URL dasar endpoint kompatibel OpenAI khusus Memori |
customModelId |
string | null |
null |
ID model yang dikirim ke endpoint khusus |
transformersEnabled |
boolean |
false |
Persetujuan penggunaan Transformers.js (MiniLM, ~400MB) |
staticEnabled |
boolean |
false |
Persetujuan penggunaan model lokal statis potion-base-8M |
rerankEnabled |
boolean |
false |
Aktifkan langkah pemeringkatan ulang (menambah +200-500ms/permintaan) |
rerankProviderModel |
string | null |
null |
Penyedia/model pemeringkatan ulang dalam format provider/model |
vectorStore |
"sqlite-vec" | "qdrant" | "auto" |
"auto" |
Backend vektor yang akan digunakan |
Semua ini diekspos melalui GET /PUT /api/settings/memory (skema MemorySettingsExtendedSchema).
Untuk sumber remote, Memori juga menerima pengaturan opsional customBaseUrl dan
customModelId. Bersama-sama, keduanya memilih endpoint /embeddings yang kompatibel
dengan OpenAI beserta modelnya tanpa mengubah registri embedding global. Endpoint
dinormalisasi sebelum digunakan dan diperiksa oleh kebijakan URL keluar penyedia:
HTTP(S) diwajibkan, kredensial tersemat dan string kueri ditolak, serta alamat
metadata cloud tetap diblokir. Nilai kosong mempertahankan penyedia registri yang
dipilih. Galat yang dikembalikan ke dasbor disanitasi dan kredensial endpoint tidak
pernah dicatat dalam log.
TODO (D20): Cakupan
global(berbagi memori di seluruh kunci API) belum diimplementasikan dalam rilis ini. Fitur ini memerlukan perubahan skema dan jalur pengambilan global. Lacak secara terpisah.
Lapisan Penyimpanan
Utama: SQLite (tabel memories)
Dibuat oleh migrasi 015_create_memories.sql:
| Kolom | Tipe | Catatan |
|---|---|---|
id |
TEXT PRIMARY KEY |
UUID yang dihasilkan melalui crypto.randomUUID() |
api_key_id |
TEXT NOT NULL |
Kunci API pemilik |
session_id |
TEXT |
Cakupan opsional per percakapan |
type |
TEXT NOT NULL |
Salah satu dari factual, episodic, procedural, semantic |
key |
TEXT |
Kunci upsert stabil, misalnya preference:i_prefer_python |
content |
TEXT NOT NULL |
Teks fakta yang sebenarnya |
metadata |
TEXT |
Blob JSON (category, extractedAt, source, ...) |
created_at / updated_at |
TEXT |
String ISO 8601 |
expires_at |
TEXT |
Kedaluwarsa opsional; NULL berarti permanen |
memory_id |
INTEGER UNIQUE |
Ditambahkan oleh 023_fix_memory_fts_uuid.sql untuk menjembatani UUID ↔ rowid FTS5 |
Indeks: api_key_id, session_id, type, expires_at, ditambah indeks unik
memory_id.
Semantik upsert: createMemory() mencari baris yang sudah ada dengan
(api_key_id, key) yang sama dan memperbaruinya langsung ketika ditemukan
(menggabungkan metadata melalui shallow spread). Hal ini mencegah tabel bertambah
tanpa batas akibat pernyataan preferensi yang berulang.
Pencarian Teks Lengkap (tabel virtual memory_fts)
022_add_memory_fts5.sql membuat tabel virtual FTS5 atas content dan
key. 023_fix_memory_fts_uuid.sql memperbaiki bug dunia nyata ketika kunci
primer UUID tidak dapat di-join dengan rowid integer milik FTS5 — migrasi tersebut
menambahkan kolom memory_id, membuat ulang tabel FTS, dan memasang trigger
(memory_fts_ai, memory_fts_ad, memory_fts_au) yang menjaga sinkronisasi FTS
saat INSERT, DELETE, dan UPDATE.
Digunakan oleh retrieval.ts untuk strategi semantic dan hybrid (lihat di bawah).
Kode pengambilan melakukan pemeriksaan dengan hasTable("memory_fts") dan beralih
ke urutan kronologis jika tabel FTS tidak ada atau kueri FTS menghasilkan galat.
Opsional: Qdrant (penyimpanan vektor tingkat 2)
src/lib/memory/qdrant.ts mengimplementasikan integrasi Qdrant opsional sebagai
penyimpanan vektor tingkat 2. Pengambilan hanya dirutekan ke Qdrant ketika pemilih
mesin memoryVectorStore === "qdrant" — nilai default "auto" (dan "sqlite-vec")
tidak pernah memilih Qdrant. Tombol alih tab Engine menetapkan keduanya,
qdrantEnabled dan memoryVectorStore, secara bersamaan: mengaktifkannya menjadikan
Qdrant sebagai penyimpanan utama, sedangkan menonaktifkannya mengatur ulang ke
"auto" (#5597 — sebelum perbaikan tersebut, pengaktifan tidak memberikan efek
karena tidak ada yang menulis ke pemilih mesin). Jika Qdrant tidak dapat dijangkau
atau tidak mengembalikan apa pun, pengambilan beralih ke sqlite-vec → FTS5.
upsertSemanticMemoryPoint()— menyematkankey + contentdengan model embedding yang dikonfigurasi, memastikan koleksi tersedia (membuat vektor dengan jarak kosinus saat pertama kali digunakan), dan melakukan upsert titik dengan payload{memoryId, apiKeyId, sessionId, key, content, metadata, createdAtUnix, expiresAtUnix}.searchSemanticMemory(query, topK, scope)— menyematkan kueri, mencari di koleksi yang difilter berdasarkankind = "omniroute_memory"dan secara opsional berdasarkanapiKeyId/sessionId. MembatasitopKke[1, 20].deleteSemanticMemoryPoint(id)— menghapus satu titik. Dipanggil olehdeleteMemory()setelah baris SQLite dihapus (D15).cleanupSemanticMemoryPoints({retentionDays})— menghapus secara massal titik-titik yangexpiresAtUnix-nya sudah berlalu atau yangcreatedAtUnix-nya lebih lama daripada batas retensi. Melakukan penghitungan terlebih dahulu agar dasbor dapat menampilkan angka aktual.checkQdrantHealth()— pemeriksaan kesehatanGET /readyzbeserta latensinya.
UI pengaturan menyediakan konfigurasi Qdrant, pemeriksaan kesehatan, pengujian pencarian semantik,
dan pembersihan di tab Engine pada /dashboard/memory. Route terkait
di bawah src/app/api/settings/qdrant/ semuanya telah terhubung sejak v3.8.6:
| Route | Metode | Deskripsi |
|---|---|---|
/api/settings/qdrant |
GET / PUT |
Membaca / memperbarui pengaturan Qdrant |
/api/settings/qdrant/health |
GET |
Pemeriksaan keaktifan + latensi |
/api/settings/qdrant/search |
POST |
Pengujian pencarian semantik |
/api/settings/qdrant/cleanup |
POST |
Menghapus titik kedaluwarsa / lama |
/api/settings/qdrant/embedding-models |
GET |
Mencantumkan model embedding yang tersedia |
Catatan perilaku (hal yang dapat diharapkan):
- Pemilihan mesin — mengaktifkan Qdrant di tab Engine menjadikannya penyimpanan
utama (menetapkan
memoryVectorStore="qdrant"); menonaktifkannya akan mengatur ulang ke"auto"(#5597). - Tanpa pengisian ulang — hanya memori yang dibuat/diperbarui setelah Qdrant diaktifkan yang ditulis ke sana (penulisan ganda secara fire-and-forget). Memori SQLite yang sudah ada tidak dimigrasikan; "Reindex Now" hanya membangun ulang indeks sqlite-vec, bukan Qdrant.
- Dimensi vektor dideteksi secara otomatis dari embedding aktual saat pertama kali digunakan — tidak ada kolom dimensi yang perlu diisi. Perubahan model embedding setelah koleksi dibuat tidak ditangani secara otomatis: koleksi yang ada dibiarkan tanpa perubahan, sedangkan operasi penulisan/pencarian dengan dimensi yang tidak cocok akan gagal dan beralih kembali ke sqlite-vec. Buat ulang koleksi (gunakan nama baru, atau hapus koleksi tersebut di Qdrant) untuk mengganti embedder.
- Metrik jarak — selalu Cosine (ditentukan secara hardcode saat koleksi dibuat; tidak dapat dikonfigurasi).
- Autentikasi — hanya kunci API (dikirim sebagai header
api-key; bersifat opsional untuk Docker lokal tanpa autentikasi). JWT/RBAC tidak digunakan. - Kolom konfigurasi — UI menyediakan
host,port,collection,embeddingModel,apiKey.vectorSize/hnswEfConstructhanya tersedia melalui env/DB danvectorSizetidak digunakan untuk pembuatan koleksi (dimensi berasal dari embedding).
Kuantisasi vektor (int8 — opsional, kedua backend)
Kedua backend vektor mendukung kuantisasi int8 opsional untuk mengurangi penggunaan memori oleh vektor yang disimpan (~4× lebih kecil daripada Float32) dengan sedikit penurunan recall. Secara default fitur ini nonaktif pada keduanya — vektor tetap menggunakan presisi penuh kecuali diaktifkan secara eksplisit.
| Backend | Pengaturan | Tipe | Default | Lokasi pembacaan |
|---|---|---|---|---|
| Qdrant | qdrantQuantization (kunci DB) |
"none" | "int8" | "binary" |
"none" |
src/lib/memory/qdrant.ts::normalizeQdrantConfig() |
| sqlite-vec | MEMORY_VEC_QUANTIZATION (env) |
"none" | "int8" |
"none" |
src/lib/memory/vectorStore.ts::requestedVecQuantization() |
- Qdrant dikonfigurasi per instans melalui kunci pengaturan
qdrantQuantization(disediakan sebagai kolomquantizationpadaPUT /api/settings/qdrant). Saat bernilai"int8",buildQuantizationConfig()meminta kuantisasi skalar (always_ram, kuantil0.99) dan pencarian mengaktifkanrescore: trueagar vektor berpresisi penuh menyempurnakan kumpulan kandidat int8. - Kuantisasi sqlite-vec bersifat khusus lingkungan (bukan pengaturan DB): tetapkan
MEMORY_VEC_QUANTIZATION=int8untuk menyimpan vektor lokal sebagai kolomint8[dim]melaluivec_quantize_int8(?, 'unit'). Mode yang dipilih dimasukkan ke dalamembedding_signature(akhiran:int8), sehingga pergantian mode memicu pengindeksan ulang penuh pada tabelvec_memories— jalur lazy-backfill yang sama seperti yang digunakan saat model embedding berubah.
Jenis Memori
MemoryType (src/lib/memory/types.ts):
| Jenis | Digunakan untuk |
|---|---|
factual |
Preferensi, fakta pengguna yang stabil, pola perilaku |
episodic |
Keputusan yang terkait dengan momen tertentu ("Saya memilih Postgres") |
procedural |
Memori alur kerja / panduan cara (dicadangkan; saat ini tidak ada pengekstrak otomatis) |
semantic |
Dicadangkan untuk entri penyimpanan vektor |
Strategi pengambilan MemoryConfig adalah salah satu dari exact, semantic, atau hybrid,
dan cakupannya adalah salah satu dari session, apiKey, atau global. Cakupan default dari
getMemorySettings() adalah apiKey.
Ekstraksi Fakta (extraction.ts)
Ekstraksi berbasis regex, bukan berbasis LLM — proses ini berjalan secara internal dengan
setImmediate() sehingga tidak pernah memblokir aliran respons:
- Pola preferensi →
MemoryType.FACTUAL(misalnyaSaya lebih suka …,Saya sangat menyukai …,favorit saya adalah …,Saya membenci …) - Pola keputusan →
MemoryType.EPISODIC(misalnyaSaya akan menggunakan …,Saya memilih …,Saya memutuskan memakai …,Saya akan mengadopsi …) - Pola kebiasaan →
MemoryType.FACTUAL(misalnyaSaya biasanya …,Saya selalu …,Saya cenderung …)
Setiap kecocokan disanitasi (trim, penciutan spasi kosong, dibatasi hingga 500 karakter),
dideduplikasi dalam batch melalui factKey(category, content) yang stabil, dan
disimpan melalui createMemory() dengan metadata
{category, extractedAt, source: "llm_response"}. Teks masukan dibatasi hingga
64 KiB (MAX_EXTRACTION_TEXT_LENGTH) — jika lebih panjang, bagian akhir teks
digunakan agar konten asisten terbaru selalu disertakan.
extractFactsFromText(text) diekspor untuk pengujian dan mengembalikan fakta
terstruktur tanpa menyimpannya.
Pengambilan (retrieval.ts)
retrieveMemories(apiKeyId, config) adalah titik masuk utama. Fungsi ini:
- Menormalisasi dan memvalidasi konfigurasi melalui
MemoryConfigSchema. - Segera mengembalikan
[]ketikaenabledbernilai false ataumaxTokens <= 0. - Membatasi
maxTokenske rentang[1, 8000]. - Mendeteksi apakah tabel
memoriesmodern tersedia (dibandingkan dengan tabelmemorylama) sehingga basis data lama tetap berfungsi. - Membuat kueri dasar dengan pemeriksaan kedaluwarsa
(
expires_at IS NULL OR datetime(expires_at) > datetime('now')), cakupan sesi opsional, dan batas wakturetentionDaysopsional. - Membuat percabangan berdasarkan strategi:
exact(default): urutan kronologisORDER BY created_at DESC LIMIT 100.semantic: jikaconfig.querytersedia danmemory_ftsada, lakukan JOINmemory_fts MATCH ?dan urutkan berdasarkan peringkat FTS; kembali ke urutan kronologis ketika FTS menghasilkan 0 baris.hybrid: gabungan hasil FTS (relevansi lebih tinggi) dan kumpulan kronologis, yang dideduplikasi berdasarkan id.
- Menghitung skor relevansi kata kunci (
getRelevanceScore) padacontent,key, dan JSONmetadataketika kueri diberikan. Baris dengan skor nol disaring. - Mengurutkan berdasarkan skor secara menurun, kemudian
createdAtsecara menurun. - Menelusuri daftar yang telah diperingkat dan menerima entri selama total berjalan
estimateTokens(content)(≈length / 4) tetap di bawah anggaran. Selalu mengembalikan setidaknya satu entri jika ada yang cocok.
estimateTokens diekspor dan digunakan oleh pengambilan, peringkasan, serta alat MCP
omniroute_memory_search.
Injeksi (injection.ts)
injectMemory(request, memories, provider):
- Menggabungkan seluruh isi memori menjadi satu string
Memory context: …. - Memilih strategi berdasarkan nama penyedia:
- Pesan sistem (default untuk OpenAI, Anthropic, Gemini, …) — menambahkan
{role: "system", content: memoryText}sebelum pesan sistem yang sudah ada sehingga prompt sistem pengguna tetap memiliki prioritas. - Pesan pengguna (fallback) — untuk penyedia dalam
PROVIDERS_WITHOUT_SYSTEM_MESSAGE:o1,o1-mini,o1-preview,glm,glmt,glm-cn,zai,qianfan. Penyedia ini menolak peran sistem dan jika tidak demikian akan menghasilkan 400 (lih. isu #1701 untuk GLM/Zhipu).
- Pesan sistem (default untuk OpenAI, Anthropic, Gemini, …) — menambahkan
- Mencatat jumlah, strategi, dan model pada
memory.injection.injected.
providerSupportsSystemMessage(provider) diekspor untuk pemanggil yang perlu
membuat keputusan perutean sendiri. Penyedia yang tidak dikenal secara default
menghasilkan true (peran sistem diizinkan) demi keamanan.
Pengaturan (settings.ts)
Konfigurasi memori disimpan dalam tabel pengaturan DB, bukan dalam variabel lingkungan.
getMemorySettings() membaca dari getSettings() dan menyimpan hasilnya dalam cache
di dalam proses; invalidateMemorySettingsCache() dipanggil oleh rute PUT pengaturan
setelah penulisan.
Bidang lama (semua versi)
| Kunci DB | Tipe | Default | Kontrol UI |
|---|---|---|---|
memoryEnabled |
boolean | false (nonaktif secara default sejak v3.8.30) |
Memori aktif/nonaktif |
memoryMaxTokens |
integer | 2000 (rentang 0–16000) |
Anggaran token untuk injeksi |
memoryRetentionDays |
integer | 30 (rentang 1–365) |
Jangka waktu retensi |
memoryStrategy |
enum | "hybrid" (salah satu dari recent, semantic, hybrid) |
Strategi pengambilan |
skillsEnabled |
boolean | false |
Mengaktifkan/menonaktifkan injeksi keterampilan per kunci (lihat SKILLS.md) |
Catatan: strategi UI "recent" dipetakan ke strategi pengambilan internal
"exact" melalui toMemoryRetrievalConfig() (urutan kronologis).
Bidang baru (v3.8.6, rencana 21 D9)
Lihat juga bagian "Ekstensi pengaturan" di atas untuk deskripsi bidang.
| Kunci DB | Bidang API | Default |
|---|---|---|
memoryEmbeddingSource |
embeddingSource |
"auto" |
memoryEmbeddingModel |
embeddingProviderModel |
null |
memoryTransformersEnabled |
transformersEnabled |
false |
memoryStaticEnabled |
staticEnabled |
false |
memoryRerankEnabled |
rerankEnabled |
false |
memoryRerankModel |
rerankProviderModel |
null |
memoryVectorStore |
vectorStore |
"auto" |
Kunci DB terkait Qdrant (qdrantEnabled, qdrantHost, qdrantPort,
qdrantApiKey, qdrantCollection dengan default "omniroute_memory",
qdrantEmbeddingModel dengan default "openai/text-embedding-3-small") dibaca oleh
normalizeQdrantConfig() dalam qdrant.ts.
Variabel lingkungan (v3.8.6)
Enam variabel lingkungan opsional menyesuaikan perilaku runtime mesin (didokumentasikan dalam .env.example):
| Variabel | Default | Deskripsi |
|---|---|---|
MEMORY_EMBEDDING_CACHE_TTL_MS |
300000 |
TTL cache embedding (5 menit) |
MEMORY_EMBEDDING_CACHE_MAX |
1000 |
Jumlah maksimum entri dalam cache LRU embedding |
MEMORY_TRANSFORMERS_MODEL |
Xenova/all-MiniLM-L6-v2 |
Repositori HF untuk model Transformers.js |
MEMORY_STATIC_MODEL |
minishlab/potion-base-8M |
Repositori HF untuk model potion statis |
MEMORY_STATIC_CACHE_DIR |
<DATA_DIR>/embeddings |
Lokasi penyimpanan model yang diunduh |
MEMORY_VEC_TOP_K |
20 |
Top-K default untuk pencarian vektor |
MEMORY_RRF_K |
60 |
Konstanta k RRF untuk pencarian hibrida |
MEMORY_VEC_QUANTIZATION |
none |
Atur ke int8 untuk menyimpan vektor sqlite-vec lokal yang dikuantisasi (~4× lebih kecil; bersifat opsional). Perubahan mode memaksa pengindeksan ulang. |
Peringkasan (summarization.ts)
summarizeMemories(apiKeyId, sessionId?, maxTokens = 4000) memadatkan konten lama ketika total token berjalan dalam memori suatu kunci melebihi anggaran. Fungsi ini mengiterasi baris secara DESC berdasarkan created_at, mempertahankan baris yang masih sesuai anggaran, dan mengganti content sisanya secara langsung dengan tiga kalimat pertama dari konten asli. tokensSaved adalah selisih estimateTokens antara konten lama dan baru.
Rutinitas ini tersedia tetapi tidak dipanggil secara otomatis dalam pipeline chat saat ini — panggil dari cron, tindakan admin, atau integrasi MemoryConfig.autoSummarize jika Anda memerlukan pemadatan berkelanjutan. Kehilangan data bersifat satu arah: teks asli akan ditimpa.
REST API
Semua endpoint memerlukan autentikasi manajemen (requireManagementAuth).
Endpoint memori inti (yang sudah ada + diperbarui)
| Metode | Path | Deskripsi |
|---|---|---|
GET |
/api/memory |
Daftar berpaginasi dengan filter: apiKeyId, type, sessionId, q, limit, page, offset. Respons mencakup stats.total, stats.tokensUsed, stats.hitRate, cacheStats |
POST |
/api/memory |
Membuat entri (divalidasi Zod: content, key, type, sessionId, apiKeyId, metadata, expiresAt opsional). Memanggil createMemory() yang melakukan upsert pada (apiKeyId, key) |
GET |
/api/memory/[id] |
Mengambil satu entri berdasarkan UUID |
PUT |
/api/memory/[id] |
Memperbarui kolom entri (type, key, content, metadata). Body: MemoryUpdatePutSchema. Juga menyinkronkan vektor jika sumber embedding tersedia. |
DELETE |
/api/memory/[id] |
Menghapus entri; juga menghapusnya dari vec_memories (D15) dan Qdrant dengan upaya terbaik. Mengembalikan 404 jika tidak ditemukan. |
GET |
/api/memory/health |
Menjalankan verifyExtractionPipeline("health-check") — proses pulang-pergi buat→daftar→hapus. Mengembalikan {working, latencyMs, error?} |
Endpoint mesin memori baru (rencana 21)
| Metode | Path | Deskripsi |
|---|---|---|
POST |
/api/memory/retrieve-preview |
Simulasi retrieveMemories — mengembalikan hasil berperingkat beserta skor, tingkat, dan token. Body: RetrievePreviewSchema. TIDAK menyisipkan atau mengubah memori. |
GET |
/api/memory/embedding-providers |
Mencantumkan penyedia beserta model embedding, dengan menunjukkan penyedia yang memiliki kunci API terkonfigurasi. |
GET |
/api/memory/engine-status |
Mengembalikan status mesin lengkap: tingkat kata kunci, resolusi embedding, statistik penyimpanan vektor, kesehatan Qdrant, konfigurasi pemeringkatan ulang. Bentuk: MemoryEngineStatusSchema. |
POST |
/api/memory/summarize |
Memicu pemadatan memori secara manual. Body: MemorySummarizeSchema (olderThanDays, apiKeyId?, dryRun). Mengembalikan {candidates, tokensSaved}. |
POST |
/api/memory/reindex |
Memicu pengindeksan ulang vektor untuk memori dengan needs_reindex=1. Body: MemoryReindexSchema (force). Mengembalikan {started, pending}. |
Endpoint pengaturan
| Metode | Path | Deskripsi |
|---|---|---|
GET |
/api/settings/memory |
MemorySettingsExtended ternormalisasi saat ini (7 kolom baru + lama) |
PUT |
/api/settings/memory |
Memperbarui kolom apa pun dari MemorySettingsExtendedSchema (total 12 kolom) |
GET |
/api/settings/qdrant |
Pengaturan Qdrant saat ini (QdrantSettingsSchema) |
PUT |
/api/settings/qdrant |
Memperbarui pengaturan Qdrant. Body: QdrantSettingsUpdateSchema. apiKey = string kosong menghapus kunci. |
GET |
/api/settings/qdrant/health |
Probe keaktifan terhadap instans Qdrant yang dikonfigurasi. Mengembalikan QdrantHealthResultSchema. |
POST |
/api/settings/qdrant/search |
Pengujian pencarian semantik terhadap Qdrant. Body: QdrantSearchSchema (query, topK). |
POST |
/api/settings/qdrant/cleanup |
Menghapus titik Qdrant untuk memori yang kedaluwarsa / lama. |
GET |
/api/settings/qdrant/embedding-models |
Mencantumkan model embedding yang tersedia untuk Qdrant. |
Kueri daftar /api/memory mendukung paginasi berbasis page
(parsePaginationParams) atau offset mentah — ketika offset tersedia,
parameter tersebut diprioritaskan dan page turunan dihitung untuk bentuk respons.
Alat MCP (open-sse/mcp-server/tools/memoryTools.ts)
Saat server MCP diaktifkan, tiga alat memori didaftarkan:
omniroute_memory_search—{apiKeyId, query?, type?, maxTokens?, limit?}→ membungkusretrieveMemories(). Sejak v3.8.6 (D16),strategydibaca darigetMemorySettings(), bukan ditetapkan secara hardcode ke"exact". Jikaquerydiberikan danstrategyadalahsemanticatauhybrid, penyimpanan vektor digunakan jika tersedia.omniroute_memory_add—{apiKeyId, sessionId?, type, key, content, metadata?}→ membungkuscreateMemory(). Hanya menerima 4 tipe kanonis:factual,episodic,procedural,semantic(D17).omniroute_memory_clear—{apiKeyId, type?, olderThan?}→ mencantumkan entri yang cocok, secara opsional memfilter berdasarkan stempel waktu sebelum pembuatan, lalu menghapus masing-masing melaluideleteMemory()(yang juga menghapus vektor dari sqlite-vec + Qdrant).
Lihat MCP-SERVER.md untuk detail transpor dan cakupan.
Dasbor (Studio Memori)
src/app/(dashboard)/dashboard/memory/page.tsx kini menjadi Studio dengan 3 tab:
Tab: Memori
- Kartu konsep (penjelasan "Cara kerjanya" yang dapat diciutkan).
- Daftar, pencarian, dan paginasi waktu nyata (debounce 300 ms).
- Filter tipe (
factual/episodic/procedural/semantic/ semua). - Modal tambah memori (kunci, konten, tipe).
- Pengeditan sebaris (tombol pensil →
PUT /api/memory/[id]). - Hapus per baris (dengan dialog konfirmasi).
- Ekspor JSON untuk halaman saat ini; impor JSON melalui pemilih berkas.
- Kartu statistik:
totalEntries,tokensUsed,hitRate. - Tombol "Ringkas yang lama" →
POST /api/memory/summarize(uji coba terlebih dahulu menampilkan jumlah kandidat, lalu meminta konfirmasi). - Indikator kesehatan hijau/merah yang dikendalikan oleh
GET /api/memory/health.
Tab: Arena Uji
- Input kueri + pemilih strategi (Eksak / Semantik / Hibrida) + anggaran token.
- "Simulasikan" →
POST /api/memory/retrieve-preview— menampilkan hasil berperingkat denganscore,tier,tokens,vecScore,ftsScore. - Panel resolusi yang menampilkan sumber embedding / penyimpanan vektor yang digunakan dan apakah terjadi fallback.
Tab: Mesin
- Panel status mesin (chip FTS5 kata kunci, chip embedding, chip penyimpanan vektor, chip kesehatan Qdrant, chip pemeringkatan ulang).
- Tombol "Indeks Ulang Sekarang" →
POST /api/memory/reindex. - Pemilih sumber embedding (otomatis / jarak jauh / statis / transformers + tombol pengalih).
- Kartu konfigurasi Qdrant (tombol pengalih aktifkan, host/port/koleksi/kunci, uji koneksi, uji pencarian semantik, pembersihan).
- Kartu konfigurasi pemeringkatan ulang (tombol pengalih aktifkan, pemilih penyedia/model).
Pengaturan Memori dan Qdrant juga tersedia di
/dashboard/settings → Memory & Skills (MemorySkillsTab.tsx) untuk
antarmuka pengaturan lama/global.
Caching
src/lib/memory/store.ts menyimpan cache dalam proses mirip LRU
(MEMORY_CACHE_TTL = 1 min, MEMORY_MAX_CACHE_SIZE = 500, dengan penggusuran 20 %
entri terlama) untuk pembacaan getMemory(id), ditambah lapisan
memoryCache kunci/nilai generik (src/lib/memory/cache.ts) dengan metode
get/set/invalidate yang digunakan oleh pemanggil yang menginginkan cache
dengan cakupannya sendiri (LRU 1.000 entri, TTL default 5 min).
Privasi & Siklus Hidup
- Kepemilikan memori ditentukan oleh id kunci API (
resolveMemoryOwnerIddichatCore.ts). TanpaapiKeyInfo.id, pengambilan, injeksi, maupun ekstraksi tidak dijalankan. - Entri dengan
expires_atdi masa mendatang disaring dari pengambilan; entri lama yang melampauiretentionDaysdikecualikan oleh klausacreated_at >= cutoffdalamretrieveMemories. - Untuk penghapusan permanen, gunakan
DELETE /api/memory/[id]atauomniroute_memory_clear. - Ekstraksi dijalankan tanpa menunggu hasil melalui
setImmediate; kegagalan dicatat di bawahmemory.extraction.background.faileddan tidak pernah diteruskan kepada pemanggil. - Perjalanan pulang-pergi verifikasi (
verifyExtractionPipeline) membersihkan entri pengujiannya sendiri dalam blokfinally.
Lihat Juga
- SKILLS.md — pengaturan
skillsEnabledmenginjeksikan definisi alat bersama memori. - MCP-SERVER.md — transportasi / cakupan MCP.
- API_REFERENCE.md — cakupan API yang lebih luas.
- Modul sumber:
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 + RRF hibridasrc/lib/memory/embedding/index.ts— lapisan embedding multisumbersrc/lib/memory/embedding/types.ts,remote.ts,staticPotion.ts,transformersLocal.ts,cache.tssrc/shared/schemas/memory.ts— skema Zod untuk semua isi permintaan API memorisrc/shared/schemas/qdrant.ts— skema Zod untuk pengaturan/operasi Qdrantsrc/lib/db/memoryVec.ts— CRUD untukmemory_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+ sub-rutesrc/app/(dashboard)/dashboard/memory/— UI Studio (halaman + komponen + tab + hook)open-sse/handlers/chatCore.ts(pengkabelan injeksi / ekstraksi)open-sse/mcp-server/tools/memoryTools.ts
Memilih Penyedia Embedding (v3.8.16+)
Mesin memori OmniRoute mendukung empat sumber embedding (src/lib/memory/embedding/). Masing-masing memiliki kompromi berbeda dalam hal latensi, biaya, kualitas model, dan kompleksitas penyiapan.
Sumber Embedding
| Penyedia | Sumber | Latensi | Biaya | Kualitas | Penyiapan |
|---|---|---|---|---|---|
transformers |
Model ONNX lokal (Xenova/all-MiniLM-L6-v2) | ~50-150ms (CPU) | Gratis | Baik | Hanya npm install |
static |
Vektor yang telah dihitung (di-cache) | <1ms | Gratis | T/A (bergantung pada cache hit) | Tidak ada |
remote |
API OpenAI / Cohere / Voyage | ~100-300ms | $0.02-0.10/1M token | Sangat baik | Kunci API |
auto |
Memilih sumber terbaik yang tersedia saat runtime | Sama seperti sumber terpilih | Gratis | Sama seperti sumber terpilih | Tidak ada |
| (cache) | Lapisan LRU dalam memori di atas sumber mana pun | <1ms (hit), latensi penuh (miss) | Gratis | Sama seperti sumber dasar | Selalu aktif (bukan sumber yang dapat dipilih) |
Pohon Keputusan
Apa konteks deployment Anda?
│
┌───────────┼───────────┬──────────────┐
│ │ │ │
DEV/UJI PROD KECIL PROD BESAR EDGE / OFFLINE
│ │ │ │
▼ ▼ ▼ ▼
transformers transformers remote (Qdrant) transformers
(gratis, tanpa API) (kualitas terbaik) (tanpa internet)
│ │ │ │
└────────┬──┴───────────┴──────────────┘
│
▼
SELALU tambahkan lapisan `cache` di atasnya
(LruCache membungkus penyedia mana pun)
Konfigurasi Database & API
Opsi embedding memori dikonfigurasi melalui API/UI Pengaturan, bukan variabel lingkungan. Kunci database pengaturan yang relevan di bawah Pengaturan (normalizeMemorySettings dalam src/lib/memory/settings.ts) adalah:
memoryEmbeddingSource:"transformers"(lokal),"remote"(berbasis API, misalnya OpenAI),"static"(penyimpanan eksternal), atau"auto"memoryEmbeddingProviderModel: Pengidentifikasi model untuk sumber remote/static (misalnya,"text-embedding-3-small")memoryTransformersEnabled:true|falsememoryStaticEnabled:true|falsememoryVectorStore:"sqlite-vec","qdrant", atau"auto"
Model Lokal (transformers)
Menggunakan transformers.js secara internal untuk menjalankan model lokal:
# Variabel lingkungan yang dibaca dalam kode (src/lib/memory/embedding/index.ts):
MEMORY_TRANSFORMERS_MODEL=Xenova/all-MiniLM-L6-v2 # Repositori model HF
MEMORY_STATIC_MODEL=minishlab/potion-base-8M # Model potion statis HF
MEMORY_STATIC_CACHE_DIR=<DATA_DIR>/embeddings # Direktori cache
Cache Embedding LRU
Cache selalu aktif secara default dan dikonfigurasi melalui variabel lingkungan:
MEMORY_EMBEDDING_CACHE_MAX=1000 # Jumlah maksimum item yang di-cache
MEMORY_EMBEDDING_CACHE_TTL_MS=300000 # TTL (5 menit)
Angka Performa
Tolok ukur pada server x86 4-core pada umumnya (teks masing-masing ~100 token):
| Penyedia | p50 | p95 | p99 | Biaya / 1 juta embedding |
|---|---|---|---|---|
transformers (CPU) |
80ms | 180ms | 350ms | Gratis |
remote (OpenAI) |
120ms | 220ms | 400ms | ~$0.02 (ada-002) / $0.13 (3-large) |
static (Qdrant) |
15ms | 30ms | 60ms | Bergantung pada hosting Qdrant |
cache (hit) |
<1ms | <1ms | 2ms | Gratis |
Pola Ekstraksi Fakta (v3.8.16+)
Modul extraction.ts (src/lib/memory/extraction.ts) menggunakan pencocokan pola regex untuk mengekstrak fakta terstruktur dari pesan percakapan. Memahami pola-pola ini membantu Anda menyesuaikan kualitas ekstraksi untuk kasus penggunaan Anda.
Kategori Pola Bawaan
| Kategori | Contoh pola | Yang ditangkap |
|---|---|---|
| PREFERENCE_PATTERNS | "I prefer <X>", "I like <X>", "I hate <X>" |
Preferensi pengguna |
| DECISION_PATTERNS | "I'll use <X>", "I decided to <X>", "I went with <X>" |
Keputusan pengguna (episodik) |
| PATTERN_PATTERNS | "I usually <X>", "I always <X>", "I never <X>" |
Pola perilaku yang persisten |
Contoh Pola (Disederhanakan)
// Dari 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];
Hal yang Diekstrak
Ketika pengguna mengatakan:
"Saya lebih memilih TypeScript. Saya akan menggunakan Postgres untuk proyek ini. Saya selalu melakukan commit sebelum push. Saya tidak menyukai Python." Ekstraksi menghasilkan 4 memori:
Kunci Kategori Jenis Konten preference:typescriptpreference factual "TypeScript" decision:postgres_for_this_projectdecision episodic "Postgres untuk proyek ini" pattern:commit_before_pushingpattern factual "commit sebelum push" preference:pythonpreference factual "Python"
Batas Ekstraksi
Untuk mencegah ekstraksi yang tidak terkendali, batas berikut diterapkan:
| Panjang konten minimum | 3 karakter | | Panjang konten maksimum | 500 karakter |
Kapan Harus Menonaktifkan Ekstraksi
Ekstraksi berjalan secara otomatis setiap kali memori diaktifkan; tidak ada tombol terpisah
khusus ekstraksi. Untuk menonaktifkannya, nonaktifkan memori sepenuhnya (enabled: false
melalui PUT /api/settings/memory). Pertimbangkan untuk melakukannya ketika:
- Anda memiliki volume pesan yang tinggi dan biaya ekstraksi tidak dapat diabaikan
- Percakapan Anda sebagian besar bersifat sementara (obrolan, debugging) tanpa nilai jangka panjang
- Anda sudah menangkap konteks melalui plugin khusus
Penyesuaian RRF Hibrida (v3.8.16+)
Algoritma Reciprocal Rank Fusion (RRF) menggabungkan hasil FTS5 (kata kunci) dan vektor (semantik). Parameter k mengontrol seberapa besar bobot yang diberikan kepada hasil dengan peringkat lebih rendah.
Rumus
Untuk setiap memori kandidat, skor RRF adalah:
RRF(d) = Σ 1 / (k + rank_i(d))
Dengan:
kadalah konstanta (bawaan 60)rank_i(d)adalah peringkat dokumenddalam sistem pengambilan ke-i (FTS, vektor)- Penjumlahan dilakukan pada semua sistem pengambilan
Bagaimana k Memengaruhi Hasil
Nilai k |
Efek | Paling sesuai untuk |
|---|---|---|
k=0 |
Fusi peringkat murni (tanpa penghalusan) | Tolok ukur teoretis |
k=10-30 |
Memberi bobot besar pada hasil teratas, peringkat rendah hampir tidak berkontribusi | Ketika 3 hasil teratas biasanya benar |
k=60 (bawaan) |
Seimbang — 10 hasil teratas semuanya berkontribusi secara berarti | Pengambilan untuk tujuan umum |
k=100+ |
Lebih datar — bahkan hasil berperingkat rendah dapat mendominasi jika muncul dalam beberapa sistem | Ketika recall > precision sangat penting |
Menyesuaikan k dalam Praktik
# Bawaan
MEMORY_RRF_K=60
# Presisi agresif (memori kecil, sedikit dokumen)
MEMORY_RRF_K=20
# Recall maksimum (memori besar, kueri beragam)
MEMORY_RRF_K=120
Contoh dengan k=20:
- Peringkat FTS 1 → kontribusi
1/21 = 0.048 - Peringkat FTS 10 → kontribusi
1/30 = 0.033 - Peringkat vektor 1 → kontribusi
0.048 - Maksimum gabungan:
0.096
Contoh dengan k=60:
- Peringkat FTS 1 → kontribusi
1/61 = 0.016 - Peringkat FTS 10 → kontribusi
1/70 = 0.014 - Peringkat vektor 1 → kontribusi
0.016 - Maksimum gabungan:
0.033
Dengan k yang lebih tinggi, perbedaan relatif antara peringkat 1 dan peringkat 10 menjadi lebih kecil, sehingga algoritma lebih mengandalkan konsensus di seluruh sistem pengambilan daripada keyakinan terhadap peringkat teratas.
Kapan Harus Mengubah k
| Gejala | Coba |
|---|---|
| Hasil teratas selalu menang, tetapi salah | Turunkan k (misalnya, 20) — keyakinan peringkat teratas lebih penting |
| Jawaban yang benar ada di 5 besar tetapi bukan peringkat 1 | Naikkan k (misalnya, 100) — penilaian yang lebih datar menghargai konsensus |
| Recall tinggi tetapi precision rendah | Turunkan k — pertajam pemeringkatan |
| Recall rendah (dokumen relevan tidak ditemukan) | Naikkan k — beri kesempatan kepada dokumen berperingkat lebih rendah |
Pembobotan RRF
Fusi peringkat resiprokal menggunakan bobot yang sama untuk peringkat vektor semantik dan peringkat pencarian teks lengkap:
RRF(d) = 1/(k + rank_vector) + 1/(k + rank_fts)
Tidak ada variabel lingkungan untuk menyesuaikan bobot individual (MEMORY_RRF_VECTOR_WEIGHT/MEMORY_RRF_FTS_WEIGHT tidak tersedia).
Strategi Peringkasan (v3.8.16+)
Modul summarization.ts (src/lib/memory/summarization.ts) memadatkan memori lama untuk menjaga agar kumpulan aktif tetap kecil sekaligus mempertahankan kemampuan mengingat kembali.
Kapan Peringkasan Dipicu
| Pemicu | Ambang batas (default) |
|---|---|
| Pemicu manual via API | tidak berlaku |
Apa yang Dirangkum
Dua titik masuk diekspor dari summarization.ts:
summarizeMemories(apiKeyId, sessionId?, maxTokens = 4000)— memadatkan memori untuk suatu sesi menjadi satu teks ringkasan yang dibatasi oleh anggaran token.summarizeMemoriesOlderThan(apiKeyId, days, dryRun)— pemadatan berbasis usia yang digunakan oleh API: fungsi ini memilih setiap memori yang lebih lama daridays, membuat satu memori ringkasan yang dipadatkan dari memori-memori tersebut, dan (ketikadryRunbernilaifalse) menghapus memori aslinya. TeruskandryRun: trueuntuk meninjau kumpulan kandidat dan total token tanpa mengubah apa pun.
Tidak ada tahap pengelompokan tag/kunci atau penilaian "inti vs dapat diringkas" per memori — pemilihan hanya berdasarkan batas usia, dan teks ringkasan berupa satu baris yang dipadatkan dan diawali tipe untuk setiap kandidat.
Memicu Peringkasan
Peringkasan bersifat manual / opsional — pengaturan autoSummarize secara
default bernilai false, sehingga tidak ada yang dipadatkan secara otomatis. Picu melalui API:
curl -X POST http://localhost:20128/api/memory/summarize \
-H "Authorization: Bearer $OMNIROUTE_KEY"
Untuk tetap menonaktifkannya, cukup pertahankan autoSummarize pada nilai default-nya (false).
Kiat Kualitas Peringkasan
- Tinjau terlebih dahulu dengan
dryRun—summarizeMemoriesOlderThan(..., true)mengembalikan daftar kandidat dan jumlah total token sehingga Anda dapat memastikan apa yang akan digabungkan sebelum menghapus memori aslinya. - Jalankan peringkasan selama jam dengan lalu lintas rendah jika Anda memiliki korpus memori yang besar — pemanggilan LLM adalah bagian yang lambat
# Gaya cron: rangkum setiap hari pada pukul 03.00
0 3 * * * curl -X POST http://localhost:20128/api/memory/summarize \
-H "Authorization: Bearer $OMNIROUTE_KEY"
Pola Penyedia MemoryBackend
Sumber acuan:
src/lib/memory/backend.ts,src/lib/memory/genericBackend.ts,src/lib/memory/manager.tsPengujian:src/lib/memory/__tests__/generic-backend.test.ts
Pola penyedia MemoryBackend memperkenalkan lapisan abstraksi backend yang dapat dipasang-lepas di atas mesin memori yang sudah ada. Alih-alih terikat pada satu implementasi penyimpanan, sistem memori kini mendukung beberapa backend (SQLite, Obsidian, Notion, backend HTTP kustom) dengan perutean utama/cadangan yang dapat dikonfigurasi.
Arsitektur
┌──────────────────────────────────────────────────────────┐
│ Rute API │
│ (src/app/api/memory/route.ts) │
└──────────────────────┬───────────────────────────────────┘
│
┌──────────────────────▼───────────────────────────────────┐
│ MemoryManager │
│ Orkestrator singleton (manager.ts) │
│ │
│ Utama ──► Backend A (mis. SQLite) │
│ Cadangan ──► Backend B (mis. Obsidian) │
│ Backend C (mis. Notion via GenericBackend)│
└──────────────────────┬───────────────────────────────────┘
│
┌──────────────┼──────────────┐
▼ ▼ ▼
┌────────────┐ ┌────────────┐ ┌──────────────────┐
│ Backend │ │ Backend │ │ Backend │
│ SQLite │ │ Obsidian │ │ GenericMemory │
│ │ │ │ │ (HTTP) │
└────────────┘ └────────────┘ └──────────────────┘
Antarmuka Inti (backend.ts)
Setiap backend harus mengimplementasikan antarmuka 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> }>;
// Pencarian
search(config: SearchConfig): Promise<Memory[]>;
// Kesehatan
health(): Promise<HealthCheckResult>;
// Siklus hidup (opsional)
initialize?(): Promise<void>;
shutdown?(): Promise<void>;
}
MemoryManager (manager.ts)
Orkestrator singleton yang:
- Mendaftarkan backend melalui
register(backend)— dipanggil saat boot dariindex.ts - Mengonfigurasi backend utama + cadangan melalui
configure(primary, fallbacks) - Merutekan CRUD/pencarian ke backend utama, dengan rantai cadangan jika terjadi kegagalan
- Memeriksa kesehatan semua backend secara berkala
Perilaku cadangan:
| Operasi | Utama | Cadangan |
|---|---|---|
create |
✅ Hanya backend utama | ❌ |
get |
✅ Coba backend utama dahulu | ✅ Gunakan cadangan jika null |
update |
✅ Hanya backend utama | ✅ Sinkronisasi tanpa menunggu hasil |
delete |
✅ Hanya backend utama | ✅ Sinkronisasi tanpa menunggu hasil |
list |
✅ Hanya backend utama | ❌ |
search |
✅ Backend utama dahulu | ✅ Cadangan jika terjadi kesalahan |
GenericMemoryBackend (genericBackend.ts)
Konektor HTTP generik yang mengadaptasi REST API apa pun menjadi MemoryBackend. Berguna untuk:
- Notion — hubungkan melalui Notion API
- Obsidian — hubungkan melalui Obsidian Local REST API
- Backend kustom — layanan apa pun yang menyediakan API memori RESTful
Konfigurasi:
interface GenericBackendConfig {
baseUrl: string; // URL dasar API backend
apiKey?: string; // Token Bearer untuk autentikasi
headers?: Record<string, string>; // Header HTTP khusus
timeout?: number; // Batas waktu permintaan (default: 30000ms)
backendType?: string; // Untuk pencatatan log
// Penggantian endpoint (default menggunakan konvensi REST)
endpoints?: {
search?: string; // default: "/memories/search"
create?: string; // default: "/memories"
list?: string; // default: "/memories"
get?: string; // default: "/memories/{id}"
update?: string; // default: "/memories/{id}"
delete?: string; // default: "/memories/{id}"
health?: string; // default: "/health"
};
// Pemetaan nama parameter kueri
queryParams?: {
query?/apiKeyId?/limit?/offset?/strategy?/maxTokens?/type?/sessionId?/orderBy?/orderDir?/options?
};
// Pemetaan nama parameter jalur
pathParams?: {
id?/memoryId?
};
}
Backend yang dikenal telah dikonfigurasi sebelumnya di KNOWN_BACKENDS:
createKnownBackend("obsidian"); // → GenericMemoryBackend yang diarahkan ke localhost:27123
createKnownBackend("notion"); // → GenericMemoryBackend yang diarahkan ke api.notion.com/v1
Backend Bawaan
SQLiteBackend (sqliteBackend.ts)
Backend utama default. Membungkus penyimpanan memori berbasis SQLite yang ada menggunakan src/lib/memory/store.ts. Didaftarkan secara otomatis saat proses boot.
import { sqliteBackend } from "./sqliteBackend";
memoryManager.register(sqliteBackend);
ObsidianBackend (obsidianBackend.ts)
Membungkus integrasi Obsidian yang ada (src/lib/memory/obsidianBackend.ts). Terhubung ke vault Obsidian melalui Obsidian Local REST API.
Pengaturan
Pengaturan backend memori disimpan dalam tabel pengaturan aplikasi dan dikelola melalui src/lib/memory/settings.ts:
| Pengaturan | Kunci Env/Konfigurasi | Default | Deskripsi |
|---|---|---|---|
| Backend utama | memoryPrimaryBackend |
"sqlite" |
ID backend utama |
| Backend fallback | memoryFallbackBackends |
[] |
ID backend fallback yang berurutan |
| Konfigurasi backend | memoryBackendConfigs |
{} |
Penggantian konfigurasi per backend |
Pengaturan dinormalisasi melalui normalizeMemorySettings() dan disimpan dalam cache di getMemorySettings().
Alur Inisialisasi
Bootstrap aplikasi
→ impor index.ts (efek samping): mendaftarkan SQLiteBackend
→ initMemoryBackends() dipanggil dari siklus hidup aplikasi:
1. Muat pengaturan (getMemorySettings)
2. Konfigurasikan backend utama + fallback
3. Inisialisasi semua backend (pemeriksaan kesehatan)
4. Siap menerima permintaan
Menambahkan Backend Baru
- Implementasikan antarmuka
MemoryBackenddisrc/lib/memory/<name>Backend.ts - Ekspor dari
src/lib/memory/index.ts - Daftarkan dengan
memoryManager.register(yourBackend)saat boot - Konfigurasikan melalui pengaturan: atur
memoryPrimaryBackendke ID backend Anda - Uji dengan
src/lib/memory/__tests__/generic-backend.test.tssebagai referensi
Contoh: 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);
Verifikasi
Pengujian unit
npx vitest run src/lib/memory/__tests__/generic-backend.test.ts --reporter=verbose
Output yang diharapkan: 35 pengujian, semuanya lulus yang mencakup:
- Konstruktor (2)
- Pemeriksaan kesehatan (4) — berhasil, kegagalan 500, kesalahan jaringan, latensi
- Inisialisasi (2) — berhasil, gagal
- Buat (2) — endpoint default, endpoint khusus
- Ambil (4) — berhasil, 404 → null, selain 404 melemparkan kesalahan, parameter jalur khusus
- Perbarui (2) — berhasil, 404 → false
- Hapus (2) — berhasil, 404 → false
- Daftar (2) — parameter kueri, nama parameter khusus
- Pencarian (3) — parameter kueri, endpoint khusus, serialisasi opsi
- Header autentikasi (2) — token Bearer, header khusus
- Factory (1)
Pemeriksaan tipe
npm run typecheck:core
Yang diharapkan: 0 kesalahan.