Skip to content

Configuration

Configuration is a Rust value, not a config server. Because uniko runs entirely in-process, every model, threshold, and pipeline cadence is a field on one struct — UnikoConfig, in uniko-store — that you set before the KnowledgeBase opens. Start from UnikoConfig::default(), override what you care about, and call validate() to catch constraint violations before they reach the database.

use uniko_store::config::{UnikoConfig, EmbeddingConfig};

let mut config = UnikoConfig::default();
config.embedding = EmbeddingConfig::nomic_v15(); // swap the embedder
config.recall_limit = 20;                        // widen the recall bundle
config.validate()?;                              // fail fast on bad combos

Note

UnikoConfig derives Serialize / Deserialize, and the struct carries #[serde(default)]. An empty JSON object ({}) deserialises to exactly UnikoConfig::default(), and any field you omit falls back to its spec default. This is what lets the benchmark harness layer a JSON profile on top of the defaults without re-stating every knob.


Swappable models

uniko's NLP, embedding, and reranking models all resolve through the uni-xervo provider layer. A model is named by a provider id (e.g. local/onnx, local/mistralrs, remote/openai) plus a model id; xervo loads and warms it. Because the model selection is data, not code, you swap a model by editing config fields — no recompile of the pipeline.

flowchart LR
    UC[UnikoConfig] --> E[embedding: EmbeddingConfig]
    UC --> N[nlp: NlpConfig]
    UC --> R[reranker: RerankerConfig]
    E --> X[uni-xervo provider layer]
    N --> X
    R --> X
    X --> ONNX[local/onnx]
    X --> MRS[local/mistralrs]
    X --> REMOTE[remote/openai · remote/voyageai · ...]

The embedder

The embedder controls auto-embedding (Message, Chunk, Observation, Summary) and the computed embeddings on Entity, Fact, Topic, Session, and the rest of the graph. The default is BGE-small-en-v1.5 at 384 dimensions, served by the bundled local/onnx provider.

EmbeddingConfig ships several presets as constructor functions, plus a preset(name) lookup for string-keyed selection:

use uniko_store::config::EmbeddingConfig;

// 384d, BERT-based, query-side prefix only — the default.
let embed = EmbeddingConfig::bge_small_en_v15();
// 768d, 8192 context. Adds search_document:/search_query: prefixes.
let embed = EmbeddingConfig::nomic_v15();
// Returns None for an unknown name — callers print the canonical list.
let embed = EmbeddingConfig::preset("bge-large")
    .expect("known preset");

The presets and their key properties, all defined in uniko-store::config:

Preset name Constructor model_id Dims Notes
bge-small bge_small_en_v15() BGESmallENV15 384 Default. Query-side prefix only.
bge-large bge_large_en_v15() BGELargeENV15 1024 Top-tier MTEB quality; ~10× the params of BGE-small, expect 2–3× slower per-query embedding.
nomic nomic_v15() NomicEmbedTextV15 768 8192 context; needs search_document:/search_query: prefixes.
nomic-q / nomic-quantized nomic_v15_quantized() NomicEmbedTextV15Q 768 Quantized — faster, lower memory.
minilm minilm_l6_v2() AllMiniLML6V2 384 Legacy default for existing databases; no task prefixes.
embeddinggemma / gemma embedding_gemma_300m() onnx-community/embeddinggemma-300m-ONNX 768 300M-param Gemma-3 retrieval embedder via the local/onnx HF-Hub fallback.
embeddinggemma-mistralrs / gemma-mistralrs embedding_gemma_300m_mistralrs() google/embeddinggemma-300m 768 Same weights via local/mistralrs (candle) on GPU.
bge-m3 bge_m3() aapot/bge-m3-onnx 1024 Hybrid: also emits a 250,002-dim learned-sparse vector and a 1024-dim ColBERT multi-vector. See Sparse + ColBERT hybrid retrieval.

Warning

The embedding dimension is part of the on-disk vector index. The dimensions field must match the model's true output, and you cannot change the embedder under an existing database without re-indexing — the index was built at the original dimension. Pick the embedder up front. validate() rejects a zero dimensions or batch_size, but it cannot detect a model/dimension mismatch.

Each preset also sets the right task prefixes. Nomic uses document_prefix = "search_document: " and query_prefix = "search_query: "; BGE uses a query-only prefix ("Represent this sentence for searching relevant passages: "); MiniLM uses none. These are baked into the preset because the retriever was trained on those literal strings.

By default the embedder resolves through local/onnx. To route embeddings to an OpenAI-compatible endpoint instead, set the provider to "remote/openai" and pass provider_options (this is where a base_url lands):

let mut embed = EmbeddingConfig::bge_small_en_v15();
embed.provider = "remote/openai".into();
embed.provider_options = Some(serde_json::json!({
    "base_url": "http://litellm-cloud:4000/v1"
}));

The NLP cascade

NER and observation extraction run through a single ONNX cascade model, kniv-deberta. NlpConfig exposes the model id, the ONNX artifact to load, and a batch size:

use uniko_store::config::NlpConfig;

let nlp = NlpConfig::default();
// model_id:        "dragonscale-ai/kniv-deberta-nlp-base-en-xsmall"
// artifact:        "onnx/cascade-int8.onnx"
// max_batch_size:  16
Field Default Meaning
model_id dragonscale-ai/kniv-deberta-nlp-base-en-xsmall HuggingFace id of the cascade model.
artifact onnx/cascade-int8.onnx Which ONNX export under model_id/ to load (e.g. INT8 vs FP32).
max_batch_size 16 Maximum batch size for the ONNX session.
execution_providers None EP override; None picks a feature-aware default (CUDA+CPU on gpu-cuda, CoreML+CPU on gpu-metal, CPU otherwise).

Swapping the cascade variant (xsmall ↔ small, INT8 ↔ FP32) is a config edit — change model_id or artifact and the loader picks it up.

The cascade's SRL (semantic-role-labelling) head is gated separately on the main config:

config.nlp_srl_enabled = true; // default: true

When true, the pipeline computes SRL frames (one extra ONNX forward per VERB per sentence) and downstream extraction uses them. Set it to false if profiling shows the per-verb re-forward cost is unacceptable for a workload; NlpResult.srl_frames then stays empty and extraction behaves as before.

The reranker

After hybrid recall fuses candidates with reciprocal rank fusion, an optional cross-encoder re-scores the top top_n items. The reranker is enabled by default with cross-encoder/ms-marco-MiniLM-L-6-v2:

use uniko_store::config::RerankerConfig;

let rr = RerankerConfig::default();
// enabled:       true
// model_id:      "cross-encoder/ms-marco-MiniLM-L-6-v2"
// top_n:         50
// apply_sigmoid: true
// style:         "cross-encoder"
Field Default Meaning
enabled true Register and invoke the reranker.
model_id cross-encoder/ms-marco-MiniLM-L-6-v2 Cross-encoder ONNX export.
top_n 50 Number of top RRF candidates re-scored. Must be >= recall_limit when enabled.
apply_sigmoid true Map raw logits to [0, 1] via sigmoid.
style cross-encoder "cross-encoder" for BERT-family encoders; "generative" for decoder-LM rerankers (e.g. Qwen3-Reranker); "colbert" for in-process ColBERT MaxSim (no reranker model — requires multivector_dimensions).
execution_providers None EP override, same semantics as the embedder.

RerankerConfig ships two presets: RerankerConfig::minilm_l6() (22M params, the default model) and RerankerConfig::bge_base() (BAAI/bge-reranker-base, 278M params, more accurate, CPU-feasible). Both construct with enabled: false; the Default impl flips MiniLM on.

Tip

MiniLM is 12× faster than BGE-reranker-base, so default-on rerank is cheap relative to the rest of recall. To turn it off entirely:

config.reranker = RerankerConfig { enabled: false, ..Default::default() };

Warning

When the reranker is enabled, validate() requires reranker.top_n >= recall_limit. If you raise recall_limit above top_n (default 50), bump top_n to match or validation fails — otherwise truncation would discard items the reranker never saw.


Tunable thresholds

UnikoConfig exposes the numeric knobs that govern the recall cascade, hybrid fusion, consolidation, and memory decay. All defaults below are from UnikoConfig::default().

Recall and fusion

Field Default Meaning
recall_limit 15 Maximum items returned from recall.
recall_token_budget 8192 Maximum total tokens in the context bundle.
recall_min_score 0.001 Minimum fused score for inclusion.
recall_vector_weight 0.5 Vector-similarity weight in hybrid fusion [0.0–1.0].
recall_bm25_weight 0.5 BM25 fulltext weight in hybrid fusion [0.0–1.0].
rrf_k 60.0 k constant for reciprocal rank fusion across query variants; higher flattens top-rank weighting.
recall_per_variant_limit None LIMIT applied to each per-variant Cypher query; None falls back to recall_limit.
query_variants [] Multi-query reformulation labels. Empty (the default) selects the single keywords variant. Pass all four — keywords, original, declarative, type_anchored — to opt into multi-query.

Cascade phase gates

The recall cascade runs in phases and exits early once a phase covers the query. Coverage gates and the Phase 1 strategy live here:

Field Default Meaning
phase2_coverage_threshold 0.65 Coverage threshold for Phase 2 (Expand) early exit.
phase1_strategy "boost" Phase 1 contribution strategy: merge, boost, or off.
phase1_boost_alpha 0.6 Multiplicative weight for Fact scores in the session-chunk boost under phase1_strategy = "boost".

boost needs finalized sessions

The boost strategy scores session-level chunks, which are built by Session::finalize() (and, best-effort, by Session::summarize()). A session that is never finalized has no such chunks, so Phase 1 contributes nothing under the default strategy. Call finalize() at the end of a conversation, or use Agent::finalize_session / unfinalized_session_ids to backfill an existing knowledge base.

| phase2_mmr_lambda | 0.7 | MMR relevance-vs-diversity for Phase 2 dedup (1.0 = pure relevance, 0.0 = pure diversity). | | phase2_mmr_duplicate_threshold | 0.85 | Token-overlap threshold above which a Phase 2 candidate is dropped as a hard duplicate. | | phase2_temporal_enabled | true | Temporal-interval channel; fires only when the query has a parsed temporal phrase. | | phase2_graph_enabled | true | Graph spreading-activation channel; fires only when the query has a resolvable entity seed. | | phase2_graph_damping | 0.85 | PPR damping factor for the graph channel. | | phase2_graph_max_iter | 30 | PPR power-iteration cap for the graph channel. | | phase2_graph_edge_weights | {} | Per-edge-type weight multipliers for graph propagation; empty map uses the built-in default_phase2_graph_edge_weights. |

Note

phase2_coverage_threshold must be in (0.0, 1.0]; validate() rejects values outside that range. The phase1_strategy is stored as a string (not an enum) so configs deserialised across feature flags stay compatible.

Entity admission

Strict admission keeps NER noise out of the :Entity graph at ingest time — temporal, measurement, and quoted-string spans (already captured as observations) and greeting/discourse fragments mis-tagged as people are dropped, and the low-confidence catch-all is gated:

Field Default Meaning
entity_strict_admission true Drop NER noise and greeting fragments. Set false to restore legacy admit-everything behavior (for A/B comparison).
entity_other_min_confidence 0.9 Minimum confidence for an Other catch-all span (Event/Product/WorkOfArt/Group/Misc) to be admitted when strict admission is on.

Consolidation

Consolidation (P4) collapses near-duplicate Facts and refreshes their embeddings:

Field Default Meaning
consolidation_threshold 20 Number of observations that trigger consolidation.
consolidation_interval_secs 900 Seconds between periodic consolidation runs.
consolidation_cluster_objects true Cosine-cluster object surface forms before mode-voting picks the canonical (vs legacy exact-string buckets).
consolidation_date_augment_embedding true Prepend a month-year prefix (from the Fact's first_observed) to the embedded Fact text so temporally-near Facts co-locate.

Memory decay

Importance decays exponentially and low-importance nodes are pruned:

config.half_life_days = 30.0; // importance * exp(-ln(2)/half_life * age_days)
config.prune_below   = 0.05;  // prune nodes below this importance
Field Default Meaning
half_life_days 30.0 Half-life (days) for importance decay. Must be positive.
prune_below 0.05 Importance threshold below which nodes are pruned. Must be in [0.0, 1.0).

Chunking and sessions

The chunking thresholds match the ingest pipeline's content router:

Field Default Meaning
message_chunk_threshold 1024 Token count above which a Message is chunked (below this it embeds directly).
action_output_artifact_threshold 256 Token count above which an Action output overflows to an Artifact.
max_chunk_tokens 256 Maximum tokens per chunk.
min_chunk_tokens 32 Minimum tokens per chunk (fragments below this merge).
chunk_overlap_tokens 0 Overlap tokens between adjacent chunks; 0 = auto (10% of max, capped at 50).
session_inactivity_secs 3600 Inactivity window after which an open Session auto-closes and is summarised; 0 disables.

Note

validate() requires min_chunk_tokens < max_chunk_tokens. The design rationale and per-content-type chunking strategies (recursive splitting, tree-sitter AST, DOM sections, etc.) are described in the ingest pipeline; these fields are the runtime levers over that machinery.

Vector index

The index algorithm and distance metric are set once at database creation:

use uniko_store::config::{VectorAlgorithm, VectorMetricChoice};

config.vector_algorithm = VectorAlgorithm::HnswSq { m: 16, ef_construction: 100 };
config.vector_metric    = VectorMetricChoice::Cosine;

The default is HnswSq { m: 16, ef_construction: 100 } with Cosine distance. Other variants cover HnswFlat, HnswPq, IvfSq, IvfPq, and IvfRq for different scale / recall / memory trade-offs.

Pipelines, retries, and circuit breaker

Field Default Meaning
ingest_queue_capacity 200 Bounded channel capacity for the ingest worker.
consolidation_queue_capacity 32 Bounded channel capacity for the consolidation worker.
retry_max_attempts 3 Maximum retry attempts for retryable operations.
retry_initial_delay_ms 500 Initial backoff delay (exponential base).
retry_max_delay_ms 30000 Backoff cap.
circuit_failure_threshold 5 Consecutive failures before the circuit breaker opens.
circuit_recovery_ms 60000 Milliseconds the breaker stays open before probing.

validate() requires retry_initial_delay_ms <= retry_max_delay_ms.

Blob storage and external files

Field Default Meaning
blob_storage BlobStorage::Lance Backend for :ArtifactContent bytes; persisted on first open. Reopening with a different variant is a hard error (no implicit migration).
catalog_path None Override the built-in xervo model catalog with a JSON file.
schema_path None Load the schema from a JSON file instead of registering it programmatically.
observation_rules_path None Load observation-extraction patterns from a YAML file instead of the bundled english.yml.

schema_path and the tracked snapshot

config/schema.json in the repo is a snapshot produced by cargo run --bin export-schema. Pointing schema_path at a snapshot replaces the programmatic schema entirely, so a stale file silently yields a graph missing whatever labels and edges were added since it was generated. Regenerate it after any schema change, or leave schema_path as None and let register_schema run.


Sparse + ColBERT hybrid retrieval

By default recall fuses dense vector search with BM25. A hybrid embedder adds two more channels: a learned-sparse vector (a term-weight distribution over the model vocabulary, better than BM25 at exact-term matching) and a ColBERT multi-vector (one vector per token, scored by late-interaction MaxSim). Both are off unless you choose a hybrid embedder.

The bge-m3 preset is the supported one:

use uniko_store::config::{EmbeddingConfig, RerankerConfig};

let mut config = UnikoConfig::default();
config.embedding = EmbeddingConfig::bge_m3();  // 1024-dim dense
                                               // + 250,002-dim sparse
                                               // + 1024-dim ColBERT
config.recall_sparse_enabled = true;           // turn on the sparse channel
config.reranker = RerankerConfig {
    enabled: true,
    style: "colbert".to_string(),              // MaxSim instead of a cross-encoder
    ..Default::default()
};
Field Default Meaning
embedding.sparse_dimensions None Some(n) adds a SPARSE_VECTOR(n) column on :Chunk and :Observation.
embedding.multivector_dimensions None Some(d) adds a List(Vector(d)) ColBERT column on the same labels.
recall_sparse_enabled false Fan out an extra uni.sparse.query channel per variant, joining the existing RRF fusion.
reranker.style cross-encoder Set to "colbert" to re-score the top top_n candidates by MaxSim instead of loading a reranker model.

Three validate() gates catch the invalid combinations at open time:

  • recall_sparse_enabled without sparse_dimensions is an error.
  • reranker.style = "colbert" without multivector_dimensions is an error.
  • reranker.top_n must be >= recall_limit whenever the reranker is enabled.

Aliases each channel uses

The two channels reach the model through different xervo aliases, because the write and query sides need different capabilities:

Alias Task Registered when Serves
embed/default Embed always dense columns on lone-dense labels (Message, Summary, …)
embed/hybrid EmbedHybrid sparse_dimensions or multivector_dimensions set the dense + ColBERT columns on :Chunk / :Observation, written in one forward pass
embed/hybrid serves both sides: the document pass that writes the columns,
and the query-side vector for uni.sparse.query and ColBERT MaxSim.

That is not free historically. A sparse query asks the runtime for a SparseEmbeddingModel and a ColBERT query for a MultiVectorEmbeddingModel, while an EmbedHybrid alias loads a HybridEmbeddingModel — a different trait object. Before uni-xervo 0.18.1 those lookups failed and both channels returned nothing on every query while ingest kept populating the columns normally, so the failure was query-only and silent (rustic-ai/uni-xervo#49). 0.18.1 adapts the hybrid handle to each narrow facade (hybrid_adapter::HybridAsSparse / HybridAsMultiVector), so one alias now covers everything.

If you are pinned below 0.18.1, register separate EmbedSparse / EmbedMultiVector aliases against the same model_id — the per-task preset tables resolve the bare repo id for each head.

Cost

The runtime caches loaded models by task, so embed/hybrid is a second load of the same weights alongside embed/default — budget roughly 2× embedder memory before enabling these channels on a constrained host.

The ColBERT index uses an exact (flat) vector index because it only ever re-scores a candidate window, never scans.

Declaring multivector_dimensions is not free even when ColBERT is off. Every :Chunk and :Observation then carries a List(Vector(d)) column, which widens the table the dense and sparse scans read. On a LoCoMo conversation (bge-m3, CPU) mean recall latency went from ~10 s/question without the column to ~46 s/question with it present but unused. Declare it only when you intend to use ColBERT.

Neither channel is reachable from the Python builder today; configure them from Rust.

What they measure on LoCoMo

Both channels were returning nothing until their query paths were fixed (2026-09-19), so no earlier number from them measured anything. The first real measurement, on LoCoMo conv-30 (105 questions, bge-m3, retrieval-only, CPU, all four arms over one shared knowledge base so only the retrieval channel varies):

arm evidence hit Δ recall latency Δ questions changed (better / worse)
dense only 66.41% — 46.9 s — —
+ sparse 61.83% −4.58 pp 69.8 s +49% 10 (2 / 8)
+ ColBERT 67.18% +0.76 pp 59.6 s +27% 1 (1 / 0)
+ sparse + ColBERT 60.31% −6.11 pp 84.3 s +80% 8 (0 / 8)

Read with care:

  • Sparse is a regression on this workload. −4.58 pp is well outside the ~0.76 pp run-to-run ingest variance measured on the same conversation, the loss is uniform across Single-hop / Multi-hop / Temporal, and enabling both channels lands below sparse alone. Adversarial questions are the one category that gains (+4.0 pp), which fits a lexical channel being better at "this term never appears" judgements.
  • ColBERT is a null result, not a win. +0.76 pp sits exactly on the noise floor and comes from a single question flipping; every category except Single-hop is identical to dense-only. The defensible claim is that its quality effect here is indistinguishable from zero at +27% latency.
  • One conversation, no LLM judge, CPU. Enough to say neither channel is free; not enough to generalise across the corpus. Re-measure on your own workload before enabling either.

Verifying a channel is actually live

Both channels fail silently if misconfigured. run_sparse_source logs a failed uni.sparse.query at debug and returns an empty hit list, and colbert_rerank logs at warn and keeps the RRF order. In both cases recall still returns results, so an enabled-but-broken channel looks exactly like one that simply did not change the ranking — while still costing a query per variant.

ColBERT fails the same way and is even easier to miss: colbert_rerank warns once per query and keeps the RRF order, so the bundle is still returned, merely un-reranked.

Check, rather than assume:

# The channel prints one line per query on the success path only.
RUST_LOG=uniko_memory=debug <your binary> 2>&1 | grep -E "sparse_source|sparse query failed"
  • RECALL_PROF sparse_source label=Chunk rows=N — the channel ran and returned N hits.
  • sparse query failed … — it errored; the channel is contributing nothing.
  • Neither line — recall_sparse_enabled never took effect.

For ColBERT, grep at warn level instead — one line per query means the reranker is inert:

<your binary> 2>&1 | grep -E "colbert query embed failed|colbert maxsim query failed"

The decisive check for either channel is a differential run: same KB, the channel on and off, comparing per-question results. If the two arms agree on every question, the channel changed nothing — which is what a silent failure looks like.

A quick end-to-end check of the storage layer, independent of recall, lives in crates/uniko-store/tests/sparse_query_alias_repro.rs. It is #[ignore]d because it loads bge-m3 (~2 GB):

cargo nextest run -p uniko-store --test sparse_query_alias_repro \
  --run-ignored all --no-capture

It asserts the columns are populated, the raw uni.sparse.query returns ranked rows, and both recall_sparse_search and recall_colbert_maxsim project them without error.

Benchmark profiles

crates/uniko-bench/bench-configs/ ships arms that enable these channels (locomo-bgem3-norerank-retrieval.json for sparse, locomo-bgem3-colbert-retrieval.json for ColBERT). Any measurement taken with them before these channels were verified live should be treated as unmeasured, not as a null result: a channel that errors on every query is indistinguishable in the summary from one that did not help.


OCR for scanned PDFs

UnikoConfig::ocr configures the OCR model behind tiered PDF extraction — the path that produces :Page / :Block document structure rather than a flat text dump.

Field Default Meaning
enabled false Register the ocr/default alias and take the tiered PDF path.
model_id monkt/paddleocr-onnx HF repo holding the detection + recognition ONNX exports (English PP-OCRv5, Apache-2.0).
rec_artifact languages/english/rec.onnx CRNN/CTC recognizer path within the repo.
char_dict_path languages/english/dict.txt Character dictionary, one token per line.
det_artifact detection/v5/det.onnx DBNet detector; enables detect → crop → recognize with real bounding boxes.
image_height 48 Recognizer input height, pixels.
image_width 320 Recognizer input width, pixels.
normalization siglip "imagenet" or "siglip"; the PP-OCRv5 export expects siglip.
execution_providers None ONNX execution providers; None is the feature-aware default.

Not reachable through the facade today

Tiered PDF extraction needs all three of: the pdf-ocr Cargo feature, ocr.enabled = true, and a live model runtime. pdf-ocr is declared only on uniko-extract and is not forwarded by uniko-memory, uniko-api, or the Python wheels — so setting ocr.enabled alone changes nothing unless you depend on uniko-extract directly and enable the feature there. Without the tiered path, PDFs still ingest via the pure-Rust, text-only extractor.


Cargo feature flags

A few capabilities are gated behind Cargo features so a CPU-only or offline build stays lean. Enable them per crate.

Crate Feature Default Effect
uniko-store gpu-cuda off Enable NVIDIA CUDA acceleration in uni-db (requires the CUDA toolkit at build time).
uniko-store gpu-metal off Enable Apple Metal/CoreML acceleration in uni-db (macOS only).
uniko-extract code-parse on tree-sitter AST chunking for Python, Rust, JavaScript, TypeScript.
uniko-extract onnx off ONNX runtime (ort + tokenizers + ndarray) for the embedder / NLP cascade adapter.
uniko-memory onnx off Pass-through that enables uniko-extract/onnx.
uniko-memory llm off Adds an LLM-rewritten abstractive path for F59 Summaries. By default, summaries are deterministic, extractive, and fully offline.
uniko-cortex llm off Backs uniko-memory/llm.
uniko-extract pdf-ocr off Tiered PDF/OCR extraction via uni-xervo-pdf. Not forwarded by any higher crate — see OCR.
uniko-store mistralrs off uni-db's mistral.rs provider, for running a local LLM in-process.
uniko-store candle off uni-db's candle provider, the other local-LLM backend.
uniko-store batch-record off Diagnostic only: captures bulk-write batches for benchmark replay. Never enable in production.

uniko-api forwards exactly five features — onnx, gpu-cuda, gpu-metal, mistralrs, candle. It does not forward llm or pdf-ocr; enable those on uniko-memory / uniko-extract directly.

Tip

The execution_providers field on EmbeddingConfig, NlpConfig, and RerankerConfig is feature-aware: with None, a build compiled with gpu-cuda defaults to CUDA→CPU, gpu-metal to CoreML→CPU, and a plain build to CPU. You only set execution_providers explicitly to override that default.


See also

  • Architecture — how the layers and the KnowledgeBase fit together.
  • Recall — what the cascade phases and fusion weights actually do.