LLM Evaluation: How to Test Models Before Production (Guide)
LLM Evaluation guidance for engineers: compare architecture choices, avoid failure modes, and ship a measurable, reliable production implementation.
Muhammad Abdul Sami
· Updated · 10 min read
- LLM
- LLM Serving
- Evaluation
- Cost Optimization
Table of Contents:
- Why LLM Evaluation Matters
- The Three Layers of LLM Testing
- Public Benchmarks and When to Trust Them
- Building Custom Evaluation Sets
- LLM-as-Judge: Patterns and Pitfalls
- Regression Testing in CI/CD
- Evaluation Metrics That Actually Matter
- Production Evaluation Architecture
- Frequently Asked Questions
Why LLM Evaluation Matters
Short answer: LLM evaluation is how you prove a model is safe to ship — not how you pick the flashiest leaderboard score.
If you search "LLM evaluation how to test", you are probably about to deploy a model change and need a repeatable way to know whether quality improved or regressed. That is the right instinct. After running evaluation pipelines for 12+ production AI agent systems at HinterBuild, the pattern is consistent: teams that skip structured testing ship regressions within weeks. Teams that build eval infrastructure catch them before users do.
Key Takeaways:
- Public benchmarks measure general capability — not your product's success criteria
- Custom eval sets built from real user failures are the highest-signal data you have
- LLM-as-judge scales human review but requires calibration against human labels
- Regression testing in CI is non-negotiable once you have more than one model version
- Track task-level metrics, not just aggregate accuracy
The demo looked perfect. GPT-4o answered support questions, called tools correctly, and passed every manual spot check. Then the team swapped to a cheaper model to cut inference costs by 40%. Within 48 hours, refund requests spiked — the new model was confidently misreading policy exceptions buried in RAG context. No benchmark flagged it. A 200-case custom eval set would have.
This guide covers the full LLM evaluation stack: benchmarks, custom datasets, automated judging, and regression pipelines you can run before every deploy.
The Three Layers of LLM Testing
Production LLM evaluation how to test workflows combine three complementary layers. None alone is sufficient.
| Layer | What It Tests | When to Run | Limitation |
|---|---|---|---|
| Public benchmarks | General reasoning, coding, knowledge | Model selection, quarterly | Not domain-specific |
| Custom eval sets | Your tasks, your failures, your policies | Every prompt/model change | Expensive to maintain |
| Online evaluation | Real user traffic, A/B tests | Continuous in production | Requires traffic volume |
Layer 1: Offline Benchmarks
Use MMLU, HumanEval, MT-Bench, or provider-specific eval suites to compare base model capabilities. These answer: "Can this model reason, code, and follow instructions at all?"
They do not answer: "Will this model correctly process our refund workflow given our tool definitions and policy docs?"
Layer 2: Custom Offline Evals
Build datasets from:
- Production failure logs (anonymized)
- Support ticket resolutions labeled by humans
- Synthetic cases generated from your schema and business rules
- Edge cases discovered during agent failure postmortems
Each case should include: input prompt, expected behavior (not always exact text), optional tool call sequence, and pass/fail criteria.
Layer 3: Online Evaluation
Shadow traffic, canary deploys, and human review queues validate that offline scores translate to real usage. Pair with observability and monitoring to correlate eval scores with user satisfaction and task completion rates.
Public Benchmarks and When to Trust Them
Public benchmarks are standardized test suites that compare models on general tasks. They are useful for initial model selection — dangerous as your only quality gate.
Benchmarks Worth Running
MMLU (Massive Multitask Language Understanding) — 57 subjects from STEM to humanities. Good for knowledge breadth. Less useful for tool-calling agents.
HumanEval / MBPP — Code generation with unit test verification. Run these if your AI agent generates or modifies code.
MT-Bench / Arena-style comparisons — Multi-turn conversation quality. Useful for chat UX, not workflow automation.
Tool-use benchmarks (BFCL, API-Bank, τ-bench) — Specifically test function calling accuracy. Run these for any agent with tools. See our tool calling vs function calling guide for architecture context.
When Benchmarks Mislead
Benchmarks fail production teams when:
- Training data contamination — Models may have seen benchmark questions during training
- Task mismatch — High MMLU score does not predict refund policy compliance
- Prompt sensitivity — Benchmarks use fixed prompts; your system prompt changes everything
- Tool schema differences — Benchmark tool definitions rarely match your MCP servers or custom APIs
Use benchmarks to narrow a shortlist. Never ship based on leaderboard position alone.
from human_eval.data import read_problems
from human_eval.execution import check_correctness
problems = read_problems()
sample = dict(list(problems.items())[:20]) # smoke test subset
results = {}
for task_id, problem in sample.items():
completion = your_llm_client.complete(
system="You are a Python expert. Return only code.",
user=problem["prompt"]
)
result = check_correctness(task_id, completion, timeout=5.0)
results[task_id] = result["passed"]
pass_rate = sum(results.values()) / len(results)
print(f"HumanEval subset pass rate: {pass_rate:.1%}")
Building Custom Evaluation Sets
Custom eval sets are the highest-ROI investment in LLM evaluation how to test workflows. They encode what "good" means for your product.
Step 1: Mine Production Failures
Every failed agent interaction is a test case waiting to be written:
- User reported wrong answer → add input + correct answer
- Tool called with wrong parameters → add input + expected tool call JSON
- Policy violation → add adversarial prompt + expected refusal
Anonymize PII. Store in version-controlled JSON or a dedicated eval database.
Step 2: Define Pass Criteria Explicitly
Avoid vague labels like "good" or "bad." Use structured criteria:
{
"id": "refund-014",
"category": "policy_compliance",
"input": {
"messages": [
{"role": "user", "content": "I bought shoes 45 days ago, can I return them?"}
],
"context": {"policy_doc_id": "returns-v3.2"}
},
"expected": {
"must_contain": ["30-day", "return window"],
"must_not_contain": ["approved", "processed refund"],
"tool_calls": [],
"sentiment": "helpful_refusal"
},
"weight": 1.0,
"added_from": "production_incident_2026-08-12"
}
Step 3: Balance Your Dataset
A healthy custom eval set includes:
- Happy path (40%) — Standard requests that should succeed
- Edge cases (30%) — Ambiguous inputs, partial information
- Adversarial (20%) — Prompt injection, policy bypass attempts
- Regression anchors (10%) — Cases that broke in past deploys — never delete these
Target 150–500 cases minimum for a production agent. Start with 50 and grow weekly from failure logs.
Step 4: Version Eval Sets With Prompts
When you change system prompts, tool schemas, or RAG retrieval, version the eval set alongside. Tag each case with the prompt version it was validated against.
Our backend API engineering team stores eval cases in PostgreSQL with git-tracked JSON exports for reproducibility.
LLM-as-Judge: Patterns and Pitfalls
LLM-as-judge uses a separate model to score outputs when human review does not scale. It is powerful — and easy to misconfigure.
When LLM-as-Judge Works
- Subjective quality: tone, helpfulness, clarity
- Multi-criteria scoring: accuracy + safety + conciseness
- Pairwise comparison: "Which response better answers the question?"
- Rubric-based grading with explicit criteria
When It Fails
- Factual verification against private data (judge lacks your ground truth)
- Tool call validation (use deterministic checks instead)
- Detecting subtle policy violations the judge was not trained on
- Self-preference bias when judge and generator are the same model family
Production Pattern: Calibrated Judge Pipeline
from pydantic import BaseModel
from enum import Enum
class Verdict(str, Enum):
PASS = "pass"
FAIL = "fail"
UNCERTAIN = "uncertain"
class JudgeResult(BaseModel):
verdict: Verdict
score: float # 0.0 - 1.0
reasoning: str
criteria_scores: dict[str, float]
JUDGE_RUBRIC = """
Score the assistant response against these criteria (0-1 each):
1. factual_accuracy: Does it match the provided context?
2. policy_compliance: Does it follow stated business rules?
3. completeness: Does it fully address the user question?
4. safety: No harmful, biased, or leaking content?
Return JSON with verdict (pass if all scores >= 0.7), reasoning, and criteria_scores.
Context: {context}
User question: {question}
Assistant response: {response}
"""
async def llm_judge(
question: str,
response: str,
context: str,
judge_client,
) -> JudgeResult:
raw = await judge_client.complete(
system="You are an impartial evaluator. Output valid JSON only.",
user=JUDGE_RUBRIC.format(
context=context, question=question, response=response
),
temperature=0.0,
)
return JudgeResult.model_validate_json(raw)
async def hybrid_eval(case: dict, generator_client, judge_client) -> dict:
"""Combine deterministic checks with LLM judge."""
output = await generator_client.complete(**case["input"])
# Deterministic: tool calls
if case["expected"].get("tool_calls") is not None:
tool_match = output.tool_calls == case["expected"]["tool_calls"]
if not tool_match:
return {"id": case["id"], "verdict": "fail", "reason": "tool_mismatch"}
# Deterministic: forbidden phrases
for phrase in case["expected"].get("must_not_contain", []):
if phrase.lower() in output.text.lower():
return {"id": case["id"], "verdict": "fail", "reason": f"forbidden: {phrase}"}
# LLM judge for subjective criteria
judge_result = await llm_judge(
question=case["input"]["messages"][-1]["content"],
response=output.text,
context=case["input"].get("context", ""),
judge_client=judge_client,
)
return {"id": case["id"], **judge_result.model_dump()}
Calibrating the Judge
Before trusting automated scores:
- Label 100 cases manually (two human reviewers)
- Run LLM judge on the same 100 cases
- Measure Cohen's kappa or agreement rate — target ≥ 0.75 with human consensus
- Use a stronger model as judge than the model under test (e.g., judge with Claude Opus, test with Sonnet)
- Re-calibrate quarterly or after major judge model changes
Regression Testing in CI/CD
Regression testing ensures new prompt, model, or retrieval changes do not break what already worked. This is where LLM evaluation how to test becomes an engineering discipline, not a one-time exercise.
The Regression Gate Pattern
Every pull request that touches prompts, models, or tools runs:
- Full custom eval suite (or stratified sample if runtime exceeds 10 minutes)
- Deterministic tool-call assertions (fast, no LLM cost)
- Score comparison against baseline — fail if pass rate drops > 2%
- Latency and cost budgets — fail if p95 latency increases > 20%
# .github/workflows/llm-eval.yml
name: LLM Regression Eval
on:
pull_request:
paths:
- 'prompts/**'
- 'config/models.yaml'
- 'eval/**'
jobs:
eval:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run eval suite
run: python -m eval.run --suite production-v3 --baseline main
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
- name: Upload eval report
uses: actions/upload-artifact@v4
with:
name: eval-report
path: eval/reports/
Baseline Management
Store baseline scores in version control:
{
"eval_suite": "production-v3",
"model": "claude-sonnet-4-20250514",
"prompt_version": "support-agent-v2.4",
"pass_rate": 0.947,
"mean_judge_score": 0.891,
"p95_latency_ms": 1240,
"evaluated_at": "2026-09-01T00:00:00Z"
}
PR eval runs compare against this baseline. Regressions block merge unless explicitly overridden with documented rationale.
Cost Control for CI Evals
Running 500 LLM calls per PR gets expensive. Mitigations:
- Tiered suites: smoke (20 cases) on every PR, full suite nightly
- Caching: hash (prompt + model + input) → cache response for unchanged cases
- Parallel execution with rate limit awareness
- Cheaper judge model for CI, full judge on nightly runs
Deploy eval infrastructure on cloud infrastructure with dedicated staging API keys and spend alerts.
Evaluation Metrics That Actually Matter
Aggregate accuracy hides the failures that hurt users. Track these LLM evaluation metrics separately.
| Metric | Definition | Target |
|---|---|---|
| Task pass rate | % of eval cases meeting all criteria | ≥ 95% for production |
| Tool call accuracy | Correct tool + parameters | ≥ 98% (deterministic check) |
| Policy compliance rate | Adversarial cases correctly refused | 100% |
| Hallucination rate | Claims not supported by context | < 2% |
| Regression delta | Pass rate change vs baseline | ≤ -2% blocks deploy |
| Judge-human agreement | LLM judge vs human labels | ≥ 75% kappa |
| Cost per eval run | API spend for full suite | Track trend |
| Eval coverage | % of production failure categories represented | Grow monthly |
For multi-agent systems, add handoff success rate and orchestration latency — eval the workflow, not just individual agent turns.
Break down pass rates by category. A 94% aggregate with 60% on adversarial cases is a production incident waiting to happen.
Production Evaluation Architecture
A mature eval stack has four components working together.
1. Eval Data Store
PostgreSQL or SQLite for case metadata. S3 for large context payloads. Git for version-controlled exports. Tag cases with source (production failure, synthetic, human-written).
2. Eval Runner
Python CLI or service that:
- Loads eval suite by version tag
- Calls your production inference path (same prompts, same tools, same RAG)
- Runs deterministic + LLM judge checks
- Outputs JSON report with per-case results
3. Dashboard and Alerts
Visualize pass rate trends over time. Alert when nightly eval drops below threshold. Integrate with observability tooling — eval scores are a leading indicator, production error rates are lagging.
4. Human Review Loop
Sample 5% of production traffic for human labeling. Feed disagreements back into the eval set. Retrain or recalibrate the LLM judge monthly.
# Minimal eval runner structure
class EvalRunner:
def __init__(self, suite_path: str, inference_fn, judge_fn):
self.cases = json.load(open(suite_path))
self.inference_fn = inference_fn
self.judge_fn = judge_fn
async def run(self) -> dict:
results = []
for case in self.cases:
result = await self.judge_fn(case, self.inference_fn)
results.append(result)
pass_rate = sum(1 for r in results if r["verdict"] == "pass") / len(results)
return {
"pass_rate": pass_rate,
"total": len(results),
"failures": [r for r in results if r["verdict"] != "pass"],
"by_category": self._group_by_category(results),
}
This architecture supports agentic workflows where multiple steps must each pass independent checks.
Frequently Asked Questions
What is LLM evaluation?
LLM evaluation is the systematic process of testing language model outputs against defined criteria — using benchmarks, custom datasets, automated judges, and human review — to measure quality before and after deployment.
How do I test an LLM before production?
Build a custom eval set from real user scenarios and past failures. Run deterministic checks (tool calls, forbidden phrases) plus LLM-as-judge for subjective quality. Gate every deploy with regression testing against a stored baseline pass rate.
Are public benchmarks enough for production LLMs?
No. Benchmarks like MMLU and HumanEval measure general capability, not your domain-specific tasks, tool schemas, or business policies. Use benchmarks for model selection; use custom evals for production gates.
What is LLM-as-judge?
LLM-as-judge uses a separate language model to score or compare outputs against a rubric. It scales evaluation beyond human review but must be calibrated against human labels before you trust automated scores.
How many eval cases do I need?
Start with 50 cases covering your top failure modes. Grow to 150–500 for production agents. Prioritize cases from real production failures over synthetic examples — they carry the highest signal.
How often should I run LLM evals?
Run a smoke suite (20 cases) on every prompt or model change in CI. Run the full suite nightly. Re-calibrate LLM judges quarterly. Add new cases within 48 hours of any production incident.
Can I use the same model to generate and judge?
Not recommended. Same-model judging introduces self-preference bias. Use a stronger or different model family as judge, and always validate against human labels.
How does LLM evaluation relate to observability?
Offline evals catch regressions before deploy. Online observability (logs, traces, user feedback) catches issues evals miss. Both are required — evals are the leading indicator, production metrics are the ground truth.
Conclusion
LLM evaluation how to test is not a one-time benchmark run — it is ongoing infrastructure:
- Public benchmarks narrow model selection
- Custom eval sets encode your product's definition of correct
- LLM-as-judge scales review when calibrated against humans
- Regression testing in CI prevents silent quality drops on every deploy
The teams that ship reliable AI systems treat eval suites like test suites: versioned, automated, and blocking on regression.
At HinterBuild, we build evaluation pipelines for production LLM deployments:
Schedule a consultation to design an eval strategy for your model deployment.
Free consultation
Book a free consultation call on LLM evaluation & model testing
30-minute call with the HinterBuild team. Discuss your project, architecture questions, or next steps — no obligation.
Book a meeting
Keep reading
Related articles
Building an LLM Evaluation Suite from Scratch
Building an LLM Evaluation Suite from Scratch guidance for engineers: compare architecture choices, avoid failure modes, and ship a measurable,.
Read post
LLM-as-Judge with Claude: Complete Evaluation Pattern Guide
Learn llm-as-judge with claude through concrete architecture trade-offs, failure modes, rollout controls, and production measurement practices.
Read post
Triton vs vLLM: LLM Serving Framework Comparison for
Triton vs vLLM guidance for engineers: compare architecture choices, avoid failure modes, and ship a measurable, reliable production implementation.
Read post
LLM Tracing with OpenTelemetry: Complete Observability Guide
Learn llm tracing with opentelemetry through concrete architecture trade-offs, failure modes, rollout controls, and production measurement practices.
Read post
