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

Full Fledged Support Agent

Build an end-to-end support agent with helper-first memory, compaction-safe context, coordination, and reinforcement.

This page shows one complete support-agent shape per SDK using the current recommended MuBit flow:

  1. register the agents involved
  2. store facts, traces, and preferences
  3. assemble context before responding
  4. checkpoint before compaction or risky transitions
  5. coordinate specialist review with handoff and feedback
  6. 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

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)
# Use the lesson id returned inline on the reflect response — avoids racing
# with the vector-store index when listing immediately after a write.
lesson_id = next(
    (l.get("lesson_id") for l in (reflection.get("lessons") or []) if l.get("lesson_id")),
    None,
)
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", [])),
})
ℹ️Note

A freshly reflected lesson may enter long-term memory as a pending candidate (the validation gate is on by default, accept threshold 0.6) and is not immediately surfaced until enough evidence accrues. The outcomes you record against it are exactly how it accrues that evidence and gets promoted to active — the control stream emits context.lesson_validation_passed / context.lesson_validation_failed alongside context.lesson_promoted. Operators can disable the gate with MUBIT_CONTROL_LESSON_VALIDATION_ENABLED=0.

ℹ️Note

The answer was grounded on more than the lesson alone, so attribute the win to every recalled entry that informed it. record_outcome accepts entry_ids (a list of the recalled entry ids — e.g. the customer fact and the billing trace) so each one's reinforcement counters update, not just the lesson reference_id (which is never double-counted). entry_ids is available in the Python, Node, and Rust record_outcome helpers.

Operational notes

  • Start helper-first and drop to raw client.* only where helper coverage is intentionally lower, such as explicit lesson listing.
  • mubit.auto is the public zero-friction path if you want Python LLM traces to feed this same memory loop automatically.

Next steps