feat(memory): add :memory-md-vector hybrid backend + :reflection-api + outbox/journal entity splits
ci / JVM build + tests (push) Failing after 2m59s
release / Publish KMP libraries → caffeine Nexus (release) Failing after 9s

- :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:
subochev
2026-09-21 23:10:28 +03:00
parent 161be41adf
commit 0a7c40688c
41 changed files with 4952 additions and 0 deletions
@@ -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)