memory: Curator (Phase 4) — фоновая архивация старых неиспользуемых заметок
* :memory-api — MemoryStore.archiveStale(maxAge, maxUseCount, now): default имплементация через list + delete (бэкенды могут переопределить). * :standalone — Curator: фоновая корутина на Dispatchers.IO, раз в сутки дёргает archiveStale(90d, 0). start()/stop(), runPass() — однократный прогон для тестов. * :standalone/Main — Curator стартует автоматически если memory включён, stop() в shutdown hook. * :standalone — публичный TestInMemoryMemoryStore вынесен из CompactionTest, переиспользуется в CuratorTest. * README — раздел "Куратор памяти" с описанием семантики для md и vector бэкендов. Tests: 288 total (+4 CuratorTest). Fatjar smoke-tested, curator стартует на app boot, выводит `[Curator] started` в лог. Defaults: interval=1d, maxAge=90d, maxUseCount=0. Override через новые config-флаги отложен.
This commit is contained in:
@@ -40,6 +40,38 @@ interface MemoryStore : AutoCloseable {
|
||||
suspend fun delete(id: String): Boolean
|
||||
suspend fun markUsed(id: String, at: Instant = Clock.System.now())
|
||||
|
||||
/**
|
||||
* Архивирует заметки, которые:
|
||||
* - не использовались дольше [maxAge] (считая от `now`);
|
||||
* - имеют `useCount <= [maxUseCount]` (по умолчанию 0, т.е. только никогда
|
||||
* не выданные в prefetch).
|
||||
*
|
||||
* Семантика архивации зависит от бэкенда:
|
||||
* - `:memory-md` — переименовывает -файл с суффиксом `.archived.{ts}`;
|
||||
* - `:memory-vector` — удаляет из SQLite и JVector (там данные
|
||||
* пересоздаются из `agentik.db` при старте).
|
||||
*
|
||||
* Default-имплементация использует [list] + [delete]; бэкенды могут
|
||||
* переопределить для более чистой семантики (особенно MD).
|
||||
*
|
||||
* @return количество архивированных заметок.
|
||||
*/
|
||||
suspend fun archiveStale(
|
||||
maxAge: kotlin.time.Duration,
|
||||
maxUseCount: Int = 0,
|
||||
now: Instant = Clock.System.now(),
|
||||
): Int {
|
||||
val all = list(limit = Int.MAX_VALUE)
|
||||
val cutoff = now - maxAge
|
||||
var archived = 0
|
||||
for (n in all) {
|
||||
if (n.lastUsedAt < cutoff && n.useCount <= maxUseCount) {
|
||||
if (delete(n.id)) archived++
|
||||
}
|
||||
}
|
||||
return archived
|
||||
}
|
||||
|
||||
fun events(): Flow<MemoryStoreEvent> = emptyFlow()
|
||||
|
||||
override fun close()
|
||||
|
||||
Reference in New Issue
Block a user