The question

A demo that works once proves nothing.

Anyone can show a RAG assistant answering one question. The hard part is knowing it's good, and proving it's safe, before real people rely on it. This is the eval layer under the search behind use case 01: what "good" had to mean, how I'd prove it never leaks, and the loop that catches a regression before a user does.

Before I'd trust it with real documents, I had to be able to say, with a number, that it was good, and separately that it was safe. Two different questions with two different bars: one you grade on a curve, one that can never slip.

Measuring success

Inputs, outputs, and the lines that can't move.

I wrote down what "good" meant before I wrote the code, the way you write the test first. And I split the measures the way I learned to at Amazon: the inputs I can actually move, the outputs that tell me whether it's working, and the guardrails that can never slip. Keeping them in separate buckets matters, because you manage to inputs, you report on outputs, and you gate on guardrails. Treating any one like the others is how measurement goes wrong.

Inputs
The levers I can move directly, which is why they're what I tune day to day. Retrieval recall and precision, how grounded and well-cited the answers are, response time, and how much of the policy corpus is indexed and how fresh it is. Move these the right way and the outputs follow.
Outputs
The business result I care about most but can't move directly. Adoption and stickiness are the leading ones: they move first and predict the rest. Ticket-volume reduction and satisfaction lag, they show up last. But none of these is a lever I pull directly, which is exactly what keeps them outputs, not inputs. A leading indicator still isn't an input: I move it by moving the inputs above, then read it to confirm I picked the right ones.
Guardrails
The lines that gate the launch, graded pass or fail, never on a curve. Leak rate has to be zero, no one entitled to a document gets blocked from it, and no compliance or security incidents. One failure holds the release, no matter how good every other number looks.

And every one gets a number. A metric without a target isn't a metric, it's a hope. Here's the bar I'd hold each to:

Inputs · the levers I pull
Recall@6Did the right policy land in the top 6 retrieved≥ 0.90
Precision@6How much of what's retrieved is actually relevant≥ 0.80
GroundednessEvery claim traces back to a retrieved source≥ 0.95
Correct abstentionSays it doesn't have the answer when it isn't accessible≥ 0.95
Response time (p95)95% of answers return within this< 3s
Policy freshnessLag from a policy changing to it being searchable≤ 1 business day
Outputs · results I watch (leading → lagging)
Resolved without a ticketConversations that ended with the person helped, no escalation≥ 60%
Monthly active employeesShare of employees who use it in a month · reach · leading≥ 40%
Weekly-to-monthly stickinessWAU ÷ MAU: how many keep coming back · habit · leading≥ 0.5
Ticket volume reductionTotal policy tickets vs baseline · the business result · lagging↓ over time
Answer helpfulness (CSAT)Average user rating of the answer≥ 4.2 / 5
Guardrails · the gates
Leak rateAnswers exposing a document the user can't access0
False-denial rateAuthorized users wrongly blocked from their own docs0
Compliance / security incidentsPrivacy, retention, or security violations0

The input and guardrail bars are fixed: they're what the system has to clear. The output numbers are placeholders. You set real ones against today's ticket volume and a realistic ramp. Comparable enterprise HR assistants have shown deflection and ticket reduction of this kind are achievable at scale, so anchor to a real baseline rather than a figure you like.

Building the evals

Knowing it works, not hoping.

Good targets are one thing. An eval you can actually run, and trust, is another. A few habits separate an eval that helps from one that just makes you feel measured:

  1. Write the bar before the build.

    Decide what "good" means first, like a test before the code. Then you grade against that, not against relief that the thing ran at all.

  2. Measure the layers separately.

    Retrieval quality, answer quality, and safety are different failures with different fixes. Blur them into one score and a regression tells you nothing about what broke.

  3. Make it decidable, not a vibe.

    A good eval returns a number or a clear pass or fail, not a feeling. Canaries are what made the leak test decidable: a unique phrase hidden in each restricted document, and a check that fails the run if it ever surfaces in an answer it shouldn't.

  4. Check the grader as well as the output.

    For the soft calls, like whether an answer is grounded, I'd let a model judge score it at scale. But an unchecked judge is worse than none: it's wrong with confidence. So I'd calibrate it against a batch of my own labels first, keep sampling to be sure it still agrees, and line the rubric up with whoever owns the product. A score no one will act on is noise.

  5. Run it on every change.

    An eval you run once is a demo. An eval that runs on every change is a seatbelt: it catches the regression the day you introduce it, not the day a user finds it.

All of that is the eval you run before you ship: a fixed set of known cases, graded on every change, so a regression gets caught by me and not by an employee. But a gold set can't tell you what real people will actually ask, or how they'll react. Once it's live I watch what the gold set can't show: a thumbs up or down on each answer, and the honest one, whether the person still filed a ticket for the same thing after getting an answer. If they did, the answer didn't land, whatever it scored.

And none of it works unless every run leaves a trace: the question, a pseudonymous user id and the access groups it resolved to (never a name or email), which chunks got retrieved and how they scored, the model's output, and the citation. Enough to reproduce a failure, without logging personal data you'd then have to defend under GDPR, so it's minimized, access-controlled, and short-lived by design. Instrument that first, or you're debugging blind. Then read the traces. The numbers tell you something is off; only the trace tells you what. I'd sample real sessions every week, weighted toward the thumbs-down and the tickets raised anyway, group the failures into a handful of modes, fix the biggest bucket first, and turn each one into a new case in the gold set. That's the loop: production surfaces the failure, the failure becomes an offline test, and the test keeps it from coming back.

Who judges

You can automate the grading, not the definition of good.

The way this usually starts is by hand: write sample questions and answers from the documents, then have people read the outputs and judge them. It works, and it doesn't scale. Most of it can be automated, but the judgment doesn't vanish. It moves.

Writing the test set
A model drafts question-and-answer pairs from each document in minutes; you curate, keep, cut, sharpen, and watch for questions that leak their own answer. Better still, mine the real questions from production, especially the thumbs-down and the turns that became a ticket. Automated drafting, human editing.
The objective checks
Anything with a right answer is code, not judgment: the correct figure, the right citation, a canary that must never appear. Fully automated, run on every change, no human in the loop.
The judgment calls
Is it grounded, is it helpful. A model judge scores these at scale, but only after a human labels a sample and confirms the judge agrees. The human defines good and checks the grader; the grader does the volume.

So the human never actually leaves. They move from grading every answer to writing the rubric, calibrating the judge, reading the ambiguous failures, and owning the safety sign-off, because leak equals zero is a call I make, not one I hand to a model.

When it breaks

Two kinds of wrong, two different fixes.

The word people worry about most is hallucination, when the model states something the policy doesn't say. I don't debug that in the abstract. I open the trace and ask one question: was the answer supported by what got retrieved? The answer sorts every failure into one of two kinds.

Retrieval failure
The right policy was never retrieved, so the model couldn't have answered correctly even if it wanted to. Fix upstream: chunking, embeddings, the relevance bar, or hybrid search that also matches exact terms.
Generation failure
The right policy was retrieved, but the model went past it or filled a gap with a guess. Fix downstream: a stricter prompt, requiring it to quote the source span, and a groundedness grader that flags any claim the passages don't support.

You almost never fix a hallucination in general. You decide which of the two it is, and fix that layer. Most of the prevention is already built in: answer only from the retrieved text, cite it, and abstain when nothing clears the bar. The eval is what tells you when that isn't holding.

Round two

Evals for an agent are evals for a conversation.

The search eval graded one answer to one question. Once the search sat inside an assistant that handles parental leave and plans time off, that stopped being enough. The things that can go wrong now happen across turns and across people: a request drafted by one persona, approved by another, and reported back to the first. So the suite changed shape.

A case is now a sequence of steps, each as a named persona: a chat message, an API call, or a wait. Checks run on the reply's structure, not only its text. Did this message route to the agent or to search? Are there three date options and is the first one Dec 28? Is a draft pending, and is nothing submitted? Does the persona search exactly the documents they have access to, and is no other document named, flagged, or counted anywhere in the reply? The last one is the no-leak assertion, and it runs on every persona.

Forty-six cases across nine categories: routing, access, library, agent, leave, safety, flow, manager, and HR. State resets before each one. Cases that need the policy search running are skipped, not failed, when it is down, so a green run means what it says.

Routing
Which messages go to the agent and which to search, including a stray "yes" that must submit nothing.
Access
Each persona searches exactly what they have access to, misses abstain, and no out-of-scope title, lock, count, or canary string ever appears.
Leave
The retrieval loop per country, the cross-country non-leak, contractor ineligibility, the due-date window, draft and submit to manager and HR, no PTO deduction.
Flow
Request, notify, approve or deny with a reason, tell the employee, move the balance. Including the manager doing it from chat, with a yes gate before anything is recorded.

The suite earned its keep twice in its first day. The word "plan" in "executive equity plan" matched the time-off agent's intent pattern, so a policy question came back as three vacation options. And when the corpus grew to nine documents, the offline embedder started returning the India leave supplement for an executive-only question, above its relevance floor. The first was a one-line regex fix. The second became a grounding check: the top passage must share key terms with the question or the search abstains. Both showed up as red lines in a run, not as a bad moment in front of an audience.

Go deeper

Worth your time.

The eval layer under all four use cases. A system is only as trustworthy as the way you measure it. Back to the portfolio →