Portfolio project · Youssf Hazem

Support Inbox X-ray

Support tickets in, a one-page audit out. An LLM tags every ticket (what the customer wants, how urgent it is, whether a bot could handle it), and the tags are checked against the dataset's real labels.

95.3%
of 2,000 tickets got the right intent label (27 to choose from)
Gemini 3.8 Flash · 95% range 94.3%–96.2%
99.1%
put in the right category
$0.14
per 1,000 tickets
$0.28 from 1 Jan 2027, when the launch price ends
7 of 27
intents a bot could take in full
the model's call, for a person to review

Is the tagging right?

Each dot is a model's accuracy on the same 2,000 tickets; the line is its 95% range. The ranges don't overlap, so the gap between the models is real, not luck. The axis starts at 85%.

Gemini 3.8 Flash 95.3%
Gemini 3.5 Flash-Lite 91.3%
ModelIntent accuracy95% rangeRight categoryPer 1,000 ticketsTime per 1,000
Gemini 3.8 Flash (thinking: low)95.3%94.3%–96.2%99.1%$0.1487 s
Gemini 3.5 Flash-Lite91.3%90.0%–92.5%97.8%$0.1238 s

What a bot could take

For each intent, the share of its tickets Gemini 3.8 Flash judged a bot could resolve, start, or leave to a person. No answer key exists for this part: it's the call a person reviews in a real audit. In a real inbox, weigh each row by how many tickets that intent gets.

Bot can resolveBot starts, person finishesNeeds a person
edit_account100%
check_cancellation_fee100%
delivery_options100%
delivery_period100%
track_order100%
check_payment_methods100%
check_refund_policy100%
recover_password99%
newsletter_subscription99%
get_invoice96%
check_invoice95%
review95%
create_account91%
contact_customer_service91%
switch_account88%
delete_account81%
set_up_shipping_address72%
track_refund68%
cancel_order41%
change_shipping_address38%
place_order38%
contact_human_agent26%
get_refund
registration_problems
payment_issue
complaint
change_order
Show as a table
IntentBot can resolveBot starts, person finishesNeeds a person
edit_account100%0%0%
check_cancellation_fee100%0%0%
delivery_options100%0%0%
delivery_period100%0%0%
track_order100%0%0%
check_payment_methods100%0%0%
check_refund_policy100%0%0%
recover_password99%1%0%
newsletter_subscription99%1%0%
get_invoice96%4%0%
check_invoice95%5%0%
review95%0%5%
create_account91%9%0%
contact_customer_service91%0%9%
switch_account88%11%1%
delete_account81%19%0%
set_up_shipping_address72%28%0%
track_refund68%31%1%
cancel_order41%59%0%
change_shipping_address38%62%0%
place_order38%55%7%
contact_human_agent26%0%74%
get_refund14%85%1%
registration_problems8%92%0%
payment_issue5%92%3%
complaint3%0%97%
change_order1%99%0%

Where the tags go wrong

Gemini 3.8 Flash's most common mix-ups (real intent → tagged as, tickets). Most are near-twin labels, not wild guesses.

  1. track_refund tagged as get_refund 21
  2. check_invoice tagged as get_invoice 20
  3. create_account tagged as registration_problems 7
  4. delivery_period tagged as track_order 7
  5. set_up_shipping_address tagged as change_shipping_address 6

How stable are the judgement calls?

Urgency and "could a bot do it" have no answer key, so two models judged the same 2,000 tickets. Agreement shows how stable the call is, not whether it is right.

76%
same urgency
Cohen's kappa 0.61 (0 = chance, 1 = perfect)
81%
same "could a bot do it"
Cohen's kappa 0.63

How it works

  1. Sample. 2,000 tickets from a public dataset, the same count for each of its 27 intents. Seeded, so anyone can redraw the same sample.
  2. Tag. 25 tickets per API call. The model must answer in a fixed JSON shape with one of the 27 labels; a half-answered batch is asked again for the missing tickets.
  3. Score. Every tag is compared with the dataset's own label. Unanswered tickets count as wrong.
  4. Price. The API's own token counts × Google's published paid-tier price, failed calls included.