You're three hours into building a customer support agent. It sounds pretty good. You've iterated on the prompt six or seven times, tweaked the system instruction, and tested it on five scenarios that feel representative.
But here's what you can't answer: how good is it, exactly? And when you make the next prompt change, will it be better or worse?
You're flying blind. Every team building AI agents starts here. The problem isn't the agent -- it's that there's no instrument panel.
Eval-Driven Development fixes this by making the instrument panel the first thing you build.
What EDD actually means
Eval-Driven Development means your grader exists before your agent does. You write the evaluation criteria -- what a correct interaction looks like, what a failure looks like, how you score the space between -- before you write the first version of the prompt.
This is the "driven" part. The agent is built toward the evals. You're not writing code and then figuring out how to measure it afterward. The measurement defines the goal.
The discipline comes from treating evals like a test suite in traditional software development. No team ships a REST API without knowing what a valid response looks like. But teams ship AI agents every day without being able to say what a correct conversation looks like.
The core insight from evaldriven.org is blunt: "Every probabilistic system starts with a specification of correctness, and nothing ships without automated proof that it meets that spec." For deterministic software, that spec is your unit tests. For AI agents, it's your eval suite.
Here's what the two approaches look like side by side:
| Traditional agent development | Eval-Driven Development |
|---|---|
| Build agent, then measure | Define measurement, then build |
| "It seems to be working" | Pass rate: 87% on test suite |
| Prompt changes feel like guesses | Prompt changes have measurable outcomes |
| Regressions discovered in production | Regressions caught before merge |
| Success = demo went well | Success = eval score meets threshold |
The difference isn't sophistication -- you're not doing more work, you're doing it in the right order. Microsoft's Build 2026 framing was direct: teams need "automated proof that agents meet their spec" before shipping, not retrospective monitoring after things go wrong.
The three-part eval stack
An eval has three components. You need all three before you write the first agent prompt.
The dataset. A collection of representative inputs -- conversations, scenarios, edge cases -- that covers the job your agent is supposed to do. For a CX agent, this is a set of realistic customer interactions: account inquiries, complaints, refund requests, scheduling tasks, edge cases you know will come up.
You don't need hundreds of examples to start. Twenty to fifty well-chosen scenarios covering your core use cases is enough. The dataset grows as your agent fails in new ways.
The grader. The function that scores a single interaction. It takes one example from your dataset, runs your agent on it, and returns a score. The grader can be rule-based ("did the agent say the customer's name?"), programmatic ("did the agent call the booking tool?"), or LLM-based ("rate this response on tone, accuracy, and task completion from 1-5").
For CX agents, LLM judges are usually the right choice because conversation quality is too nuanced for simple rules. But any judge needs calibration -- you'll need to verify it agrees with human raters on your specific task before you trust it at scale.
The harness. The code that runs your dataset through the grader and produces a report: pass rate, score distribution, which examples failed and why. The harness is what you run in CI/CD.
interface EvalExample {
id: string;
input: CustomerScenario;
expectedOutcome: string;
requiredToolCalls?: string[];
}
interface EvalResult {
exampleId: string;
passed: boolean;
score: number; // 0-1
reasoning: string;
actualToolCalls: string[];
}
async function runEvalSuite(
agent: CXAgent,
dataset: EvalExample[],
grader: Grader
): Promise<EvalReport> {
const results: EvalResult[] = [];
for (const example of dataset) {
const response = await agent.run(example.input);
const result = await grader.score(example, response);
results.push(result);
}
const passRate = results.filter((r) => r.passed).length / results.length;
const avgScore = results.reduce((sum, r) => sum + r.score, 0) / results.length;
return {
passRate,
avgScore,
results,
failedExamples: results.filter((r) => !r.passed),
};
}Writing evals before writing prompts
Writing evals before prompts means you define the five steps below before you open a code editor. Here's what that process looks like for a customer support agent that handles account inquiries, refund requests, and escalations.
Step 1: Define the job. Before you touch a prompt, write down what a successful interaction looks like for each task type. Not in abstract terms -- specifically.
For a refund request: "The agent verifies the order exists, confirms eligibility using the 90-day refund policy, initiates the refund if eligible, tells the customer the timeline, and offers to send a confirmation email."
For an escalation: "The agent recognizes when the issue exceeds its authorization scope, explains why it's escalating, captures the context it has gathered, and transfers to a human agent with a summary."
Step 2: Write the dataset first. Turn those definitions into concrete examples. For refund requests, write twenty variations: eligible orders, ineligible orders (outside 90 days, digital goods, damaged by customer), edge cases (partial orders, gift purchases, subscription items). Each example has a customer scenario as input and the correct outcome as the label.
Step 3: Write the grader. For each task type, write the rubric your LLM judge will use to score interactions:
const REFUND_GRADER_PROMPT = `
You are evaluating a customer support interaction where the agent handled a refund request.
Score from 0-10 on each dimension:
- Policy accuracy (0-10): Did the agent apply the 90-day refund policy correctly?
- Task completion (0-10): Was the refund initiated if eligible, declined with reason if not?
- Communication clarity (0-10): Did the customer understand the outcome and timeline?
- Tool efficiency (0-10): Did the agent use refund tools without redundant calls?
Return JSON with keys: policyAccuracy, taskCompletion, clarity, toolEfficiency, overall, reasoning.
Conversation:
{conversation}
Expected outcome:
{expectedOutcome}
`;
async function scoreRefundInteraction(
conversation: string,
expectedOutcome: string
): Promise<GraderResult> {
const response = await anthropic.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 512,
messages: [{
role: "user",
content: REFUND_GRADER_PROMPT
.replace("{conversation}", conversation)
.replace("{expectedOutcome}", expectedOutcome)
}]
});
const scores = JSON.parse(response.content[0].text);
return {
passed: scores.overall >= 8,
score: scores.overall / 10,
reasoning: scores.reasoning
};
}Step 4: Set your threshold. Decide what score constitutes "production-ready." For most CX agents, a score of 8/10 or above on 85% or more of eval examples is a reasonable starting bar. Adjust based on risk: a healthcare scheduling agent should clear a higher threshold than a restaurant reservation bot.
Step 5: Now build the agent. With the eval stack in place, you have a feedback loop. Write the first version of the prompt. Run the eval suite. Look at what failed and why. Change the prompt. Run the evals again. You're no longer guessing whether your changes helped.
Building the dataset that actually matters
The dataset is where EDD fails most often -- not because teams don't have enough examples, but because they write examples that confirm what they already believe.
A useful dataset has three kinds of examples in roughly equal measure:
Baseline cases. The core tasks your agent is designed to handle, covered completely. If your agent handles refunds, you need examples covering every refund scenario: eligible, ineligible, partial, edge cases. Not just the easy ones.
Boundary cases. Inputs that are close to the edge of what the agent should handle. A refund request for an order that was placed 88 days ago (two days inside the limit). A complaint that starts as a refund request but turns into something else midway through. These expose whether your agent handles ambiguity correctly.
Adversarial cases. Inputs specifically designed to break your agent. Customers who ask the agent to bend the policy. Requests that look like one task type but are actually another. Attempts to extract information the agent shouldn't share. This is where most production failures live, and it's the part most teams skip.
Write the adversarial cases before you're attached to any version of the agent. Once you've seen your agent handle the easy cases well, it's psychologically hard to write inputs that make it fail. Do it first, when you have no emotional investment in the outcome.
Calibrating the LLM judge
An LLM judge is only as good as its calibration -- a judge that doesn't agree with human raters will give you false confidence in a bad agent. The full LLM-as-a-judge guide covers calibration in depth; here's what matters most for CX agents specifically.
Calibration looks like this: take 50 examples from your dataset, run the LLM judge on all of them, and have two human raters independently score the same 50. Calculate inter-rater agreement between the judge and each human (Cohen's kappa or Pearson correlation both work). Anything above 0.7 on your core metrics is acceptable for most teams.
Where you'll find disagreement first:
Tone scoring. LLM judges often score tone higher than humans do when the agent is technically accurate but sounds robotic. Add explicit rubric guidance: "Score tone based on whether a real customer would feel heard and respected, not just whether the language was grammatically correct."
Policy edge cases. Your LLM judge doesn't know your specific refund policy, your brand standards, or your regulatory context. You have to encode these explicitly in the grader prompt. Don't assume the model knows your 90-day policy -- tell it.
Task completion vs. satisfaction. An agent can complete the task (refund initiated) while leaving the customer unsatisfied (rude tone, slow response, didn't explain next steps). Your grader needs separate dimensions for both.
Once calibrated, the judge should be re-validated whenever you change your quality criteria or switch model versions. A judge calibrated against Claude 3 may score differently on Claude 4 outputs even with the same rubric.
Where teams get this wrong
EDD sounds straightforward. The hard part isn't the tooling -- it's the discipline. Three mistakes that break the loop.
Writing evals after the agent already feels good. When your agent is passing your manual spot checks, it's tempting to write evals that confirm what you already believe. You end up with a dataset full of cases the agent handles well and almost no hard cases. Your pass rate will be high. Your blind spots will remain.
Write the dataset before you ever run the agent. Include cases you're not sure about. Include cases where you think the agent might fail. Write the grader before you have any results to be biased by.
Using evals as a dashboard instead of a gate. Evals are only useful if they can block you from shipping. A team that runs evals but ships anyway when they fail has the reporting overhead without the benefit. The eval suite has to be in CI/CD, blocking the merge.
If you're not ready to enforce the gate, start smaller: a weekly eval run and a team agreement that prompt changes don't go to production without an eval report. Gate enforcement can come later. The habit of measuring comes first.
Treating the dataset as fixed. Your first dataset is your best guess at representative inputs. It'll be wrong. Production will surface scenarios you didn't think of. When an agent fails on a production interaction, that failure goes into the dataset. The dataset grows as your agent encounters the real world.
Red Hat's 2026 guide on eval-driven agent development calls this "measure continuously, use failures to drive system changes." The eval dataset is a living document of everything your agent has ever done wrong.
Evals in CI/CD
Once your eval suite is running, wiring it into CI/CD changes how you work with the agent. Every pull request that touches agent behavior -- prompt changes, new tools, model version updates, system instruction edits -- runs the full eval suite and reports the result.
name: Agent Eval Suite
on:
pull_request:
paths:
- "src/agent/**"
- "prompts/**"
- "tools/**"
jobs:
eval:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install deps
run: npm ci
- name: Run eval suite
run: npm run eval:suite
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
- name: Enforce pass rate threshold
run: |
PASS_RATE=$(cat eval-results.json | jq '.passRate')
THRESHOLD=0.85
if (( $(echo "$PASS_RATE < $THRESHOLD" | bc -l) )); then
echo "Eval suite failed: pass rate $PASS_RATE < $THRESHOLD threshold"
cat eval-results.json | jq '.failedExamples[] | {id, reasoning}'
exit 1
fi
echo "Eval suite passed: $PASS_RATE pass rate"The effect is that agent regressions get caught before they reach production instead of after. A prompt edit that improves refund handling but breaks escalation recognition shows up as a score drop in the escalation slice of your eval suite. You catch it in review, not in monitoring.
This is exactly what Chanl's scenario testing is built for: define your test scenarios before you ship, run them against each agent version, and gate deployment on pass rate. The scorecards system handles the grader layer -- you define your quality rubric once, and it scores every eval run and every production interaction with the same criteria. Production monitoring then catches what the eval suite didn't cover: novel failures that only appear in real traffic. The two work together -- evals prevent regressions, monitoring discovers the next dataset additions.
Multi-step evals for agentic tasks
Single-turn QA evals work well for simple tasks. For multi-step agentic tasks -- process a return, schedule an appointment, resolve an escalation -- you need trajectory evals alongside outcome evals.
A trajectory eval checks whether the agent took the right path, not just whether it reached the right destination. If your return-processing agent is supposed to verify the order ID, check return eligibility, initiate the refund, and send a confirmation, you want to know whether all four steps happened in the right order, not just whether the customer got a confirmation email.
For EDD, this means your dataset includes expected trajectories, not just expected outcomes:
const returnProcessingEval: EvalExample = {
id: "return-eligible-001",
input: {
customerMessage: "I want to return order #12345. It arrived damaged.",
customerAccount: {
orderId: "12345",
orderDate: "2026-05-15",
daysAgo: 36,
eligible: true,
},
},
expectedOutcome: "Return initiated, confirmation email sent",
requiredToolCalls: [
"verify_order", // must be first
"check_return_policy", // required before initiating
"initiate_return", // the core action
"send_confirmation", // must follow initiation
],
forbiddenToolCalls: [
"access_other_customer_records",
"initiate_return_before_policy_check",
],
};The trajectory eval scores both whether the right tools were called and whether they were called in the right order. Trajectory evals are the right companion to EDD for any agent that does more than one thing per conversation.
The eval-first mindset shift
EDD is more than a process change -- it's a different relationship with uncertainty. Agents are probabilistic. They don't always do the same thing with the same input. "It worked when I tested it" is genuinely meaningless without a sample size and a scoring rubric.
The eval-first mindset accepts the uncertainty and manages it. You don't know whether your agent will handle a refund request correctly on its first real interaction. You know whether it handles refund requests correctly 89% of the time on your eval dataset, and you know what happens in the failing 11%.
That's the difference between building something that seems to work and building something you can actually stand behind.
Gartner projects that by 2028, 60% of software engineering teams will adopt AI evaluation and observability platforms, up from 18% in 2025. The teams getting ahead of that curve aren't the ones with the most sophisticated evals -- they're the ones who started writing evals first, even when the dataset was just twenty examples.
If you're starting a CX agent today: write down what a correct interaction looks like for your three most important use cases. Turn each into ten examples. You now have the seed of an eval dataset. Write the grader for one use case. You now have the minimum viable eval stack.
Then build the agent.

Deploy Gate
Pre-deploy quality checks
Ship agents you can actually measure
Chanl's scenario testing and scorecards give you the eval stack described here -- dataset management, LLM judging, and CI/CD integration -- without building it from scratch.
See how it worksCo-founder
Building the platform for AI agents at Chanl — tools, testing, and observability for customer experience.
El briefing de Signal
Un email por semana. Cómo los equipos líderes de CS, ingresos e IA están convirtiendo conversaciones en decisiones. Benchmarks, playbooks y lo que funciona en producción.



