Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ Key concepts: Hot → Warm → Cold tiers. Hybrid retrieval (FTS + pgvector HNSW
| Variable | Default | Description |
|----------|---------|-------------|
| `KEYWORD_OVERLAP_BOOST` | `0.3` | Score boost when query tokens overlap memory keywords |
| `HYBRID_SEMANTIC_WEIGHT` | `1.5` | Weight on the semantic arm's RRF contribution in hybrid search. With RRF K=60, 1.5 means semantic ranks 1-31 outscore a keyword-only rank-1 hit; lower values give the lexical arm more say. Optimal value depends on the embedding model — measure before changing. |
| `TEMPORAL_PROXIMITY_DAYS` | `7` | Days window for temporal proximity boost |
| `CONSOLIDATION_INNER_BATCH_SIZE` | `50` | Hot-tier rows per inner consolidation batch |
| `TEMPORAL_DECAY_RATE` | `0` | Score decay per hour (0 = disabled) |
Expand Down
11 changes: 8 additions & 3 deletions src/memory-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,7 @@ const DEFAULTS: MemForgeConfig = {
temporalDecayRate: 0,
consolidationInnerBatchSize: 50,
keywordOverlapBoost: 0.3,
hybridSemanticWeight: 1.5,
temporalProximityDays: 7,
enableLlmRerank: false,
enableLlmIngest: false,
Expand Down Expand Up @@ -1167,11 +1168,15 @@ Ranking (numbers only):`;
}
});

// Semantic results weighted 1.5x — paraphrase matching is the primary failure mode
// in conversational memory retrieval (users ask differently than memories are stored)
// Semantic arm weighted above 1.0 because paraphrase matching is the
// primary failure mode in conversational memory retrieval (users ask
// differently than memories are stored). The multiplier is configurable —
// at 1.5 with K=60, semantic ranks 1-31 outscore a keyword-only rank-1
// hit, which is a strong thumb on the scale and worth measuring.
const semanticWeight = this.config.hybridSemanticWeight ?? 1.5;
semanticResults.forEach((row, idx) => {
const key = String(row.id);
const rrf = 1.5 / (K + idx + 1);
const rrf = semanticWeight / (K + idx + 1);
const existing = scores.get(key);
if (existing) {
existing.score += rrf;
Expand Down
1 change: 1 addition & 0 deletions src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ const manager = new MemoryManager({
temporalDecayRate: parseFloat(process.env['TEMPORAL_DECAY_RATE'] ?? '0'),
consolidationInnerBatchSize: parseInt(process.env['CONSOLIDATION_INNER_BATCH_SIZE'] ?? '50', 10),
keywordOverlapBoost: parseFloat(process.env['KEYWORD_OVERLAP_BOOST'] ?? '0.3'),
hybridSemanticWeight: parseFloat(process.env['HYBRID_SEMANTIC_WEIGHT'] ?? '1.5'),
temporalProximityDays: parseFloat(process.env['TEMPORAL_PROXIMITY_DAYS'] ?? '7'),
enableLlmRerank: process.env['ENABLE_LLM_RERANK'] === 'true',
enableLlmIngest: process.env['ENABLE_LLM_INGEST'] === 'true',
Expand Down
9 changes: 9 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -806,6 +806,15 @@ export interface MemForgeConfig {
consolidationInnerBatchSize: number;
/** Keyword overlap boost factor for hybrid search (default 0.3, 0 = disabled). */
keywordOverlapBoost: number;
/**
* Weight applied to the semantic arm's RRF contribution in hybrid search
* (default 1.5). Above 1.0 the semantic ranking dominates: with the standard
* RRF constant K=60, a weight of 1.5 means semantic ranks 1-31 all outscore
* a keyword-only rank-1 hit, so lexical matches the vector arm missed are
* effectively invisible. Exposed as HYBRID_SEMANTIC_WEIGHT so the balance
* can be measured rather than assumed.
*/
hybridSemanticWeight: number;
/** Temporal proximity window in days for time-aware scoring (default 7, 0 = disabled). */
temporalProximityDays: number;
/** Enable LLM post-retrieval reranking (default false — opt-in, adds ~2K tokens/query) */
Expand Down