Come to think of it—how does the AI Agent get to know me better the more I use it?
How do you retrieve data from the vault? Seven components, one pipeline—the complete path for cross-session retrieval.
It’s not hard to store memories. The hard part is that when I need them, the right memory will surface on its own.
— That afternoon, after finishing “Write Track,” I jotted down the starting point in the terminal
The two articles, “Write Track” and “Background Track,” discuss how memories are stored and forgotten. When these two threads come together, the vault organizes itself, but there’s still one final step missing—how to retrieve them.
How to retrieve memories was actually the first problem I encountered: the day after finishing Write Track, I opened a new chat and tried asking the AI, "How is the progress of that OCR project from last week?" It couldn't answer—the data was clearly there, but it couldn't find it. At that moment, I knew that just storing things away wasn't enough.
Actually, the initial idea for Memvault came from @AI Super-Metadomain. He mentioned a line in the openclaw course: "make the agent understand me better the more I use it." After seeing that, I really resonated with it and had been wanting to build a set of auxiliary tools. Later, I switched back from openclaw to claude code, thinking I might as well start by building with claude code to create a prototype of memvault. Even later, I saw him introduce LanceDB Pro...and we've also drawn inspiration from many of its ingenious design elements, refining MemVault bit by bit. We are truly grateful.
The road called "Recallable" is the core of the entire memory system—this article will break it down: how the seven components divide the labor, the design philosophy behind it, and a few boundaries that are still being polished. This pathway is running every day now; the OCR progress that couldn't be queried the day Write Track was finished can now be picked right back up when asked across sessions or weeks apart.
Let me start by explaining the original concept.
At first I thought of it very simply: ingest it, build an index, and when searching, just find the few most similar entries. After working on it for a week, I realized that wasn't enough—"Where was I up to on that task?", "What have we been talking about recently?", and "What do you think my next step should be?" If you use the same retrieval path for all three of these questions, they all go off track; you need three different search approaches.
I. First, let me guess what kind of question you're asking
"Where did we leave off on that OCR last week?", "What have we been talking about lately?", "I'm thinking about whether to do X, what do you think?"—these three types of questions require three different paths. The first step is to distinguish whether you are asking about progress, memories, or suggestions, so that the downstream ranking weights and depth can adjust accordingly. At first, I didn't separate this layer, and as a result, the word "task" would match unrelated conversations from three weeks ago.
Classification uses a two-tier funnel: first, a keyword scan at zero cost; second, semantic vector calculation taking a few milliseconds. Only when both are uncertain is an LLM called for judgment, which is much more expensive. Most issues are sorted in the first two tiers, and the LLM is only invoked when things are truly unclear.
After the branching, the weights of each downstream layer adjust automatically—when asking about progress, time-sorting is weighted heavier; when asking about definitions, semantic similarity is weighted heavier; when asking for suggestions, the branch that triggers extended thinking is weighted heavier. The exact same agent receives different memories depending on how the question is phrased. This is why starting a new session and asking, "Where did we leave off with that OCR thing last week?" allows the agent to pick right back up—it’s not because its memory is so strong, but because it first accurately judged what you were asking.
But "what are you asking" can be broken down another layer—the same phrase "how have you been" asks about progress when you are busy, and review when you are on vacation, which cannot be told just by looking at the literal text. Therefore, another layer of personal preference is stacked on top of classification: the system looks at whether you tend to ask more of one type of question over the last seven, thirty, or ninety days, and blends this preference into its judgment. For someone who constantly brings up the past, "how have you been" will be biased toward review; for someone who constantly chases progress, it will be biased toward progress. The exact same sentence fed to different people takes different paths.
The personalized preference layer shares the same origin as the interest profile of the Background Track—what you've looked at, how long you stayed, and how many times you came back slowly outline a profile of your tastes. Intent classification is responsible for "what the literal text is asking," while this layer is responsible for "what you most frequently want to know." Only when the two are combined can we get close to the truly desired answer.
II. Search Using Both Methods: Meaning and Keywords
Semantic search and keyword search each have their limitations, so in this step, both are run simultaneously: one calculates semantic similarity while the other calculates keyword match scores. Once the two rankings are generated, they are merged using RRF (Reciprocal Rank Fusion).
- Search by meaning only: When asked "MLX," it recalls "Apple Silicon Inference Framework." The meaning matches, but the keywords don't—however, it struggles with acronyms, proper names, and project codes.
- Search by keyword only: Very strong exact match; if you phrase the question differently, it won't find anything.
RRF converts each ranker’s ranking into a score; the higher a memory is ranked, the greater its contribution—we use ranking votes rather than score votes because the two scoring scales are different, and simply adding them together would introduce bias.
This step is purely about gathering candidates; there might be as many as 100 entries, and the next step is to narrow them down.
III. Selecting the Best Strokes: Arranged in Eleven Layers
The sorting process narrows down the 100 candidates to the top 15 by using eleven stacked layers of signals, with each layer applying a multiplier to each candidate:
- Time—The newer it is, the more points it earns. The decay curve isn’t a straight line—it follows a Weibull distribution: the rate of decay is slow for the first day or two after saving, accelerates after a month, and then enters a low, stable tail phase.
- Trust Level—Your handwritten work earns more points than what the AI generates automatically.
- I've used it a few times—Points are added for frequently reviewed material, and deducted for obscure material. Memory decay slows down for material that’s frequently called upon for review (this explains why, occasionally, something from two or three years ago might still come up—that’s the logic behind it).
- Graph Centrality—Bonus points for being frequently cited within the knowledge network
- Semantic Similarity—The more similar it is to the question, the more points you get
- Minimum Score—Scores that are too low are automatically eliminated
- Remove Duplicates—If two entries are too similar, compare them again after adjusting their scores.
These 11 layers were not randomly stacked—each layer corresponds to a mature concept in retrieval or memory research: time decay, trust, graph centrality, forgetting curve, semantics, noise, and deduplication. What was done is not inventing new methods, but picking signals from dozens of papers and assembling them into a ranking suited to personal memory scenarios—behind which lie two core design principles.
First, different intents are assigned different weights. Fact-checking prioritizes trust, exploration prioritizes time, and entity-finding prioritizes semantics. For the same set of candidates, different query formulations can completely flip the ranking results. That is why the first step must be intent classification—if the intent is wrong, the weights are wrong, and no matter how precise the subsequent ranking is, it is meaningless.
Second, writing and reading form a closed loop; they are not two separate pipelines. Write Track assigns a source credibility score to each memory upon writing; Read Track uses this score as a weight during sorting. More critically, the reverse is true: every time a memory is read, the system writes the fact that it was "read" back into the memory itself, extending its lifespan next time. Pay a little more when writing, calculate a little less when reading; the act of reading in turn extends the memory's lifespan. Standard RAG lacks this feedback loop—their vector databases are static and remain unchanged once written.
Still working on it: Sorting stability
The difficulty of this multi-layer ranking system is that the weights of each layer pull against each other—tweak one, and several other metrics might shift in a chain reaction. Currently, we rely on a set of golden queries (dozens of questions with known correct answers) as a safeguard; every time we adjust the weights, we run through them first to check for regressions. It runs stably in most scenarios, but keeping it from drifting across all intents and all time scales is something we are still continuously tweaking.
IV. The "Careful Selection" Stage: The Gate of Attention
Attention Gate skips most unnecessary fine-tuning—cases where there are too few candidates (two or three), where the scores of the top few candidates have already diverged significantly, or where all candidates are clustered within a narrow range. Not every problem is worth running through the entire process.
After the initial sorting is complete, there is a final round of fine-tuning that uses a more expensive model. This model combines the queries and candidate memories and scores them jointly, which is much more accurate than a simple comparison; however, the model must be run for each record, making the process slow and costly.
If the reranking model fails three times in a row, it will automatically fall back to plain scoring for 10 minutes—it won’t keep trying—just like a fuse.
V. The Fork in the Road That Comes from Asking Deep Questions: Borrowing from *Thinking, Fast and Slow*
For high-certainty questions like asking about progress or recalling memories, the first four layers are enough. However, for questions like "I'm thinking about whether to do X, what do you think?", Read Track takes an additional, slower detour—starting from the matched concepts, it walks a few steps along the knowledge graph to retrieve neighboring memories as well. This path is more costly, so shallow questions bypass it; it is activated only when it determines you are asking a question that requires extended thinking.
Simply "finding the most similar paragraph" won't provide any useful answers to this kind of question—the agent will only offer generic suggestions, like those found in online articles. Truly useful answers have to grow out of your own context: what you've done in the past, what you've been focusing on recently, and which concepts are connected to X.
I didn't come up with this "fast/slow" distinction—I borrowed it from Kahneman's *Thinking, Fast and Slow*. The human brain naturally has two systems: one fast, intuitive, and effortless; the other slow, logical, and demanding. The memory system shouldn't treat every problem as a difficult task, nor should it treat every question as an intuitive one. Therefore, Read Track presents both paths side by side, letting the questions themselves decide which way to go, without requiring the user to manually choose.
When you ask, “I’m thinking about doing X—what do you think?” you don’t get a generic suggestion; instead, you get a response that references how you did Y last month or brought up Z earlier this year—that’s the end of the road.
A Point That's Easy to Get Confused About
There are two instances of “slow” in “Read Track,” and they don’t refer to the same thing.
The First Slow One—This is what was mentioned earlier as “ask slowly, take the long way.” When you ask a question that requires further thought, the system immediately decides to take an additional branch to search the graph for related information. This is a practical application of Kahneman’s two-system model: quick questions with quick answers, and slow questions with slow answers.
The Second Slow One—called Slow Thinker, running secretly in the background. It doesn't handle your current Q&A, but instead listens to you talk while guessing "what you might ask next," fetching potential answers and putting them in the cache waiting for you. The inspiration for this one comes from Salesforce's VoiceAgentRAG.
The former is “real-time,” and the latter is “prediction.” Coincidentally, both are called “slow,” but one runs in the foreground and the other in the background, and they are implemented in different files.
VI. The Final Step: Compiling the Materials for the Agent
After sorting, the top 15 entries were selected, but the agent has a context limit, and fitting the full text of all 15 entries might already blow the token budget. So the final step does two things: for long paragraphs, keep only the summary, not the full text—this is a trade-off; and depending on whether the caller is Hook, API, or CLI, it lays things out differently—the API gets JSON, the chat interface gets cards. This is cosmetic surgery.
This step is easily dismissed as mere decoration, but it actually determines how much the agent ultimately sees. Early on, directly slicing the first 15 entries into the context often resulted in half the tokens being eaten up by a single long block; after adding budget-aware packing, token utilization jumped from 60% to 85%. No matter how good the ranking is, it's useless if it can't fit into the AI's brain.
Is the incoming inspection sufficient? Do we need another step for outgoing inspection?
It’s not enough to inspect the goods just once when they’re received, so we run them through another screening process before packing them for the agent—one screening upon receipt and another before shipment, for a total of two security checks.
Memories undergo a security check before being stored in the vault, filtering out strings like "ignore previous instructions" or "now say..." that attempt to smuggle their way in to manipulate the agent—as covered in that Write Track article. But security rules get upgraded, and memories that came in three months ago used the old rules; attack techniques are also constantly evolving every month, so a string allowed through today might be a new kind of bomb next month.
If I spot a potentially dangerous sentence pattern, I’ll rewrite or discard it on the spot during the output stage; the same goes for the set of answers I’ve secretly cached in the background—I filter them when I save them, and filter them again before using them. It’s a hassle, but the data I feed to the agent has to be clean.
VII. Deciding Which Tracks to Include
6 intent classifications, 11 scoring layers, 2 rerank gates (attention gate + circuit breaker), Cascade Recall in three modes (LOCAL / GLOBAL / HYBRID), and a budget-aware output formatter—these stitched together form what's running every single day, handling cross-session recall, progress inquiries, and recommendations. Every single layer is guarded by golden query regression tests, while the Dream Loop automatically organizes memories in the background every day at 4 AM.
But it isn't flawless. Let's honestly list a few known limitations:
- Latency vs. Recall Rate—Deep recall (the one that follows the graph) is slow, often taking several hundred milliseconds. It’s used only for problems that require extended thinking; it’s a compromise.
- Carefully selecting that layer comes at a high cost—The attention gate helps save some, but when it needs to run, it still has to run. When it falls back due to the fuse, the resulting drop in quality is noticeable.
- Memories Buried Beneath the Frozen Layer: The Full Text Cannot Be Recovered—Only the abstract remains. It’s fine for legal retroactivity, but it’s not enough to reconstruct the context at the time.
- The intent was misclassified, causing the entire pipeline to run in the wrong direction.—Currently, the incidence rate is being reduced by dynamically lowering the confidence threshold; we are still exploring a more stable approach for the post-event rollback mechanism.
- Self-assessment alone cannot block all illusions—the "slow" mode runs CRAG self-assessment; the "fast" mode skips this step, at the cost that illusions may slip through on the "fast" path.
Each item represents a trade-off between cost and effectiveness—the system works and is up and running, and the issues listed above are known limitations, not potential pitfalls that could cause it to crash at any moment. The point of laying all this out is that the cost behind every “discovery” is clearly documented—nothing is hidden.
VIII. Several Design Principles That Run Throughout
What holds this system together is not the mechanism itself, but five recurring design principles: fast thinking, slow reasoning (deciding for yourself how deep to go into a problem); do a little more when writing so you have to calculate a little less when reading; the act of reading itself loops back to extend memory longevity; intent determines everything; and trade-offs are laid out on paper instead of hidden away.
- Fast Thinking, Slow Thinking—You don’t have to go all out on every question. The depth of the question is determined by the question itself; there’s no need for the user to manually select a difficulty level.
- Pay a little extra when you write it down, and you won't have to do the math when you look it up.—
trust_score,access_count, triple-tuple double projection—these "extra steps" taken during write operations are all designed to reduce the number of levels traversed during lookups. - The read operation resets the memory lifespan.—Every time you ask, the validity period of that memory is extended. Unused memories naturally fade away, while those you revisit often remain. Just as paths you’ve walked don’t grow over with grass.
- Intent determines everything—Different phrasings result in different retrieval strategies, different ranking weights, and different search depths. For the same agent, different queries essentially trigger different retrieval systems.
- Write down the trade-offs on paper—You can’t pretend there’s no cost. Write it down—so I can see it now, and so my future self can see it later.
IX. Thinking Isn’t Enough—Be Ready Before You Speak
A good server doesn’t just take orders—he notices when your glass is empty and refills it without being asked. Read Track has someone working quietly in the background who anticipates your needs, searches for what you’re looking for, and has the answer ready before you even speak.
So far, we've been talking about "you ask, and the system finds it." It does five things:
- Let me hear what you're saying—Focus on the current conversation and use the last few sentences to determine what you’re most likely to ask next.
- Following a logical flow—Arrange the possible questions you’ve come up with in the order that people would naturally ask them
- Let's sneak a peek at the answers first—Pick out the top few most likely questions, run a full search in the background, and keep the results handy.
- Trigger Sparingly—Not every sentence triggers a response. If the conversation is too short, it doesn't trigger a response; if what was just said is clearly off-topic, it doesn't trigger a response; if a prediction was just made, it doesn't trigger a repeat response. I don't want to waste resources.
- Until You Actually Speak Up—The moment you ask, first check the batch of prepared materials you have on hand to see if any of them match; if they do, use them right away to skip a full search; if not, follow the standard procedure—no harm done.
This is the logic behind the head chef preparing the next dish. The guests haven't ordered yet, but after finishing the appetizer, they usually order a main course, so the chef cuts the ingredients for two or three possible dishes in advance and places them on the station. If the guests actually order it, it only takes a few seconds to serve; if the guests change their minds and order something else, that batch of prep work might still be useful for the next table, and at worst, it gets thrown away.
It is easily confused with the "Thinking, Fast and Slow fork" mentioned earlier. That one asks whether to decide on the spot to take an extra deep path after a query is made; this one is already secretly running even before the query is made. One is in the foreground, the other is in the background. Coincidentally, both are called "slow," but what they do is completely different.
When it matches, there's almost no delay from the question to the answer—the prep work was already chopped and ready; when it doesn't match, that batch of prep work is wasted. Therefore, triggering sparingly is crucial—if the conversation is too short, don't move; if it goes off-topic, don't move; if it just moved, don't move again. It's better to guess less than to force a guess.
The batch of preparation materials found by the background search must be disinfected once before entering the storage room, and disinfected again before being taken out of the storage room to show to the agent—the aforementioned double security check must also be followed for this preparation line. We cannot let our guard down just because the materials were prepared by "one of our own."
So here’s the full picture of Read Track as it stands now: easy to understand, easy to find, properly organized, accurate selection, fits perfectly, and on time. We’re still fine-tuning each aspect, but the framework is already in place and running every day.
Read Track: That’s where it ends. Intend, locate, arrange, select, insert, disinfect, retrieve—seven components, one pipeline.
It doesn't help me "remember what needs to be remembered"—that's the job of Write Track and Background Track. It only does one thing: when I need that piece of memory, the right one surfaces on its own.
That opening line—“It’s not hard to save money; the hard part is letting the right amount come to mind on its own”—well, now it comes to mind.
Architecture Overview
The entry point for Read Track is recall(query, context); the pipeline framework is QueryClassify (Intent Classification) → Fast Search → (Cascade Recall, slow only) → Output Formatter. Regardless of whether the caller is a Hook, MCP (Model Context Protocol), CLI (Command Line Interface), or API (Application Programming Interface), they all go through the exact same pipeline; the only difference is the serialized format of the final Output Formatter. Scoring and Reranking are not independent stages—they are embedded inside qdrant_search() (services.py).
This pipeline is based on more than 10 research papers: HippoRAG's PPR, LightRAG's dual-mode search, GraphRAG's three-layer KG, HyDE's dual-task design, CRAG's self-assessment gate, Kahneman's division of labor between fast and slow, VoiceAgentRAG's prefetching, Weibull decay curves, RRF fusion, and Attention-Residual's intent-dependent weights. Each component below will cite its corresponding source.
I. Query Router Intent Classification
The Query Router first categorizes each query into one of six intents: entity_lookup, factual, conceptual, exploratory, cross_domain, unknown (query_archetypes.py), and the classification results determine the following three downstream actions: LayerPlan(Floor Plan—Check Which Floors), ScoringConfig(Scoring configuration, weight vectors for 11 stages), RetrievalMode(Search modes: LOCAL / GLOBAL / HYBRID).
Determining a Two-Track Approach:
- Keyword Match:
~0 ms, Pure Regular Expression / Lexicon Match - Linguistic Intention Scale:
~5 ms, compare the query—converted into an embedding vector—with the archetype vector
The confidence scores from both sides are combined; only if the combined score remains below the dynamic threshold is the LLM triggered (~500 ms). Inside query_router.py, QueryClassifyOp combines this entire chain into a single operator—the Slow Thinker's prefetch result is also injected from this layer.
Personalized Router: A Layer of Personal Preferences Beyond Intent
memvault.query.personalized_router
adds a re-weighting layer after QueryClassifyOp to address the blind spot of pure archetype classification—for the same query, “How have things been lately?”, users whose attention profile leans toward factual and users whose attention profile leans toward exploratory arrive at different correct intents:
- Pull the 7/30/90-day intent distribution from
attention_profile(same source as Background Track's Interest Profile, written byattention_tracker.py) - Apply prior weighting to the confidence scores of the archetypes for the six intents:
p_final = p_classify × (1 + α × intent_freq_norm), α default 0.25 - Low-Sample Gatekeeping: if
profile.sample_count < 50, bypass directly (to avoid cold-start offset) - Output
PersonalizedIntent; downstream,ScoringConfig/RetrievalModeuse this value instead of the original archetype result
This reads the same attention_profile record as the Background Track's Interest Profile. Writes are computed by BG; Read consumes it at query time—yet another write-read loop.
II. Fast Search: Qdrant Hybrid (Hybrid Index) + RRF
qdrant_search() (services.py nearby) queries both dense (dense vector, Qwen3 0.6B MLX, 1024d) and sparse (sparse vector, BM25 per-service avgdl) at the same time—this is the core that Fast Search must run, and the two sides are then merged using Reciprocal Rank Fusion (RRF):
score_rrf(doc) = Σ_ranker 1 / (k + rank_in_that_ranker)
k defaults to 60. Use ranking votes instead of score-based votes—the dense and sparse scoring scales are completely different, and simply adding them together will result in the ranker with the higher score dominating the results.
Both Scoring and Reranking are embedded inside qdrant_search() rather than being a standalone stage—this is intentional: the access_count, provenance, and attention_profile that scoring needs are all within qdrant_search's scope, and pulling it out into a separate stage would require moving a lot more state.
III. 11-Stage Scoring Pipeline
The 11 stages (scoring_pipeline.py) assign different weights per intent—entity_lookup pushes SemanticBoost up to 0.5, exploratory raises the "Recency" value, and factual weights Trust even more heavily. When all intents shared a single set of weights early on, factual and exploratory overlapped on 8 of the top 10 results; only after splitting by intent could they be separated. Each stage is a ScoringOp (a scoring operator, following the Operator protocol):
1. RecencyBoost (Recency Weight) × (1 + 0.15 × e^(-age/14))
2. ImportanceWeight (Confidence Weight) × (0.7 + 0.3 × confidence)
3. TrustBoost (Trust Weight) × (1 - 0.3 × (1 - trust))
4. FeedbackBoost (Feedback Weight) × (1 + 0.15 × tanh(net/3))
5. LengthNorm (Length Normalization)÷ (1 + 0.3 × |log2(len/500)|)
6. WeibullDecay (Weibull Decay) 4-tier: Core 180d · Hot 60d · Warm 30d · Cold 14d
7. PPRBoost (Graph Centrality) × (1 + 0.3 × ppr_score) # HippoRAG-inspired
8. SemanticBoost (Semantic Weight) × (1 + 0.3 × cosine_sim)
9. MinScoreGate (Min Score Gate) hard filter< 0.10
10. NoiseFilter (雜訊過濾) 7 類 quarantine tag
11. PairwiseDedup (成對去重) cosine > 0.85 → ×0.5, then min_score
The 4-tier Weibull distribution is determined by confidence: high confidence goes into the Core tier and decays slowly; low confidence goes into the Cold tier, dropping by half in 14 days.
access_count extends the effective half-life (up to a maximum of 10x the cap)—this is one of the closed loops between reads and writes: the act of reading writes back to access_count, extending its lifespan (mentioned in Write Track's "Provenance" section).
IV. Jina v3 Reranker + Attention Gate
Attention Gate uses three skip rules to prevent unnecessary reranking:
- If there are ≤ 2 candidates—return them directly; do not rerank them.
- Dominant Score: The difference between the first two scores is greater than the threshold—the result is already clear, so no reranking is performed.
- Cluster of closely grouped scores: All candidate scores are clustered within a small range—even reranking cannot distinguish them, so skip them.
Inspired by TurboQuant+’s "early return" strategy, reranker.py runs on a Cross-encoder 0.6B MLX (rerank_bridge.py) that jointly scores query and doc together—much more accurate than a pure dual-encoder approach, but the problem is that it’s computationally expensive: the model has to be run once for every candidate.
Circuit Breaker: After 3 consecutive failures, the system enters a 600-second recovery period, during which it bypasses the reranker and uses pure scoring.
Score Blending: default 0.3 × scoring + 0.7 × rerank, but it varies by intent—entity_lookup's 0.2/0.8 gives rerank more weight; exploratory's 0.5/0.5 brings scoring back to half, because exploratory questions have no absolute right or wrong answer, so the raw score matters more.
V. Cascade Recall (slow intent only)
The fast path always runs, while Cascade is triggered only by slow intent.—It's not an either/or situation. Inside kg_services.py, cascade_recall() (chain recall) merges Fast Search's main results (schema field cards) with the supplementary results that Cascade Recall diffuses out (cascade_cards) into a single response.
This "fast / slow dichotomy" skeleton comes from Kahneman's dual systems (System 1 fast thinking / System 2 slow thinking). Early versions let users manually select fast or slow, but it was later refactored to be determined by intent (choose_thinking_mode()), but the division of labor between the “fast track” and the “slow track” itself has not changed.
Internally, it is divided into two paths:
- GLOBAL (Global): L2 Summary (LLM-pregenerated community summary) + L1 Community (Leiden algorithm clustering results). These two layers do not go through the scoring pipeline—they are pre-compressed views and can be fetched directly
- LOCAL (Local): L0 Triple + PPR (Personalized PageRank) Walk. Starting from the hit entities, walk a few steps along the graph with a damping factor of 0.85. Neighboring triples diffuse back
The mode is determined by the intent: entity_lookup/factual go LOCAL (for precision), conceptual/exploratory go GLOBAL (for a bird's-eye view), and cross_domain goes HYBRID (mixed, full search).
L0/L1/L2 do not perform scoring themselves. Only the Blocks layer still goes through qdrant_search()'s full scoring and reranking—this ensures that the Blocks portion inside cascade_cards follows the same sorting logic as cards, so there is no conflict between the "fast" and "cascade" scores for the same memory.
A point that's easy to confuse—the "slow" in "Cascade Recall" is not the same thing as the "Slow Thinker" mentioned in the next panel-end section. The former is of Kahneman lineage: deciding at query time whether to dig deeper. The latter is of VoiceAgentRAG lineage: predicting the next question in the background and fetching it ahead of time into the cache. Both happen to be called "slow," but they're implemented in different files (kg_services.py vs slow_thinker.py).
Pipeline Ordering Vulnerabilities
The order of the 11 stages has dependencies. RecencyBoost comes before TrustBoost because the trust merged after a certain dream loop will be overwritten by the temporal sequence of old and new memories, so time correction must be applied first.
Order-dependent pipelines are inherently brittle—swapping the positions of two stages can completely change the top-k results. Currently, we rely on a set of regression tests to guard against this: dozens of golden queries have expected rankings, and we run a round of tests before tweaking any weights, blocking the change if the top-k difference exceeds a threshold. The current setup is workable, and we are still fine-tuning it to make it more robust.
VI. Output Formatter and Token Budget
The Output Formatter is the final step in the pipeline—the token budget dictates three things: cards and cascade_cards split the budget roughly 60/40; for extra-long blocks, keep only the summary and let the agent decide whether to expand the full text; for duplicate paragraphs, keep only the one with the highest score.
- The token budget for the main results (
cards) and the diffused results (cascade_cards) is split (typically 60/40) - For extra-long blocks, attach the summary first, then put the original text in the
expandfield—letting the agent decide for itself whether to dig into it - For duplicate entries (cosine > 0.9), only the one with the highest score is retained.
All entry points (Hook, API, MCP, CLI) follow the same pipeline; the only difference is this: format (Format) is a serialization parameter (text / json / cards), which has nothing to do with the upstream retrieval strategy. I had underestimated this layer before—directly truncating the top-15 and throwing them into the context often resulted in a single long block eating up half the tokens, leaving no room for subsequent memories; after adding budget-aware packing, the average utilization rate jumped from 60% to 85%.
Read-Time Sanitize: Symmetrical to Write Injection Guard
The Read side runs a symmetrical pass through memvault.security.read_sanitize once—this patches a gap in Write Track's injection_guard.is_unsafe(), which only blocks against the rule version in effect at write time. A block that entered the vault three months ago used the old rules, and attack samples keep evolving:
- Scan all
cards/cascade_cardswithis_unsafe_for_injection()before the Output Formatter (shares the Write side's ruleset, synced with every deployment) - A block that hits a rule goes through
sanitize_for_injection(): dangerous token sequences are replaced with a[REDACTED]placeholder, preserving semantic context; entries are not discarded outright (to avoid top-k dilution) - The Slow Thinker's prefetch cache applies the same principle—filtering once before entering the cache (write-time), and filtering again before leaving the cache to the agent (read-time)—a double layer of defense to prevent the cache from becoming a backdoor that bypasses read sanitization.
- Hit Event Write
audit_log, trace which old block triggered the new rules → drive the Write-side ruleset upgrade in reverse
Asymmetric security checks have a flaw—they only monitor the write side, which assumes that rules will never be updated and attacks will never evolve. Running the process once on both the write and read sides ensures that older blocks are also subject to the new rules.
VII. Trade-off List
Read Track can’t be fast, accurate, and comprehensive all at once—each aspect involves a trade-off between cost and effectiveness, so you can only choose two. The issues that still haven’t been fully resolved are probably these:
| Question | Current Situation |
|---|---|
| Latency vs. Recall Rate | The "slow path" has a significant impact on p95 (95th percentile latency). With the current intent-based gating mechanism, only problems that truly require extended thinking follow this path. |
| Reranker is expensive | The Attention Gate blocks approximately 30% of rerank requests; the Circuit Breaker isolates failures for 600 seconds. |
| Only the summary remains for the Cold/Frozen tier | Once the entire text has been compressed, it cannot be retrieved. The planned tier upgrade will allow frequently called "cold blocks" to be promoted to "warm" status. |
| Misjudgment of Intent | The entire pipeline is on the wrong track. Currently, we’re relying on dynamic confidence thresholds to reduce the false positive rate, but there’s no good way to backtrack afterward. |
| CRAG Self-Assessment Oversights | CRAG is only run in "deep" mode; "fast" mode skips this step, at the cost of potentially missing hallucinations in the fast path. |
VIII. Design Decisions Throughout the Project
Underneath the mechanisms in the seven sections above are five recurring design principles: intents dispatched downstream everywhere (intent-driven everywhere); the write-read closed loop formed by trust_score and access_count; Kahneman's fast/slow made concrete as thinking_mode; "defense in depth" on both the write and read sides; and laying every trade-off out on paper instead of pretending there is no cost.
- Intent-driven everywhere:
QueryClassifyOp's intent is dispatched downstream to scoring weights, retrieval mode, score blending ratio, and cascade layer routing. The same query takes a completely different path depending on intent. - Write-Read Closed-Loop:
trust_scoreis scored bysource_trackerat write time and consumed byTrustBoostOpat read time;access_countis written back at read time and affectsWeibullDecay's effective half-life on the next read. This closed loop spans both the Write and Read tracks—it is not a one-way pipeline. - Kahneman's "fast" and "slow":
thinking_mode = fast / slowoperationalizes Kahneman's dual systems. Slow runs Cascade Recall + CRAG self-evaluation on top of fast, not as an either-or choice—slow = fast + extras - Defense in Depth: Write Track three gates interception + injection guard, Read Track attention gate skips meaningless rerank + circuit breaker isolates rerank failures. Each layer allows the next layer to fail without breaking the whole pipeline
- Trade-off on paper: Latency vs. Recall, Computational Cost vs. Quality, Cold Storage vs. Full-Text Restoration—every trade-off is clearly outlined in §7; we don’t pretend otherwise.
IX. Slow Thinker: Predictive Prefetch
Slow Thinker is a standalone backend pipeline (memvault.slow_thinker), does not block the main recall(), the source of the event is conversation.utterance.appended, inspired by Salesforce AI Research's VoiceAgentRAG(Dual Agent: Background Slow Thinker + Frontend Fast Talker + FAISS speculative cache) — "The head chef prepares the next dish" is a metaphor borrowed for the technical name of this component.
Five Components
- NextQuestionPredictor: Consume the most recent N=8 utterances and call a small LLM to generate top-3 predicted queries. The prompt restricts queries to be specific and searchable (empty queries like "and then?" are not allowed).
- PredictionPipeline: 5 Ops strung together—
ContextSampler→QuestionGenerator→QuestionRanker(Sorted by how naturally the dialogue flows) →QuestionDeduplicator(Excluding duplicates from predictions made in the last 5 minutes) →QuestionEmitter. Each step can be implemented separately. - SpeculativeFetcher: Run the query using top-1 / top-2 predictions until it completes
recall()(Includes 11-stage scoring + reranker + cascade, depending on the intent); results are written to Redismemvault:prefetch:{user_id}:{query_hash}, TTL 300s. Fire-and-forget; no retries on failure (a new batch will be generated for the next round of utterances). - Admission Control: 5 filtering rules; prefetch is allowed only if all are passed
conversation.length < 3 turns→ skip (too few samples)topic_drift_score > 0.7→ skip (Just went off on a tangent; the old context is irrelevant)last_predict_age < 30s→ skip (just updated—to avoid repeated fetches)predicted_query.confidence < 0.4→ skip (Even small LLMs aren't sure—forcing it just wastes tokens)min_sample_threshold: if the user'sconversation_count < 10, bypass the entire Slow Thinker (not enabled during the cold-start period)
- Injection Point: inside
QueryClassifyOp. Whenrecall()actually comes in, it first checksmemvault:prefetch:{user_id}:{query_hash}; on a hit, the prefetched result goes straight through the Output Formatter (skipping the entire pipeline in between); on a miss, it follows the normal process—a zero-cost fallback.
Relationship to Read-Time Sanitization
The prefetch cache is Sanitize's two-layer application zone—sanitize_for_injection() runs before entering the cache (write-time), and the Output Formatter sanitizes it once more before it leaves the cache to the agent (read-time). The cache must not become a backdoor that bypasses read-side sanitization; this is an invariant locked in by design.
Why this design?
The core observation of VoiceAgentRAG is that the inter-utterance interval of a voice agent (from when a person starts speaking to when they finish) is long enough to run a complete RAG pipeline. By using this "speaking time of the human" to speculatively fetch, when there is a cache hit, the latency is slashed from ~800ms to < 100ms (Redis hit + formatting), and memvault's conversation interface also leverages this same time window.
The observed miss rate is about 55–65% (meaning one of the top-3 predictions hits), which means 35–45% of the prefetches are purely wasted—this is a deliberate choice to "trade idle tokens for zero latency upon a hit," so the 5 rules of Admission Control are the lifeblood of this component, not just nice-to-haves.
Read Track trilogy comes to this conclusion: intent → recall → ranking → reranking → graph → packaging → dual-end sanitize → predictive prefetching. Every layer has a fallback, a trade-off, and golden queries guarding it, running every day.
Read Track is now disassembled. QueryClassify → fast/slow split → 11-stage scoring → reranker → OutputFormatter → read_sanitize → SlowThinker prefetch loop—seven components, one entry-agnostic pipeline.
It is not responsible for "whether memvault has remembered what it should remember"—that is the responsibility of Write Track and Dream Loop. It is only responsible for one thing: given a query, surfacing the correct memory across sessions.
The real time-consuming part isn't tuning new components—it's tuning long-term parameters like scoring weights, prefetch hit rates, and cascade boundary detection. That concludes the trilogy.
At the rate you're typing, will you have time to get the ingredients ready?
Background pre-fetching prepares data while you're thinking about your next sentence, so it's directly related to how fast you type. Enter four numbers that you know, and calculate how many pre-retrievals will be allowed per day according to the rules in this article, and approximately how many of those will result in wasted attempts. You don’t need to measure anything in any of the four fields; all calculations are performed within the browser, and no data is sent out.
The figures are taken from this article: "no action until after 3 rounds," "30-second cooldown," "5-minute TTL," and "the wait time saved by a hit is reduced from '~800 ms' to '<100 ms'." The calculated number of releases is the upper limit, not the actual value.—of the five release rules in this article, the other two (topic drift and prediction confidence) depend on what you are currently talking about, which the browser does not know, so the actual number will only be fewer. That hit rate rule is an observation by the author on their own data, not a general rule.
Health Check Prompts for AI Agents
Relying on the same vector search for all problems is like taking the same path for fact-checking and seeking inspiration—neither approach is correct. This article breaks down the reading paths according to the problem’s intent (hierarchical classification, multi-signal ranking, dynamic weights, cost-saving Attention Gate, etc.) and serves as a reference table.
Send the text below to your AI and have it compare your RAG/memory system point by point against the reading design outlined in this article:
Further Reading
Resources that have actually influenced the design of this Read Track.
| Resources | Why It Matters |
|---|---|
| HippoRAG (NeurIPS '24) | PPR Boost and the strategy of traversing the graph starting from the hit entity. Cascade Recall's LOCAL mode is essentially the practical implementation described in this article. |
| LightRAG | The naming conventions for LOCAL, GLOBAL, and HYBRID, as well as the "intent → mode" mapping rules, are taken directly from here. |
| Microsoft GraphRAG | The concept of L0 / L1 / L2 three-tier architecture and using L2 community summaries for zero-latency recall comes from [Source/Inspiration]. Read's GLOBAL mode consumes these pre-generated L2 views. |
| CRAG: Corrective Retrieval-Augmented Generation | The self-evaluation layer is only run on the slow path. The CRAG evaluator is used to perform a quality gate after reranking—in fast mode, this layer is skipped to save tokens. |
| Attention-Residual for Intent-Dependent Scoring | The theoretical basis for intent-dependent weights in 11-stage scoring—different intents adjust the weight vectors of different signals; the same scoring scheme is not used for every question. |
| Cormack et al. 2009: Reciprocal Rank Fusion | The original approach for merging rankings from a Dense + Sparse dual-ranker. Fast Search's RRF fusion adopts this formula directly, and k=60 is also this post's default value. |
| TheTom/turboquant_plus | The early return strategy for Attention-gated Reranking comes from here. My three skip rules (too few candidates, score dominant, tight cluster) are rewritten using its judgment logic. |
| Kahneman — Maps of Bounded Rationality (Nobel Lecture) | The theoretical framework for the Fast/Slow dichotomy. thinking_mode originally required the user to choose it themselves; it was later refactored so the intent determines it automatically, but the division of labor between System 1 and System 2 itself never changed. Cascade Recall triggering only in slow mode is a direct descendant of this lineage. |
| VoiceAgentRAG (Salesforce AI Research) | Slow Thinker / Pre-fetched inspiration source. The dual-agent architecture (background Slow Thinker + foreground Fast Talker + FAISS cache) corresponds to my slow_thinker.py + QueryJournal prediction + Redis speculative cache. |
| win4r / memory-lancedb-pro (LanceDB Pro autoRecall) | The original inspiration for the entire memvault design. autoRecall's philosophy of "proactive injection" (stuffing relevant memories into the context before the agent thinks), the five-layer retrieval + three-layer promotion + Weibull decay, all grew their corresponding implementations here. Brought in via the YouTube channel AI Hyperdomain's introduction of openclaw |
| memvault Write Track — After the conversation ends | Part One of the Trilogy. Before you can read, you need something to read—three gates, dual-track writing, and how to fill out a source history form |
| memvault Background Track — After Saving | Part Two of the trilogy. The L1/L2 summaries, PPR, and Interest Profile used by Read were all generated by the Background module. |