Back to course
Module 2 · Day 11

Build the Eval Harness, End to End

Day 8-এ হাতে-grade করা ১০টা case আজ code দিয়ে check হবে। ৫টা file — tests.csv, prompt template, Python runner, assertions, results — মিলিয়ে একটা সম্পূর্ণ eval harness বানাও, আর শেখো কখন assertion যথেষ্ট, কখন judge model লাগবে।

~300 min read6 learning objectives
What you'll learn
  • Eval harness মানে কী, এবং কেন এটাই AI engineer-এর daily contract — সেটা বলতে পারা।
  • 5-file architecture চিনতে পারা: tests.csv + prompt.txt + runner.py + assertions.py + results.csv।
  • Assertion type-এর cost-ordered taxonomy — exact_match → contains → regex → llm_rubric।
  • Day 8-এর hand-built 10 cases → CSV format-এ migrate করা।
  • Python runner-এর 5 phases বুঝতে পারা: load → loop → call → score → save।
  • LLM-as-judge কখন needed, কখন not — এবং কেন cheap judge model ঠিক।
Before this
  • Day 8 শেষ — `localStorage.day8.testCases` + `day8.prompt`-এ artifact saved।
  • Day 4-এর assertion taxonomy + Day 9-এর JSON schema concept fresh।
  • Python basics — list, dict, function। কোনো execution লাগবে না, কিন্তু code পড়তে পারা লাগবে।
1

The Big Picture

চোখের জায়গায় code বসানো — ৫টা file, একটা folder, কোনো framework ছাড়া।

Day 8-এ তুমি ১০টা case নিজের চোখে দেখে pass/fail বলেছ। এক run-এর জন্য এটা ঠিক আছে, কিন্তু দশ বার prompt change করলে? আজ চোখের জায়গায় code বসাচ্ছি — `python runner.py` চালালেই test cases load হবে, model call হবে, প্রতিটা output-এ একটা assertion apply হবে, আর একটা results file-এ final pass rate লেখা থাকবে। সবকিছুই text file, git-এ থাকে। প্রতিটা prompt change-এর পর re-run করাটা তখন থেকে একদম free।

Eval harnessthe 5-file pipeline

tests.csv + prompt.txt + runner.py + assertions.py + results.csv — সব text file, git-এ। Framework নয়। pytest-এর mental model: test cases + runner + assertions। শুধু prompt-কে target করে, code-কে না। 5 hours-এ wire করলে পরের ৫০ দিন প্রতিটা prompt change-এ free running।

Why it matters

এই suite Day 12-এ git history-এর সাথে integrate হবে — প্রতিটা commit-এ pass rate plot হবে। পরের module-এ chain-of-thought যোগ করলে সেটার measurement-ও এই harness দিয়েই হবে। Module 2-এর শেষে তোমার eval suite-ই portfolio-এর spine।

নিচে ৫টা file-এর প্রতিটায় tap করে দেখো — কোনটা কী কাজ করে, ভেতরে কেমন দেখতে।

CSV test caseshuman-editable, git-diff-able

Test cases CSV-এ live করে, JSON-এ না। কেন: (১) Excel/Numbers-এ open হয় — stakeholders দেখতে পারো। (২) git diff পরিষ্কার — কোন row added/changed চোখে পড়ে। (৩) Pandas-এ pd.read_csv এক line-এ। Columns standard: id, input, expected, assertion_type, category।

Why it matters

Day 8-এর localStorage-এ `day8.testCases` JSON shape-এ ছিল। আজ CSV-এ migrate — Excel-এ open করতে, git-এ track করতে, team-এর সাথে share করতে।

Day 8-এ তোমার ১০টা test case browser-এর localStorage-এ save হয়েছিল। নিচে সেগুলোই CSV-এ বদলে যাচ্ছে — real download button-সহ।

2

Core Concepts

কোন assertion কখন, runner-এর ৫টা phase, আর কেন cheap judge model normal।

Assertion typescost-ordered, cheapest first

৪টা assertion type, cost ascending: exact_match (free, instant), contains (free, instant), regex (free, instant), llm_rubric (paid, ~2s/test, ~$0.001/test)। Rule of thumb: cheapest assertion যেটা actual failure mode catch করে।

Why it matters

প্রতিটা test case-এ ভুল assertion মানে wasted money বা missed bugs। Classification-এ exact_match যথেষ্ট; summarization-এ llm_rubric অবধারিত। সঠিক assertion বাছা AI engineer-এর daily skill।

নিচে ৬টা test case দেওয়া আছে — প্রতিটার জন্য সবচেয়ে সস্তা assertion বেছে নাও যেটা কাজ করে। Pick করলে কেন সেটাই best, দেখবে।

Runner architectureload → loop → call → score → save

runner.py-এর spine: (১) CSV + prompt load, (২) ThreadPoolExecutor দিয়ে tests loop, (৩) API call (prompt template-এ `{input}` substitution), (৪) assertion-based score, (৫) results.csv write + pass rate print। ৩০ lines-এ ship-ready।

Why it matters

Concurrency-এর গুরুত্ব বোঝা জরুরি — 30 tests serial-এ 90s, threaded 5-workers-এ 18s। Iteration speed = eval speed। Slow eval মানে ineffective eval।

নিচে runner.py-এর ৫টা phase step-by-step দেখো — প্রতিটায় কী কোড চলছে, আর কেন।

python
import csv, anthropic
from concurrent.futures import ThreadPoolExecutor

client = anthropic.Anthropic()
prompt_template = open("prompts/v1.txt").read()

def run_case(row):
    msg = client.messages.create(
        model="claude-haiku-4-5-20251001",
        max_tokens=20,
        messages=[{
            "role": "user",
            "content": prompt_template.format(input=row["input"])
        }],
    )
    actual = msg.content[0].text.strip()
    passed = score(actual, row["expected"], row["assertion_type"])
    return {**row, "actual": actual, "pass": passed}

with open("tests.csv") as f:
    tests = list(csv.DictReader(f))

with ThreadPoolExecutor(max_workers=5) as ex:
    results = list(ex.map(run_case, tests))

with open("results.csv", "w") as f:
    csv.DictWriter(f, fieldnames=results[0].keys()).writeheader()
    csv.DictWriter(f, fieldnames=results[0].keys()).writerows(results)

pass_rate = sum(r["pass"] for r in results) / len(results)
print(f"Pass rate: {pass_rate:.0%}")
python
import re, anthropic

judge = anthropic.Anthropic()

def score(actual: str, expected: str, kind: str) -> bool:
    a = actual.strip().lower()
    e = expected.strip().lower()
    if kind == "exact_match":
        return a == e
    if kind == "contains":
        return e in a
    if kind == "regex":
        return bool(re.search(expected, actual))
    if kind == "llm_rubric":
        return llm_judge(actual, expected) >= 4
    raise ValueError(f"Unknown assertion: {kind}")

def llm_judge(actual: str, rubric: str) -> int:
    msg = judge.messages.create(
        model="claude-haiku-4-5-20251001",
        max_tokens=20,
        messages=[{"role": "user", "content": f"""
On a 1-5 scale, how well does the OUTPUT match the RUBRIC?
RUBRIC: {rubric}
OUTPUT: {actual}
Reply with the single digit only."""}],
    )
    try:
        return int(msg.content[0].text.strip()[0])
    except (ValueError, IndexError):
        return 0

LLM-as-judgerubric + cheap judge model

Open-ended output (summary, draft, Q&A) — exact_match impossible। Solution: আরেকটা model judge হিসেবে। Pattern: rubric prompt (criteria) → judge model → 1-5 score → pass threshold (usually ≥4)। Judge model cheap — Haiku দিয়ে Opus-এর output grade করা normal এবং desired।

Why it matters

পরের module-গুলোতে (agent design, continuous eval) judge model pattern আবার লাগবে। আজ pattern শিখলে পুরো course জুড়ে apply হবে।

Summary বা open-ended output-এ exact_match কাজ করে না। নিচে একই input-এর তিনটা ভিন্ন output select করে দেখো — judge model কেন কোন score দিচ্ছে।

Judge biaseslength, position, self-preference

Judge model-এর ৩টা known bias: (১) Length bias — longer আউটপুট-কে better মনে করা, (২) Positional bias — A/B comparison-এ প্রথম option prefer করা, (৩) Self-preference — একই model নিজের output judge করলে বেশি score দেওয়া। Mitigations: (a) rubric-এ explicit anchor, (b) order shuffle, (c) ground truth hide করা।

Why it matters

পরের module-গুলোতে judge model-এর details নিয়ে আরও deep dive হবে। আজ শুধু awareness — naive judging trap-এ পড়বে না।

3

Try It Yourself

Golden set discipline, regression-এর মানে, আর আজকের কাজ।

Golden setlocked, versioned, curated

৩০-case suite (Day 8-এ ১০টা ship করেছ, Day 11-12 জুড়ে ২০টা যোগ করবে)। Locked মানে — frequent edits না। Curated মানে — human reviewed। Versioned মানে — git-এ tests.csv একটা file, commit হয়।

Why it matters

Golden set drift মানে silent failure। কেউ test "fix" করে দিলে baseline meaningless হয়ে যায়। PR review-এ tests.csv-এর change আলাদা approval require করে। Production-এ এই discipline standard।

Regressionpass rate drop = block merge

Previous commit-এ pass rate 78%, current commit-এ 73% = regression। PR block করো যতক্ষণ না investigate হয়। Day 12-এ git-এর সাথে integrate করব — CI/CD plumbing-এর core। Pass rate-এর absolute value নয়, trajectory matters করে।

Why it matters

Day 12 পুরোটাই এই topic নিয়ে — versioning + trajectory plotting। আজ vocabulary lock করো। পরের কোনো module-এ production-এ এই regression detection automate করব।

Tip

আজ তিনটা জিনিস করো — (১) Day 8-এর ১০টা test case CSV-এ migrate করো (উপরের widget-এর download button দিয়ে); (২) নিজের project-এর জন্য runner.py + assertions.py লেখা শুরু করো (উপরের code reference থেকে copy করে বসাতে পারো); (৩) তোমার task যদি classification না হয়ে summarization বা drafting-জাতীয় হয়, নিজের output-এর জন্য একটা judge rubric লেখো।

4

Check Your Understanding

ভুল হলে সমস্যা নেই — প্রতিটার ব্যাখ্যা আছে।

Misconception check

Eval harness build করতে dedicated framework (DeepEval / Braintrust / Promptfoo) লাগবে

Misconception check

LLM-as-judge expensive — শুধু exact_match-এ stick করব

Misconception check

Judge model হিসেবে strongest model use করতে হবে যেহেতু grading critical

Misconception check

Pass rate 60-70% baseline-এ থাকলে prompt fundamentally broken

Misconception check

Tests.csv git-এ track করতে হবে না — সেটা just dev convenience

Misconception check

ThreadPoolExecutor optional — sequential loop ১০টা test-এর জন্য fine

Quick check

Eval harness কয়টা file minimum লাগে?

ভুল হলেও সমস্যা নেই — প্রতিটা option-এর সাথে ব্যাখ্যা আছে।

Quick check

Classification test ("output must be exactly 'positive'") — সবচেয়ে সস্তা assertion কোনটা?

ভুল হলেও সমস্যা নেই — প্রতিটা option-এর সাথে ব্যাখ্যা আছে।

Quick check

Open-ended summary test — কোন assertion?

ভুল হলেও সমস্যা নেই — প্রতিটা option-এর সাথে ব্যাখ্যা আছে।

Quick check

Judge model হিসেবে Haiku ব্যবহার করা — Opus-এর prompt grade করার জন্য — এটা কেমন idea?

ভুল হলেও সমস্যা নেই — প্রতিটা option-এর সাথে ব্যাখ্যা আছে।

Quick check

৩০টা test serial-এ 90s নেয়। ThreadPoolExecutor(max_workers=5) দিয়ে কত সময় লাগবে?

ভুল হলেও সমস্যা নেই — প্রতিটা option-এর সাথে ব্যাখ্যা আছে।

Quick check

Pass rate v1: 65%, v2: 78%, v3: 73% — v3-এর PR নিয়ে কী করবে?

ভুল হলেও সমস্যা নেই — প্রতিটা option-এর সাথে ব্যাখ্যা আছে।

5

Prove It To Yourself

চারটা takeaway মনে রাখো। নিজের ভাষায় লেখো। Day 11 শেষ।

Note

চারটা জিনিস মনে রাখো — ৫টা file, framework না: tests.csv + prompt.txt + runner.py + assertions.py + results.csv, framework optional। Cheapest assertion যেটা কাজ করে: exact_match → contains → regex → llm_rubric, cost ascending order-এ। Cheap judge model normal: Haiku দিয়ে Opus grade করা actively desired, bias mitigation = rubric design। Concurrency অথবা কষ্ট: ThreadPoolExecutor 90s-কে 18s করে দেয়, iteration speed = eval speed।

আজকের শব্দগুলো — eval harness, assertion type, exact_match, contains, regex, llm_rubric, runner, judge model, pass rate, regression, golden set — এখন থেকে English-ই থাকবে; team-এর PR review-এ, tool পছন্দ করার সময় এভাবেই দেখবে। পুরো তালিকা নিচের glossary-তে।

Glossary

শব্দার্থ — পরে ফিরে দেখার জন্য

একনজরে সব key term। কোনো শব্দ ভুলে গেলে এখানে এসে খুঁজে নিও।

Assertion type
পদ্ধতি যা expected vs actual compare করে। 4টা cost-ordered type।
Concurrent workers
ThreadPoolExecutor-এর parameter — কতগুলো API call simultaneously চলবে।
contains
Expected substring actual-এ আছে কিনা — extraction test-এর জন্য।
CSV
Comma-separated values — human-editable, git-diff-able, Excel-openable format।
DictReader
Python csv module-এর class — প্রতিটা row dict হিসেবে read হয়।
Eval harness
5-file pipeline: tests + prompt + runner + assertions + results।
exact_match
Identical string check (trim/lowercase করে)। সবচেয়ে সস্তা assertion।
Expected output
Test case-এ defined correct answer। Ground truth।
Golden set
Locked, versioned, curated test suite। ৩০ cases target।
Judge biases
Length bias, positional bias, self-preference — judge model-এর known issue।
Length bias
Judge model longer output-কে better মনে করে।
llm_rubric / llm_judge
আরেকটা model judge হিসেবে — open-ended output grade করতে।
max_tokens
API parameter — output cap। Classification-এ 20 যথেষ্ট।
Pass rate
% test cases passed। AI engineer-এর daily contract metric।
Positional bias
A/B comparison-এ judge প্রথম option prefer করে।
Prompt template
Prompt file with `{input}` placeholder — runner প্রতি-test substitute করে।
Regex
Pattern matching — date/email/phone format check-এর জন্য।
Regression
কোনো change-এ pass rate drop। Investigate না করা পর্যন্ত PR block।
Results table
results.csv — per-case pass/fail + actual output + pass rate।
Rubric
Judge model-কে কী criteria দিয়ে grade করতে বলা — explicit anchor।
Runner
runner.py — eval suite execute করার orchestrator script।
Self-preference bias
একই model নিজের output judge করলে score বেশি দেয়।
ThreadPoolExecutor
Python concurrent.futures class — parallel API call।
Trajectory
Pass rate over time/commit। Day 12-এ plot করব।
References

Sources consulted to author this lesson. Citation style is informal — follow the links if you want to dig deeper.

Exit check

দুই বাক্যে নিজের ভাষায় লেখো

নিজের ভাষায় দুই বাক্যে লেখো — তোমার ৫-file harness-এর প্রতিটা file কী কাজ করে, আর তোমার task classification নাকি open-ended সেটার ভিত্তিতে কোন assertion type ব্যবহার করবে।

0 chars
Back to course