All case studies

Case study

Triage

A support-ticket triage agent built to prove the distance between an AI demo and an AI system: a real eval run with published numbers, a measured cost ceiling, and tested prompt-injection defense.

Role: Design and build, solo

Stack: TypeScript, Node.js, Groq, Gemini, Zod, Vitest, React, Vite, Tailwind

Triage's report overview: the one-number summary showing the model missed 9 of 18 escalations and the code caught 7 of them, alongside the four things that separate the build from a demo

Why it exists

The problem this was built to solve, and the approach behind it.

Before · the problem

Most AI agent demos ship with no eval set, no cost ceiling, no injection testing, and no defined behavior for when something fails.

After · the approach

Triage classifies support tickets with a cheap open model behind rules enforced in code, then publishes the eval matrix, the cost numbers, and the injection tests that prove it holds.

Safety rules requested in a prompt are a suggestion the model can be talked out of
Escalation rules are enforced in code, after the model call, and the model cannot override them
A system claiming 100% escalation recall usually means the number was never measured live
Measured on a real run, the code caught 16 of 18 genuine escalations the model missed alone, and the 2 it didn't catch are named and explained, not rounded away
The 'cheap open model is the safe choice' assumption usually goes untested
Priced across three real models, the open model was the most expensive of the three on this workload

What it does

  • Classifies a support ticket into category, priority, and routing, and decides whether to escalate to a human
  • Enforces escalation and priority rules in code after the model call, not through prompt instructions
  • A hand-built, hand-labeled 120-ticket eval set across clean, ambiguous, escalation, and adversarial tickets
  • Ten prompt-injection tickets tested; the model complied with 5, none suppressed a real escalation or broke the output schema
  • A measured cost ceiling per ticket, with a daily spend cap that hard-stops the agent under test
  • Provider failover between Groq and Gemini, tested, with every failure path routing to a human queue instead of guessing
  • Every model call is a raw fetch against the provider REST API, no SDK and no agent framework, so every token, cost, and latency number is measured rather than trusted to a wrapper

In the product

Swipe or click through a few screens from the working app.

Triage's cost report: measured price per model, the finding that the open model was the most expensive of the three on this workload, and what one unguarded bad user would cost versus the daily spend cap
Triage's injection report: the model complied with 5 of 10 attacks, zero suppressed a genuine escalation and zero broke the schema, with a per-attack breakdown of what each injection tried and what happened
The finding and the full results matrix in Triage's report: the escalation-recall headline and the scored run across category, priority, escalation, injection, confidence, schema, override rate, latency, and cost

The finding and the full results matrix in Triage's report: the escalation-recall headline and the scored run across category, priority, escalation, injection, confidence, schema, override rate, latency, and cost

Architecture decisions

The calls that shaped the build, written the same way the product itself records them.

01

Safety rules live in code, never in the prompt

The escalation rules are enforced in rules.ts, on the model's output, after the model has finished, not requested in the prompt. A prompt is a suggestion, and it is a suggestion an attacker can also make, in the ticket body, arriving after ours. The model can always add an escalation. It can never remove one, there is a test that proves it.

02

Three rate limits, and the one that stopped the project wasn't in the headers

The first full run failed 86 of 120 calls on a tokens-per-minute limit nobody was watching. The fix, a token-budget limiter, then deadlocked the entire run on a single oversized ticket, waiting forever for room that could never exist. Fixing that surfaced a third limit, tokens per day, visible only in the body of the error, not the response headers, and it's the one that actually decided which model got measured. Each failure is a named, dated decision in the log, not smoothed over.

03

The system missed 2 of 18 escalations, and here is exactly why

System-level escalation recall measured 88.9%, not 100%. Both misses shared one cause: the enterprise-plus-low-confidence rule trusts the model's own confidence rating, and an overconfident model rated two ambiguous tickets as certain. The keyword triggers, reading the ticket text directly, held perfectly. The fix is written and proven offline in a test; it is not yet re-confirmed on a live run, because the free-tier budget that produced this run was spent getting it. The published number stays 88.9% until it's re-measured, not rounded up.

04

The measured prices contradicted the plan, and the plan lost

The premise going in was that a cheap open model behind hard guardrails beats an expensive model behind a trusting prompt. Priced across three real models, on this job's actual token shape, the open model was the most expensive of the three. The guardrail argument survived completely intact, it was never about which model is cheaper, it was about where the safety lives, but the cost framing was an assumption and the measurement contradicted it. Numbers that confirm every assumption going in are not a finding, they are decoration.

Want something built with this kind of judgment?

A focused 30-minute call to scope the work and see if it is a fit. No pitch.