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 লাগবে।
- •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 ঠিক।
- •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 পড়তে পারা লাগবে।
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।
এই 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।
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-সহ।
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 করে।
প্রতিটা 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।
Concurrency-এর গুরুত্ব বোঝা জরুরি — 30 tests serial-এ 90s, threaded 5-workers-এ 18s। Iteration speed = eval speed। Slow eval মানে ineffective eval।
নিচে runner.py-এর ৫টা phase step-by-step দেখো — প্রতিটায় কী কোড চলছে, আর কেন।
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%}")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 0LLM-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।
পরের 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 করা।
পরের module-গুলোতে judge model-এর details নিয়ে আরও deep dive হবে। আজ শুধু awareness — naive judging trap-এ পড়বে না।
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 হয়।
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 করে।
Day 12 পুরোটাই এই topic নিয়ে — versioning + trajectory plotting। আজ vocabulary lock করো। পরের কোনো module-এ production-এ এই regression detection automate করব।
আজ তিনটা জিনিস করো — (১) Day 8-এর ১০টা test case CSV-এ migrate করো (উপরের widget-এর download button দিয়ে); (২) নিজের project-এর জন্য runner.py + assertions.py লেখা শুরু করো (উপরের code reference থেকে copy করে বসাতে পারো); (৩) তোমার task যদি classification না হয়ে summarization বা drafting-জাতীয় হয়, নিজের output-এর জন্য একটা judge rubric লেখো।
Check Your Understanding
ভুল হলে সমস্যা নেই — প্রতিটার ব্যাখ্যা আছে।
Eval harness build করতে dedicated framework (DeepEval / Braintrust / Promptfoo) লাগবে
LLM-as-judge expensive — শুধু exact_match-এ stick করব
Judge model হিসেবে strongest model use করতে হবে যেহেতু grading critical
Pass rate 60-70% baseline-এ থাকলে prompt fundamentally broken
Tests.csv git-এ track করতে হবে না — সেটা just dev convenience
ThreadPoolExecutor optional — sequential loop ১০টা test-এর জন্য fine
Eval harness কয়টা file minimum লাগে?
ভুল হলেও সমস্যা নেই — প্রতিটা option-এর সাথে ব্যাখ্যা আছে।
Classification test ("output must be exactly 'positive'") — সবচেয়ে সস্তা assertion কোনটা?
ভুল হলেও সমস্যা নেই — প্রতিটা option-এর সাথে ব্যাখ্যা আছে।
Open-ended summary test — কোন assertion?
ভুল হলেও সমস্যা নেই — প্রতিটা option-এর সাথে ব্যাখ্যা আছে।
Judge model হিসেবে Haiku ব্যবহার করা — Opus-এর prompt grade করার জন্য — এটা কেমন idea?
ভুল হলেও সমস্যা নেই — প্রতিটা option-এর সাথে ব্যাখ্যা আছে।
৩০টা test serial-এ 90s নেয়। ThreadPoolExecutor(max_workers=5) দিয়ে কত সময় লাগবে?
ভুল হলেও সমস্যা নেই — প্রতিটা option-এর সাথে ব্যাখ্যা আছে।
Pass rate v1: 65%, v2: 78%, v3: 73% — v3-এর PR নিয়ে কী করবে?
ভুল হলেও সমস্যা নেই — প্রতিটা option-এর সাথে ব্যাখ্যা আছে।
Prove It To Yourself
চারটা takeaway মনে রাখো। নিজের ভাষায় লেখো। Day 11 শেষ।
চারটা জিনিস মনে রাখো — ৫টা 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-তে।
শব্দার্থ — পরে ফিরে দেখার জন্য
একনজরে সব 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 করব।
Sources consulted to author this lesson. Citation style is informal — follow the links if you want to dig deeper.
- Develop test cases and the evaluation pipeline — Anthropic (2025)
- Using LLMs as evaluators — Anthropic (2025)
- Anthropic cookbook — Building Evals notebook — Anthropic (2024)
- Promptfoo — Expected outputs (assertions) — Promptfoo (2024)
- Your AI Product Needs Evals — Hamel Husain (2024)
- Patterns for Building LLM-based Systems — Eugene Yan (2024)
দুই বাক্যে নিজের ভাষায় লেখো
নিজের ভাষায় দুই বাক্যে লেখো — তোমার ৫-file harness-এর প্রতিটা file কী কাজ করে, আর তোমার task classification নাকি open-ended সেটার ভিত্তিতে কোন assertion type ব্যবহার করবে।