The learning loop
How traces become lessons, lessons earn trust through outcomes, and the runtime maintains memory quality underneath.
Mubit's learning loop is what makes an agent better on run 50 than on run 1 without retraining the model. Raw experience goes in as traces, outcomes attach reward signals to the memories that produced them, reflection distills reusable lessons, and those lessons are injected into future queries — with a promotion ladder deciding how widely each lesson applies. Read this page to understand what each stage does and which parts run automatically.
Mental model: the loop is experience → attribution → distillation → injection → promotion. You drive the first three with API calls; injection and promotion happen at query time and in the background.
The five stages
- Ingest experience
Your agent writes what happened —
remember()for facts and observations, raw ingest for traces. Ingestion is asynchronous: an ingestion agent classifies each item into a typed entry (fact, trace, observation, …). If you need a write to be immediately recallable, poll the ingest job untildone=true(see How Mubit works). - Record outcomes
When an attempt succeeds or fails,
record_outcome()attaches the result to the memories that produced it. This is the reward signal everything downstream feeds on. - Reflect
reflect()distills the run's evidence into lessons — each with alesson_type(success, failure, observation, rule, preference), ascope(run, session, global), animportance(low → critical), andconditionsdescribing when it applies. A validation gate scores each fresh lesson: it becomes active once its evidence score crosses the acceptance threshold (default 0.6), is rejected at or below 0.25, and stays pending in between. An auto-reflection cadence can be enabled per instance so reflection fires after every N ingests or on a negative-outcome streak, without an explicit call. - Lesson injection
On later queries, relevant lessons ride along as a
lesson_overlay— even across runs. The overlay is relevance-gated (embedding similarity between the query and stored lesson embeddings) and token-budgeted (default ~1,600 tokens; the first item is always admitted so one long critical lesson cannot starve the overlay). Rules always inject; lessons compete on relevance. - Promotion
Lessons that keep proving useful climb the scope ladder: run → session → global. Promotion to org-wide sharing is shadow-gated — a candidate lesson is trialled against a control group and only promoted when the outcome delta justifies it.
Outcomes close the loop
record_outcome() is the highest-leverage call in the loop — without it, reflection has only "what happened", never "what worked".
| Field | Meaning |
|---|---|
reference_id (required) | The lesson or entry ID this outcome is about |
outcome | "success", "failure", "partial", "neutral" |
signal | Numeric reward in [-1.0, 1.0] — positive reinforces, negative penalizes |
entry_ids | Additional entry IDs that contributed (multi-entry attribution). Each listed entry's reinforcement counters update; the primary reference_id is never double-counted |
verified_in_production | Marks the outcome as observed in live production, not a test — the strongest trust signal (see Trust, confidence, and staleness) |
rationale | Free-text why, kept for audit |
For dense per-step signals inside a single run, use record_step_outcome() — see Step-level outcomes.
The outcome request also accepts an idempotency_key so a retried call is applied at most once. From SDK v0.12.0 the typed record_outcome() helpers forward it directly in all three languages (record_outcome(..., idempotency_key="...")); on earlier versions it is a wire-level field — send it directly over the transport (POST /v2/control/outcome).
What the runtime maintains for you
Alongside the loop you drive, hosted instances run always-on maintenance (each is per-instance configurable):
| Feature | What it does |
|---|---|
| Write-time lesson reconciliation | A re-learned near-duplicate lesson becomes a recurrence bump on the canonical entry instead of a new copy, so outcome history never splits across duplicates. |
| Bi-temporal supersession | When a belief is superseded, the old entry is excluded from current-state answers but kept for history (flagged is_stale, with superseded_by pointing at its replacement). |
| Sleep-time consolidation | While a run is idle, near-duplicate guidance entries missed at write time are merged and clusters of entity facts are distilled into consolidated observations. |
| Workflow induction | Repeated successful traces of the same procedure are distilled into reusable workflow entries. |
| Guardrail distillation | Repeated failure traces are distilled into preventive lessons with trigger preconditions. |
| Utility-weighted forgetting (opt-in) | Aged episodic evidence that is never retrieved or reinforced is moved to an archive tier — reversible, and entries stay reachable by ID. |
Nimbus example: the bad regex becomes a guardrail
Nimbus's support agent Ari ships a regex fix that takes down the export pipeline. The incident run contains the failure traces; closing the loop turns it into prevention:
# The fix failed in production — attribute the failure to the memories used.
client.record_outcome(
session_id="support:incident-0142",
reference_id=lesson_id, # returned by an earlier reflect()
outcome="failure",
signal=-0.9,
rationale="Regex change broke multi-line CSV rows in the export pipeline",
verified_in_production=True,
entry_ids=recalled_entry_ids, # every entry that informed the fix
)
# Distill the incident into lessons.
lessons = client.reflect(session_id="support:incident-0142")Reflection produces a failure lesson ("test regex changes against multi-line CSV fixtures before deploying") with conditions attached. Because similar failure traces recur, guardrail distillation reinforces it into a preventive lesson. Weeks later, when Ari recalls context for another regex change, the guardrail arrives via lesson_overlay — and this time Rex, the reviewer agent, blocks the deploy until the fixture test passes. When it holds up, a record_outcome(..., outcome="success", verified_in_production=True) strengthens the lesson toward global scope.
Configuration. The maintenance features are instance configuration, controlled by MUBIT_CL_* environment variables on the instance (e.g. MUBIT_CL_BITEMPORAL=0 to disable supersession, MUBIT_CL_FORGET=1 to opt in to forgetting, MUBIT_CL_OVERLAY_TOKEN_BUDGET to resize the lesson overlay). The lesson validation gate is likewise instance-level (MUBIT_CONTROL_LESSON_VALIDATION_ENABLED). On hosted instances the defaults above are already in effect.
Related
- Retrieval semantics — how injected lessons and other lanes are fused at query time
- The memory model — the entry types the loop reads and writes
- Runs, sessions, and scopes — the scope ladder that promotion climbs
- Trust, confidence, and staleness — how reinforcement and verification show up as
knowledge_confidence - Step-level outcomes — process-reward signals within a run
- Support agent loop — the loop end-to-end in a working agent
- How Mubit works — the loop in the context of the whole system