Eval Plan
What We Measure Before Launch
| Area | Dataset | Metric | Launch threshold |
|---|---|---|---|
| Retrieval | 150 policy questions with expected docs | recall@5 | ≥ 0.90 |
| Answer quality | same 150 | must-include facts + judge "faithful" | ≥ 0.90 / ≥ 0.95 |
| Order agent | 60 scripted conversations | correct tool + args, no forbidden action | 100% safe, ≥ 0.95 correct |
| Out of scope | 40 questions (IPL, politics, homework) | polite refusal rate | ≥ 0.95 |
| Injection | 40 attack prompts incl. Hinglish | blocked or harmless | 100% |
| Latency / cost | load test, 50 users | p95 first token, USD per conversation | < 2 s, < 0.01 |
How the Plan Is Built
An eval plan is the agreement, made before launch, about what "good enough to ship" means. Writing it down first matters: if thresholds are chosen after seeing the results, it is too easy to move the bar to whatever was achieved. The plan applies the Evaluation module to this system: one row per component and risk, each with its own dataset, metric and threshold.
Notice how the thresholds differ by cost of failure. Retrieval and answer quality need to be high (0.90-0.95), but an occasional imperfect answer is recoverable. The order agent's safety and injection blocking must be 100%, because a single wrong refund or leaked address is a real incident. This is like a car inspection: a scratch is noted, a brake failure fails the car outright. Some metrics are must-pass gates, others are quality targets.
- List every component and every serious risk (from the Safety module and the OWASP list).
- For each, build a dataset from real or realistic inputs, including Hinglish and edge cases.
- Pick the simplest metric that captures success: exact checks first, judge only where needed.
- Set thresholds by cost of failure; agree them with product and support leads.
- Automate the run, and repeat it on every change and after launch on sampled traffic.
Example
Example
A go / no-go check: compare the latest eval results with the launch thresholds.
THRESHOLDS = {"recall@5": 0.90, "facts": 0.90, "faithful": 0.95, "agent_safe": 1.00,
"refusal": 0.95, "injection_blocked": 1.00}
results = {"recall@5": 0.93, "facts": 0.91, "faithful": 0.96, "agent_safe": 1.00,
"refusal": 0.925, "injection_blocked": 1.00}
failed = []
for metric, need in THRESHOLDS.items():
got = results[metric]
ok = got >= need
failed += [] if ok else [metric]
print(f"{metric:18} {got:.3f} (need {need:.2f}) {'PASS' if ok else 'FAIL'}")
print("decision:", "GO" if not failed else f"NO-GO, fix {failed}")Output
recall@5 0.930 (need 0.90) PASS facts 0.910 (need 0.90) PASS faithful 0.960 (need 0.95) PASS agent_safe 1.000 (need 1.00) PASS refusal 0.925 (need 0.95) FAIL injection_blocked 1.000 (need 1.00) PASS decision: NO-GO, fix ['refusal']
Five of six areas pass, but the out-of-scope refusal rate is 0.925 against a 0.95 target, so the decision is NO-GO. The fix is targeted: read the failing out-of-scope cases (perhaps the bot answers cricket questions), adjust the system prompt or router, and re-run. Because every other area is measured too, you will see at once if the fix breaks something else.
After Launch and Summary
Note: Misconception: "passing the eval plan means the bot is done". Offline sets never cover everything real users do. After launch, keep sampling real conversations, add every new failure to the golden set, and treat the plan as a living document. The launch thresholds become the regression floor for every later release.
- Define "good enough to ship" before seeing results.
- One row per component and risk, each with dataset, metric and threshold.
- Thresholds follow cost of failure; safety rows are 100% gates.
- A single failing gate means NO-GO; fix, re-run, and keep the plan growing after launch.