feat(memory): add :memory-md-vector hybrid backend + :reflection-api + outbox/journal entity splits
- :memory-md-vector (KMP jvm+linuxX64+mingwX64): .md-файлы как source of truth, векторный индекс (sqlite-vec) как derived cache. reconcile() на старте: orphan-cleanup + content-hash-gated re-embed. Гибридный скор 0.7*vector + 0.3*keyword. Заменяет EmbeddingProvider на KMP-TextEmbeddingExecutor из :memory-api. - :reflection-api: новый 4-й API-модуль (Reflection, ReflectionStore, ReflectionEvent). Зависит только от :memory-api. - :journal-api получил ConversationRecord/ConversationStore/Ids (бывший :message-store-api, полностью удалён). :outbox-api получил Event, CommonEvent, AgentEvent (бывший :event-store). - :memory-api получил MemoryVectorIndex + NoteMatches + TextEmbeddingExecutor (suspend-обёртка над TextEmbeddingExtractor). - :memory-vector KMP-цели достигнуты через commonMain-only TextEmbedding- Executor, EmbeddingProvider выпилен; :memory-md-vector тянет text-embedding-api транзитивно через :memory-api. - :standalone flatten в commonMain/commonTest завершён (тесты из jvmTest переехали в commonTest). Включён optional деп :memory-md-vector через AGENTIK_MEMORY_BACKEND=md-vector. jvmTest: 96 задач, 407 тестов, 0 падений.
This commit is contained in:
+41
@@ -0,0 +1,41 @@
|
||||
package pw.binom.agentik.memory.vector
|
||||
|
||||
import pw.binom.agentik.memory.TextEmbeddingExecutor
|
||||
import pw.binom.voice.embeddingtext.TextEmbedding
|
||||
import pw.binom.voice.embeddingtext.TextEmbeddingExtractor
|
||||
|
||||
/**
|
||||
* Детерминированный [TextEmbeddingExtractor] для тестов: хеширует текст в
|
||||
* псевдо-вектор фиксированной размерности. L2-normalize чтобы cosine
|
||||
* работал осмысленно.
|
||||
*
|
||||
* НЕ suspend, как и положено extractor'у — suspend-обёртка живёт в
|
||||
* [TextEmbeddingExecutor] (используется в VectorMemoryStore через
|
||||
* [fakeExecutor]).
|
||||
*/
|
||||
class FakeTextEmbeddingExtractor(
|
||||
val dimension: Int = 32,
|
||||
) : TextEmbeddingExtractor {
|
||||
override fun embed(text: String): TextEmbedding {
|
||||
val v = FloatArray(dimension)
|
||||
var seed = text.hashCode().toLong() and 0xFFFFFFFFL
|
||||
for (i in 0 until dimension) {
|
||||
seed = (seed * 6364136223846793005L + 1442695040888963407L) and 0xFFFFFFFFL
|
||||
v[i] = ((seed.toInt() and 0xFFFF) / 65535f) * 2f - 1f
|
||||
}
|
||||
var norm = 0f
|
||||
for (x in v) norm += x * x
|
||||
norm = kotlin.math.sqrt(norm)
|
||||
if (norm > 0f) for (i in v.indices) v[i] /= norm
|
||||
return TextEmbedding(v)
|
||||
}
|
||||
|
||||
override fun close() = Unit
|
||||
}
|
||||
|
||||
/**
|
||||
* Удобная обёртка для тестов: создаёт `FakeTextEmbeddingExtractor` и
|
||||
* сразу заворачивает в [TextEmbeddingExecutor] с пред-объявленной размерностью.
|
||||
*/
|
||||
fun fakeEmbeddingExecutor(dimension: Int = 32): TextEmbeddingExecutor =
|
||||
TextEmbeddingExecutor(FakeTextEmbeddingExtractor(dimension), knownDimension = dimension)
|
||||
Reference in New Issue
Block a user