Skip to content

feat(semantic): hybrid search + temporal decay for memory#27

Merged
samestrin merged 8 commits into
mainfrom
feat/memory-hybrid-search
Apr 17, 2026
Merged

feat(semantic): hybrid search + temporal decay for memory#27
samestrin merged 8 commits into
mainfrom
feat/memory-hybrid-search

Conversation

@samestrin
Copy link
Copy Markdown
Owner

Summary

  • Add FTS5 hybrid search for memory entries (dense cosine + BM25 lexical with RRF fusion)
  • Add temporal decay for memory search (recent decisions rank higher)
  • Trim MCP tool schema tokens across all three MCPs (~45% reduction, ~9,700 tokens saved)

Hybrid Memory Search

  • memory_fts virtual table with porter unicode61 tokenizer indexing question + answer
  • INSERT/UPDATE/DELETE triggers for automatic FTS5 sync (including upsert via ON CONFLICT)
  • LexicalSearchMemory() with BM25 scoring, tag/status/source filtering
  • FuseRRFMemory() and FuseWeightedMemory() for result fusion
  • HybridSearchMemory() orchestration: dense + lexical → RRF fusion → optional decay → topK
  • Backfill support for migrating existing databases
  • New flags: --hybrid (CLI), hybrid (MCP)

Temporal Decay

  • Exponential decay: score *= exp(-ln(2) / halflife * age_days)
  • At age=0, multiplier=1.0; at age=halflife, multiplier=0.5
  • Configurable half-life (default: 90 days)
  • Works with both hybrid and dense-only search modes
  • New flags: --decay, --decay-half-life (CLI), decay, decay_half_life (MCP)

MCP Token Trimming

  • Removed self-evident property descriptions, json/min params (handlers force these)
  • Removed repeated storage/collection/profile/config from auto-profile semantic tools
  • Truncated verbose tool descriptions

Test plan

  • go test -run "^Test[^Q]" ./internal/semantic/... — all pass
  • go test ./internal/semantic/commands/... — all pass
  • go test ./internal/semantic/mcpserver/... — all pass
  • All 6 binaries build cleanly
  • Adversarial review completed — all critical/high issues fixed
  • TDD: tests written before implementation for each phase

Add TemporalDecayConfig with exponential decay formula:
score *= exp(-ln(2) / halflife * age_days)

At age=0, multiplier=1.0. At age=halflife, multiplier=0.5.
Handles edge cases: negative age, empty/unparseable dates,
NaN/Inf protection.
Add --decay and --decay-half-life flags to memory search command.
Add decay/decay_half_life properties to MCP memory_search tool.
Decay is applied after search results, then re-sorted by score.
Create memory_fts virtual table with porter unicode61 tokenizer,
indexing question + answer columns. Add INSERT/UPDATE/DELETE
triggers for automatic sync. Add backfill for migration from
pre-FTS databases. Wire into initSchema().
Implement MemoryLexicalSearcher interface on SQLiteStorage.
Searches memory_fts (question + answer) with BM25 scoring,
supports status/source/tag filtering. Converts BM25 scores
to positive scale matching vector search convention.
Add FuseRRFMemory, FuseRRFMemoryWithTopK, FuseWeightedMemory
for combining dense and lexical memory search results. Same
algorithms as chunk-based fusion, keyed by Entry.ID.
Combines dense (cosine) + lexical (BM25) memory search with
RRF or weighted fusion. Supports optional temporal decay.
Over-fetches for better fusion quality, then applies topK.
Gracefully degrades to dense-only if lexical search fails.
Add --hybrid flag to memory search command. When enabled, uses
HybridSearchMemory (dense + lexical BM25 with RRF fusion).
Decay works with both hybrid and dense-only modes.
Add hybrid property to MCP memory_search tool schema.
- Validate decay-half-life before use (prevent div-by-zero with 0)
- Apply temporal decay before topK truncation for correct ranking
- Add COALESCE to chunk FTS5 backfill name column
- Replace custom contains() with strings.Contains
@samestrin samestrin merged commit becd324 into main Apr 17, 2026
1 check passed
@samestrin samestrin deleted the feat/memory-hybrid-search branch April 17, 2026 20:13
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