Skip to content

Concepts

MicroResolve is a deterministic decision engine. It classifies natural language queries against a set of named intents you define, using a weighted lexical index that improves from corrections. This page explains the core concepts.

MicroResolve

The MicroResolve struct is the top-level object. One instance can hold many namespaces. It manages data persistence: when a data_dir is configured, namespaces are stored on disk and auto-loaded on startup.

use microresolve::{MicroResolve, MicroResolveConfig};
let engine = MicroResolve::new(MicroResolveConfig {
data_dir: Some("~/.local/share/microresolve".into()),
default_threshold: 0.3,
..Default::default()
})?;

Namespace

A namespace is an isolated classifier. It has its own set of intents, its own learned weights, and its own threshold configuration. Namespaces do not share vocabulary or signal — a word that means “cancel” in your support namespace has no effect on your security namespace.

Use namespaces to separate concerns:

NamespacePurpose
supportCustomer intent classification
securityPrompt injection / abuse detection
toolsMCP tool pre-selection
moodSentiment / tone filtering
let ns = engine.namespace("support");
// Or with a custom threshold
use microresolve::NamespaceConfig;
let ns = engine.namespace_with("security", NamespaceConfig {
default_threshold: Some(1.3),
..Default::default()
});

Intent

An intent is a named category you want to classify into. Each intent has:

  • ID — machine-readable name (cancel_order, jailbreak_attempt)
  • Seed phrases — representative natural language examples used to build the index
  • Learned weights — accumulated from corrections and phrase additions over time
use microresolve::IntentSeeds;
ns.add_intent("cancel_order", &IntentSeeds::Phrases(vec![
"cancel my order".into(),
"I want to cancel".into(),
"stop my order".into(),
]))?;

Classification

Classification (called resolve) takes a query string and returns a ranked list of matching intents with scores. A score above the threshold means the engine is confident enough to act.

let matches = ns.resolve("I need to cancel this");
// [Match { id: "cancel_order", score: 0.91 }]

The engine uses a weighted lexical index: tokens in the query are looked up in a word→intent weight table. Each token’s weight is multiplied by its IDF (inverse document frequency) — common words like “the” contribute little; distinctive words like “jailbreak” contribute a lot. Scores are summed across tokens.

Reflex layers

A query passes through three reflex layers before scoring. Each layer is per-namespace toggleable — defaults are on and work for the typical case; turn one off when its behavior is wrong for your content.

LayerPurposeToggle fieldDisable when
L0Typo correction (char n-gram Jaccard)l0_enabledMedical / legal / code namespaces — auto-correcting domain terms is dangerous (cahnge may be a real identifier, not a typo of change)
L1Vocabulary bridging — three edge kindsthree flags belowper kind, see below
L2Intent scoring (IDF-weighted)(always on)core scorer; not toggleable

L1 has three independently toggleable edge kinds:

  • l1_morphologycancelingcancel (inflectional variants).
  • l1_synonym — user-defined equivalences, OOV-gated (only fires for tokens not already in the namespace’s vocabulary).
  • l1_abbreviationprpull request. Disable for code search where short tokens carry literal meaning.

Toggles apply at both index time and resolve time, so trained vectors and runtime preprocessing stay in sync. Save with Resolver::update_namespace or via PATCH /api/namespaces. Studio surfaces the toggles on the Namespaces edit modal, the L0 page, and the L1 page; the sidebar shows an off or partial pill on the layer’s nav item when something is disabled for the active namespace.

use microresolve::NamespaceEdit;
ns.with_resolver_mut(|r| r.update_namespace(NamespaceEdit {
l0_enabled: Some(false), // medical namespace: don't auto-correct drug names
l1_abbreviation: Some(false), // code search: don't expand `pr`
..Default::default()
})).unwrap();

Threshold and gap

Two parameters control what gets returned:

  • Threshold — minimum score to include an intent. Default: 0.3. Raise it for noisy domains (safety filters) where shared vocabulary causes false positives.
  • Gap — score ratio between the top match and the runner-up before the top is accepted alone. Default: 1.5. Lower it to surface multi-intent queries.

See Threshold Tuning for detailed guidance.

use microresolve::ResolveOptions;
let matches = ns.resolve_with("cancel my order and get a refund", ResolveOptions {
threshold: 0.3,
gap: 1.5,
});

Corrections

When the engine classifies incorrectly, call correct(). This moves signal toward the right intent and suppresses the wrong one for similar queries. No batch retraining. The improvement takes effect immediately.

ns.correct("stop my shipment", Some("cancel_order"), "track_order")?;

Pass None as the wrong intent if the engine returned no matches (you are teaching it from a miss rather than a misfire).

Continuous learning

MicroResolve is designed to improve from live traffic, not just bootstrap data. Every correction, every added phrase, every auto-learn pass makes future classifications more accurate. See Threshold Tuning and the Server Studio for the full auto-learn pipeline.

Git data layer

When a data_dir is configured, every mutation (intent added, phrase added, correction applied) is auto-committed to a git repository inside that directory. This gives you:

  • Full audit trail of every change
  • Rollback to any historical state per namespace
  • Optional remote sync via git remote

See Git Data Layer for details.

Next