Retrieval semantics
How a recall query becomes an answer — routing, retrieval lanes, fusion scoring, budget tiers, and the explain breakdown.
When your agent calls recall(), Mubit does more than a vector search. The query is routed, candidates are gathered from several independent retrieval lanes, the lanes are fused into a single ranked evidence list, and an answer is synthesized with citations back into that list. This page explains each stage so you can predict what a query will return — and tune it when it doesn't.
Mental model: one query fans out into parallel retrieval lanes; fusion scoring merges them into one ranked evidence list; synthesis reads that list and cites it. Every knob on the request (entry_types, limit, rank_by, budget, temporal bounds) shapes exactly one of those stages.
Routing
Every query carries a mode:
| Mode | What happens |
|---|---|
agent_routed (default) | A router agent classifies the query and may expand it into a small set of alternative phrasings (variants) to improve semantic recall over paraphrased memories. Variants are grounded in the input — the router never invents new concepts — and are capped (default 3). Evidence is then synthesized into a final_answer. |
direct_bypass | Skips the router and answer synthesis; runs the direct semantic-search lane and returns raw evidence. Lower latency, no LLM in the loop. |
Retrieval lanes
Candidates are gathered from independent lanes. Each returned evidence item is tagged with the lane that produced it in its retrieval_mode field:
retrieval_mode | Lane |
|---|---|
semantic | Embedding similarity search over long-term memory (the main lane; also blends lexical/sparse term matching in fusion) |
working_memory | Live working-memory variables and goals for the run (on by default; disable with include_working_memory=False) |
chronological | Temporal scan — activated when the request sets min_timestamp/max_timestamp; filters by each entry's occurrence time (falling back to ingest time) |
trace_scan | Direct scan over run traces |
exact_reference | Exact lookup by a stable reference_id (byte-for-byte re-read) |
checkpoint | Stored context checkpoints |
rule_overlay | Rules injected regardless of similarity — hard constraints always ride along |
lesson_overlay | Cross-run lessons injected by relevance (see The learning loop) |
archive_overlay | Archived artifacts surfaced alongside semantic hits |
recency_fallback | Most-recent entries returned when no lane produced a confident match |
Fusion scoring
Each candidate's final score is a weighted blend of component scores:
- Semantic — embedding similarity to the query (and its variants).
- Lexical — term/sparse match strength.
- Recency — newer entries score higher. Recency decays exponentially with a half-life (default 7 days: an entry scores half as much at 7 days old, a quarter at 14).
- Reinforcement — entries with positive outcome history (see
record_outcome) get a multiplicative boost; a fully-reinforced, production-verified entry can reorder above fresher near-duplicates.
Two adjustments apply on top: entries whose stored env_tags intersect the request's env_tags get a relevance boost (conflicting tags get a small penalty), and superseded entries take a 0.5x stale penalty.
rank_by shifts the blend:
rank_by | Behavior | Weights (semantic / lexical / recency) |
|---|---|---|
relevance (default) | Semantic-dominant | current default weights |
freshness | Recency boosted | 0.50 / 0.10 / 0.40 |
balanced | Even split | 0.60 / 0.15 / 0.25 |
Budget tiers
budget controls the latency/quality trade-off of candidate gathering:
budget | Behavior |
|---|---|
low | Skips deep graph traversal, fewer ANN candidates — fastest |
mid (default) | Standard retrieval |
high | Deeper graph traversal, more candidates, optional reranking — best recall |
Shaping the candidate set
| Request field | Effect |
|---|---|
entry_types | Only evidence of these types is returned (e.g. ["fact", "lesson"]) |
limit | Maximum evidence items (default 5) |
min_timestamp / max_timestamp | Inclusive occurrence-time window (unix seconds); activates the temporal lane |
env_tags | Caller's stack context, e.g. ["lang:python:3.12"] — boosts stack-matching entries |
agent_id | Filters evidence to the types in that agent's registered read_scopes |
user_id / lane | Scope to one user's memories / one coordination lane |
prefer_current_run | Current-run evidence only; skips cross-run lesson overlay |
evidence_only | Skip answer synthesis, return evidence only — saves the per-query LLM call |
From SDK v0.12.0 the typed recall() helpers forward budget, rank_by, explain, min_timestamp/max_timestamp, and evidence_only directly in all three languages (client.recall(query=..., explain=True)). On earlier SDK versions these are wire-level fields the typed Python helper does not forward — send them directly over the transport (POST /v2/control/query with e.g. {"run_id": ..., "query": ..., "explain": true}) or via client.advanced.query({...}). entry_types, env_tags, limit, and the scope fields are recall() kwargs on every version. See Control HTTP API.
Explain
Set explain: true on the request and every evidence item carries an ExplainInfo breakdown. A returned evidence item looks like this:
{
"id": "8f2c1a9e-...",
"content": "CSV export fails when column headers contain commas; quote headers before export.",
"source": "agent",
"score": 0.81,
"run_id": "support:acme:ticket-118",
"entry_type": "lesson",
"retrieval_mode": "lesson_overlay",
"reference_id": "ltm:lesson:8f2c1a9e",
"referenceable": true,
"is_stale": false,
"superseded_by": "",
"knowledge_confidence": 0.74,
"explain_info": {
"semantic_score": 0.78,
"lexical_score": 0.42,
"recency_score": 0.31,
"temporal_decay_factor": 0.62,
"stale_penalty_applied": false,
"temporal_intent_detected": false,
"rank_by_mode": "relevance",
"fusion_weights_used": { "semantic": 1.0, "lexical": 0.25, "recency": 0.1 }
}
}score is retrieval relevance for this query; knowledge_confidence is durable reliability of the entry itself — different axes. See Trust, confidence, and staleness.
Citations
When the response includes a synthesized final_answer, the citations field lists 0-based indices into evidence that the answer is grounded in. Indices are validated server-side against the evidence length, so every value is safe to dereference — use them to render citations or audit grounding. An empty list means the model cited nothing or abstained.
Example: Ari checks for a known error
Nimbus's support agent Ari gets a ticket about a failed CSV export and asks memory before answering:
result = client.recall(
session_id="support:acme:ticket-241",
query="Have we seen this CSV export error before?",
entry_types=["lesson", "fact", "trace"],
env_tags=["service:export", "format:csv"],
limit=5,
)
for i in result.get("citations", []):
print(result["evidence"][i]["content"])The recurring CSV-export bug has produced lessons in earlier runs, so the lesson_overlay lane surfaces them even though this ticket is a brand-new run — that cross-run carry-over is the point of the overlay lanes.
Related
- The learning loop — how lessons get into the overlay in the first place
- The memory model — the entry types behind
entry_typesfiltering - Trust, confidence, and staleness — deciding whether to act on returned evidence
- Retrieve data and Query patterns — SDK-level how-to
- Temporal memory patterns — recipes using time-bounded recall
- Control HTTP API — the full wire-level query request