Evaluating it.
How I decided what "good" meant, and proved the search never leaks, before trusting it with real documents. The companion to the HR policy search.
Read the suite itself, case by case. Every persona, message, and check, plus the source files.
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.
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.
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:
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.
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:
-
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.
-
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.
-
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.
-
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.
-
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.
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.
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.
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.
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.
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.
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.
Worth your time.
-
Demystifying evals for AI agents
Anthropic. Offline vs online, graders you can trust, and reading the transcripts.
-
Building Effective Agents
Anthropic. Where evaluation fits in the loop: a model that uses tools on feedback, over and over.
The eval layer under all four use cases. A system is only as trustworthy as the way you measure it. Back to the portfolio →