Support agents that remember the customer — and ask before they refund.
Customer support AI hits two walls: it forgets the account between sessions, and it takes irreversible actions (refunds, account changes) without a gate. ONTO's typed memory and Plan Mode fix both.
The pain.
The agent forgets the account.
Conversation buffer memory dies with the session. Vector memory retrieves "similar" but not "true now." You end up paying the LLM to re-learn the customer every conversation.
Refunds are irreversible.
A support agent should not autonomously issue a $5,000 refund. But a human shouldn't review every $5 refund either. The right answer is policy-driven gating — and that needs a Plan/Execute split.
Multi-tenant data leak risk.
In SaaS support, tenant A's agent must not surface tenant B's data. "Trust the prompt" is not a security model.
Cost attribution is opaque.
When the LLM bill arrives, you can't tell which customer or which tenant drove it. Hard to bill, hard to optimize.
The primitives that map to it.
Each piece of the pain above maps to a specific ONTO primitive — not a workaround layered on top.
Durable per-account memory
UPO assertions partitioned by tenant_id + user_id. The agent loads the account's typed facts (tier, signup year, escalation history) at the start of every conversation.
Plan Mode for refunds
High-value refunds halt in Plan Mode. Your business logic decides which tier auto-approves and which escalates — outside the LLM, in plain code.
Tenant-scoped consent
Every tool call carries tenant_id in its scope. Cross-tenant reads refuse with a structured error you can audit.
Per-tenant cost meter
onto.cost(tenant_id=...) returns the inference cost rolled up by tenant, separated foreground vs background. Bill the customer or burn down the bug.
A runnable example.
A support agent that loads account memory, proposes a refund plan, then auto-approves only if policy says so. Same code path for gold-tier auto and free-tier escalation.
import { Onto, Models, BuiltinTools } from "@onto/core";
const onto = new Onto({ model: Models.GPT_OSS_20B, builtinTools: BuiltinTools.STANDARD });
// 1. Load durable per-account memory (tier, signup year, history).
// Plus extract new typed facts from the incoming message.
await onto.run(incomingMessage, {
userId: "acct_42",
tenantId: "acme-corp",
consentScopes: ["memory:write", "support:read"],
extract: true,
});
// 2. Propose a plan: refund + apology + ops flag.
const plan = await onto.run("Plan response to this customer.", {
userId: "acct_42",
tenantId: "acme-corp",
mode: "plan_only",
consentScopes: ["tasks:write", "support:read"],
});
// 3. Auto-approve based on policy (gold tier → auto, new account → escalate).
const decision = await policy.decide(plan, account);
if (decision === "auto-approve") {
await onto.run("Execute the approved plan.", {
userId: "acct_42",
tenantId: "acme-corp",
consentScopes: ["support:refund", "support:notify"],
});
} else {
await escalateToHuman(plan, account);
} Considerations at scale.
CRM and billing integration
MCP wires Salesforce, Zendesk, Stripe, etc. Each MCP tool declares its consent scope. Tools refuse if the call doesn't carry the scope.
Conflict resolution
When the customer says one thing today and another tomorrow, ONTO's conflict detection surfaces it as a Plan Mode task so the agent or a human resolves it before the UPO updates.
Background memory worker
Conversation summarization and intent extraction run on a cheaper model in the background. Foreground latency stays low.
Observability
OpenTelemetry traces every gate decision and every tool call. Drop into Grafana, Honeycomb, or Datadog without adapter work.
Stop letting your agent re-learn the customer every conversation.
Read the full customer-support example or the supporting article on multi-tenant memory.