feat(semantic): hybrid search + temporal decay for memory#27
Merged
Conversation
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Hybrid Memory Search
memory_ftsvirtual table withporter unicode61tokenizer indexing question + answerLexicalSearchMemory()with BM25 scoring, tag/status/source filteringFuseRRFMemory()andFuseWeightedMemory()for result fusionHybridSearchMemory()orchestration: dense + lexical → RRF fusion → optional decay → topK--hybrid(CLI),hybrid(MCP)Temporal Decay
score *= exp(-ln(2) / halflife * age_days)--decay,--decay-half-life(CLI),decay,decay_half_life(MCP)MCP Token Trimming
json/minparams (handlers force these)storage/collection/profile/configfrom auto-profile semantic toolsTest plan
go test -run "^Test[^Q]" ./internal/semantic/...— all passgo test ./internal/semantic/commands/...— all passgo test ./internal/semantic/mcpserver/...— all pass