Support tooling

Support Triage Assistant

Paste a support ticket and Claude will classify it — category, severity, a one-line summary, a suggested first response, and whether it needs engineering escalation.

Try a sample:

How do I know it works?

Every prompt change is scored against 22 hand-labeled tickets. The same prompt runs on two providers, so the results compare models directly. Latest category accuracy:

openai · gpt-5.5
45%
anthropic · claude-sonnet-4-6
68%

Training Notes

What building this project taught me.

Pin temperature before you measure

The first scores looked good at the default temperature. Pinning temperature to 0 made them drop, which showed the earlier numbers were partly luck. A lower score you can reproduce beats a higher one you cannot, because only a stable score can tell you whether a prompt change helped.

A failing score can mean the label is wrong

Twice in the first version, the model was right and the hand-written gold label was wrong. Later, when Claude and GPT gave the same wrong answer on the same ticket, the label became the first suspect. An eval checks your data as much as your model.

Ground the answer where the model is confidently wrong

Retrieval was tested on the same tickets with and without the knowledge base. It helped on 3 of 5, and its real wins were fixing 2 confident factual errors: a wrong claim about SSO and error codes from the wrong product. It never made an answer worse, and when no article fit, the model cited none.

Prove an agent's exit paths, not just its happy path

An agent decides which tools to call, so some code paths only run when the model chooses them. Forcing the iteration cap to 1 confirmed the loop fails cleanly with its tool log intact. The tool log is the main debugging artifact: it shows whether a wrong answer came from a skipped tool or a bad conclusion.

A prompt tuned on one model favors it

With the same prompt, Claude scored 68% on category and GPT scored 45%. But the rubric was refined against Claude's mistakes, so part of that gap is the prompt, not the model. A fair comparison names what it was tuned on.

Swap the model, keep the pipeline

The triage package talks to a small TriageProvider interface instead of a specific SDK. Anthropic and OpenAI each get an adapter with their own retry rules, and both receive a byte-identical prompt, so the comparison measures the model and not the plumbing.

Prerender the evidence

The runs dashboard reads saved JSON run files during next build and ships static HTML. Nothing reads the file system at request time, which avoids monorepo file-tracing problems on Vercel and makes the pages instant.

Split Server and Client Components at the data boundary

The page wrapper (page.tsx) stays a Server Component for metadata and layout. The form itself becomes a Client Component (TriageForm.tsx) because it needs useState and event handlers. Only the leaf that needs interactivity opts in to 'use client'.

API routes keep credentials server-side

The browser never calls Claude. It posts the ticket to the Next.js route at /api/triage, which runs the shared triage package in a Node.js serverless function. The API keys live in Vercel's environment settings for that project and never reach the client. Those settings are read when a deployment is created, so a new or changed key needs a redeploy.

Streaming vs. single-shot responses are a product decision

For triage classification, a complete JSON object is easier to parse than a stream. Streaming makes more sense for long-form responses where showing partial output improves perceived speed.