A Go library that stores and ranks memories, ages them on each read, retrieves top results plus a few random ones, and reranks based on feedback.
Specifics:
- Number of top memories and random retrievals are configurable based on use case
- Reranking changes the score of each memory. This is determined by: freshness (age), how often it was useful, and whether it was randomly recalled
store := storage.NewInMemoryStorage()
ranker := ranking.NewStandardRanking(nil)
ms := memory.NewService(store, ranker)
// Add memories
ms.AddMemory(ctx, "Liam likes Go")
// Get top memories
memories, _ := ms.ListBestMemories(ctx, 10, 1)
// Provide feedback to re-rank
ms.FeedbackMemories(ctx, feedback)# Run full simulation with detailed output
go run .
# Run only the final analysis (useful for
# large number of rounds)
go run . -finalOnlyAdjust constants in sim/sim.go for realistic scenarios:
rounds int = 100 // Memory addition cycles
readOnlyRounds int = 400 // Read-only cycles between additionstype Memory struct {
ID string
Content string
Rating float64
CreatedAt time.Time
FeedbackCount int
Age int
}- Add →
rating = INITIAL_RATING,feedbackCount = 0,age = 0,createdAt = now - Read → increment
agefor all memories (each list call = one cycle) and order byRating - Feedback →
CalibrateRating(memory, useful)per read item - Auto-remove (optional) → delete when heavily aged and low-quality
model/model.godefines models and interfaces, for examplemodel.Rankingandmodel.Storage.ranking/contains implementations ofmodel.Ranking.storage/contains implementations ofmodel.Storage.memory/memory.gocontains the main service that uses the ranking and storage implementations.
Note: Today there is a single implementation of each interface:
ranking/standardranking.goimplementsmodel.RankingviaStandardRanking.storage/inmemorystorage.goimplementsmodel.StorageviaInMemoryStorage.
Additional implementations (e.g., storage/dbstorage.go) can be added later without changing the rest of the system.
Combines freshness-augmented expectation, staleness-aware K, and exploration into a single update:
// effectiveRating = Rating + freshness(Age)
// expected = logistic(baselineRating - effectiveRating)
// kBase = kfactor(FeedbackCount, Age)
// explorationFactor = 1 + explorationBonus(FeedbackCount, Age)/400
// k = kBase * explorationFactor
// Rating += k * (actual - expected)
// FeedbackCount++Defaults:
initialRating = 1200
baselineRating = 1500
kFactorInitial = 64
kFactorMin = 8
lambdaStaleness = 0.5
recencyBoost = 400
halfLifeAge = 4
explorationBonus scale used inside calibrationreturn memory.Rating < rs.config.ratingFloor && rs.freshness(memory.Age) < 1model/Memory{}: noScorefieldstorage/inmemorystorage.goTopIDsByScore: sorts byRatingdescendingmemory.ListBestMemories: increments age only, does not mutate ratingsmemory.FeedbackMemories: appliesCalibrateRating(exploration built-in)ranking.StandardRanking.CalibrateRating: unified function