AI Evaluation and Benchmarks
Build automated test suites, implement LLM-as-a-judge rubrics, and track accuracy benchmarks for AI pipelines.
TL;DR
- Construct quantitative regression evaluation datasets using typed
EvalSamplerecords. - Score qualitative output performance utilizing structured
LLM-as-a-judgerubrics. - Automate continuous evaluation passes inside GitHub Actions
CI/CDdeployment workflows.
LLM-as-a-Judge Rubrics
Structured Scoring Rubric PromptPrompt frontier model to evaluate answer quality on 1-5 scale.
function buildJudgePrompt(
query: string, answer: string, expected: string
) {
return 'Score answer against expected (1 to 5).\n' +
`Q: ${query}\nExp: ${expected}\nAns: ${answer}\n` +
'Return JSON: { score, reasoning }';
}Judge Score Parser & Zod ValidatorValidate that judge model outputs typed numeric score.
import { z } from 'zod';
const JudgeSchema = z.object({
score: z.number().min(1).max(5),
reasoning: z.string().min(10),
});
const json = JSON.parse(judgeOutput);
const result = JudgeSchema.parse(json);Pairwise Comparison with Bias SwapEvaluate two model candidates swapping order to negate bias.
async function comparePairwise(
q: string, a1: string, a2: string
) {
const [s1, s2] = await Promise.all([
judgePair(q, a1, a2),
judgePair(q, a2, a1),
]);
const pickA = s1.winner === 'A' && s2.winner === 'B';
return pickA ? a1 : a2;
}Deterministic Evaluation Metrics
Exact Match & Normalized EquivalenceFast zero-cost accuracy check for factual outputs.
function exactMatch(actual: string, expected: string) {
const norm = (s: string) => {
return s.trim().toLowerCase().replace(/\s+/g, ' ');
};
return norm(actual) === norm(expected);
}Token F1 Score CalculationCalculate precision, recall, and harmonic F1 across tokens.
function tokenF1(actual: string, expected: string) {
const a = actual.toLowerCase().split(/\s+/);
const e = expected.toLowerCase().split(/\s+/);
const aTokens = new Set(a);
const eTokens = new Set(e);
const list = [...aTokens];
const match = (t: string) => eTokens.has(t);
const overlap = list.filter(match).length;
if (overlap === 0) return 0;
const p = overlap / aTokens.size;
const r = overlap / eTokens.size;
return (2 * p * r) / (p + r);
}Regex & Schema AssertionsAssert output satisfies expected format constraints.
function assertFormat(val: string, re: RegExp) {
if (!re.test(val)) {
throw new Error(`Failed pattern: ${re}`);
}
}Golden Dataset & Test Suite Runner
Eval Sample Interface ContractStructure test case records with inputs and expectations.
type EvalSample = {
id: string;
query: string;
expected: string;
category: 'rag' | 'math' | 'formatting';
minScore: number;
};Continuous Evaluation Suite RunnerIterate across golden test dataset and aggregate metrics.
async function runTestSuite(
samples: EvalSample[],
pipelineFn: (q: string) => Promise<string>
) {
const results = [];
for (const s of samples) {
const answer = await pipelineFn(s.query);
const pass = exactMatch(answer, s.expected);
results.push({ id: s.id, pass });
}
const passed = results.filter(r => r.pass).length;
const passRate = passed / samples.length;
return { passRate, results };
}CI/CD Regression Threshold CheckFail pull request build if test pass rate regresses.
const MIN_PASS_RATE = 0.95;
if (report.passRate < MIN_PASS_RATE) {
const r = report.passRate;
console.error(`Rate ${r} < ${MIN_PASS_RATE}`);
process.exit(1);
}Synthetic Dataset Generation
Q&A Pair Generation from DocumentsGenerate synthetic test queries from knowledge documents.
async function makeQA(doc: string, client: any) {
const p = 'Read document and output 3 queries ' +
'and answers as JSON:\n' + doc;
const res = await client.chat.completions.create({
model: 'gpt-4o',
messages: [{ role: 'user', content: p }],
response_format: { type: 'json_object' },
});
return JSON.parse(res.choices[0].message.content!);
}Adversarial Edge Case GeneratorSynthesize edge cases to test system resilience.
const edgePrompt = 'Generate 5 tricky edge cases ' +
'testing ambiguous dates and null fields.';Dataset Deduplication & CleaningRemove duplicate synthetic queries using embedding distance.
function dedupeSamples(samples: EvalSample[]) {
const seen = new Set<string>();
return samples.filter(s => {
const k = s.query.trim().toLowerCase();
if (seen.has(k)) return false;
seen.add(k);
return true;
});
}Tips
- Swap candidate answer positions during pairwise
llmJudgescoring passes to eliminate model order-bias preferences completely across comparative runs. - Measure fast deterministic metrics like exact match, regex assertions, and
zodSchema validity before executing expensive LLM judge evaluations.
Warnings
- Never ship prompt modifications or model migrations to production without verifying pass rates across your reference
golden-dataset. - Avoid using weak lightweight models as judges because grading nuanced reasoning requires frontier-class
gpt-4oor Claude evaluation.
In Practice
Executes an answer generation test and evaluates response quality using GPT-4o as an automated judge.
- Define test case query and expected reference truth.
- Invoke application candidate pipeline to produce answer.
- Dispatch judge prompt to GPT-4o with structured scoring rubric.
- Validate numeric score meets threshold and log reasoning.
import OpenAI from 'openai';
const client = new OpenAI();
async function evalAns(
q: string, act: string, exp: string
) {
const p = `Q:${q}\nExp:${exp}\nAct:${act}\n` +
'Score 1-5. Return JSON: { score }';
const res = await client.chat.completions.create({
model: 'gpt-4o-mini',
messages: [{ role: 'user', content: p }],
response_format: { type: 'json_object' },
});
return JSON.parse(res.choices[0].message.content!);
}
const r = await evalAns('Port?', '8080', 'Port 8080');
console.log(r.score, r.reason);FAQ
LLM-as-a-judge is an evaluation pattern where a capable foundation model grades candidate responses against a detailed scoring rubric, returning quantitative scores and reasoning.
Judge models tend to prefer the first answer presented. Mitigate this by evaluating every pair twice with reversed order and averaging the resulting scores.
A golden dataset contains 50-200 curated, representative customer queries, their expected reference outputs, and edge cases representing previous production bugs.