mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 18:18:28 +00:00
feat(memory): Add opt-in temporal decay for hybrid search scoring
Exponential decay (half-life configurable, default 30 days) applied
before MMR re-ranking. Dated daily files (memory/YYYY-MM-DD.md) use
filename date; evergreen files (MEMORY.md, topic files) are not
decayed; other sources fall back to file mtime.
Config: memorySearch.query.hybrid.temporalDecay.{enabled, halfLifeDays}
Default: disabled (backwards compatible, opt-in).
This commit is contained in:
committed by
Peter Steinberger
parent
fa9420069a
commit
6b3e0710f4
@@ -1,8 +1,14 @@
|
||||
import { applyMMRToHybridResults, type MMRConfig, DEFAULT_MMR_CONFIG } from "./mmr.js";
|
||||
import {
|
||||
applyTemporalDecayToHybridResults,
|
||||
type TemporalDecayConfig,
|
||||
DEFAULT_TEMPORAL_DECAY_CONFIG,
|
||||
} from "./temporal-decay.js";
|
||||
|
||||
export type HybridSource = string;
|
||||
|
||||
export { type MMRConfig, DEFAULT_MMR_CONFIG };
|
||||
export { type TemporalDecayConfig, DEFAULT_TEMPORAL_DECAY_CONFIG };
|
||||
|
||||
export type HybridVectorResult = {
|
||||
id: string;
|
||||
@@ -42,21 +48,28 @@ export function bm25RankToScore(rank: number): number {
|
||||
return 1 / (1 + normalized);
|
||||
}
|
||||
|
||||
export function mergeHybridResults(params: {
|
||||
export async function mergeHybridResults(params: {
|
||||
vector: HybridVectorResult[];
|
||||
keyword: HybridKeywordResult[];
|
||||
vectorWeight: number;
|
||||
textWeight: number;
|
||||
workspaceDir?: string;
|
||||
/** MMR configuration for diversity-aware re-ranking */
|
||||
mmr?: Partial<MMRConfig>;
|
||||
}): Array<{
|
||||
path: string;
|
||||
startLine: number;
|
||||
endLine: number;
|
||||
score: number;
|
||||
snippet: string;
|
||||
source: HybridSource;
|
||||
}> {
|
||||
/** Temporal decay configuration for recency-aware scoring */
|
||||
temporalDecay?: Partial<TemporalDecayConfig>;
|
||||
/** Test seam for deterministic time-dependent behavior */
|
||||
nowMs?: number;
|
||||
}): Promise<
|
||||
Array<{
|
||||
path: string;
|
||||
startLine: number;
|
||||
endLine: number;
|
||||
score: number;
|
||||
snippet: string;
|
||||
source: HybridSource;
|
||||
}>
|
||||
> {
|
||||
const byId = new Map<
|
||||
string,
|
||||
{
|
||||
@@ -117,7 +130,14 @@ export function mergeHybridResults(params: {
|
||||
};
|
||||
});
|
||||
|
||||
const sorted = merged.toSorted((a, b) => b.score - a.score);
|
||||
const temporalDecayConfig = { ...DEFAULT_TEMPORAL_DECAY_CONFIG, ...params.temporalDecay };
|
||||
const decayed = await applyTemporalDecayToHybridResults({
|
||||
results: merged,
|
||||
temporalDecay: temporalDecayConfig,
|
||||
workspaceDir: params.workspaceDir,
|
||||
nowMs: params.nowMs,
|
||||
});
|
||||
const sorted = decayed.toSorted((a, b) => b.score - a.score);
|
||||
|
||||
// Apply MMR re-ranking if enabled
|
||||
const mmrConfig = { ...DEFAULT_MMR_CONFIG, ...params.mmr };
|
||||
|
||||
Reference in New Issue
Block a user