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

Why it exists
The problem this was built to solve, and the approach behind it.
Most AI agent demos ship with no eval set, no cost ceiling, no injection testing, and no defined behavior for when something fails.
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.
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.



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.
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.
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.
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.
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.