From 135c6a419d8969f5625853a3057eda8886289a8b Mon Sep 17 00:00:00 2001 From: subochev Date: Tue, 15 Sep 2026 08:17:42 +0300 Subject: [PATCH] fix(memory-vector): seed JVector index from SQLite on VectorMemorySystem.open() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit После рестарта in-RAM граф JVector создавался пустым (seedEntries не проходились из metaStore), поэтому search возвращал [], пока не появлялись новые upsert'ы — память терялась после каждого рестарта. - VectorMemorySystem.open(): JVectorMemoryIndex(dimension, metaStore.allEntries()) - SqliteMemoryMetaStore.open(): убрал случайное двойное конструирование - регресс-тест openSeedsIndexFromSqliteAfterRestart (save → close → open → search) --- .../memory/vector/SqliteMemoryMetaStore.kt | 1 - .../agentik/memory/vector/VectorMemorySystem.kt | 5 ++++- .../memory/vector/VectorMemoryStoreTest.kt | 17 +++++++++++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/memory-vector/src/jvmMain/kotlin/pw/binom/agentik/memory/vector/SqliteMemoryMetaStore.kt b/memory-vector/src/jvmMain/kotlin/pw/binom/agentik/memory/vector/SqliteMemoryMetaStore.kt index e298fa6..7417b85 100644 --- a/memory-vector/src/jvmMain/kotlin/pw/binom/agentik/memory/vector/SqliteMemoryMetaStore.kt +++ b/memory-vector/src/jvmMain/kotlin/pw/binom/agentik/memory/vector/SqliteMemoryMetaStore.kt @@ -194,7 +194,6 @@ class SqliteMemoryMetaStore( */ fun open(dbPath: String, dimension: Int): SqliteMemoryMetaStore { val conn = DriverManager.getConnection("jdbc:sqlite:$dbPath") - SqliteMemoryMetaStore(conn, dimension) return SqliteMemoryMetaStore(conn, dimension) } } diff --git a/memory-vector/src/jvmMain/kotlin/pw/binom/agentik/memory/vector/VectorMemorySystem.kt b/memory-vector/src/jvmMain/kotlin/pw/binom/agentik/memory/vector/VectorMemorySystem.kt index f3d816b..3fc2da2 100644 --- a/memory-vector/src/jvmMain/kotlin/pw/binom/agentik/memory/vector/VectorMemorySystem.kt +++ b/memory-vector/src/jvmMain/kotlin/pw/binom/agentik/memory/vector/VectorMemorySystem.kt @@ -45,7 +45,10 @@ class VectorMemorySystem( topK: Int = 10, ): VectorMemorySystem { val metaStore = SqliteMemoryMetaStore.open(dbPath, embedding.dimension) - val index = JVectorMemoryIndex(embedding.dimension) + // Граф пересобирается из SQLite (источник правды): без seed'ов + // после рестарта in-RAM индекс пуст и search возвращал бы [], + // пока не появятся новые upsert'ы. + val index = JVectorMemoryIndex(embedding.dimension, metaStore.allEntries()) val store = VectorMemoryStore(index, metaStore, embedding) val prefetcher = VectorPrefetcher(store, topK) val reviewer = VectorMemoryReviewer(store) diff --git a/memory-vector/src/jvmTest/kotlin/pw/binom/agentik/memory/vector/VectorMemoryStoreTest.kt b/memory-vector/src/jvmTest/kotlin/pw/binom/agentik/memory/vector/VectorMemoryStoreTest.kt index 6e8afa3..3a0e75c 100644 --- a/memory-vector/src/jvmTest/kotlin/pw/binom/agentik/memory/vector/VectorMemoryStoreTest.kt +++ b/memory-vector/src/jvmTest/kotlin/pw/binom/agentik/memory/vector/VectorMemoryStoreTest.kt @@ -136,4 +136,21 @@ class VectorMemoryStoreTest { store2.close() } } + + @Test + fun openSeedsIndexFromSqliteAfterRestart() = runTest { + // Регрессия: VectorMemorySystem.open() обязан пересадить in-RAM граф + // из SQLite — иначе после рестарта search возвращает [] до первого upsert. + val first = VectorMemorySystem.open(file.absolutePath, FakeEmbeddingProvider(dimension = dim)) + first.store.upsert(makeNote("r", "restarted fact: dog rex poodle")) + first.close() + + val second = VectorMemorySystem.open(file.absolutePath, FakeEmbeddingProvider(dimension = dim)) + try { + val results = second.store.search(MemorySearchQuery(query = "restarted fact", topK = 5)) + assertTrue(results.any { it.note.id == "r" }) + } finally { + second.close() + } + } }