Recall pipeline
Retrieval runs in two places. The daemon answers a recall call by widening a candidate pool, over-selecting it with cheap scalar rules, and optionally reranking. The hook that runs on every prompt fans out across several lanes, fuses them, and then decides what is actually allowed into the context window.
Status as of 2026-09-20 · Sources: hook contract and pinned scoring defaults66.
Daemon path
5 stages ▾One recall call inside chittad, from query to returned hits. The design premise, measured on the golden set, is that the top-20 cut — not ranking precision — was losing gold memories the retriever had already found. So the pool gets wider and the filter that narrows it is deliberately generous.
CHITTA_RERANK_BUDGET, 24 by default) using scalar-only rules — no embeddings, no per-candidate store round-trips, linear in pool size. A candidate survives if any rule fires, and that asymmetry is the point:
- its rank is already inside
limit, so nothing that would have been returned is ever dropped; - it literally contains a distinctive query token;
- it shares realm and kind with a top-5 hit and scores at least half the maximum;
- it is a one-hop association neighbour of the head of the ranking.
prefilter: false, or set CHITTA_RECALL_PREFILTER=0, to restore the previous narrow-pool path exactly.#<id> [pct%] [type] text; explain: true adds a per-hit score decomposition.Strategies
7 strategies ▾Over MCP, recall is a gateway: the strategy argument picks which daemon retrieval tool actually runs, and the gateway applies the rerank stage around it. The default is hybrid, not pure semantic.
| strategy | Runs | Use it for |
|---|---|---|
hybrid (default) | hybrid_recall | General queries. Measured a strict superset of pure semantic on the golden set: nDCG@20 +0.08 on active queries, +2 passes, no regressions. Its BM25 lane catches literal tokens that a low cosine similarity buries. |
semantic | recall | The pre-gateway behaviour, for callers that want embedding similarity alone. |
keyword | recall_keyword | BM25 only, realm-scoped. Exact filenames, identifiers, error strings. |
smart | smart_recall | Multi-lane reciprocal-rank fusion3 across semantic, typed, spreading-activation and session lanes. |
priority | recall_by_priority | Priority-tier ordering rather than similarity ordering. |
temporal | recall_temporal | Time-scoped questions. |
fused | recall | Explicit mapping to the native fused recall tool. The old field value is unrecognized and falls back to native recall. |
Multi-lane fusion uses reciprocal-rank fusion with k = 60. Passing disable_hdc: true skips the hyperdimensional-computing10 lane; it exists for ablation and benchmarking, not for normal use.
recall_analogy performs explicit proportional relation transfer (a:b :: c:?) only. Indexed triplets identify the predicates from a to b and return actual outgoing neighbors of c, with supporting edges. Missing relations produce an abstention with a reason. No model call or vector ranking is used. The frozen benchmark returned hit@1 and hit@3 of 14/14, 14/14 negative abstentions and zero unsupported answers; see the evidence.
Hook lanes
6 lanes ▾On every prompt, hooks/prompt-core.sh runs several recall calls in parallel — each in a backgrounded subshell writing to its own file — then merges them with a lane marker per line. The whole hook works to a wall-clock budget (CHITTA_HOOK_BUDGET_MS, 6000 ms by default) because the harness gives UserPromptSubmit far less time than the individual timeouts sum to.
| Lane | What it asks for | Why it exists |
|---|---|---|
sem | Structured or smart recall on the literal turn | Fuzzy semantic match on what was just said. |
ctx | Semantic recall against a recency-weighted thread query | Conversational continuity across turns, not just this one. |
hyb | recall --strategy hybrid --limit 5 | Fused hits, and the header that carries the calibrated confidence scalar. |
kw | BM25 only, filtered to knowledge types | Exact tokens. Unfiltered it scored 0.345 useful on 29 replayed prompts; restricting to wisdom, insight, research, data, milestone and convergence types lifted that to 0.818. |
corr | Correction-tagged recall | Past corrections that should not be repeated. |
xr | Unscoped hybrid retry | Cross-realm fallback, fired only when the realm-scoped merge came back empty. |
A deterministic correction lookup runs alongside these and is not one of the fuzzy lanes: it is a keyed check, not a ranked search, and it survives the silencing rules below.
Any lane can be turned off for a single agent process with CHITTA_ABLATE_LANES, a comma list of the short names above. That is the mechanism the benchmark’s ablation matrix drives; see Benchmarks.
Admission
5 drop causes ▾Retrieving a memory is not the same as showing it to the model. Every merged candidate carries an admission reason — the lane it came from — and every rejection is counted by cause. The hook renders that as per-item lane tags plus one summary line, so the policy that decides what enters the context window is inspectable rather than implicit.
| Drop cause | Rule |
|---|---|
conf | Below the confidence floor. 30% for the ranked lanes; the BM25 lane is scored on its own much lower scale and gets a floor of 1 instead. |
unk | Silenced by the UNKNOWN rule described in the next section. |
dup | Already injected earlier in this session, tracked by content hash. |
meta | Process artifacts: thinking-block episodes and synthesis memories, which are the system talking about itself. |
cap | Over the per-turn budget. The summary line and the confidence tag are reserved space before memories are appended. |
Terse negations and turns with no distinctive tokens skip topic recall lanes. Under UNKNOWN, surviving hybrid, keyword and cross-realm rows must share a distinctive turn token; semantic/context rows require the small-realm exception as well. Keyword rows from a turn with fewer than two distinctive tokens need BM25 at least 60 (CHITTA_KW_SINGLE_TOKEN_MIN). Deterministic correction checks remain separate. The broader semantic entity check still records shadow evidence.
Confidence band
2 bands ▾The hybrid lane’s response header carries the daemon’s Platt-calibrated maximum relevance, a single scalar standing in for “does this store know anything about this question”. The hook bins it into two bands and states the answer to the model in words. This is hook-side admission logic, not a daemon mechanism — there is no confidence-band parameter inside chittad.
| Band | Raw maxrel | What the model is told |
|---|---|---|
KNOWN | ≥ 81 | Relevant memory is present; verify before fully trusting it, and recall explicitly if it matters. |
UNKNOWN | < 81 | This is the boundary of what is known; do not lean on it, ask or recall explicitly. |
Two bands, not three, on purpose. Version 5.70.0 shipped a three-band tag; store drift then inverted the boundary between the top two bands, with the supposedly stronger band scoring gold-hit@3 0.62 against the weaker band’s 0.64, and no threshold restored a monotonic separation. A confident-sounding tag that can be wrong at rank one is worse than a coarser honest one, so the two top bands were merged into a single confident-but-verify band. The KNOWN-versus-UNKNOWN split is monotonic.
The displayed percentage is recalibrated separately. Raw Platt maxrel read a mean of 0.844 on a frozen golden snapshot where the true hit@3 was 0.533, an expected calibration error of 0.31. A logistic refit over the logit of maxrel cuts leave-one-out calibration error to 0.159 and removes the over-confidence. Only the number shown is recalibrated; the band itself stays on raw maxrel, so behaviour is unchanged and the displayed percentage uses the fitted calibration.
UNKNOWN silence. The per-item percentage from multi-lane fusion is max-normalized, so rank one always reads 100% for any query at all, off-topic included — which defeats a per-item floor. When the absolute signal says UNKNOWN, the semantic, context and fuzzy-correction lanes are treated as noise and dropped. Keyword, hybrid and cross-realm hits survive only if they share a distinctive token with the current turn, which preserves the exact-lookup case without letting stop-word matches leak arbitrary memories. Set CHITTA_UNKNOWN_SILENCE=0 to disable.
Small-realm relaxation. A cross-project calibration under-scores a genuinely small realm: a handful of on-topic memories can pin the band below the threshold even when the top hit is strong. When the hybrid header shows a short result page in a non-default realm with a confident top hit, the token-share rule is extended to the semantic and context lanes instead of dropping them outright. The fuzzy correction lane stays silenced regardless, because a wrong correction costs more than a missed one.
Rerank
optional ▾When a cross-encoder is available, the MCP recall gateway fetches four times the requested limit and scores query/document pairs with cross-encoder/ms-marco-MiniLM-L-6-v2. Ranking uses sigmoid(logit) multiplied by the kind envelope 0.7 + 0.3 × kind_prior, preserving the distinction between curated corrections and operational fragments. Missing, insufficient or malformed candidate/score output falls back to the daemon's ordering. This gateway stage is separate from native candidate pooling.
An ONNX int8 export is preferred when CHITTA_RERANK_ONNX_DIR points at one. Thread count and maximum sequence length are set by CHITTA_RERANK_THREADS (8) and CHITTA_RERANK_MAX_LEN (128). If no reranker can be loaded the pipeline runs without it — this stage is optional.
Knobs
recall and scoring ▾The project was renamed from cc-soul on 2026-09-02; legacy CC_SOUL_* aliases are documented in the rename note.
| Variable | Default | Effect |
|---|---|---|
CHITTA_RECALL_POOL | 60 | Candidate pool depth before the pre-filter, capped at 160. |
CHITTA_RECALL_PREFILTER | 1 | Set to 0 to restore the narrow-pool path. |
CHITTA_RERANK_BUDGET | 24 | Survivor cap the pre-filter over-selects down to. |
CHITTA_RERANK_ONNX_DIR | unset | Directory holding the int8 ONNX reranker export. |
CHITTA_RERANK_THREADS | 8 | ONNX intra-op thread count. |
CHITTA_RERANK_MAX_LEN | 128 | Maximum reranker sequence length. |
CHITTA_ABLATE_LANES | unset | Comma list of hook lanes to skip: sem, ctx, hyb, kw, corr, xr. |
CHITTA_UNKNOWN_SILENCE | 1 | Set to 0 to keep low-confidence lanes instead of silencing them. |
CHITTA_C2_SMALL_REALM | 1 | Small-realm relaxation, with _MAXN (3) and _MINPCT (50) thresholds. |
CHITTA_HOOK_BUDGET_MS | 6000 | Wall-clock budget for the whole prompt hook. |
Set CHITTA_ADMIT_DEBUG=1 to get one line on stderr per candidate, showing its lane, confidence, the confidence band, and whether the small-realm relaxation applied.
Store scoring reads scoring.json. Kind priors are correction/preference 1.3, wisdom 1.1, insight 1.05, operational 0.8 and episode 0.7. The operational prior ranks verbatim distiller fragments below curated conclusions at equal similarity. These are inputs to the bounded scoring pipeline, not calibrated probabilities.
replication_max defaults to 0 (disabled); CHITTA_REPLICATION_MAX overrides it. The opt-in 1.15 cap weights distinct identifiable source sessions, not repeated reads. The measured store had no memories with two independent replications, and the golden delta was zero; no benefit has been established. CHITTA_RECALL_PROFILE=1 writes phase timers to stderr.
Measured
golden set ▾Retrieval changes are graded on a fixed golden set of 30 queries with hand-labelled gold memory ids, scored as mean nDCG at a limit of 20 with the reranker on. The set is small and drawn from one store, so it is directional evidence, not a general claim.
| Run | mean nDCG@20 |
|---|---|
| Pre-filter off | 0.435 |
| Pre-filter on (current default) | 0.487 |
The immediately preceding baseline on the same set read 0.442. Separately, making hybrid the default strategy measured as a strict superset of pure semantic: nDCG@20 up 0.08 on active queries, two more passing queries, and no regressions.
The end-to-end question — does any of this make an agent finish a real task more often or more cheaply — is a different measurement, and it lives on the Benchmarks page.
References
- Gordon V. Cormack, Charles L. A. Clarke, and Stefan Buettcher. Reciprocal rank fusion outperforms condorcet and individual rank learning methods. SIGIR, 758–759 (2009). https://doi.org/10.1145/1571941.1572114
- Yu. A. Malkov and D. A. Yashunin. Efficient and robust approximate nearest neighbor search using Hierarchical Navigable Small World graphs. IEEE TPAMI 42(4), 824–836 (2020); arXiv:1603.09320 (2016). https://arxiv.org/abs/1603.09320 https://doi.org/10.1109/TPAMI.2018.2889473
- Pentti Kanerva. Hyperdimensional Computing: An Introduction to Computing in Distributed Representation with High-Dimensional Random Vectors. Cognitive Computation 1, 139–159 (2009). https://doi.org/10.1007/s12559-009-9009-8
- Stephen Robertson and Hugo Zaragoza. The Probabilistic Relevance Framework: BM25 and Beyond. Foundations and Trends in Information Retrieval 3(4), 333–389 (2009). https://doi.org/10.1561/1500000019
- genomewalker. chitta-field scoring defaults, config.rs at f3176e587dced64dd3a6a663a40a58e3802b0a10. GitHub source (accessed 2026-09-16). https://github.com/genomewalker/chitta-field/blob/f3176e587dced64dd3a6a663a40a58e3802b0a10/src/scoring/config.rs