style(bench): gate the rerank-sweep flag and helper on the embeddings feature

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
osobh
2026-09-19 19:42:05 -07:00
co-authored by Claude Opus 5
parent 306a35347c
commit 1e18ff5a86
3 changed files with 64 additions and 3 deletions
+1
View File
@@ -554,6 +554,7 @@ impl MemoryBackend for ClawhdfBackend {
timestamp: r.timestamp,
source_channel: r.source_channel.clone(),
raw_activation: r.activation,
relevance: r.score,
})
.collect();
+51 -2
View File
@@ -4,8 +4,10 @@
//! into a single composite score for each retrieved result.
/// Configuration for the multi-factor re-ranker.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Copy)]
pub struct ReRankConfig {
/// Weight applied to the retrieval score the candidate arrived with.
pub relevance_weight: f32,
/// Weight applied to the temporal decay score (0.01.0).
pub temporal_weight: f32,
/// Weight applied to the source authority score (0.01.0).
@@ -20,6 +22,9 @@ pub struct ReRankConfig {
impl Default for ReRankConfig {
fn default() -> Self {
Self {
// Relevance leads: the metadata signals break ties and nudge, they
// do not decide. See `BENCHMARKS.md`, "Recency discrimination".
relevance_weight: 1.0,
temporal_weight: 0.3,
authority_weight: 0.2,
activation_weight: 0.5,
@@ -41,6 +46,8 @@ pub struct ReRankResult {
pub authority_score: f32,
/// Normalised Hebbian activation score in [0, 1].
pub activation_score: f32,
/// The retrieval score carried through from the input.
pub relevance_score: f32,
}
/// Compute an exponential decay temporal score.
@@ -105,6 +112,15 @@ pub struct RerankInput {
pub source_channel: String,
/// Raw Hebbian activation weight for this entry.
pub raw_activation: f32,
/// The retrieval score that put this entry in the candidate list.
///
/// Re-ranking is meant to *adjust* the retriever's ordering with signals
/// it does not have, not to replace it. Without this the combined score
/// was made of recency, authority and activation alone, so a candidate
/// pool came back ordered by age with its relevance ordering discarded.
/// Callers with no meaningful score can pass the same value for every
/// entry, which reduces to the old behaviour.
pub relevance: f32,
}
/// Re-rank a list of retrieval results using multi-factor scoring.
@@ -138,7 +154,8 @@ pub fn rerank(
let auth = source_authority_score(&inp.source_channel);
let act = activation_score(inp.raw_activation);
let combined = config.temporal_weight * ts
let combined = config.relevance_weight * inp.relevance
+ config.temporal_weight * ts
+ config.authority_weight * auth
+ config.activation_weight * act;
@@ -148,6 +165,7 @@ pub fn rerank(
temporal_score: ts,
authority_score: auth,
activation_score: act,
relevance_score: inp.relevance,
}
})
.collect();
@@ -253,22 +271,51 @@ mod tests {
timestamp: 0.0, // very old
source_channel: "other".to_string(),
raw_activation: 0.1,
relevance: 0.0,
},
RerankInput {
index: 1,
timestamp: 86_400.0, // one day ago
source_channel: "conversation".to_string(),
raw_activation: 0.5,
relevance: 0.0,
},
RerankInput {
index: 2,
timestamp: 172_800.0, // "now"
source_channel: "user_correction".to_string(),
raw_activation: 1.0,
relevance: 0.0,
},
]
}
#[test]
fn relevance_leads_but_recency_breaks_near_ties() {
let entry = |index, timestamp, relevance| RerankInput {
index,
timestamp,
source_channel: "conversation".to_string(),
raw_activation: 1.0,
relevance,
};
let now = 10.0 * 86_400.0;
let config = ReRankConfig::default();
// A clearly better match wins despite being much older. Before
// `relevance` existed the combined score ignored it entirely, so this
// returned the newer, irrelevant entry.
let ranked = rerank(&[entry(0, 0.0, 1.0), entry(1, now, 0.1)], &config, now);
assert_eq!(ranked[0].index, 0, "{ranked:?}");
// Between near-equal matches, the newer one wins.
let ranked = rerank(&[entry(0, 0.0, 0.80), entry(1, now, 0.79)], &config, now);
assert_eq!(ranked[0].index, 1, "{ranked:?}");
// The breakdown carries the relevance through.
assert_eq!(ranked[0].relevance_score, 0.79);
}
#[test]
fn rerank_returns_all_entries() {
let inputs = make_inputs();
@@ -302,6 +349,7 @@ mod tests {
#[test]
fn rerank_score_breakdown_matches_manual_calculation() {
let config = ReRankConfig {
relevance_weight: 0.0,
temporal_weight: 1.0,
authority_weight: 0.0,
activation_weight: 0.0,
@@ -312,6 +360,7 @@ mod tests {
timestamp: 0.0,
source_channel: "other".to_string(),
raw_activation: 0.5,
relevance: 0.0,
}];
let now = 3600.0_f64; // exactly one half-life later
let results = rerank(&inputs, &config, now);