Vibe Engines
YouTube

Vibe Engines is free, interactive AI engineering & system design — runs in your browser. Pick a learning path

AI System Design

Design a Support Resolution Agent

Step 1 / 9

Learn AI system design by building an AI agent that triages, resolves, and escalates customer support tickets end-to-end.

The whole design, in writing

Every step of the build above, written out: the problem each piece solves, the option that was taken and the ones that were not, the numbers, and how it fails in production.

The big idea

Knowing when NOT to answer matters more than answering

A support agent that confidently answers every ticket, including ones where it genuinely doesn't have the right information, is worse than one that admits uncertainty and hands off — a wrong answer about someone's actual order or account erodes trust far more than a quick, honest escalation.

Customerhas an issue
New in this step: Customer.

We'll build around that principle: grounding in real account state, confidence-aware escalation, tiered resolution by risk, seamless handoff, and measuring success by whether the issue actually STAYED resolved.

What the new pieces do

Customerclient
Wants their issue actually resolved, not a confidently-worded non-answer — and never wants to repeat themselves after being escalated.

Step 1 · The baseline

Answer from general knowledge alone

Simplest version: the agent answers every ticket using general product knowledge — documentation, FAQs, common patterns. What breaks when the real answer depends on something specific to THIS customer?

Customerhas an issueSupport APIticket intake
New in this step: Support API.

The agent answers from general product knowledge alone. What breaks for account-specific questions?

  1. A large share of real support tickets ("where's my order," "why was I charged this amount," "what plan am I on") depend entirely on facts specific to THIS customer's account — general product documentation simply doesn't contain that information, correct or otherwise.

  2. General knowledge is fundamentally the wrong source for questions like "where is MY order" or "why was I charged this" — these require actual, current, customer-specific data, and answering from general knowledge alone means either a vague non-answer or a confident guess that could easily be factually wrong.

  3. Account-specific questions (order status, billing, plan details) are typically a LARGE share of real support volume, not a small edge case — this is a central problem to solve, not a minor one.

General product knowledge can't answer questions that depend on a specific customer's actual account or order state — attempting to anyway produces vague non-answers or confident guesses that may be factually wrong, which is worse than honestly not knowing.

Why this piece earns its place

The hard part of this baseline is not that general knowledge is thin — it is that the split between a general question and an account-specific one is itself a classification, and it fails quietly in one direction. Do you offer refunds is general. Do you offer refunds on the order I placed in March is not, and it arrives worded almost identically. An agent reading the surface form answers the general version of a specific question, and the customer gets a fluent paragraph that has nothing to do with their order. Worse, your deflection number rewards exactly that: the ticket closed, nobody escalated, the dashboard improved. Ship this baseline anyway, and run it for a couple of weeks before building anything else. It gives you the real distribution of what people actually ask, which is the only honest input into deciding which system to integrate against first — orders before billing, or the reverse, is a question your ticket log answers and your intuition does not. You would skip the baseline only for an internal tool where nothing is per-customer.

What the new pieces do

Support APIgateway
Accepts every incoming ticket and orchestrates triage, grounding, resolution, and escalation.

Step 2 · Ground in real account data

Retrieve the actual facts, not just documentation

Fix Step 1: connect the agent to the customer's real account and order state.

Support APIticket intakeAccount/Order Statethis customer, specifically
New in this step: Account/Order State.

The agent needs to answer account-specific questions. What should it retrieve to do so accurately?

  1. A general summary of COMMON issues still doesn't contain THIS customer's specific facts — it might improve the vagueness of a guess but doesn't solve the underlying problem of not having the real, current data.

  2. Retrieving the real, live account/order data means the agent's answer is actually correct for THIS customer, right now — not a plausible-sounding guess based on what's typical, but the genuine current state of their account.

  3. Account and order state changes constantly (new orders, plan changes, billing events) — a one-time cached snapshot from signup would be stale for almost every real support scenario.

Ground responses in the customer's actual, current Account/Order State, retrieved live from the real systems of record — so answers reflect genuine, verifiable facts about this specific customer, not general patterns or stale snapshots.

Why this piece earns its place

Grounding turns the agent into a client of billing, orders, shipping and identity all at once, and that fan-out is where the real work sits. Each of those systems has its own auth, its own rate limit and its own bad day, so the retrieval layer needs a per-system timeout and a partial-result contract — I have orders, I do not have billing, here is what I can safely say. The distinction that bites is between an empty result and a failed read. No rows matched, and the query never ran, arrive at the agent looking almost identical, and collapsing them produces the worst sentence in support: we have no record of that order, said to someone whose order exists. Absence and failure have to leave the retrieval layer as different things, and only one of them is ever allowed to reach a customer. Two more consequences. A read and the action taken on it are separate moments, so anything consequential re-reads at the point of execution rather than trusting the number the turn opened with. And whatever you retrieve now lives in your logs and in every request you send a model provider, so what gets redacted before it leaves is decided here, not after the first audit.

What the new pieces do

Account/Order Statedata
The actual, current facts about THIS customer's account and orders — what most real support answers genuinely depend on, not general product knowledge.

Step 3 · Recognize uncertainty

Confidence has to be a first-class signal

Even with real account data, some tickets are genuinely ambiguous, require judgment, or touch high-stakes situations (a billing dispute, an angry customer, a legal threat). Should the agent always attempt a full resolution?

Support APIticket intakeTriage + Confidencecategorize, assess certainty
New in this step: Triage + Confidence.

A ticket is ambiguous or high-stakes. Should the agent always attempt to fully resolve it itself?

  1. Attempting resolution on genuinely ambiguous or high-stakes tickets risks producing a wrong or inappropriate response BEFORE a human ever sees it — for the categories where confidence should be low, guessing first and correcting later is the wrong order of operations.

  2. Recognizing low confidence and escalating BEFORE attempting a risky guess avoids ever producing (and potentially acting on) a wrong response for exactly the tickets where being wrong matters most — confidence has to gate action, not just be assessed after the fact.

  3. Relying on customers to know to ask for escalation misses cases where the CUSTOMER doesn't realize their issue is actually ambiguous or high-stakes from the agent's perspective — the agent's own confidence assessment needs to drive this, not customer awareness.

Build Triage + Confidence assessment as a first-class step: the agent estimates how confident it genuinely is, and low-confidence, ambiguous, or high-stakes tickets escalate before any resolution attempt — rather than guessing first and hoping it's right.

Why this piece earns its place

Confidence is only useful if it is calibrated, and a model asked how sure it is will not give you that on its own — it gives you a fluent number that barely moves when it is wrong. In practice the signal is assembled: did the account lookup return one unambiguous match or three plausible ones, how often has this category actually stayed resolved before, and only then the model's own claim. Keep the threshold in configuration, per category, not in the prompt. You will move it more than once, and a prompt edit is a deploy while a config value is a decision the people reading the queue can make for themselves. The consequence nobody plans for is that this threshold is a staffing dial. Every notch you lower it sends more tickets to humans, so a model upgrade that shifts calibration arrives as a queue backing up on a Tuesday rather than as a quality alert — which is why a threshold change and a model change should never ship together. And log the score beside what eventually happened to the ticket, or you are setting the most consequential number in the system by feel.

What the new pieces do

Triage + Confidenceservice
Classifies the ticket and — critically — estimates how confident it should be, recognizing uncertainty rather than guessing on ambiguous or high-stakes issues.

Step 4 · Tiered resolution by category and risk

Not every ticket needs the same treatment

Some tickets are simple and low-risk (a password reset, an order-status lookup); others are complex or consequential (a refund dispute, an account closure). Should every ticket go through the same resolution path?

CustomerSupport APIAccount/Order StateTriage + ConfidenceAuto-Resolver
New in this step: Auto-Resolver. · swipe to pan the diagram

Ticket complexity and risk vary enormously. Should every ticket follow the same resolution path?

  1. Treating a simple password-reset request the same as a high-stakes billing dispute either over-automates the risky case or over-escalates the trivial one — neither serves customers or support efficiency well.

  2. Tiering matches the level of automation to the actual risk and complexity of the category — trivial, well-understood issues get fast full automation, moderately complex ones get an assisted draft a human reviews, and genuinely complex or high-stakes issues go straight to a human, unautomated.

  3. A blanket assisted-review tier for even the most trivial, well-understood cases (a password reset) adds unnecessary human review overhead for something that could be safely and fully automated — tiering by actual risk avoids this waste.

Tier resolution by category and risk: fully auto-resolve well-understood, low-risk tickets; partially automate (draft for human review) moderately complex ones; fully escalate genuinely ambiguous or high-stakes tickets — matching the level of automation to actual risk rather than one uniform process for everything.

Why this piece earns its place

The tier table is the part of this design that support owns, not engineering. It is a list of categories and what the system may do with each, it changes as the business changes, and if it exists only in code then every change is a ticket filed against a team that does not take the calls. Give it an audit trail too — who moved refunds to assisted, and when — because that is the first question asked after anything goes wrong. The discipline worth keeping is that nothing starts in the auto tier. A category enters as assisted and graduates only when its measured results earn it, which turns an unwinnable argument about what feels safe into a promotion you can point at. The demotion path matters just as much: a category whose results degrade has to be able to fall back by itself, or your only lever mid-incident is a deploy. And the assisted tier quietly pays for itself twice — every time a human edits a draft before sending it, the difference between what was written and what went out is the most direct signal you will ever get about where the agent is wrong. Capture that diff, not just the sent text.

What the new pieces do

Auto-Resolverservice
Fully resolves well-understood, low-risk categories — but never a consequential action without actual identity/authorization verification.

Step 5 · Seamless handoff

The customer should never have to repeat themselves

A ticket escalates to a human agent. Should the human start from scratch, or receive what the AI agent already gathered?

Human Agentreceives escalationsHandoff Contextfull transfer
New in this step: Human Agent, Handoff Context.

A ticket escalates to a human. Should the human start the conversation over from scratch?

  1. Starting over means the customer has to repeat everything they already explained — one of the most common and frustrating pain points in real support experiences, and one this design has every ability to avoid.

  2. A complete context transfer lets the human agent pick up exactly where the AI left off — reading what's already been established rather than making the customer re-explain their issue from the beginning, which is exactly the frustration escalation should avoid causing.

  3. Requiring the human to manually reconstruct context that was already captured in the conversation wastes time and still risks the customer needing to re-explain details that were already discussed with the AI agent.

Transfer the full conversation and any partial diagnosis via Handoff Context to the receiving human agent — so escalation feels like a continuation, not a restart, and the customer is never asked to repeat what they've already explained.

Why this piece earns its place

The failure here is not losing the context, it is dumping it. A human picks this up with a queue behind them and about a glance to spend, so a raw transcript is technically a complete transfer and practically the same as none. What they need first is a short lead: what the customer is asking for, what has actually been verified, and — the part that gets left out — what the agent was unsure about. A partial diagnosis handed over without its uncertainty is worse than no diagnosis, because a human who trusts it inherits the mistake and stops checking. Two operational edges. Ownership has to be explicit at the moment of transfer: while a handoff sits in a queue the customer keeps typing, and if nobody decided who holds the conversation you get the agent and the human replying to the same person in the same thread. And the seam has to run both ways — if the human's resolution never comes back into the system, every escalated ticket is a permanent hole in what you know about your hardest cases, which are precisely the ones worth learning from.

What the new pieces do

Human Agentclient
Handles ambiguous, high-stakes, or low-confidence tickets — and needs the full prior context to avoid making the customer repeat themselves.
Handoff Contextdata
The complete conversation and any partial diagnosis, transferred whole to whoever picks up an escalated ticket.

Step 6 · Measure success by outcome, not activity

"A response was sent" isn't the goal

How should resolution quality actually be measured — by whether the agent sent a reply, or by something else?

Triage + Confidencecategorize, assess certaintyAuto-Resolverwell-understood, verifiedHuman Agentreceives escalationsHandoff Contextfull transferOutcome Trackerdid it stay resolved?
New in this step: Outcome Tracker.

Track outcomes via the Outcome Tracker: whether the customer actually stopped contacting support about the SAME issue within a reasonable window, not just whether a response was sent. A ticket "closed" by the agent that the customer reopens or re-contacts about days later wasn't actually resolved — it just looked resolved in the moment.

Why this piece earns its place

This metric is lagging by construction: you only know a resolution held once the window has passed, so it can never gate a live request. That means running two numbers at once — a fast proxy on the request path, something like how often the agent declines or escalates, and the slow outcome number whose only job is to tell you whether that proxy is lying. Watch the denominator. Measure only the tickets the agent chose to close and the figure improves every time it escalates more, which is the most comfortable way to build a number that rises while the system gets less useful; count everything that entered the category instead. The honest limitation is that silence is ambiguous — a satisfied customer and one who gave up and left look identical from here, so this needs something beside it that can tell them apart. And treat the window length as versioned configuration, not a tuning knob: lengthen it and every historical figure silently re-scores itself, leaving you a trend whose shape belongs to the change rather than to anything the system did.

What the new pieces do

Outcome Trackerdata
Tracks whether a customer actually stopped contacting support about the same issue — the real measure of resolution quality, not just "a reply was sent."

Step 7 · Tone and de-escalation

A frustrated customer needs a different response, not just a correct one

A technically correct response delivered in a flat, generic tone can make an already-frustrated customer feel worse, not better.

CustomerSupport APIAccount/Order StateTriage + ConfidenceAuto-ResolverHuman AgentHandoff ContextOutcome Tracker
The system as it stands at this step. · swipe to pan the diagram

Detect frustration or anger signals in the conversation and adjust accordingly — a more empathetic tone, and, for genuinely escalated emotional situations, prioritizing faster handoff to a human agent even for issues that might otherwise be auto-resolvable. Being technically correct isn't sufficient when the delivery itself affects whether the interaction actually resolves the customer's underlying frustration.

Why this piece earns its place

Tone is worth building only if it changes behavior rather than vocabulary. Rewriting the same reply with warmer adjectives is cosmetic; what actually helps a frustrated customer is the agent asking fewer diagnostic questions, not asking for information it could look up itself, and getting to the point faster. Order matters: adjustment belongs after the confidence gate, never instead of it. A warm, apologetic wrong answer reads as insincere in a way a flat wrong answer does not, so empathy applied to an ungrounded response makes the interaction worse than the plain version would have been. Score the conversation, not the message. Frustration is a trajectory — someone patient three turns ago now writing in short sentences is the case you want to catch, and a classifier reading only the last line will miss it while over-firing on people who are simply blunt. That last part is why this needs checking per language and per channel before you trust it: directness that reads as anger in one locale is ordinary phrasing in another, and a false positive here spends a human agent on someone who was never upset.

Step 8 · The sharp edges

Social engineering and gaming attempts

Two adversarial patterns: a customer confidently claiming authority they may not actually have to push through an unauthorized consequential action, and a customer repeatedly rephrasing the same request hoping to get a more favorable automated answer by chance.

CustomerSupport APIAccount/Order StateTriage + ConfidenceAuto-ResolverHuman AgentHandoff ContextOutcome Tracker
The system as it stands at this step. · swipe to pan the diagram

Require actual identity/authorization verification before any consequential action (a refund, an account change) — a confident claim asserted in a chat message is never treated as proof of authorization, no matter how it's phrased. For gaming attempts, detect repeated near-identical requests and route them to human review rather than letting persistence alone eventually produce a favorable automated outcome by chance.

Why this piece earns its place

Verification has to be enforced where the action happens, not where the text is written. A rule living in the prompt is a suggestion made to a system whose input is controlled by the person you are defending against; the refund endpoint itself should refuse any call that does not carry proof verification actually ran. Then a conversation that talks its way past every instruction still cannot move money, because the thing holding the line was never reading the conversation. Everything consequential also needs an idempotency key and an audit record naming what was verified and by which method. After an incident the question is what the agent did and on whose authority, and a transcript does not answer that — it shows what was said, not what was checked. Scope the repetition signal carefully too: counting near-identical requests per session is beaten by opening a new session, which is the first thing anyone tries, so it has to be counted against the customer. And this is the step you would skip outright for a read-only agent — verification, audit, idempotency and rate limits all arrive with the first tool that writes.

You did it

You just designed a support resolution agent.

CustomerSupport APIAccount/Order StateTriage + ConfidenceAuto-ResolverHuman AgentHandoff ContextOutcome Tracker
The finished design, end to end. · swipe to pan the diagram

Everything you assembled, in order

  • General product knowledge can't answer account-specific questions — a confident guess is worse than an honest escalation.
  • Grounding in real, live account/order state is what makes answers actually correct for a specific customer, not just plausible-sounding.
  • Confidence assessment has to gate action BEFORE a resolution attempt, not just be reported after the fact.
  • Resolution is tiered by category and risk — full automation, assisted drafting, or full escalation — rather than one uniform process.
  • Escalation transfers full context so customers never have to repeat what they've already explained.
  • Resolution quality is measured by whether the issue actually stayed resolved, not just whether a response was sent.
  • Tone-aware de-escalation matters — correctness alone doesn't address an already-frustrated customer's experience.
  • Consequential actions require actual identity verification, never just a confidently-asserted claim, and repeated gaming attempts are their own signal for review.

Where an interviewer pokes next

Getting the boxes right is the easy half. These are the questions that separate a candidate who drew the diagram from one who has run the thing. Answer each one out loud before you open it.

  1. Why is outcome-based tracking (Step 6) harder to implement than it might sound?

    It requires attributing a LATER event (the customer contacting support again) back to an EARLIER resolution as the same underlying issue, which isn't always a clean, obvious match — a customer might return about a related-but-distinct problem, or the same issue phrased very differently the second time. Real implementations typically use some combination of matching heuristics (same order id, similar issue category, timing proximity) plus a review process for ambiguous cases, accepting that outcome attribution will sometimes be imperfect — but even an imperfect outcome signal is far more meaningful than "a response was sent," which tells you nothing about whether the underlying problem was actually solved.

  2. How does the confidence-based escalation here differ from the Email Agent's reversibility-based approval gate?

    They're scoping autonomy along different primary axes suited to their domains. The Email Agent gates on REVERSIBILITY — can this specific action be undone if wrong — largely independent of how confident the draft itself is. This design gates primarily on CONFIDENCE — does the agent genuinely have enough certainty and the right grounding data to answer correctly at all — which is a different question from whether the resulting action is reversible. In practice a mature system might use both: escalate low-confidence tickets before attempting resolution, AND separately require approval for any consequential action even when confidence is high, similar to how the Sales CRM Agent design combines confidence-informed scoring with a suggest-don't-send default.

  3. What should happen if account data retrieval succeeds but returns genuinely ambiguous or conflicting information (e.g., two orders that could both match what the customer described)?

    This is functionally a confidence problem, not a data-availability problem — the Triage + Confidence step should treat "the retrieved data is itself ambiguous" as a reason for LOW confidence, the same way missing data would be, rather than picking one of the ambiguous matches arbitrarily and proceeding as if it were certain. Ambiguity in the grounding data itself is exactly the kind of situation that should route to a human, who can ask the customer a clarifying question far more naturally than an automated system guessing between two plausible matches.

  4. Could tone/frustration detection itself be gamed — a customer feigning anger to get faster human escalation for a routine issue?

    This is a real, minor risk, but the asymmetry of consequences makes it a reasonable trade-off: if feigned frustration occasionally results in a routine issue getting a slightly-faster human touch than strictly necessary, the cost is a modest, bounded inefficiency. If GENUINE frustration is missed and a truly upset customer is left with a flat, automated response, the cost is a meaningfully worse customer experience and potential churn. Given that asymmetry, erring toward taking frustration signals seriously (even if occasionally gamed) is the more defensible design choice than trying to build a strict "is this frustration genuine" verifier, which would add complexity for a comparatively low-stakes gaming risk.

  5. Why require identity verification for consequential actions even when the request comes through an already-authenticated session?

    Being logged into an account doesn't always mean the person making a SPECIFIC request has the authority for every possible action — for instance, a shared or business account might be logged in by someone who isn't authorized to approve a large refund or account closure on their own. Session authentication verifies "this is a valid session for this account"; action-specific authorization verifies "this specific action, by this specific requester, is actually permitted" — for sufficiently consequential actions, the two aren't automatically the same guarantee, which is why high-stakes actions often warrant an explicit verification step beyond simply being logged in.

Check yourself — the answers, and why

Nine steps in, these are the calls you should be able to make cold. Pick one, then read why.

  1. Answering support tickets from general product knowledge alone fails for account-specific questions because…

    A large share of real tickets depend on account-specific data no amount of general knowledge can provide.

  2. Confidence assessment should gate action…

    Real safety requires confidence to determine whether to act autonomously at all, not just be reported afterward.

  3. Tiered resolution by category and risk means…

    The level of automation should track actual risk and complexity, not be uniform across every ticket.

  4. Handoff context transfer exists to…

    The AI agent's prior work has real value that shouldn't be discarded at the point of escalation.

  5. Resolution quality should be measured by…

    An outcome metric (did the problem actually go away) is meaningfully different from an activity metric (a response was sent).

How you’d open this design in an interview

Before any boxes: agree what it must do, pin the qualities that shape everything, then build — naming each trade-off as you make it. The walkthrough above is that exact order.

What it must do

Agree on these before drawing a single box.

  • Triage: take an incoming ticket, categorize it, and route it by category and risk.
  • Ground in account state: retrieve this customer’s actual, live account/order data before answering.
  • Auto-resolve or escalate: fully resolve low-risk tickets; escalate ambiguous or high-stakes ones to a human.
  • Hand off with context: transfer the full conversation so the customer never repeats themselves.
  • Verify before acting: require identity/authorization before any consequential action like a refund.

The qualities that shape everything

Each one names the mechanism that buys it.

Answers correct for this customer, not plausible
Ground responses in the customer’s actual, current Account/Order State retrieved live from the systems of record — not general product knowledge or a stale snapshot.
Never guess on a high-stakes ticket
A Triage + Confidence step estimates the agent’s own certainty, and low-confidence, ambiguous or high-stakes tickets escalate before any resolution attempt — confidence gates action.
Automation matched to risk
Tier resolution by category and risk — full auto-resolve for well-understood low-risk, assisted drafting for moderate, full escalation for high-stakes — instead of one uniform path.
Escalation never restarts the customer
Transfer the full conversation and any partial diagnosis via Handoff Context to the human agent, so escalation is a continuation, not a repeat.
Measure resolution, not activity
The Outcome Tracker records whether the customer actually stopped contacting support about the same issue in a window — not just that a reply was sent.
Resist social engineering
Consequential actions run only behind a real authorization check that has to have returned before the action executes, so an assertion made inside the conversation carries no weight on its own; persistent re-asking is itself routed to a human.

The trade-offs you say out loud

Senior signal isn’t the boxes — it’s naming what you gave up and why it was the right price.

Recognize account-specific vs general questions over answering everything from general product knowledge

A large share of tickets — “where’s my order”, “why was I charged” — depend on facts only in this customer’s account. General docs don’t contain them, so answering anyway means a vague non-answer or a confident wrong guess.

Live account/order state from systems of record over a cached snapshot taken once at signup

Account and order state changes constantly — new orders, plan changes, billing events — so a one-time snapshot is stale for almost every real scenario. Grounding in live data is what makes an answer actually correct for this customer.

Confidence gates action before resolving over attempting resolution first and escalating only if it clearly fails

Guessing first on ambiguous or high-stakes tickets risks producing a wrong response before any human sees it — for the cases where being wrong matters most. Confidence has to decide whether to act, not be assessed after.

Tier resolution by category and risk over one consistent process for every ticket

Treating a password reset like a billing dispute either over-automates the risky case or over-escalates the trivial one. Matching the automation level to actual risk serves both customers and support efficiency.

Transfer full conversation + partial diagnosis over a fresh start so the human isn’t biased

Starting over forces the customer to repeat everything they explained — the exact frustration escalation should avoid. The AI’s work up to handoff has real value; carrying it makes escalation a continuation.

The answer, out loud

What a strong answer to “Design a Support Resolution Agent” sounds like, first question to last trade-off. It is about 7 minutes of talking; the whiteboard and the interviewer fill the rest of the 45. Read it aloud once, then close the page and give it yourself.

  1. 0–3 min

    What kind of problem this is

    I want to say what kind of system this is before I draw anything. A support queue looks like a question-answering problem and it isn't one. Most of what arrives is a question about one person — their shipment, their invoice, their subscription — and the model has never met them. So what I'm designing for isn't eloquence. It's something that can tell the difference between what it knows and what it's improvising, and stops itself on the second one. I'd rather this thing hand a ticket over too early than sound certain about a stranger's money.

  2. 3–8 min

    The version with no account data

    The first version has one source of knowledge: everything we've published — help center, product docs, whatever anyone could read without logging in. That handles how do I change my password, and it collapses the moment the answer lives in a record instead of an article, because no help content contains the date a particular parcel shipped. What makes that dangerous rather than merely unhelpful is the shape of the failure. It isn't a blank. It's a fluent paragraph about how shipping generally works, handed to someone asking about theirs.

    Built in step 1: Answer from general knowledge alone
  3. 8–13 min

    Give it the record

    I'd wire the agent into the systems that hold the record — orders, billing, subscription state — and read them at the moment of the question. I'd argue against copying it somewhere convenient, because an account isn't a document. It's edited all day by the customer and by half the company, so a copy is only ever correct by luck. Reading live turns the reply from something typical into something true about this person, and it makes it checkable: a human can open the same record and either agree with us or not.

    Built in step 2: Retrieve the actual facts, not just documentation
  4. 13–18 min

    Let uncertainty decide first

    Real data doesn't make every ticket answerable. Some are contested, some are furious, some have a lawyer somewhere behind them, and on those the agent's own uncertainty is the most useful thing it produces. So triage scores how sure it is, and that score decides whether the agent writes a reply at all. The ordering is the whole argument. Do it the other way round and the ticket you were least qualified to speak on is the one you've already spoken on, and you're now correcting yourself in front of someone already unhappy.

    Built in step 3: Confidence has to be a first-class signal
  5. 18–23 min

    Three levels, not one process

    Then I'd stop treating the queue as one thing. Some categories are boring in the good way and the agent can finish them alone. Some are safe to answer but not to send unread, so it drafts and a person signs off. Some shouldn't be automated at all, because being wrong on them is expensive. What three levels buy over one is the ability to be wrong cheaply. A single setting is aimed at either the expensive ticket or the constant one, and it's badly aimed for the other all day.

    Built in step 4: Not every ticket needs the same treatment
  6. 23–28 min

    The handoff is the product

    Escalation is where the customer decides whether this company is any good, so I'd design it properly rather than treat it as the failure exit. The human receives the conversation and whatever the agent worked out, not a ticket number and a fresh hello. Everything before the handoff has already been paid for — the customer spent their evening typing it — and a transfer that starts from nothing throws that away and charges them for it twice. I'd judge this part by whether the person picking it up can carry on mid-sentence.

    Built in step 5: The customer should never have to repeat themselves
  7. 28–33 min

    Measure whether it stayed fixed

    For quality I'd refuse the easy number. Anything countable on the day — replies sent, tickets closed, first-response time — measures us, not them. The one I'd put on the wall is whether the same person came back about the same thing. That makes calling a ticket resolved a prediction rather than an observation: you learn days later whether you were right, and a lot of confident-looking work turns out to be the first half of a longer conversation.

    Built in step 6: "A response was sent" isn't the goal
  8. 33–37 min

    Tone can override the tier

    Two more things before I'd call it done. First, tone — because the category isn't the only thing that should decide how a ticket is handled. The same billing question from someone mildly curious and from someone already writing in capitals are different problems, and only one is really about billing. So I'd let frustration override the tier: a category that normally runs unattended can drop to a person on the strength of how the conversation is going. That costs us automation on tickets we could have closed, and I'd take the cost.

    Built in step 7: A frustrated customer needs a different response, not just a correct one
  9. 37–42 min

    The people who will try it on

    Second, adversarial use. An agent built to be helpful is an attractive target, because the usual defense against a persuasive stranger is a suspicious human, and we've just taken that human out of the loop. So before anything that moves money or changes an account, the system has to establish who it's talking to somewhere other than the conversation itself. The other pattern is patience — the same request over and over, reworded until one version lands better. An agent that can be worn down is a slower way of saying yes.

    Built in step 8: Social engineering and gaming attempts
  10. 42–45 min

    The dial, and what I've waved past

    Underneath all of it is one dial: how much the agent finishes on its own against what it costs when it's wrong. Every threshold I've named is a position on it, and there's no correct one, only a defensible one. Mine leans toward escalating more than strictly necessary, because an unneeded escalation shows up as workload, and a wrong answer often shows up as nothing at all. With more time, two things. Where the categories come from — I've assumed a tidy list, and real queues have a tail that fits none of it. And the agent sees the account but not the relationship: it can't tell this person has been let down by us before, which is exactly the ticket I'd want handled differently.

What this teaches

Learn AI system design by building an AI agent that triages, resolves, and escalates customer support tickets end-to-end. An interactive guide covering why answering from general knowledge alone is dangerous when the real answer depends on a specific customer's account state, grounding responses in real account/order data, confidence-based escalation instead of guessing, tiered auto/assisted/escalate resolution by category and risk, seamless handoff context so customers never repeat themselves, outcome-based quality tracking (did the issue actually stay resolved), tone-aware de-escalation, and identity verification against social engineering.

Key takeaways

  • General product knowledge can't answer account-specific questions — a confident guess is worse than an honest escalation.
  • Grounding in real, live account/order state is what makes answers actually correct for a specific customer, not just plausible-sounding.
  • Confidence assessment has to gate action BEFORE a resolution attempt, not just be reported after the fact.
  • Resolution is tiered by category and risk — full automation, assisted drafting, or full escalation — rather than one uniform process.
  • Escalation transfers full context so customers never have to repeat what they've already explained.
  • Resolution quality is measured by whether the issue actually stayed resolved, not just whether a response was sent.
  • Tone-aware de-escalation matters — correctness alone doesn't address an already-frustrated customer's experience.
  • Consequential actions require actual identity verification, never just a confidently-asserted claim, and repeated gaming attempts are their own signal for review.

Concepts covered

  • Knowing when NOT to answer matters more than answering
  • Answer from general knowledge alone
  • Retrieve the actual facts, not just documentation
  • Confidence has to be a first-class signal
  • Not every ticket needs the same treatment
  • The customer should never have to repeat themselves
  • "A response was sent" isn't the goal
  • A frustrated customer needs a different response, not just a correct one
  • Social engineering and gaming attempts
RUN IT YOURSELF

The confidence number that was never a probability of anything you care about

Step 3’s escalation gate, built twice over one 4,000-ticket queue and running for real in your browser — once thresholding the number the model reports, once thresholding that number after it has been checked against what actually happened. The model is deliberately good: its confidence is an almost perfect estimate of whether its answer is correct. It still ships 15.5 unresolved tickets per 100 deflections through a gate that allows 10. Change CUT at the top, hit Run, and watch the broken count and the queue that lands on humans move together.

HOW TO READ THE CODE — 6 IDEAS
  1. Step 3 says confidence gates action. This prints what that costs when the number is taken at face value: at 0.90 the raw gate deflects 60.0% of the queue and 15.5 per 100 of those come back — against the 10 the cutoff was promising.
  2. The reliability table is why. In the band the cutoff sits in, the model says 0.93 and the tickets land 0.74 — and every band is short, so there is no cutoff that rescues the raw number.
  3. The model is not lying about itself. It scores whether its answer is correct; step 6 scores whether the issue stayed resolved, and a correctly answered billing dispute is still an argument about money. The calibrator subtracts -0.06 from password resets and -0.51 from billing disputes — the gap is the category, not the model.
  4. Each bin is scored by the lower end of what its sample supports, not its measured rate — 50 tickets that all stayed resolved are evidence for 0.95, not 1.00. That is what makes binning conservative; without it the calibrator hands the thin top bin its own luck as a promise, which is the raw gate’s mistake one level up.
  5. The correction is not free, and this is the line to quote: honouring the promise hands 33.9% more of the queue to humans. Step 3 calls that a staffing dial, which is why a model change and a threshold change must never ship together.
  6. The last line is the falsification, run in-file: rebuild the identical queue with a correct answer always ending the matter and the raw gate keeps its promise (3.9 against 10). The defect is the gap between the two questions, not the toy.
CPython · WebAssembly
built to know exactly when it doesn't know — make the calls, try to social-engineer a refund, run the gauntlet.
Finished this one? 0 / 65 AI System Designs done
NextDesign a Conversational AI Closest AI system design to this one

Explore the topic

See this alongside everything else on the same subject — handbooks, system designs, challenges and tools, in one place.

More AI System Designs