opsx: memory-relevance-gate — cross-encoder relevance gate for memory recall (design) - #1588
Merged
Aaronontheweb merged 1 commit intoJul 6, 2026
Conversation
Collaborator
Author
|
TL;DR;, a search re-ranker powered by ONNX is the most competitive design here. |
2 tasks
Aaronontheweb
added a commit
that referenced
this pull request
Jul 8, 2026
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.
What we found, in plain English
After the hybrid-recall work, the memory system is good at finding a relevant memory when one exists. The remaining problem is the opposite case. On about six out of ten real questions, nothing in memory is relevant, and the right move is to inject nothing. The system almost never makes that move. Its similarity floor keeps out the obviously unrelated stuff, but "similar" and "useful" are different questions: ask about GPU benchmark numbers and it will happily inject the GPU rack-layout doc. Same topic, zero help, wasted context.
We prototyped four fixes and measured all of them against real traffic, twice: once on the 93 labeled queries we had, then again on 450 fresh ones mined from the full session history, so nothing got to grade its own homework.
A statistics-only gate. Inject only when the best match stands out from the crowd. Looked great on the first 93 queries (70% correct silence). On the 450 fresh ones it fell apart: it started blocking real answers, and its wins turned out to be tuning luck. Dead.
A small trained model over cheap signals. Not enough data to trust it. Even after growing the dataset five-fold, real traffic only contains 39 examples of "a memory that survived the floor and was actually relevant." The model memorized the haystack instead of learning the needle, and fell apart on transfer. Shelved until we have far more labeled traffic.
Punishing repeat offenders — memories that keep getting injected and keep being useless. Sound idea, wrong corpus: 80% of what recall surfaces has never been injected before, so there is no track record to act on. Dead as a gate. The bookkeeping may return later as telemetry.
A cross-encoder. A 22MB model that reads the question and the memory together and scores "would this actually help answer it?" It was trained on millions of human relevance judgments, so it arrives already knowing what relevance looks like; our data only had to pick a threshold. This one held. On 450 queries it had never seen, it stays correctly silent on 87% of nothing-relevant questions (the floor alone manages 7%) while keeping 98% of the recalls we actually want. Cost: ~11ms per turn and ~100MB of memory, which leaves the 1GB pod budget intact.
One correction the bigger dataset forced on us: our first threshold choice (0.08) came from a sample with only 8 positive examples and looked like a free lunch — silence with no recall cost. With 39 positives the free lunch disappeared; 0.08 sits exactly on the edge of the recall budget. The design freezes 0.02 instead: we give up 2 points of silence accuracy to buy an 8-point safety margin on recall.
Two caveats ship with this, recorded rather than hidden. The cross-encoder learned relevance from web-search data, so it slightly undervalues procedural memories ("how we cut releases") — that costs about 1.7% of good recalls at the chosen threshold, and fixing it means domain calibration later. And the judges that labeled the expanded validation set agreed with each other less than the original round's judges did (κ 0.435 vs 0.754), so we aggregated labels conservatively: a memory only counts as relevant if the judges converged on it.
The numbers (reference)
z_top50 ≥ 2.80Xenova/ms-marco-MiniLM-L-6-v2int8, pair scoringWinner artifact:
model_quantized.onnx, 23,143,499 bytes, SHA-256e9d8ebf845c413e981c175bfe49a3bfa9b3dcce2a3ba54875ee5df5a58639fbe. Efficiency: ~11ms p50 / ~35ms p95 for 3 pairs (reference CPU), ~103MB incremental RSS; combined with int8 embeddings + daemon peak ≈ 763MB against the 1GB pod limit.Architecture (reuses memory-core-redesign's infra wholesale)
IRelevanceScorerseam inNetclaw.Actors(mirrorsIMemoryEmbedder) +OnnxCrossEncoderScorerinNetclaw.Embeddings(pair encoding, dynamic seq length, sigmoid over the single-logit head).EmbeddingModelProvisionerallowlist: pinned URL/SHA-256/size plus the calibrated threshold, carried in the manifest like every other measured operating point. Nothing new for an operator to configure.SQLiteMemoryRecallCoordinator: after the cosine floor, score the surviving candidates (≤3) under a ~60ms sub-budget; drop below threshold; zero survivors means inject nothing. Model unavailable or timed out means floor-only behavior with a rate-limitedmemory_recall_gate_degradedmarker and doctor visibility.Memory.Embeddings.Enabled.Memory.Recall.RelevanceGate { Enabled, Threshold }exists for explicit override only, both nullable.memory_retrieval_finalgainsgateScores+droppedByGate; a new eval case asserts the zero-injection behavior end to end.Implementation target
Implementation lands on
feature/memory-embeddings, not directly ondev.Test plan
openspec validate --changes memory-relevance-gatepassesopenspec status --change memory-relevance-gateshows all four artifacts done