Ranking agent memories without a neural scorer
How a small composite score — kind weights, stored relevance, keyword overlap, capped access — picks what fits a task budget.
Ranking agent memories without a neural scorer
Agent memory stores fill up. Before packing under a token budget, candidates need a rank: which decision, fact, or failure is worth injecting for this task?
Credit: scoreMemory in Agent-Observer (internal/smartctx/packer.go). The memory value is a core.Memory with fields such as Kind, Content, RelevanceScore, and AccessCount (prose only — the scorer reads those, it does not define the type).
The function
func scoreMemory(m core.Memory, task string) float64 {
score := 0.0
// Kind weight
switch m.Kind {
case "decision":
score += 0.35
case "fact":
score += 0.30
case "preference":
score += 0.25
case "failure":
score += 0.20
case "attempt":
score += 0.05
}
// Relevance score from DB
score += m.RelevanceScore * 0.30
// Task keyword overlap
taskWords := strings.Fields(strings.ToLower(task))
contentLower := strings.ToLower(m.Content)
matchCount := 0
for _, w := range taskWords {
if len(w) >= 3 && strings.Contains(contentLower, w) {
matchCount++
}
}
if len(taskWords) > 0 {
overlap := float64(matchCount) / float64(len(taskWords))
score += overlap * 0.25
}
// Access count bonus (capped)
if m.AccessCount > 0 {
acBonus := float64(m.AccessCount) / 50.0
if acBonus > 0.10 {
acBonus = 0.10
}
score += acBonus
}
return score
}
What it does
Builds a composite float from four signals:
- Kind prior — decisions and facts outrank attempts (hard-coded weights).
- Stored relevance — 30% of the DB’s
RelevanceScore(search/FTS quality). - Keyword overlap — fraction of task words (length ≥ 3) that appear in content, worth up to 0.25.
- Access bonus — popular memories get a bump, hard-capped at 0.10 so churn cannot dominate.
Higher score → earlier in the packer’s sort → more likely to win budget slots.
Why this shape
- Interpretable and tunable without embedding models.
- Separates type prior (decisions matter) from task fit (overlap) and popularity (access).
- Caps popularity so a frequently read noise entry cannot outrank a fresh decision.
Gotcha
Substring Contains is not tokenization: the task word "log" matches "dialog", and short stop-ish words (≥ 3 chars) still count. Overlap is also unweighted — every task word counts equally, including ones that are not discriminative. Treat scores as relative ranks inside one pack call, not as calibrated probabilities.
Takeaway
Ranking for injection is multiplicative product sense in additive clothing: priors + retrieval signal + lexical fit + soft popularity. Keep each term bounded so one bad feature cannot own the context window.