:vector-index-ksqlite — ANN-индекс поверх SQLite (brute-force)
Что это
KMP-реализация MutableVectorIndexStore поверх
ksqlite. Persistence
"бесплатно" — обычная SQLite БД.
Стратегия поиска: brute-force cosine similarity. Один SELECT
всех записей → декодирование BLOB → cosine sim в Kotlin → sort desc →
топ-K. Просто, KMP-совместимо, без sqlite-vec extension.
Используется как persistent ANN-индекс для small-to-medium датасетов
(≤10K записей на embedding ~512d). Для больших — переезжать на
:vector-index-jvector или sqlite-vec.
Где используется
Standalone-агентом, когда нужна persistence embeddings и масштаб не
превышает ~10K записей. Прямо сейчас модуль не подключён в
:standalone — это API-готовый компонент, ждущий интеграции.
Что внутри
class KsqliteVectorIndexStore private constructor(
override val dimension: Int,
connection: SQLiteConnection,
ownsConnection: Boolean,
) : MutableVectorIndexStore {
constructor(dimension: Int, path: String)
constructor(dimension: Int, connection: SQLiteConnection)
override suspend fun getSize(): Long
override suspend fun add(id: String?, embedding: FloatArray, payload: String?): VectorIndex
override suspend fun delete(id: String): Boolean
override suspend fun clear(): Long
override suspend fun search(embedding: FloatArray, limit: Int): List<VectorSearchResult>
override fun close()
companion object {
fun memory(dimension: Int, name: String? = null): KsqliteVectorIndexStore
}
}
Lifecycle:
(dimension, path)— открывает файловое соединение, мигрирует, закрывает вclose().(dimension, connection)— внешнее соединение (shared bundle); store НЕ закрывает его.memory(dimension, name)— in-memory, мигрирует, закрывает.
Schema: единственная таблица vector_index (id TEXT PRIMARY KEY, dimension INTEGER, embedding BLOB, payload TEXT). Embedding хранится
как raw float32 little-endian через ручной FloatArray.toLittleEndianBytes() /
ByteArray.toFloatArray() (hand-rolled в commonMain — без
java.nio.ByteBuffer).
Миграции: PRAGMA user_version + BEGIN/COMMIT. Идемпотентен.
UUID: kotlin.uuid.Uuid.random() (commonMain-safe, не JVM-only
java.util.UUID).
Цели сборки
Только: jvm(), linuxX64(), linuxArm64(), mingwX64() —
ksqlite 0.1.2 публикует нативные бинари только для этих 4 целей
(плюс 4 Android NDK). Нет macOS/iOS — для Apple использовать
:vector-index-jvector.
Как подключить
dependencies {
implementation("pw.binom.agentik:vector-index-ksqlite:0.1.0")
implementation("pw.binom.agentik:vector-index-api:0.1.0")
}
val store = KsqliteVectorIndexStore(dimension = 768, path = "~/.agentik/vectors.db")
store.add(id = "doc-1", embedding = embeddingOf("hello"), payload = """{"src":"chat"}""")
val top = store.search(embedding = queryEmbedding, limit = 10)
store.close()
Тесты
./gradlew :vector-index-ksqlite:jvmTest
Покрывают: explicit/auto id, duplicate throws, delete+idempotency, clear, top-K sort, payload round-trip, BLOB bit-exact round-trip (FloatArray<->BLOB encoding), schema migration idempotency.
Зависимости
pw.binom.db:ksqlite:0.1.2pw.binom.agentik:vector-index-apikotlinx-coroutines-core
Чего здесь НЕТ
- ANN на стороне SQL — нет sqlite-vec. Brute-force в Kotlin.
- Фильтрации в
search()— чистый ANN. - Apple targets (см. выше).
Текущий статус
Реализация готова, тесты зелёные. Интеграция в :standalone
отложена до момента, когда понадобится.