Skip to content

feat(agent-memory): Phase 2 — recall() ranking#249

Open
jamby77 wants to merge 1 commit into
feature/agent-memory-phase1-rememberfrom
feature/agent-memory-phase2-recall
Open

feat(agent-memory): Phase 2 — recall() ranking#249
jamby77 wants to merge 1 commit into
feature/agent-memory-phase1-rememberfrom
feature/agent-memory-phase2-recall

Conversation

@jamby77

@jamby77 jamby77 commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator

Agent Memory — Phase 2: recall() ranking

Stacked on #248 (Phase 1). The highest-value TDD target — composite-score math first, then KNN wiring.

What's new

  • Pure compositeScore = w.similarity·similarity + w.recency·recency + w.importance·importance, where recency = exp(-ln2·age/halfLife) (true half-life: 0.5 at one half-life) and similarity = 1 − distance/2. Table-driven tests (decay-at-half-life, ranking, recency-promotion, importance tie-break).
  • buildRecallQuery — KNN FT.SEARCH with scope (threadId/agentId/namespace) + AND-tag TAG filters (escaped), or * when unfiltered.
  • parseMemoryItem — FT.SEARCH hash → MemoryItem.
  • MemoryStore.recall() — embed query → widened KNN (k*4) → distance-threshold filter → composite re-rank → sort desc → cap at k. Constructor recall defaults: threshold 0.25, weights .6/.25/.15, half-life 7d.

Tests

20 unit tests; tsc --noEmit + prettier clean.

Review-driven change

  • Drop candidates with a missing/non-numeric distance — a missing __score yielded NaN, which bypassed the > threshold guard and produced a NaN score; now filtered with Number.isFinite.

Notes / deliberate divergences

  • MemoryHit.similarity holds the cosine distance (0..2, lower=closer) — per spec (§"same convention as semantic-cache"), not a 0..1 score.
  • Recency uses a true half-life (exp(-ln2·age/halfLife), 0.5 at one half-life) — the spec's literal exp(-age/halfLife) is inconsistent with the halfLifeSeconds name and the plan's test; impl follows the plan + field name.
  • reinforce (RecallOptions) is Phase 4; not implemented here.

Next

Phase 3 (forget / forgetByScope).


Note

Medium Risk
New retrieval path depends on Valkey FT.SEARCH/KNN and embedding consistency; ranking defaults affect which memories agents see, but changes are localized to agent-memory with solid unit coverage.

Overview
Adds MemoryStore.recall() so agents can retrieve stored memories by semantic query, with optional scope (threadId / agentId / namespace) and tag filters.

The flow embeds the query, runs a widened KNN FT.SEARCH (k×4), filters by cosine distance threshold, then re-ranks with a weighted composite score (similarity, half-life recency, importance). Constructor defaults cover threshold 0.25, weights .6/.25/.15, and a 7-day half-life; RecallOptions can override k, threshold, tags, and weights.

New helpers buildRecallQuery, parseMemoryItem, and compositeScore / similarityFromDistance wire Valkey search and ranking; MemoryItem, MemoryHit, and RecallOptions are exported. Invalid or missing KNN __score values and non-finite composite scores are dropped so bad index rows do not leak into results. MemoryHit.similarity keeps raw cosine distance (lower = closer), not a 0–1 score.

Reviewed by Cursor Bugbot for commit df17cbd. Bugbot is set up for automated code reviews on this repo. Configure here.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 50a2011. Configure here.

Comment thread packages/agent-memory/src/MemoryStore.ts
Comment thread packages/agent-memory/src/MemoryStore.ts
- Pure compositeScore: weighted similarity + half-life recency + importance
  (true half-life: 0.5 at one halfLifeSeconds); add similarityFromDistance()
- buildRecallQuery: KNN FT.SEARCH with scope + AND-tag TAG filters (escaped)
- parseMemoryItem: FT.SEARCH hash -> MemoryItem
- MemoryStore.recall(): embed query, widened KNN (k*4), distance-threshold
  filter, composite re-rank, sort desc, cap at k
- Constructor recall defaults: threshold 0.25, weights .6/.25/.15, half-life 7d
- Export recall types (MemoryItem, RecallOptions, MemoryHit, RecallWeights)
@jamby77 jamby77 force-pushed the feature/agent-memory-phase1-remember branch from fe9da3d to b433d60 Compare June 18, 2026 06:57
@jamby77 jamby77 force-pushed the feature/agent-memory-phase2-recall branch from 13a2279 to df17cbd Compare June 18, 2026 06:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant