Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/openai/openai-cookbook/llms.txt

Use this file to discover all available pages before exploring further.

Evaluation is the discipline of systematically measuring how well your model or agent performs on tasks that matter to your application. Without evals, every model update, prompt change, or configuration tweak is a leap of faith. With a solid eval suite, you can catch regressions before they reach production, compare model versions objectively, and build confidence that your system is doing what you intend. This guide covers the main evaluation patterns — rule-based grading, LLM-as-a-judge, the Evals API, and hallucination detection — so you can build the right combination for your use case.

Why evaluation matters

Measure quality

Quantify how often your model produces correct, relevant, or on-policy responses rather than relying on qualitative impressions.

Catch regressions

Detect when a new model version, prompt edit, or configuration change silently degrades performance on inputs that worked before.

Compare options

Make data-driven decisions when choosing between model sizes, prompting strategies, or fine-tuned variants.
Evaluation is most valuable when it is continuous. Build evals into your development workflow so every significant change is tested before deployment. For production systems, run evals on a sample of live traffic to monitor real-world drift over time.

Types of evaluation

There are three main evaluation approaches, each with different trade-offs in cost, speed, and sensitivity.
Rule-based evals use deterministic code to grade model outputs. They are fast, cheap, and fully reproducible. Use them for outputs with a clear correct answer.Common rule-based graders:
  • Exact match — output must equal the expected string (useful for classification or short factual answers)
  • Contains — output must include a specific substring or token
  • Regex — output must match a regular expression pattern
  • JSON parse — output must be valid JSON, optionally conforming to a schema
  • F1 / token overlap — partial credit based on shared tokens between output and reference
def exact_match_grader(output: str, expected: str) -> float:
    return 1.0 if output.strip() == expected.strip() else 0.0

def contains_grader(output: str, required: str) -> float:
    return 1.0 if required.lower() in output.lower() else 0.0

def json_valid_grader(output: str) -> float:
    import json
    try:
        json.loads(output)
        return 1.0
    except ValueError:
        return 0.0
Rule-based evals break down when outputs are open-ended or when multiple valid phrasings exist. In those cases, move to LLM-as-a-judge.
LLM-as-a-judge uses a capable model (typically GPT-4o) to score or compare model outputs. This approach handles the ambiguity of natural language and can evaluate qualities that are hard to specify as rules — such as helpfulness, fluency, and factual accuracy relative to a reference.LLM-as-a-judge is slower and more expensive than rule-based evals, but is far more flexible. It is the right choice when:
  • Outputs are free-form and multiple valid answers exist
  • You need to evaluate subjective qualities like tone or clarity
  • You are comparing two responses and need a preference judgment
See the code example in the section below for a reference implementation.
Human evaluation is the gold standard — it captures nuances that neither code nor models can reliably detect. Use it to:
  • Calibrate your automated evals (check that your grader agrees with humans)
  • Audit a sample of LLM-as-a-judge decisions for bias or systematic errors
  • Evaluate safety-critical outputs before deployment
Human eval is expensive to scale, so automate as much as possible and use human review strategically to validate your automated pipeline.

LLM-as-a-judge pattern

The core of the LLM-as-a-judge pattern is a prompt that gives the grading model all the context it needs: the original question, the reference answer (if available), and the model’s response. The grader returns a score and optionally a brief rationale.
from openai import OpenAI
client = OpenAI()

def evaluate_response(question, answer, reference):
    prompt = f"""Rate this answer on a scale of 1-5.
Question: {question}
Reference answer: {reference}
Model answer: {answer}

Score (1-5) and brief reasoning:"""
    
    result = client.chat.completions.create(
        model="gpt-4o",
        messages=[{"role": "user", "content": prompt}]
    )
    return result.choices[0].message.content

Design considerations

Prompt the grader carefully. The quality of LLM-as-a-judge depends entirely on the grading prompt. Be explicit about the rubric — what makes an answer a 5 versus a 3 versus a 1. Without clear criteria, the grader will be inconsistent. Use a rubric with defined levels. Vague instructions like “rate from 1-5” produce noisy scores. Define each level:
RUBRIC = """
Score 5: Correct, complete, and clearly written. Directly answers the question.
Score 4: Mostly correct with minor omissions or imprecise wording.
Score 3: Partially correct — captures the main idea but misses important detail.
Score 2: Marginally relevant but contains significant factual errors.
Score 1: Incorrect or entirely unrelated to the question.
"""
Validate against human judgments. Run your grader on a calibration set where you have human-assigned scores, and compute the agreement rate. If inter-rater agreement is low, refine your rubric. Use structured output to extract scores reliably. Parsing free-form text is fragile. Ask the model to respond in JSON:
import json

def evaluate_structured(question, answer, reference):
    prompt = f"""You are an evaluator. Score the model answer below.

Question: {question}
Reference: {reference}
Answer: {answer}

Respond with JSON only: {{"score": <1-5>, "reasoning": "<one sentence>"}}"""

    result = client.chat.completions.create(
        model="gpt-4o",
        messages=[{"role": "user", "content": prompt}],
        response_format={"type": "json_object"}
    )
    return json.loads(result.choices[0].message.content)

The Evals API

OpenAI’s hosted Evals API provides a structured way to run evaluations at scale without managing your own infrastructure. You define an eval — a dataset of prompts with expected outputs and a grader — and the API runs it, records results, and lets you compare runs over time.
1

Define your eval dataset

Create a JSONL file where each line is an evaluation example. Include the input prompt and the reference (correct) output.
{"prompt": "What is the capital of France?", "reference": "Paris"}
{"prompt": "What is 12 multiplied by 8?", "reference": "96"}
2

Create and run the eval

Use the API to create an eval and a run. Specify the model to evaluate and the grader type.
from openai import OpenAI
client = OpenAI()

# Create an eval
eval_obj = client.evals.create(
    name="My QA eval",
    data_source_config={
        "type": "stored_completions",
        "metadata": {"usecase": "qa"}
    },
    testing_criteria=[
        {
            "type": "string_check",
            "name": "exact_match",
            "input": "{{item.prompt}}",
            "reference": "{{item.reference}}",
            "operation": "eq"
        }
    ]
)

print(eval_obj.id)
3

Review results

Results are available in the OpenAI dashboard under the Evals section. You can compare runs side by side to track performance changes across model versions or prompt variants.

Hallucination detection

Hallucinations — outputs that are fluent and confident but factually wrong — are one of the most important failure modes to detect and guard against. The standard approach is to build a grader that checks whether every factual claim in the model’s response is supported by a provided context.
1

Identify the criteria

Define what counts as a hallucination for your use case. Common criteria:
  • The response contains a claim not found in the provided source documents
  • The response contradicts a known fact in the context
  • The response fabricates a citation, name, or statistic
HALLUCINATION_CRITERIA = [
    "The response contains factual claims not present in the provided context.",
    "The response contradicts information in the provided context.",
    "The response fabricates names, dates, or statistics not mentioned in the context.",
]
2

Build a hallucination grader

Use GPT-4o to check each criterion and return a binary verdict plus a list of hallucinated claims.
def detect_hallucination(context: str, response: str) -> dict:
    prompt = f"""You are a fact-checking assistant.

Context (source of truth):
{context}

Model response:
{response}

Does the model response contain any claims that are not supported by or contradict the context?
Respond with JSON: {{"hallucination_detected": true/false, "unsupported_claims": ["..."]}}"""

    result = client.chat.completions.create(
        model="gpt-4o",
        messages=[{"role": "user", "content": prompt}],
        response_format={"type": "json_object"}
    )
    return json.loads(result.choices[0].message.content)
3

Calibrate with a labeled test set

Build a small set of examples where you have manually labeled which responses are hallucinations. Measure the precision and recall of your grader to understand its reliability before deploying it as a guardrail.
from sklearn.metrics import precision_score, recall_score

y_true = [1, 0, 1, 1, 0]  # Human labels: 1=hallucination, 0=ok
y_pred = []

for context, response, label in zip(contexts, responses, y_true):
    result = detect_hallucination(context, response)
    y_pred.append(1 if result["hallucination_detected"] else 0)

print("Precision:", precision_score(y_true, y_pred))
print("Recall:", recall_score(y_true, y_pred))
LLM-based hallucination graders can themselves hallucinate. Validate your grader on a labeled set before treating its outputs as ground truth. Few-shot examples in the grading prompt significantly improve accuracy and consistency.

Building an eval pipeline

A mature eval pipeline runs automatically, stores results over time, and alerts you to regressions. The key components are:
  1. Dataset — a representative set of inputs covering common and edge-case scenarios
  2. Grader — one or more grading functions suited to your output type
  3. Runner — code that feeds each input to your model and records the output
  4. Storage — a persistent log of scores and outputs per run, keyed by model version and date
  5. Alerting — a threshold below which a regression is flagged for human review
Start small: even 50 well-chosen examples produce useful signal. Grow the dataset over time by adding cases where the model fails in production.

Next steps

Fine-tuning

Use your eval results to decide when to fine-tune, and then measure the improvement with the same eval suite.

Reinforcement fine-tuning

For tasks with verifiable outputs, learn how RFT uses reward signals — essentially grader functions — to train models that reason more reliably.

Build docs developers (and LLMs) love