Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content
Build with MuBit

SDK methods

Three integration layers — from zero-config closed-loop to full low-level control.

MuBit SDKs expose three layers:

LayerWhat it doesWhen to use
Learn (mubit.learn / learn)Auto-ingest all LLM interactions + auto-inject lessons + auto-reflectZero-config closed-loop — agents learn with one line of setup
Helpers on Client17 explicit methods for memory, context, reflection, multi-agentFine-grained control over what gets remembered and when
Raw domains (auth, control, core)1:1 endpoint mappingsWire debugging, async job polling, advanced routes

Start with learn or helpers. Drop to raw domains only when you need exact payload control.

Learn module (closed-loop)

Before each LLM call, the learn module retrieves relevant lessons from MuBit and injects them into the system message. After the call, the interaction is ingested automatically. On run end, reflection extracts new lessons.

import mubit.learn
 
mubit.learn.init(api_key="mbt_...", agent_id="my-agent")
 
# All OpenAI/Anthropic/LiteLLM calls now auto-inject lessons + auto-ingest.
 
@mubit.learn.run(agent_id="planner", auto_reflect=True)
def plan_task(task):
    return openai.OpenAI().chat.completions.create(
        model="gpt-4o", messages=[{"role": "user", "content": task}],
    ).choices[0].message.content

Helper method bundles

Use caseMethodsWhat they do
Basic memoryremember, recallIngest content with intent classification; semantic query with evidence scoring
Prompt contextgetContext / get_contextToken-budgeted context block for LLM injection (rules → lessons → facts)
Exact artifactsarchive, archiveBlock / archive_block, dereferenceBit-exact storage with stable reference IDs; retrieval without semantic search
Run lifecyclecheckpoint, reflect, recordOutcome / record_outcome, client.control.recordStepOutcome / record_step_outcomeDurable state; LLM lesson extraction; reinforcement feedback; per-step process rewards
Ingest lifecycleclient.control.getIngestJob / get_ingest_job, client.control.getRunIngestStats / get_run_ingest_statsPoll async ingest jobs (details); per-run counters. remember() and ingest() return a job_id — call get_ingest_job(job_id) until done=True before reading the same run.
Multi-agentregisterAgent / register_agent, listAgents / list_agents, handoff, feedbackScoped access per agent; task transfer
DiagnosticsmemoryHealth / memory_health, diagnose, surfaceStrategies / surface_strategies, forgetStaleness metrics; error debugging; lesson clustering; deletion

The learning loop

1. remember()            → ingest facts, traces, lessons
2. record_step_outcome() → per-step process reward signals (optional, for dense RL)
3. reflect()             → LLM extracts lessons from evidence
                            (recurring lessons promote: run → session → global)
4. get_context()         → retrieve relevant lessons for the next call
5. record_outcome()      → reinforce what worked at the run level

With learn, steps 1–3 happen automatically. With helpers, you orchestrate them yourself.

ℹ️Note

Reflected lessons may start as pending candidates and only become active once enough evidence accrues — a lesson's score must cross the accept threshold (default 0.6) before it is surfaced in retrieval. Low-scoring candidates (≤0.25) are marked rejected and de-prioritized as stale. The control stream emits context.lesson_validation_passed / context.lesson_validation_failed alongside context.lesson_promoted. This validation gate is a server-side default; set MUBIT_CONTROL_LESSON_VALIDATION_ENABLED=false for the legacy "store immediately as active" behavior.

Current helper catalog

  • remember
  • recall
  • archive
  • archive_block
  • dereference
  • get_context
  • memory_health
  • diagnose
  • reflect
  • forget
  • checkpoint
  • register_agent
  • list_agents
  • record_outcome
  • record_step_outcome
  • surface_strategies
  • handoff
  • feedback
  • get_ingest_job, get_run_ingest_stats — poll async ingest and inspect per-run counters
  • mubit.auto for automatic trace capture (ingestion only)
  • mubit.learn for closed-loop auto-inject + auto-ingest + auto-reflect (supports auto_extract=True for heuristic extraction)

Step-level outcomes

Record per-step process rewards for dense RL signal within a run. Use after each agentic step, then reflect with the step outcomes folded in.

client.record_step_outcome(
    step_id="tool_call_1",
    step_name="search_api",
    outcome="success",
    signal=0.8,
    rationale="Found the correct document on first try",
    directive_hint="Keep using search before browsing",
)

The Python reflect() helper only accepts session_id, include_linked_runs, user_id, and last_n_items — there is no include_step_outcomes kwarg. To fold step outcomes into reflection, set the include_step_outcomes field on the wire Reflect request (gRPC/HTTP), or call it from JS:

// Later: reflect with step outcomes included
await client.control.reflect({ run_id: "my-run", include_step_outcomes: true });

Lane-scoped memory

Lanes partition memory within a shared run so each agent sees only relevant entries.

# Ingest into a specific lane
client.remember(content="Planning output: task A depends on B", intent="fact", lane="planning")
 
# Query only the planning lane
result = client.recall(query="task dependencies", lane="planning")
 
# Register an agent with lane participation
client.register_agent(agent_id="planner", role="planner", shared_memory_lanes=["planning", "shared"])
ℹ️Note

lane (MAS memory isolation) is distinct from direct_lane (core data-plane retrieval routing). They serve different purposes and do not interact.

Step-wise reflection

Scope reflection to recent evidence for incremental lesson extraction.

# Reflect over only the 5 most recent items
client.reflect(last_n_items=5)

To reflect with step outcomes included, use the wire include_step_outcomes field (gRPC/HTTP) or the JS control helper:

await client.control.reflect({ run_id: "my-run", include_step_outcomes: true });

Auto-extraction in learn

Enable heuristic extraction of rules, lessons, and facts from LLM responses without an extra LLM call.

mubit.learn.init(
    api_key="mbt_...",
    agent_id="my-agent",
    auto_extract=True,          # extract structured items from LLM responses
    extraction_mode="heuristic", # no LLM call needed
)

When to use what

ScenarioUse
Agents should learn with zero codemubit.learn.init() (Python), learn.init() (JS), LearnSession::new() (Rust)
Passive trace capture onlymubit.auto.instrument() (Python only)
Control exactly what gets rememberedclient.remember() + client.recall()
Token-budgeted context for promptsclient.get_context() / client.getContext()
Multiple agents with scoped accessclient.register_agent() + client.handoff()
Bit-exact artifact storageclient.archive() + client.dereference()
Wire-level debuggingclient.* / client.core.*

When to use raw control methods directly

Use client.* when you need one of these explicitly:

  • control.ingest plus get_ingest_job job polling
  • control.batch_insert
  • exact raw request/response debugging against HTTP or gRPC
  • advanced or compatibility state-management routes
  • control.get_ingest_job, control.get_run_ingest_stats for job polling
  • control.list_run_history, control.link_run, control.unlink_run, control.delete_run for run management
  • control.context_snapshot / control.contextSnapshot for full context snapshots

Ingest job tracking

remember() and the raw ingest() are asynchronous — they return a job_id and return immediately. If the next step in your code reads the same run (for example, recall() or get_context()), poll get_ingest_job(job_id) until done=True to guarantee the new item is visible. Framework adapters that advertise "synchronous put" semantics (e.g. MubitStore in mubit-langgraph) do this polling for you; the raw SDK does not.

Retrieve per-run statistics with get_run_ingest_stats() when you want aggregate counts without scanning activity.

# Submit + poll until the job is durable
job_resp = client.ingest({"run_id": "my-run", "items": [{"text": "…", "intent": "fact"}]})
while True:
    status = client.get_ingest_job(job_id=job_resp["job_id"])
    if status.get("done"):
        break
 
# Per-run aggregate stats
stats = client.get_run_ingest_stats(run_id="my-run")
ℹ️Note

For bulk writes, prefer one ingest({ items: [...] }) call with a list of items over many remember() calls — it is one job instead of N. Each ingest call accepts at most 1000 items; chunk larger workloads into multiple ingest calls (requests over the cap return 400 / InvalidArgument).

Run management

List run history, link, unlink, and delete runs. The server clamps the limit on run-history listing to a max of 1000 (default 100).

# List recent run history (advanced domain)
runs = client.advanced.list_run_history(limit=100)
 
# Link a child run to a parent
client.link_run(run_id="parent-run", linked_run_id="child-run")
 
# Unlink
client.unlink_run(run_id="parent-run", linked_run_id="child-run")
 
# Delete a run and its data
client.delete_run(run_id="old-run")

Context snapshot

Retrieve a full context snapshot for a run, including working memory, attention state, and active goals.

snapshot = client.advanced.context_snapshot(run_id="my-run")

Temporal and quality features

Occurrence time

MuBit tracks two time dimensions for every memory entry: ingestion time (when the system learned it) and occurrence time (when the event actually happened). Set occurrence_time to record when an event happened, separate from when it was ingested.

The Python remember() helper has a fixed signature with no occurrence_time kwarg — route it through the raw ingest passthrough, which forwards arbitrary item fields:

import time
 
# Event happened 3 days ago, ingested now
client.ingest({
    "run_id": "my-run",
    "items": [{
        "text": "New CI/CD pipeline reduced deployment time by 60%.",
        "intent": "fact",
        "occurrence_time": int(time.time()) - 86400 * 3,
    }],
})
 
# Historical event from January 2025
client.ingest({
    "run_id": "my-run",
    "items": [{
        "text": "Server migration to AWS completed with zero downtime.",
        "intent": "fact",
        "occurrence_time": 1736899200,  # Jan 15 2025 UTC
    }],
})

Temporal queries

Use min_timestamp and max_timestamp to filter evidence to a specific time window. The filter checks occurrence_time first, falling back to ingestion time.

The Python recall() helper has a fixed signature with no min_timestamp/max_timestamp kwargs — route temporal filters through the raw query passthrough, which forwards arbitrary body fields:

# "What happened in January 2025?"
results = client.query(
    run_id="my-run",
    query="What technical changes were made?",
    min_timestamp=1735689600,   # Jan 1 2025
    max_timestamp=1738367999,   # Jan 31 2025
)
 
for evidence in results["evidence"]:
    print(f"  {evidence['content'][:80]}")

Without temporal bounds, queries like "What happened last week?" use natural language temporal intent detection and prioritize entries by occurrence time in the recency ranking.

Search budget

The budget parameter controls the depth of retrieval. Use "low" for real-time agents and "high" for accuracy-critical offline analysis.

BudgetBehaviorTypical latency
"low"Fewer candidates, skip deep traversal< 500ms
"mid"Standard retrieval (default)500ms–2s
"high"More candidates, deeper graph traversal1–5s

The Python recall() helper has a fixed signature with no budget kwarg — route it through the raw query passthrough:

# Fast retrieval for a real-time chatbot
fast = client.query(run_id="my-run", query="user question", budget="low")
 
# Deep retrieval for a research report
deep = client.query(run_id="my-run", query="comprehensive analysis topic", budget="high")

Staleness detection

When a newer fact contradicts an older one, MuBit marks the older entry as stale and deprioritizes it in ranking. The staleness metadata is available in evidence responses.

results = client.recall(query="Where is the office?")
for evidence in results["evidence"]:
    stale = evidence.get("is_stale", False)
    status = " [STALE]" if stale else ""
    print(f"  {evidence['content'][:60]}{status}")
ℹ️Note

Stale entries are still returned for transparency. The ranking penalty ensures they appear below the current fact. Filter them out in your application if you only want current information.

Mental models

The mental_model entry type stores consolidated entity summaries that are prioritized over raw facts in context assembly. Use this for entities your agent tracks over time.

client.remember(
    content="Alice Chen is a senior engineer specializing in distributed systems. "
            "She prefers async communication and reviews PRs within 24 hours.",
    intent="mental_model",
    metadata={"entity": "alice chen", "consolidated": True},
)

Mental models are returned with higher priority than individual facts in recall() and get_context(). Update them periodically as your agent learns more about an entity.

Failure modes and troubleshooting

SymptomRoot causeFix
SDK usage becomes inconsistent across teamsRaw and helper paths mixed arbitrarilySet helpers as the default integration contract
Debugging a route contract is awkwardHelper layer hides wire detailsUse the raw client.* call for that investigation
Docs and examples drift from SDK realityHelpers undocumentedTreat the top-level helper surface as the public default

Next steps