- register the agents involved
- store facts, traces, and preferences
- assemble context before responding
- checkpoint before compaction or risky transitions
- coordinate specialist review with handoff and feedback
- reflect and record outcomes so future cases improve
Environment
.env
MUBIT_API_KEY="mbt_<instance>_<key_id>_<secret>"
MUBIT_ENDPOINT="https://api.mubit.ai"
Full implementation
- Python
- Node / TS
- Rust
import os
from mubit import Client
RUN_ID = "support:acme:ticket-42"
client = Client(
endpoint=os.getenv("MUBIT_ENDPOINT", "https://api.mubit.ai"),
api_key=os.environ["MUBIT_API_KEY"],
run_id=RUN_ID,
transport="http",
)
client.register_agent(session_id=RUN_ID, agent_id="support", role="support")
client.register_agent(session_id=RUN_ID, agent_id="billing", role="billing")
client.remember(
session_id=RUN_ID,
agent_id="support",
content="Customer Taylor wants concise Friday updates and is asking about invoice INV-7781.",
intent="fact",
metadata={"customer": "taylor", "ticket": 42},
)
client.remember(
session_id=RUN_ID,
agent_id="billing",
content="Invoice INV-7781 has a stale tax table mismatch on line item 4.",
intent="trace",
metadata={"invoice": "INV-7781", "source": "billing-review"},
)
context = client.get_context(
session_id=RUN_ID,
query="Draft the next customer-safe update with the billing root cause and next step.",
mode="full",
max_token_budget=800,
)
checkpoint = client.checkpoint(
session_id=RUN_ID,
agent_id="support",
label="pre-response-compaction",
context_snapshot=context.get("context_block", ""),
)
handoff = client.handoff(
session_id=RUN_ID,
task_id="ticket-42-billing-review",
from_agent_id="support",
to_agent_id="billing",
content="Confirm the billing root cause for invoice INV-7781 before we reply.",
requested_action="review",
)
feedback = client.feedback(
session_id=RUN_ID,
handoff_id=handoff["handoff_id"],
from_agent_id="billing",
verdict="approve",
comments="Root cause confirmed: stale tax table mismatch on line item 4.",
)
reflection = client.reflect(session_id=RUN_ID)
lessons = client.control.lessons({"run_id": RUN_ID, "limit": 20})
lesson_id = (lessons.get("lessons") or [{}])[0].get("id")
if lesson_id:
client.record_outcome(
session_id=RUN_ID,
agent_id="support",
reference_id=lesson_id,
outcome="success",
signal=0.75,
rationale="The support response used the stored customer preference and the validated billing root cause.",
)
strategies = client.surface_strategies(
session_id=RUN_ID,
lesson_types=["success", "failure"],
max_strategies=5,
)
print({
"checkpoint_id": checkpoint.get("checkpoint_id"),
"handoff_id": handoff.get("handoff_id"),
"feedback_id": feedback.get("feedback_id"),
"lessons_stored": reflection.get("lessons_stored"),
"strategy_count": len(strategies.get("strategies", [])),
})
import { Client } from "@mubit-ai/sdk";
const RUN_ID = "support:acme:ticket-42";
const client = new Client({
endpoint: process.env.MUBIT_ENDPOINT ?? "https://api.mubit.ai",
api_key: process.env.MUBIT_API_KEY,
run_id: RUN_ID,
transport: "http",
});
await client.registerAgent({ session_id: RUN_ID, agent_id: "support", role: "support" });
await client.registerAgent({ session_id: RUN_ID, agent_id: "billing", role: "billing" });
await client.remember({
session_id: RUN_ID,
agent_id: "support",
content: "Customer Taylor wants concise Friday updates and is asking about invoice INV-7781.",
intent: "fact",
metadata: { customer: "taylor", ticket: 42 },
});
await client.remember({
session_id: RUN_ID,
agent_id: "billing",
content: "Invoice INV-7781 has a stale tax table mismatch on line item 4.",
intent: "trace",
metadata: { invoice: "INV-7781", source: "billing-review" },
});
const context = await client.getContext({
session_id: RUN_ID,
query: "Draft the next customer-safe update with the billing root cause and next step.",
mode: "full",
max_token_budget: 800,
});
const checkpoint = await client.checkpoint({
session_id: RUN_ID,
agent_id: "support",
label: "pre-response-compaction",
context_snapshot: context.context_block ?? "",
});
const handoff = await client.handoff({
session_id: RUN_ID,
task_id: "ticket-42-billing-review",
from_agent_id: "support",
to_agent_id: "billing",
content: "Confirm the billing root cause for invoice INV-7781 before we reply.",
requested_action: "review",
});
const feedback = await client.feedback({
session_id: RUN_ID,
handoff_id: handoff.handoff_id,
from_agent_id: "billing",
verdict: "approve",
comments: "Root cause confirmed: stale tax table mismatch on line item 4.",
});
const reflection = await client.reflect({ session_id: RUN_ID });
const lessons = await client.control.lessons({ run_id: RUN_ID, limit: 20 });
const lessonId = lessons.lessons?.[0]?.id;
if (lessonId) {
await client.recordOutcome({
session_id: RUN_ID,
agent_id: "support",
reference_id: lessonId,
outcome: "success",
signal: 0.75,
rationale: "The support response used the stored customer preference and the validated billing root cause.",
});
}
const strategies = await client.surfaceStrategies({
session_id: RUN_ID,
lesson_types: ["success", "failure"],
max_strategies: 5,
});
console.log({
checkpoint_id: checkpoint.checkpoint_id,
handoff_id: handoff.handoff_id,
feedback_id: feedback.feedback_id,
lessons_stored: reflection.lessons_stored,
strategy_count: strategies.strategies?.length ?? 0,
});
use mubit_sdk::{
CheckpointOptions, Client, ClientConfig, FeedbackOptions, GetContextOptions, HandoffOptions,
RecordOutcomeOptions, ReflectOptions, RegisterAgentOptions, RememberOptions, SurfaceStrategiesOptions,
};
use serde_json::json;
use std::env;
let run_id = "support:acme:ticket-42".to_string();
let config = ClientConfig::new("https://api.mubit.ai")
.api_key(env::var("MUBIT_API_KEY")?)
.run_id(&run_id);
let client = Client::connect(config).await?;
let mut support = RegisterAgentOptions::new("support");
support.run_id = Some(run_id.clone());
support.role = "support".to_string();
client.register_agent(support).await?;
let mut billing = RegisterAgentOptions::new("billing");
billing.run_id = Some(run_id.clone());
billing.role = "billing".to_string();
client.register_agent(billing).await?;
let mut support_fact = RememberOptions::new(
"Customer Taylor wants concise Friday updates and is asking about invoice INV-7781.",
);
support_fact.run_id = Some(run_id.clone());
support_fact.agent_id = Some("support".to_string());
support_fact.intent = Some("fact".to_string());
support_fact.metadata = Some(json!({"customer":"taylor","ticket":42}));
client.remember(support_fact).await?;
let mut billing_trace = RememberOptions::new(
"Invoice INV-7781 has a stale tax table mismatch on line item 4.",
);
billing_trace.run_id = Some(run_id.clone());
billing_trace.agent_id = Some("billing".to_string());
billing_trace.intent = Some("trace".to_string());
billing_trace.metadata = Some(json!({"invoice":"INV-7781","source":"billing-review"}));
client.remember(billing_trace).await?;
let mut context = GetContextOptions::default();
context.run_id = Some(run_id.clone());
context.query = Some("Draft the next customer-safe update with the billing root cause and next step.".to_string());
context.mode = Some("full".to_string());
context.max_token_budget = Some(800);
let context = client.get_context(context).await?;
let mut checkpoint = CheckpointOptions::new(
"pre-response-compaction",
context.get("context_block").and_then(|v| v.as_str()).unwrap_or(""),
);
checkpoint.run_id = Some(run_id.clone());
checkpoint.agent_id = Some("support".to_string());
let checkpoint = client.checkpoint(checkpoint).await?;
let mut handoff = HandoffOptions::new(
"ticket-42-billing-review",
"support",
"billing",
"Confirm the billing root cause for invoice INV-7781 before we reply.",
);
handoff.run_id = Some(run_id.clone());
handoff.requested_action = "review".to_string();
let handoff = client.handoff(handoff).await?;
let handoff_id = handoff.get("handoff_id").and_then(|v| v.as_str()).unwrap().to_string();
let mut feedback = FeedbackOptions::new(handoff_id.clone(), "approve");
feedback.run_id = Some(run_id.clone());
feedback.from_agent_id = Some("billing".to_string());
feedback.comments = "Root cause confirmed: stale tax table mismatch on line item 4.".to_string();
let feedback = client.feedback(feedback).await?;
let mut reflect = ReflectOptions::default();
reflect.run_id = Some(run_id.clone());
let reflection = client.reflect(reflect).await?;
let lessons = client.control.list_lessons(json!({"run_id": run_id, "limit": 20})).await?;
if let Some(lesson_id) = lessons["lessons"][0]["id"].as_str() {
let mut outcome = RecordOutcomeOptions::new(lesson_id, "success");
outcome.run_id = Some(run_id.clone());
outcome.agent_id = Some("support".to_string());
outcome.signal = 0.75;
outcome.rationale = "The support response used the stored customer preference and the validated billing root cause.".to_string();
client.record_outcome(outcome).await?;
}
let mut strategies = SurfaceStrategiesOptions::default();
strategies.run_id = Some(run_id.clone());
strategies.lesson_types = vec!["success".to_string(), "failure".to_string()];
strategies.max_strategies = 5;
let strategies = client.surface_strategies(strategies).await?;
println!("{} {} {} {} {}",
checkpoint["checkpoint_id"],
handoff["handoff_id"],
feedback["feedback_id"],
reflection["lessons_stored"],
strategies["strategies"]
);
Operational notes
- Start helper-first and drop to raw
client.control.*only where helper coverage is intentionally lower, such as explicit lesson listing. mubit.autois the public zero-friction path if you want Python LLM traces to feed this same memory loop automatically.
Next steps
- Read the route contract at Control HTTP reference.
- Use the narrower examples at Support agent memory loop and Multi-agent shared state.