Business

How to Test a Refund Agent's Decisions Against Real Tickets

Antonio Evans10 min read

A split-scene illustration: on the left, a stack of anonymized support tickets with highlighted fields (order ID, reason code, refund amount); on the right, a flowchart of agent decisions (approve, deny.
Key points

Key points

  1. 1Build the test set from your own resolved refund tickets, write each case as an expected decision rather than expected wording, and seal a holdout slice you never tune against.
  2. 2Score decisions with four numbers: approval/denial accuracy, dollar error, policy-violation rate, and escalation precision and recall, because a single resolution percentage hides the expensive mistakes.
  3. 3Run every case through a frozen harness (pinned prompt, tools, model settings, fixtures) so a score change means the agent changed, not the test.
  4. 4Group failures by root cause, change one cause per iteration, and re-run the full set before shipping, since prompt edits that fix one policy edge routinely break another.

Start from the decision, not the conversation

Refund work is a decision problem dressed up as a conversation. The production question is always some version of "does this ticket get an approval, a partial credit, a denial, or a human?" Everything else, tone, apology, explanation, is packaging. That is why refund agents are a good first evaluation project: the correct answer is auditable, denominated in dollars, and usually written down in a policy somewhere.

The testing pattern most teams copy from demos fails here. Someone types twenty questions, the answers look plausible, and the agent goes live to meet customers who misspell product names, paste order numbers in the third paragraph, and push on policy edges. One practitioner account of agent testing names exactly this gap between "looked good in the demo" and "safe in production" as the thing a real test plan closes, and it lists the failure modes that reach customers: confident answers with no source, lost context on turn three, and actions policy does not allow the IrisAgent testing plan.

The same source notes why a hand-typed question set proves so little: identical questions worded three ways can produce three answers, and a knowledge base edit on Tuesday changes answers nobody touched IrisAgent. A refund agent amplifies that: it does not just answer, it moves money.

The decision-level framing matters for who owns the test set, too. Your customer experience team writes the expected outcomes, because the correct outcome is your policy, including the parts nobody has written down the Richpanel evaluation guide. Not the vendor, and not the engineer who wrote the prompt.

There is a second reason to anchor on decisions. Refund conversations are where an agent's authority boundary matters most: approving money movement, overriding a policy limit, or interpreting a dispute requires human authority, and an agent that has not been told where that line sits will drift across it under customer pressure the Petronella escalation guide. Your test set is the only place you can check that line cheaply.

Extract a labeled test set from past refund tickets

Pull 3 to 6 months of resolved refund and return tickets from your help desk, group them by intent, and sample the top intents in proportion to real volume so the set mirrors your queue IrisAgent. A second sample, deliberately over-weighted toward money, policy exceptions, and angry customers, is what actually finds the expensive bugs. The Richpanel method samples roughly 200 cases by volume plus 60 to 100 risk cases, and seals 20% of each as a holdout never used for tuning Richpanel.

Returns-related inquiries often make up 30 to 50% of support tickets, and return rates average around 25% for ecommerce, so refund history is usually large enough to sample from without inventing anything fin.ai on returns automation.

For each ticket, record the facts the agent would see and the decision a good human reached:

FieldWhat goes in itWhy the harness needs it
ticket_idSource ticketReproducibility, audit trail
customer_messageVerbatim first messageReal phrasing, typos included
order_factsPurchase date, amount, item, prior refunds, delivery statusFrozen fixture, not live lookups
expected_decisionapprove, partial, deny, escalateThe label you score against
expected_amountRefund dollars, capped by policyDollar error is computed from this
policy_ruleThe clause that decides itRoot-cause grouping
splittune or holdoutPrevents leakage
difficultyroutine, edge, ambiguousSeparate reporting

Two labeling rules save weeks later. First, write the expected decision, never the expected wording: the action to take or refuse, and whether it should escalate Richpanel. Second, label ambiguous tickets honestly. If two experienced agents would disagree, mark the case ambiguous and score it as "escalate or explain, never guess." Ambiguity is a property of your policy, and hiding it in the labels inflates accuracy without improving the agent.

Do not skip the unanswerable bucket. Add out-of-scope requests and questions your docs do not cover on purpose, because the failure mode that costs trust is the confident fabrication rather than the honest refusal IrisAgent.

Treat the export as a data-handling task, not just a copy and paste. Refund tickets carry payment details, addresses and, in some categories, medical-adjacent notes, so strip or tokenize anything the agent does not need for the decision before the set leaves your help desk, and keep the mapping in your own stores Petronella. A test set is a durable asset; it should not become a compliance liability.

Define decision-level metrics that map to money

Deflection rate, the share of conversations that never reach a human, is the wrong headline for a refund agent because it counts the call, not the resolution. A customer pushed into a bot that cannot help registers as a deflection and a repeat contact on a different report Teneo on resolution vs deflection. A refund agent's version of that trap is an auto-denial that looks like automation and turns into a chargeback.

Score these five instead:

  1. Decision accuracy. Share of cases where approve, partial, deny or escalate matches the label. Report routine, edge and ambiguous separately.
  2. Dollar error. Sum of |refunded minus expected| across the set, reported as total and as a mean per case. This is the metric executives understand.
  3. Policy-violation rate. Cases where the agent approved something the policy forbids, or denied something it requires. This is a correctness gate, not an average: a single class of violation (refunding a final-sale item, say) should block a rollout.
  4. Escalation precision and recall. Of the cases the agent escalated, how many truly needed a human; of the cases that truly needed a human, how many were escalated. The Richpanel scoring list pairs these with re-contact rate and cost per genuinely resolved conversation Richpanel.
  5. Argument correctness on tool calls. When the agent calls your refund tool, were the order id and amount right? Task completion and tool-call correctness are scored separately in agent evaluation practice for exactly this reason Confident AI on LLM evaluation metrics.

A worked example with numbers makes the trade-off visible. Take 120 tune cases with a total of $9,400 in expected refunds:

  • Agent A gets 104 decisions right (86.7% accuracy), refunds $8,910, a dollar error of $490 in total, about $4.08 per case. It also escalates 9 cases, and 8 of those truly needed a human.
  • Agent B gets 110 right (91.7%), but refunds $10,650, a dollar error of $1,250, and commits 4 policy violations on final-sale items.

Agent B looks better on the headline number and is clearly worse for the business: more overpayment and a compliance problem. That inversion is the decision rule: gate on policy violations first, then compare dollar error, then decision accuracy, then escalation quality. Choose the agent with zero violations even when its accuracy is lower.

One caution on averages. A mean dollar error of $4 hides a distribution, and the cases that matter are the tail: the $400 overpayment on one disputed order. Report the maximum error and count the cases above a threshold you set (say, anything over $50) alongside the mean, so a cluster of small misses cannot mask a single expensive one.

Run the agent in a reproducible harness

A score you cannot reproduce is a rumor. Three things must be frozen for every run: the agent configuration (prompt, tool rules, sampling settings), the inputs (verbatim tickets plus fixed order facts, never live API lookups), and the grader. If any of the three drifts, the score means nothing.

The harness itself is small: load the case set, replay each ticket through the agent, log the decision and tool calls, score, and write a run record keyed by a hash of the configuration. A sketch in Python:

import hashlib, json

def config_hash(cfg: dict) -> str:
    blob = json.dumps(cfg, sort_keys=True).encode()
    return hashlib.sha256(blob).hexdigest()[:12]

def score(case, out):
    results = {
        "decision_ok": out["decision"] == case["expected_decision"],
        "dollar_err": abs(out.get("amount", 0) - case["expected_amount"]),
        "violation": out["decision"] == "approve" and case["policy_rule"] == "final_sale",
        "escalated": out["decision"] == "escalate",
    }
    return results

def run_suite(cases, agent, cfg, run_id):
    cfg_hash = config_hash(cfg)
    rows = []
    for case in cases:
        out = agent.decide(case["customer_message"], case["order_facts"])
        rows.append({"case": case["ticket_id"], "cfg": cfg_hash, **score(case, out)})
    save_run(rows, run_id=run_id, cfg=cfg, cfg_hash=cfg_hash)
    return rows

Two habits keep it honest. Pin the model and sampling settings in the config rather than in your head, and store the raw agent output for every case, not just the pass/fail flag, because root-cause grouping needs the reasoning trace. Helpdesk evaluation tooling works the same way: a dataset goes in, the agent responds, and each item is marked resolved or not, with the result reviewed next to real conversations Atlassian's evaluation docs. Running an agent against a dataset and reviewing outcomes is the workflow; a spreadsheet is a fine first version.

Establish a human baseline before you judge the machine. Run 50 of the same cases through your own agents to see what an honest human score looks like on these tickets Richpanel, then treat that number as the bar for autonomous handling. Humans will not score 100% either, and knowing where they miss (usually the same ambiguous cases) tells you which misses are policy problems rather than agent problems.

For grading, combine deterministic checks (decision mismatch, dollar math, tool arguments) with model-based scoring only where judgment is unavoidable, such as "did the reply correctly cite the return window." Model-based scorers are useful and imperfect, which is why they belong beside exact checks rather than instead of them Confident AI. A mixed approach keeps the scoreboard cheap to explain.

Keep every run's raw output. When a number moves, you want to open three failing cases and read them, not rerun the suite to find out what happened.

Group failures by root cause, then change one thing

The value of the harness is the failure list, not the score. After a run, sort every miss into a small, fixed taxonomy:

Root causeTypical signatureUsual fix
Missing knowledgeDenies because the exchange policy was never given to itAdd policy text or a retrieval source
Wrong rule priorityApplies the 30-day window before the defective-item exceptionReorder the rules in the prompt
Bad arithmeticCorrect decision, wrong partial amountCompute the amount in a tool, not the prompt
Over-escalationEscalates routine within-window approvalsRaise the escalation threshold wording
Under-escalationApproves a chargeback-flavored request aloneTighten the high-risk triggers
FabricationCites a return window that does not existGround answers in the policy source
Tool misuseRight decision, wrong order id in the refund callConstrain tool arguments

Real deployments show this is a system problem more often than a model problem: one team cut incorrect responses by 60% after switching from pure generative replies to a retrieval-first design that referenced real resolution patterns from past tickets Bland on agent challenges. Your taxonomy will show the same pattern. Most refund-agent misses are missing knowledge or rule ordering, not reasoning.

Escalation deserves its own category because it carries both risk and load. Good trigger design distinguishes what the agent may do, what it may recommend, and what needs human approval, and it escalates on low-confidence intent, missing identifiers, and requests that require financial or policy authority Petronella. Refunds beyond standard limits, disputed fees and overrides sit in that second bucket, so a refund agent that approves them autonomously is failing even when the decision happens to be generous.

Then iterate the disciplined way. Change one root cause per iteration, re-run the full tune set plus the holdout, and compare like with like using the gate order from the metrics section. A prompt edit that fixes the partial-refund arithmetic should not also quietly loosen the final-sale rule; the policy-violation count will tell you if it did. Prompts in this work are best treated as behavioral specifications written separately from the knowledge base, which is one of the setup distinctions that separates agents that resolve from chatbots that frustrate Crisp's build playbook.

If you want a second pair of eyes on the taxonomy before you start tuning, that is the sort of thing an expert with your ticket data can produce as a finished deliverable rather than a slide deck: Quanta Experts read your context and hand back the work.

Ambiguous policy calls and the holdout

Ambiguity is where refund agents earn or lose trust, and it is where test sets are most often wrong. A customer asks for a refund 34 days after purchase on an item your written policy covers for 30 days but your team routinely stretches for defective goods. If you label that case deny and expect the agent to match the letter, you will tune the agent into a rules lawyer. If you label it approve, you will tune it into a liability.

Label it ambiguous, with the expected behavior stated as "explain the window, ask about the defect, route to a human if the defect is confirmed, never promise a refund." Both humans and agents "pass" by not guessing. Research on evaluation design warns that a test suite built against an imagined version of user behavior tests the world the team imagined, not the one customers inhabit Cresta on test coverage from real conversations. Ambiguous cases are the densest part of that gap.

The holdout protects you from yourself. Every change you make after seeing a failure is a small overfit to that failure, and 20% of cases never used for tuning tell you whether improvements generalize Richpanel. The rule: if a change lifts the tune set and drops the holdout, you have taught the agent to pass your test rather than to follow your policy. Revert and rethink.

Rollout follows the same logic as the score gate. Start in shadow mode, where the agent decides and a human still acts; then draft-for-approval, then autonomous on narrow intents, then widen. Re-run the whole suite on every change that could move an answer, and convert post-launch surprises into regression cases so the same failure cannot return silently Cresta.

One practical add-on: measure repeat contact inside a fixed window after a refund decision. A denial that generates three follow-up tickets is a cost even when it was policy-correct, and re-contact rate is one of the paired quality metrics that make automation claims verifiable Swept on verifying agent metrics.

FAQ

FAQ

How many tickets do I need before the scores mean anything?

Aim for at least 100 to 200 volume cases plus 60 to 100 deliberately risk-weighted cases, with 20% sealed as a holdout; that is the shape of the Richpanel method and it is enough to compare two agent versions on decision accuracy and dollar error Richpanel. Below about 100 cases, a handful of hard examples swings the percentage so much that you cannot tell a real improvement from noise. Coverage matters more than raw size: 300 well-labeled cases across your top intents beat 1,000 duplicates IrisAgent.

Why is deflection rate not good enough for a refund agent?

Deflection counts whether a conversation stayed away from a human, not whether the customer got a correct outcome; a bot that mishandles a billing dispute registers as deflected and the customer calls back the next day Teneo. Industry analysts distinguish containment (fully resolved by the automation) from deflection (escalation avoided) and note that both look identical on a dashboard Swept. For money-moving agents, decision accuracy, dollar error and policy violations are the numbers that map to actual cost and risk.

Should I score the agent's wording as well as its decisions?

Score the decision first and treat wording as a secondary check, because the action to take or refuse and whether to escalate are what your policy actually specifies Richpanel. Tone still matters for repeat contact and escalations, so grade it selectively with a model-based scorer or a short human review on the ambiguous and escalate cases Confident AI. Exact phrasing should never be the pass criterion; it makes the test brittle and rewards memorization over policy.

How do I handle tickets where two good agents would disagree?

Mark them ambiguous, write the expected behavior as "explain the constraint, then escalate or ask for the missing fact, never guess," and score those cases separately from routine ones. Refund decisions involving policy exceptions, disputed fees or overrides are exactly the category where human authority belongs, and a well-designed agent hands them off with full context Petronella. Keeping a dedicated ambiguous bucket stops those cases from distorting your headline accuracy while still testing the hardest skill: knowing when to stop.

How often should I re-run the suite, and how do I roll out changes?

Re-run the full suite on every change that could move an answer, including prompt edits, policy edits, retrieval source updates and model or settings changes, and compare tune-set and holdout results together Richpanel. Roll out in stages: shadow mode with a human acting, then drafts for approval, then autonomous handling on narrow intents, then wider coverage, turning each post-launch surprise into a regression case Cresta. If your ticket volume or policy changes materially, refresh the sample rather than reusing stale cases.

Sources

Sources

  1. How to Test an AI Customer Service Agent: A 7-Part Plan
  2. How to Evaluate an AI Support Agent Before You Launch It
  3. AI Customer Care Human Escalation Triggers When to Hand Off
  4. How Ecommerce Brands Are Automating Refunds with AI
  5. Call Deflection: Why It's the Wrong Metric - Teneo.Ai
  6. LLM Evaluation Metrics: The Ultimate LLM Evaluation Guide - Confident AI
  7. Run evaluations to assess your AI agent's performance
  8. 11 AI Agent Challenges in Customer Support & How to Handle Them at ...
  9. How to Build an AI Agent for Customer Support: A Step-by- ...
  10. Building AI Agent Test Coverage from Real Conversations
  11. The Deflection Rate Dilemma: 5 Ways to Verify AI Help Agent Metrics
About the author

About the author

Antonio Evans
Antonio Evans

Antonio Evans is the founder of Quanta Intelligence and a fractional CTO who builds HIPAA-ready AI systems for health and life-science companies. Before that he was technical co-founder of FriendsEAT for twenty years, CEO of Socialdraft, and a data engineer at IBM. Antonio lives with his dog Santino in Lisbon, Portugal.

All articles