Back to Blog

LLM Evaluation Fundamentals: Golden Datasets and Metrics

7 min read

Traditional software development is guided by deterministic tests. If you write an billing algorithm, you feed it structured inputs, assert the output matches an exact expected dollar amount, and run the test in CI/CD.

Large Language Models (LLMs) break this paradigm. They are non-deterministic, highly sensitive to prompt formatting, and output free-form natural language. An assertion like assert response == "Hello, World!" is guaranteed to fail in production as the model updates or outputs subtle variations (e.g., "Hello! World").

To deploy LLM applications safely, engineers must transition to probabilistic testing. This requires building curated evaluation suites composed of Golden Datasets and multi-tiered scoring metrics. This article breaks down these core fundamentals.


1. What is a Golden Dataset?

A Golden Dataset is a representative, high-quality, human-curated collection of inputs and expected outputs that acts as the source of truth for your AI system. It is the benchmark against which every prompt rewrite, model upgrade, or system configuration is measured.

A robust Golden Dataset contains three key elements for each test case:

  1. The Input Prompt: The raw query or system message sent to the model.
  2. Context (Optional): Reference materials (such as document snippets in RAG or transactional data from databases) that the model should use.
  3. The Ground Truth (Target Output): A high-quality, human-verified reference response representing the ideal output.

How to Curate a Golden Dataset

  • Start Small: 50 to 100 high-value test cases are sufficient for initial testing.
  • Diversify Scenarios: Include standard queries, common edge cases, negative/adversarial inputs (jailbreak attempts), and queries containing technical or domain-specific terminology.
  • Log Production Queries: Periodically review actual user chat logs, clean them of PII, and add representative queries to the golden set.

2. The Hierarchy of Evaluation Metrics

Evaluating natural language outputs requires a multi-layered approach, ranging from fast, low-cost heuristic checks to expensive semantic judgements.

LLM Evaluation Fundamentals: LLM evaluation framework flowchart showing golden datasets, rule checks, and LLM-as-a-judge flowLLM Evaluation Fundamentals: LLM evaluation framework flowchart showing golden datasets, rule checks, and LLM-as-a-judge flow

Layer 1: Rule-Based Heuristics (Low Cost, High Speed)

These are simple checks implemented via standard Python code:

  • Structural Integrity: If the prompt demands JSON, does the response parse successfully?
  • Containment Checks: Does the output contain forbidden words (e.g., error codes, API keys) or required disclaimers?
  • Length Boundaries: Ensure output fits within expected token sizes.

Layer 2: Statistical Semantic Matching (Medium Cost)

Traditional NLP metrics measure overlap and similarity between the output and ground truth:

  • ROUGE / BLEU: Measures N-gram overlap. Useful for summarization but penalizes valid answers that use synonyms.
  • Embedding Similarity: Encodes both the output and ground truth with an embedding model and computes the Cosine similarity. This evaluates semantic alignment, ignoring structural variations.

Layer 3: LLM-as-a-Judge (High Cost, High Accuracy)

For complex reasoning, tone, and correctness, a powerful model (e.g., GPT-4) acts as the evaluator. The judge is provided with the input query, the generated response, the ground truth, and a strict rubric (e.g., rating from 1 to 5 for helpfulness, tone alignment, and factual correctness).


3. Python Implementation: An Evaluation Framework

The following script implements a lightweight, complete evaluation runner. It processes a small Golden Dataset, executes rule-based validation, computes semantic embedding scores, and runs a structured judge mock to compile a test report.

import json
import re
from typing import List, Dict, Any
from sentence_transformers import SentenceTransformer, util

# Initialize embedding model for semantic similarity check
similarity_model = SentenceTransformer("all-MiniLM-L6-v2")

# 1. Define our Golden Dataset
golden_dataset: List[Dict[str, Any]] = [
    {
        "id": "TC_001",
        "input": "Generate a valid JSON object representing a user profile with fields: username and age.",
        "ground_truth": '{"username": "johndoe", "age": 30}',
        "checks": ["json_format", "similarity"]
    },
    {
        "id": "TC_002",
        "input": "Explain the concept of inheritance in object-oriented programming in one sentence.",
        "ground_truth": "Inheritance allows a new child class to adopt the attributes and methods of an existing parent class.",
        "checks": ["length", "similarity"]
    }
]

class LLMEvaluator:
    def run_tests(self, model_outputs: Dict[str, str]) -> List[Dict[str, Any]]:
        results = []
        for test in golden_dataset:
            test_id = test["id"]
            output = model_outputs.get(test_id, "")
            ground_truth = test["ground_truth"]
            
            report = {
                "test_id": test_id,
                "input": test["input"],
                "generated": output,
                "passed_all": True,
                "metrics": {}
            }
            
            # Executing specified checks
            for check in test["checks"]:
                if check == "json_format":
                    try:
                        json.loads(output)
                        report["metrics"]["json_valid"] = True
                    except json.JSONDecodeError:
                        report["metrics"]["json_valid"] = False
                        report["passed_all"] = False
                        
                elif check == "length":
                    # One sentence check: count period marks or check length
                    sentence_count = len(re.split(r'[.!?]+', output.strip())) - 1
                    is_short = sentence_count <= 2
                    report["metrics"]["one_sentence_check"] = is_short
                    if not is_short:
                        report["passed_all"] = False
                        
                elif check == "similarity":
                    # Compute semantic embedding cosine similarity
                    emb1 = similarity_model.encode(output, convert_to_tensor=True)
                    emb2 = similarity_model.encode(ground_truth, convert_to_tensor=True)
                    score = float(util.cos_sim(emb1, emb2)[0][0].item())
                    
                    report["metrics"]["semantic_similarity"] = score
                    if score < 0.70:  # Threshold for pass/fail
                        report["passed_all"] = False
                        
            results.append(report)
        return results

# Execution
if __name__ == "__main__":
    # Mock output generated by the LLM system we are testing
    mock_llm_outputs = {
        "TC_001": '{"username": "alice", "age": 25}',  # Valid JSON, different values
        "TC_002": "Inheritance is an OOP mechanism where a subclass derives characteristics from a superclass." # Semantic synonym
    }
    
    evaluator = LLMEvaluator()
    evaluation_report = evaluator.run_tests(mock_llm_outputs)
    
    print("\n--- LLM System Evaluation Report ---")
    print(json.dumps(evaluation_report, indent=2))

4. Continuous Integration for LLM Prompts

Integrating this process into developer workflows ensures high quality:

  • Version Control Prompts: Keep prompts in separate text/YAML files within your codebase, versioned under Git.
  • Run Regressions: Run your golden evaluation suite inside your CI/CD pipeline (e.g., GitHub Actions) on every Pull Request.
  • Fail the Build: Set soft thresholds. If the mean semantic similarity score drops below 0.80 or if formatting fails on any core test cases, reject the pull request to prevent regressions in production behavior.