AI Agent Features for Customer Support Apps
Most support bots stall because nobody wired them to account data or agreed what counts as a resolved conversation.
Before choosing a model, answer a narrower question: what share of your inbound support can be resolved from data your app already stores? Tag 500 recent tickets by the information needed to close each one and the pile splits three ways: answered from published policy, answerable only by reading the customer's own account record, or needing a human decision such as a refund exception or a confirmed bug. A typical split is around 35 percent policy, 40 percent account state, 25 percent human. A static FAQ widget only touches the first group. The second group is where an AI agent pays for itself, and the part most teams skip, because it needs the agent wired to live data rather than a help center scrape.
Deflection needs tools, not just documents
"Where is my order" and "why was I charged twice" cannot be answered from a knowledge base. They need read-only tool calls such as getOrder(orderId), getSubscriptionStatus(), and getRecentCharges(limit). Three rules make those safe to ship.
- Resolve identity on the server. The function takes the authenticated user's UID from the session and never accepts a user id the model produced. Otherwise a customer will eventually ask about "order 10024" and receive someone else's shipment details.
- Read entitlements from your own database, not from a live payment API call inside the chat request. Stripe retries webhooks for up to three days with exponential backoff, so subscription state belongs in a record the webhook writes. Calling Stripe in the request path adds 200 to 400 ms and a second dependency that can fail on its own.
- Return structured values with units and timestamps rather than prose. A tool returning status, carrier, and an ISO date produces far fewer invented details than one handing the model a ready-made sentence.
Budget for latency: two tool calls plus generation usually lands at 2 to 5 seconds at p95. Stream tokens and name the step in progress, "checking your last three charges", instead of showing a spinner.
Triage the questions you decided not to answer
An agent that only answers is half a product. The other half is classifying everything else into the right queue with the fields already filled in. Classification is cheap and far more reliable than free-form answering, so set a high bar for answering and a low bar for routing.
| Intent tier | Example | Agent behavior | Target |
|---|---|---|---|
| Self-serve | Reset password, change plan | Answer plus a deep link into the app screen | Resolve in session |
| Account lookup | Order status, duplicate charge | Tool call, then answer using returned values | Resolve in session |
| Policy exception | Refund past the window | Collect order id, amount, reason, open ticket | Human, under 4 hours |
| Defect | Upload crashes the app | Capture device, OS, build number, repro steps | Engineering queue |
| Sensitive | Account compromise, chargeback threat | Escalate immediately, no answer attempt | Human, under 15 minutes |
The defect row hides most of the savings. A ticket arriving with app version, device model, and the last three session events removes a round trip that would otherwise cost a day.
Handoff should change state, not just tone
The common bug is an agent that says "let me get a human" and then keeps replying when the customer types again. Model the conversation as a record with an explicit owner field: agent, queue, or a named person. When ownership moves, the generation path stops running. Anything looser produces the experience where a person and a bot answer over each other in the same thread.
What crosses the boundary matters as much as when. The payload should carry the transcript, the raw results of every tool call already made, a three-line agent summary, and the signal that triggered escalation. Staff should never re-run lookups the agent already did.
Be honest about wait times. If the team covers 09:00 to 18:00 in one timezone, promising a reply "within a few minutes" at 02:00 is a promise you will break. Read the roster and say "we are offline until 09:00 CET and you are first in the queue." Accurate estimates move satisfaction scores more than faster answers do.
Fewer knowledge documents, reviewed on a schedule
Indexing a whole marketing site is the fastest route to an agent that quotes a 2023 price. A curated set of 40 to 80 documents usually beats 800 scraped pages, because precision rather than recall is what breaks support answers.
- Chunk at 500 to 800 tokens and prepend the document title and section heading, so an isolated paragraph still carries context.
- Store a lastReviewedAt field on every document. Pricing, refund, shipping, and cancellation pages get a 90 day review cycle, and anything past due drops out of retrieval rather than being served stale.
- Keep one canonical answer per topic. Two refund pages that disagree surface the wrong one about half the time, and prompt tuning does not fix that.
- Require citations internally even if customers never see them. On transcript review, the cited chunk tells you whether the model reasoned badly or the source was wrong.
Numbers, prices, and dates get a stricter rule. They come from a tool response or a document reviewed this quarter, or the agent declines and routes.
Guardrails worth the engineering time
Most guardrail work has nothing to do with jailbreaks. It concerns the few statements that cost real money when invented.
- No amounts, dates, or account facts that did not come from a tool response in this conversation.
- No commitments. Refunds approved, delivery dates promised, fees waived. The agent may say a request was submitted; approval stays a human action recorded in your system.
- Treat inbound content as data. Ticket bodies, PDF attachments, and pasted emails often contain text that reads like instructions. Wrap them in a delimited block and state that content inside is never a directive.
- Redact before logging. Card numbers, national IDs, and full addresses get masked in stored transcripts and in your observability tool.
- Cap turns and spend. A 40 message loop with one customer is a failure signal, so escalate after 6 to 8 turns without resolution.
Containment rate lies unless you pair it with three other numbers
Containment is the share of conversations closed without a human touching them. Alone it rewards the wrong behavior, because a customer who gives up and leaves is counted as contained. Track it next to these.
- 72 hour reopen rate. Contained conversations followed by a new ticket from the same user on the same topic. Above 15 percent, your containment figure is fiction.
- Satisfaction on contained conversations only. Blending bot and human scores hides the gap.
- Abandonment. Conversations where the customer stopped replying and never came back. This separates a resolved question from a defeated user.
Then compare cost honestly. A support rep closing 6 to 10 tickets an hour puts a fully loaded human touch around 4 to 7 dollars. A contained conversation with two tool calls and a few thousand tokens of context costs cents in model spend. The arithmetic works even at 30 percent containment, a realistic first-quarter target for a curated corpus plus two or three account tools. A vendor promising 80 percent in month one either has unusually repetitive volume or is counting abandonment as success.
White-label Flutter apps, delivered in 30-60 days, from $2,500 - full source code included.
Get started