অধ্যায় 6 · অনুশীলন ও ক্যাপস্টোন
প্র্যাকটিস ল্যাব: টয় ডেটা ও Python দিয়ে একটা সীমিত AI ফিচারের মূল্যায়ন
- পৃষ্ঠা 21 / 22
- 27 মিনিট পড়া
প্রথম কুড়িটা পাতা একটা অভ্যাস শিখিয়েছে: কী যাচাই করেছেন তা লিখে রাখুন, কী করেননি তা বলুন, আর ফাঁক লুকানোর বদলে প্রোগ্রামকে তা জানাতে দিন। এই ল্যাবে পাঁচটা নম্বরযুক্ত চ্যালেঞ্জ (ঝুঁকির রেজিস্টার, টয় ন্যায্যতার টেবিল, সিস্টেম কার্ড, বৃত্তি-বাছাই ফিচারের পর্যালোচনা ও একটা সিমুলেটেড ঘটনা) আর আটটা ইন্টারভিউ-ধাঁচের প্রশ্ন আছে।
আপনার দরকার Python 3.12 এবং শুধু তার স্ট্যান্ডার্ড লাইব্রেরি। ব্লকগুলো ক্রমে একটা সেশনে লিখুন; এগুলো ভেরিয়েবল ভাগ করে নেয়, আর প্রতিটা ব্লকের নিচের লেখাটা তার আসল আউটপুট।
সততার নিয়ম। প্রতিটা প্রতিষ্ঠান, মানুষ, গ্রুপ ও সংখ্যা কাল্পনিক, আর প্রতিটা ডেটাসেটের আগে
টয় ডেটা:লেবেল আছে। ফল শুধু দেখায় একটা গণনা বা পরীক্ষা কীভাবে কাজ করে। বাস্তব মানুষ বা বাস্তব সিস্টেম নিয়ে এটা কিছুই বলে না। তথ্য না থাকলে স্ক্রিপ্ট MISSING, UNKNOWN বা NOT TESTED লেখে, কিছু বানায় না। এখানে কিছুই আইনি পরামর্শ নয়।
যা শিখবেন
- অ্যাডভান্সড পাতাগুলোকে পাঁচটা ছোট, যাচাইযোগ্য কাজে কীভাবে রূপ দেবেন।
- পাস চেকলিস্ট দিয়ে নিজেকে সৎভাবে কীভাবে নম্বর দেবেন।
- AI-সহায়ক, শুধু মানুষের পর্যালোচনা ও AI-ছাড়া বিকল্প তুলনা করে গো/নো-গো সুপারিশ কীভাবে লিখবেন।
- আটটা সাধারণ ইন্টারভিউ-ধাঁচের প্রশ্নের উত্তর কয়েক বাক্যে কীভাবে দেবেন।
ল্যাবে নম্বর কীভাবে দেওয়া হয়
একটা চ্যালেঞ্জে আপনার নম্বর হলো পাস চেকলিস্টের যতগুলো দফা সততার সঙ্গে টিক দিতে পারেন তার অনুপাতকে ওই স্তরের নম্বর দিয়ে গুণ করলে যা হয়, তা পূর্ণসংখ্যায় ঘুরিয়ে।
| স্তর | চ্যালেঞ্জ | প্রতিটার নম্বর | সর্বোচ্চ |
|---|---|---|---|
| সহজ | E1 | ১০ | ১০ |
| মাঝারি | M1, M2 | ২০ | ৪০ |
| কঠিন | H1, H2 | ৩০ | ৬০ |
| মোট | ৫ | ১১০ |
০ থেকে ৫৪: যে চ্যালেঞ্জগুলোতে নম্বর হারিয়েছেন, "কেন এটা কাজ করে" অংশের পাতাগুলো আবার পড়ুন, তারপর পরিস্থিতি বদলে আবার করুন। ৫৫ থেকে ৮৭: মজবুত; যে দফায় টিক পড়েনি সেগুলো দেখায় কী অনুশীলন করতে হবে। ৮৮ থেকে ১১০: খুব ভালো; ক্যাপস্টোনের জন্য তৈরি। কোনো দফা গোনা হবে শুধু তখনই, যখন তা আপনার লিখিত উত্তরে বা চালানো কোডে আছে। ইন্টারভিউ-প্রশ্নে নম্বর নেই: আগে জোরে উত্তর দিন, তারপর মিলিয়ে দেখুন।
এই স্কোর একটা শেখার সহায়ক, সনদ নয়। এটা কাজে আপনার দক্ষতা মাপে না, আর কোনো মানুষ বা সিস্টেমকে প্রত্যয়ন করতে কারও এটা ব্যবহার করা উচিত নয়। যেখানে যুক্তিসঙ্গত মানুষদের মত আলাদা (বিশেষ করে H1), সেখানে চেকলিস্ট একটা মতকে নয়, যুক্তি, প্রমাণ ও শর্তকে নম্বর দেয়।
সমাধান পড়ার আগে নিজের উত্তর লিখুন, তারপর চেকলিস্টে টিক দিন। দুই থেকে চারজনের দলে কোথায় আপনারা আলাদা তা মিলিয়ে কারণ জিজ্ঞেস করুন; ভোট দেবেন না। প্রতিটা সমাধান একটা শক্তিশালী উত্তর, একমাত্র উত্তর নয়।
সহজ চ্যালেঞ্জ
E1: একটা রিস্ক রেজিস্টার বানান
কাজ। তীব্রতা (severity), সম্ভাবনা (likelihood), মালিক (owner) ও প্রশমন (mitigation) ফিল্ডসহ Python ডিকশনারির একটা তালিকা হিসেবে রিস্ক রেজিস্টার বানান। প্রয়োজনীয় ফিল্ড যাচাই করুন, নামধারী মালিক নেই এমন রেকর্ড চিহ্নিত করুন, পড়ার মতো একটা সারসংক্ষেপ ছাপুন, আর স্কোরকে নিরপেক্ষ সত্য হিসেবে দেখাবেন না।
টয় ডেটা: FeedbackHelper-এর ছয়টা কাল্পনিক ঝুঁকি; ফিচারটা শিক্ষার্থীদের রচনায় মন্তব্যের খসড়া লেখে। চারটা রেকর্ড ইচ্ছা করেই ত্রুটিপূর্ণ।
ইঙ্গিত। check(record) লিখুন, যা সমস্যার তালিকা ফেরত দেয় আর রেকর্ড কখনও বদলায় না। "the team" মানে মালিক নেই। তীব্রতা ও সম্ভাবনা আলাদা কলামে ছাপুন।
সমাধান। সমস্যাযুক্ত রেকর্ড তালিকায় দেখানো হয় ও সারসংক্ষেপের বাইরে থাকে; কিছুই মেরামত হয় না।
# Toy data: six invented risks for FeedbackHelper, a feature that drafts comments on student essays
RISKS = [
dict(id="R1", title="Comment misreads second-language writing", severity=4, likelihood=3,
owner="Head of English", mitigation="teacher reads every comment before release"),
dict(id="R2", title="Essay text and names sent to an outside service", severity=4, likelihood=2,
owner="", mitigation="remove names before sending"),
dict(id="R3", title="Suggested grade band believed without question", severity=3, likelihood=4,
owner="Head of English", mitigation="no grade bands in the draft"),
dict(id="R4", title="Parents not told AI is used", severity=2, likelihood=5,
owner="the team", mitigation="letter home before the pilot"),
dict(id="R5", title="Generic praise lowers trust in feedback", severity=2, likelihood=3,
owner="Deputy head", mitigation=""),
dict(id="R6", title="Appeal route unclear", severity=7, likelihood=3,
owner="Deputy head", mitigation="name a second reader"),
]
RISK_FIELDS = ("id", "title", "severity", "likelihood", "owner", "mitigation")
VAGUE_OWNERS = ("", "the team", "everyone", "tbd")
def check(r):
"""Report problems; never fill a gap."""
found = [f"{k} is missing" for k in RISK_FIELDS if k not in ("owner",) and not r.get(k)]
if r.get("owner", "").lower() in VAGUE_OWNERS:
found.append("owner is missing or not a named role")
for k in ("severity", "likelihood"):
if r.get(k) and not 1 <= r[k] <= 5:
found.append(f"{k} {r[k]} is outside 1-5")
return found
def summary(risks):
ok = sorted((r for r in risks if not check(r)), key=lambda r: (-r["severity"] * r["likelihood"], r["id"]))
lines = ["id sev lik pts owner title"]
lines += [f"{r['id']:<4}{r['severity']:>3}{r['likelihood']:>4}{r['severity'] * r['likelihood']:>5} "
f"{r['owner']:<20}{r['title']}" for r in ok]
return "\n".join(lines)
for r in RISKS:
for p in check(r):
print(f"{r['id']} problem: {p}")
print(f"\n{sum(not check(r) for r in RISKS)} of {len(RISKS)} records complete\n")
print(summary(RISKS))
print("\nNote: points = severity x likelihood is a method we chose, not a measurement.")R2 problem: owner is missing or not a named role
R4 problem: owner is missing or not a named role
R5 problem: mitigation is missing
R6 problem: severity 7 is outside 1-5
2 of 6 records complete
id sev lik pts owner title
R1 4 3 12 Head of English Comment misreads second-language writing
R3 3 4 12 Head of English Suggested grade band believed without question
Note: points = severity x likelihood is a method we chose, not a measurement.পাস চেকলিস্ট।
- শক্তিশালী: প্রয়োজনীয় ফিল্ড যাচাই হয়েছে; "the team" ও ফাঁকা মালিক চিহ্নিত; তীব্রতা ৭ সীমার বাইরে বলে জানানো; ত্রুটিপূর্ণ রেকর্ড স্কোর বা মেরামত হয়নি; তীব্রতা ও সম্ভাবনা দৃশ্যমান; আউটপুট বলছে পয়েন্ট একটা বেছে নেওয়া পদ্ধতি।
- দুর্বল: প্রোগ্রাম নিজে মালিক বা প্রশমন বসিয়ে দেয়; শুধু মোট দেখানো হয়; পয়েন্টের ক্রমকে কোন ঝুঁকি সবচেয়ে জরুরি তার সত্য হিসেবে দেখানো।
কেন এটা কাজ করে। এটা ইমপ্যাক্ট অ্যাসেসমেন্ট: একটা সীমিত AI ফিচারের পরিধি, স্টেকহোল্ডার ও ক্ষতি পাতার রেকর্ড ও চেকার, আর ঝুঁকির স্কোরিং, নিয়ন্ত্রণ ও সিদ্ধান্ত: একটা টয় রিস্ক রেজিস্টার বানানো পাতার মালিকের নিয়ম কাজে লাগায়: ফাঁক জানান, প্রমাণ বানাবেন না, একটা ভূমিকার নাম দিন।
মাঝারি চ্যালেঞ্জ
M1: টয় ডেটায় ন্যায্যতা মাপুন
কাজ। গ্রুপ লেবেল, ফলাফল ও পূর্বাভাসের একটা ছোট টেবিল থেকে প্রতিটা গ্রুপের সিলেকশন রেট, ফল্স-পজিটিভ রেট ও ফল্স-নেগেটিভ রেট হিসাব করুন। ছোট গ্রুপ চিহ্নিত করুন, আর লিখুন কেন এই ফল বাস্তব জগতের ন্যায্যতা প্রমাণ করতে পারে না।
টয় ডেটা: P, Q ও R গ্রুপে ১৬০ জন কাল্পনিক আবেদনকারী, সিড-দেওয়া জেনারেটর থেকে। প্রতি ফলাফলে ২০ জনের ফ্ল্যাগ-সীমাটা আমাদের বেছে নেওয়া।
ইঙ্গিত। প্রতি গ্রুপে চারটা (label, prediction) ঘর গুনুন। ফল্স-পজিটিভ ভাগ করুন অযোগ্যদের দিয়ে, ফল্স-নেগেটিভ ভাগ করুন যোগ্যদের দিয়ে; ভাজক শূন্য হলে None ফেরত দিন।
সমাধান।
import random
from collections import Counter
# Toy data: 160 invented applicants in groups P, Q and R. label = judged suitable in a later review,
# pred = shortlisted by an invented tool. The generator is seeded, so every run gives the same rows.
def make_rows(seed=11, sizes=(("P", 90), ("Q", 60), ("R", 10))):
rnd, rows = random.Random(seed), []
for g, n in sizes:
for _ in range(n):
label = int(rnd.random() < 0.5)
hit = (0.80 if g != "Q" else 0.60) if label else 0.15 # Q's suitable people are missed more
rows.append(dict(group=g, label=label, pred=int(rnd.random() < hit)))
return rows
ROWS = make_rows()
def rates(rows):
out = {}
for g in sorted({r["group"] for r in rows}):
c = Counter((r["label"], r["pred"]) for r in rows if r["group"] == g)
n, pos, neg = sum(c.values()), c[1, 1] + c[1, 0], c[0, 1] + c[0, 0]
out[g] = dict(n=n, pos=pos, neg=neg, sel=(c[1, 1] + c[0, 1]) / n,
fpr=c[0, 1] / neg if neg else None, fnr=c[1, 0] / pos if pos else None)
return out
def small(v, minimum=20):
return min(v["pos"], v["neg"]) < minimum # our chosen limit, not a standard
R = rates(ROWS)
f = lambda x: "n/a" if x is None else f"{x:.2f}"
print("group n suitable unsuitable sel fpr fnr flag")
for g, v in R.items():
print(f"{g:<6}{v['n']:>4}{v['pos']:>9}{v['neg']:>11} {v['sel']:.2f} {f(v['fpr'])} {f(v['fnr'])} "
+ (f"SMALL: one person moves fnr by {1 / v['pos']:.2f}" if small(v) else ""))
for g in ("Q",):
print(f"gap {g} - P: selection {R[g]['sel'] - R['P']['sel']:+.2f}, "
f"fpr {R[g]['fpr'] - R['P']['fpr']:+.2f}, fnr {R[g]['fnr'] - R['P']['fnr']:+.2f}")
print("Toy data: this illustrates a calculation, not any real system.")group n suitable unsuitable sel fpr fnr flag
P 90 50 40 0.48 0.07 0.20
Q 60 27 33 0.32 0.06 0.37
R 10 4 6 0.30 0.17 0.50 SMALL: one person moves fnr by 0.25
gap Q - P: selection -0.16, fpr -0.01, fnr +0.17
Toy data: this illustrates a calculation, not any real system.Q গ্রুপের যোগ্য মানুষ বেশি বাদ পড়ছে (০.৩৭, P-তে ০.২০), আর তার সিলেকশন রেট ০.১৬ কম। R গ্রুপে ফ্ল্যাগ পড়েছে: ৪ জন যোগ্যের মধ্যে একটা সিদ্ধান্ত বদলালে রেট ০.২৫ নড়ে, তাই সেটা গণনাসহ জানানো হয়, তুলনা করা হয় না।
কেন এটা বাস্তব জগতের ন্যায্যতা প্রমাণ করতে পারে না: মানুষ ও জেনারেটর কাল্পনিক; লেবেলটা একটা বিচার, আর পক্ষপাতদুষ্ট লেবেল প্রতিটা রেটকে পক্ষপাতদুষ্ট করে; ফারাক তার কারণ দেখায় না; প্রক্সি ও অন্য গ্রুপ অনুপস্থিত; ছোট স্যাম্পলে শুধু ভাগ্যেই ফারাক হতে পারে; আর অন্য মেট্রিক অন্য ছবি দিত।
পাস চেকলিস্ট।
- শক্তিশালী: ভাজক ঠিক; অনির্ধারিত রেট n/a, ০ নয়; ছোট গ্রুপ কারণসহ ফ্ল্যাগ ও তুলনা-বহির্ভূত; ওপরের সীমাগুলো (কাল্পনিক ডেটা, লেবেল, কারণ, স্যাম্পলের আকার) উল্লিখিত; ফলাফলে "ন্যায্য" বা "পক্ষপাতদুষ্ট" শব্দ নেই।
- দুর্বল: একটাই সামগ্রিক নির্ভুলতা (accuracy); R গ্রুপের জন্য ফারাক জানানো; টয় সংখ্যা থেকে "টুলটা ন্যায্য/অন্যায্য" বাক্য।
কেন এটা কাজ করে। গণনা ও রেট এসেছে টয় ডেটায় ন্যায্যতার মেট্রিক: Python-এ সিলেকশন রেট ও এরর রেট থেকে; ছোট-স্যাম্পলের ফ্ল্যাগ ও "দেখাতে পারে না" তালিকা এসেছে ন্যায্যতার লক্ষ্য তুলনা ও তার সীমা: ছোট স্যাম্পল ও পরস্পরবিরোধী মেট্রিক থেকে।
M2: একটা সিস্টেম কার্ড বানান ও যাচাই করুন
কাজ। Python ডিকশনারি থেকে একটা খসড়া সিস্টেম কার্ড বানান। ব্যবহারের উদ্দেশ্য, সীমাবদ্ধতা, তদারকি ও মূল্যায়নের যে ফিল্ড নেই তা জানান, আর ইচ্ছা করে রাখা ফাঁককে UNKNOWN চিহ্নিত করুন। প্রমাণ কখনও বানাবেন না।
টয় ডেটা: LeafletHelper-এর আংশিক পূরণ করা একটা কার্ড; এটা কাল্পনিক একটা টুল, যা লাইব্রেরির নোটিশ সহজ ভাষায় আবার লেখে।
ইঙ্গিত। ডট-পাথ দিয়ে ভেতরের ফিল্ড পড়ুন। ফাঁকা তালিকা বা অনুপস্থিত key (চাবি) হলো MISSING; status: UNKNOWN হলো কেউ ইচ্ছা করে লিখে রাখা ফাঁক। উদ্ধৃত ফাইল আছে কি না দেখুন।
সমাধান।
from pathlib import Path
MISSING = object()
CARD_FIELDS = [("name", "Feature"), ("versions.system", "System version"), ("intended_use", "Intended use"),
("limitations", "Limitations"), ("human_oversight.escalation", "Human oversight"),
("evaluation.evidence_file", "Evaluation evidence"), ("accessibility", "Accessibility"),
("next_review", "Next review")]
def get(card, path):
for key in path.split("."):
if not isinstance(card, dict) or key not in card:
return MISSING
card = card[key]
return card
def status(v):
if v is MISSING or v in ("", [], {}, None):
return "MISSING"
return "UNKNOWN" if isinstance(v, dict) and v.get("status") == "UNKNOWN" else "ok"
def show(v):
if isinstance(v, list):
return "; ".join(map(str, v))
return ", ".join(f"{k}: {x}" for k, x in v.items()) if isinstance(v, dict) else str(v)
def draft(card):
lines = ["SYSTEM CARD (DRAFT, not reviewed)"]
for path, label in CARD_FIELDS:
v, s = get(card, path), status(get(card, path))
text = ("MISSING - the owner must supply this" if s == "MISSING"
else "UNKNOWN - " + v["why"] if s == "UNKNOWN" else show(v))
lines.append(f"{label}: {text}")
return "\n".join(lines)
def evidence_flags(card):
p = get(card, "evaluation.evidence_file")
return [f"evaluation cites {p} but the file does not exist"] if isinstance(p, str) and not Path(p).exists() else []
# Toy data: a partly filled card for LeafletHelper, a tool that rewrites notices in plain language
CARD = {
"name": "LeafletHelper (hypothetical)",
"versions": {"system": "0.2"},
"intended_use": "draft plain-language versions of library notices; a librarian approves each one",
"limitations": [],
"evaluation": {"claim": "12 notices tested by the author", "evidence_file": "eval/leaflet_check.csv"},
"accessibility": {"status": "UNKNOWN", "why": "nobody has tested the notices with a screen reader"},
"next_review": "2027-01-15",
}
print(draft(CARD))
rows = [(status(get(CARD, p)), label) for p, label in CARD_FIELDS]
print({s: sum(r[0] == s for r in rows) for s in ("ok", "UNKNOWN", "MISSING")})
print("MISSING:", [label for s, label in rows if s == "MISSING"])
print("evidence:", evidence_flags(CARD))SYSTEM CARD (DRAFT, not reviewed)
Feature: LeafletHelper (hypothetical)
System version: 0.2
Intended use: draft plain-language versions of library notices; a librarian approves each one
Limitations: MISSING - the owner must supply this
Human oversight: MISSING - the owner must supply this
Evaluation evidence: eval/leaflet_check.csv
Accessibility: UNKNOWN - nobody has tested the notices with a screen reader
Next review: 2027-01-15
{'ok': 5, 'UNKNOWN': 1, 'MISSING': 2}
MISSING: ['Limitations', 'Human oversight']
evidence: ['evaluation cites eval/leaflet_check.csv but the file does not exist']খসড়ায় দুবার MISSING আর একবার UNKNOWN আছে, আর কোনো সীমাবদ্ধতা বা তদারকির পরিকল্পনা যোগ হয়নি। প্রমাণ-যাচাই দেখায় "১২টা নোটিশ পরীক্ষা করা হয়েছে" এমন একটা ফাইলের দিকে ইঙ্গিত করে যা নেই, তাই দাবিটা আজ প্রমাণহীন।
পাস চেকলিস্ট।
- শক্তিশালী: MISSING ও UNKNOWN আলাদা; খসড়া "পর্যালোচিত নয়" বলে চিহ্নিত; অকেজো প্রমাণ-রেফারেন্স ফ্ল্যাগ করা; স্ক্রিপ্টের লেখায় বানানো কোনো সীমাবদ্ধতা, পরীক্ষার ফল বা অনুমোদন নেই।
- দুর্বল: ফাঁকা সীমাবদ্ধতার জায়গায় "none known"; ফাইল না দেখেই রেফারেন্স মেনে নেওয়া; পরিষ্কার রান-কে "কার্ডটা নির্ভুল" বলা (এর মানে শুধু এই নিয়মগুলো কোনো ফাঁক পায়নি)।
কেন এটা কাজ করে। এটা প্রকাশ ও ডকুমেন্টেশন যাচাই: Python দিয়ে সিস্টেম কার্ড পরীক্ষা পাতার ভ্যালিডেটর আবার বানায়, আর ফিল্ডের তালিকা নেয় মডেল কার্ড, সিস্টেম কার্ড ও ডেটাশিট: কী নথিভুক্ত করবেন এবং কার জন্য থেকে।
কঠিন চ্যালেঞ্জ
H1: ইমপ্যাক্ট অ্যাসেসমেন্ট পর্যালোচনা
কাজ। একটা কাল্পনিক AI-ভিত্তিক বৃত্তি-বাছাই ফিচার পর্যালোচনা করুন। AI-সহায়ক পর্যালোচনা, শুধু মানুষের পর্যালোচনা ও AI-ছাড়া বিকল্প তুলনা করুন। দিন: স্টেকহোল্ডার মানচিত্র, রিস্ক রেজিস্টার, প্রশমন পরিকল্পনা, ন্যায্যতা-পরীক্ষার প্রস্তাব, আপিলের পথ আর নথিভুক্ত একটা গো/নো-গো সুপারিশ।
কাল্পনিক পরিস্থিতি:
Meridian Bursary Fund (invented) gives 120 awards a year from about 3000 applications. Four
readers take about 12 minutes per form. A vendor offers "MeritScore": it reads each form and
essay and gives a score from 0 to 100, and a three-line summary. The fund's plan: readers open
the top 900 forms; a random 10% of the other 2100 are also read; the rest are not.
Applicants write in several languages. The director wants a decision before the next round.
The vendor keeps essay text for a period not yet stated in writing. There is no appeal route.ইঙ্গিত। জিজ্ঞেস করুন কারা মত দেওয়ার সুযোগ ছাড়াই মূল্য দেয়, কোন সিদ্ধান্ত কেউ নেয় না (না-পড়া ফর্ম), ন্যায্যতার লেবেল কে বানিয়েছে, আর লাইভে যাওয়ার আগে সস্তায় কী শেখা যায়। প্রতিটা শর্তে একটা পরিমাপ, একটা থামার নিয়ম ও একজন মালিক দিন।
সমাধান। কোড বিকল্পগুলো তুলনা করে, পর্যালোচনাটা ডেটা হিসেবে রাখে আর নিজের ফাঁক নিজে জানায়। E1 থেকে check আবার ব্যবহার করা হয়েছে।
# Toy data: Meridian Bursary Fund (invented): 3000 applications, 120 awards, four readers
FORMS = 3000
OPTIONS = { # forms a person reads in full, minutes per form, and minutes per form for any other check
"A human-only review": dict(full=3000, other=0),
"B AI-assisted review": dict(full=900 + 210, other=0), # top 900 + a 10% sample of the other 2100
"C non-AI: checklist + draw": dict(full=0, other=3000), # 4-minute eligibility checklist, draw among eligible
}
print(f"{'option':<28}{'hours':>6}{'per reader':>11}{'read in full':>14}{'never read in full':>24}")
for name, o in OPTIONS.items():
hours = (o["full"] * 12 + o["other"] * 4) / 60
never = f"{FORMS - o['full']}" + (" (checklist only)" if o["full"] == 0 else "")
print(f"{name:<28}{hours:>6.0f}{hours / 4:>11.0f}{o['full']:>14}{never:>24}")
REVIEW = {
"stakeholders": { # group: (gains, pays, has a say)
"applicants": ("a sooner decision", "wrong or unread decisions", "no"),
"second-language writers": ("none known", "essay scored down for style", "no"),
"readers": ("less reading", "pressure to follow the score", "some"),
"donors": ("lower cost", "reputation if awards look unfair", "some"),
},
"risks": [ # same fields as the E1 register; check() from E1 runs on them
dict(id="H1", title="Essay score favours one writing style", severity=4, likelihood=3,
owner="Fund director", mitigation="group test on last year's forms before live use"),
dict(id="H2", title="Readers follow the score, skip the sample", severity=4, likelihood=3,
owner="Head of readers", mitigation="sample drawn by script and logged"),
dict(id="H3", title="Vendor keeps essay text", severity=3, likelihood=2,
owner="Data lead", mitigation="written retention limit before the pilot"),
dict(id="H4", title="No second look for rejected applicants", severity=4, likelihood=4,
owner="Fund director", mitigation="appeal route below"),
],
"fairness_test": dict(label="awarded after two full reads", groups="region, first language (if policy allows)",
metrics="selection and false-negative rate, counts, interval", min_each=20,
stop_rule="a gap above 0.10 (chosen) stops live use", owner="Data lead"),
"appeal": dict(who="any applicant", days=10, reviewer="a reader who did not see the first decision"),
"recommendation": dict(
decision="conditional go: shadow pilot only, the score is not shown to readers",
conditions=[("report group tables on last year's 3000 forms", "tables with counts", "gap > 0.10", "Data lead"),
("readers decide every award", "100% human decisions", "any live use", "Fund director"),
("write the vendor retention limit", "signed text", "none by 2027-01-15", "Data lead")],
accountable="Fund director", review_date="2027-02-01"),
}
NEED = ("stakeholders", "risks", "fairness_test", "appeal", "recommendation")
def gaps(review):
found = [f"{k} is missing" for k in NEED if not review.get(k)]
for r in review.get("risks", []):
found += [f"{r['id']}: {p}" for p in check(r)] # check() comes from the E1 solution
for what, measure, stop, owner in review.get("recommendation", {}).get("conditions", []):
if not (measure and stop and owner):
found.append(f"condition '{what[:30]}' lacks a measure, stop rule or owner")
if review.get("appeal") and not review["appeal"].get("reviewer"):
found.append("appeal has no named reviewer")
return found
print("\npay without a say:", [g for g, v in REVIEW["stakeholders"].items() if v[2] == "no"])
print("gaps in the review:", gaps(REVIEW) or "none")
weak = {**REVIEW, "recommendation": {**REVIEW["recommendation"], "conditions": [("add a tool", "", "", "")]}}
print("gaps if a condition is empty:", gaps(weak))
print("recommendation:", REVIEW["recommendation"]["decision"], "| owner:", REVIEW["recommendation"]["accountable"],
"| review:", REVIEW["recommendation"]["review_date"])option hours per reader read in full never read in full
A human-only review 600 150 3000 0
B AI-assisted review 222 56 1110 1890
C non-AI: checklist + draw 200 50 0 3000 (checklist only)
pay without a say: ['applicants', 'second-language writers']
gaps in the review: none
gaps if a condition is empty: ["condition 'add a tool' lacks a measure, stop rule or owner"]
recommendation: conditional go: shadow pilot only, the score is not shown to readers | owner: Fund director | review: 2027-02-01মডেল উত্তর, কথায়:
- বিকল্প। A (শুধু মানুষ) সবচেয়ে ধীর, কোনো ফর্ম না-পড়া থাকে না। B পুরোটা পড়ে ১১১০টা ফর্ম, আর ১৮৯০টা ছেড়ে দেয় স্কোরের হাতে। C শুধু যোগ্যতার প্রশ্ন করে ও যোগ্যদের মধ্যে লটারি করে; এতে মডেলের বিচার এড়ানো যায়, কিন্তু মানুষের মেধা-বিচারও বাদ যায়, আর কেউ বলবেন সেটা অন্য ধরনের তহবিল।
- মত ছাড়াই মূল্য দেয় যারা। আবেদনকারী ও দ্বিতীয় ভাষায় লেখা মানুষ স্কোর দেখতে বা তাতে আপত্তি তুলতে পারে না।
- ন্যায্যতা-পরীক্ষা। গত বছরের ফর্মে স্কোর চালান, গণনা ও ইন্টারভালসহ সিলেকশন ও ফল্স-নেগেটিভ রেট জানান, আর প্রতি ফলাফলে ২০ জনের কম এমন গ্রুপ বাদ দিন। লেবেলটাও একটা মানুষের বিচার; তা বলুন।
- আপিল। যেকোনো আবেদনকারী ১০ দিনের মধ্যে, এমন একজন রিডারের কাছে যিনি প্রথম সিদ্ধান্ত দেখেননি।
- সুপারিশ। শুধু শ্যাডো পাইলটের জন্য শর্তসাপেক্ষ গো: স্কোর গণনা হবে কিন্তু দেখানো হবে না, রিডাররা প্রতিটা বৃত্তি ঠিক করবেন, আর টেবিল ও ধারণ-নীতির লেখা তৈরি হলে তবেই লাইভ ব্যবহার একটা নতুন সিদ্ধান্ত। দায়ী: Fund director। পর্যালোচনা: ২০২৭-০২-০১।
পক্ষে দাঁড়ানো যায় এমন বিকল্প। কারণ লিখে রাখলে অন্য সুপারিশও সমান শক্তিশালী হতে পারে।
| সুপারিশ | সবচেয়ে জোরালো কারণ | প্রধান খরচ বা সংশয় |
|---|---|---|
| শর্তসাপেক্ষ গো: শ্যাডো পাইলট (মডেল উত্তর) | কেউ ক্ষতিগ্রস্ত হওয়ার আগে আসল ঝুঁকি জানা যায় | এই রাউন্ডে সময় বাঁচে না |
| এখন শুধু মানুষ, আগামী বছর আবার দেখা | প্রত্যেক আবেদনকারীর ফর্ম পড়া হয়; ব্যাখ্যা ও আপিল সহজ | ৬০০ ঘণ্টা; খরচ তহবিলকেই দিতে হয় |
| AI-ছাড়া চেকলিস্ট ও লটারি | সস্তা, স্বচ্ছ, অডিট করার মডেল নেই | মেধা-বিচার ছাড়তে হয়; কেউ আপত্তি করবেন |
| AI-সহায়ক লাইভ, শুধু যদি প্রতিটা ফর্ম পড়া হয় | স্কোর শুধু কাজের ক্রম ঠিক করে | সময় সামান্যই বাঁচে; অটোমেশন বায়াস থেকে যায় |
পাস চেকলিস্ট।
- শক্তিশালী: তিন বিকল্প একই সংখ্যায় তুলনা করা; না-পড়া ফর্মকে একটা সিদ্ধান্ত হিসেবে চিহ্নিত করা; আবেদনকারী ও দ্বিতীয় ভাষায় লেখা মানুষকে মত-ছাড়া-মূল্য-দেওয়া হিসেবে নাম ধরা; প্রতিটা ঝুঁকির মালিক ও প্রশমন; লেবেল, গ্রুপ, ন্যূনতম গণনা ও থামার নিয়মসহ ন্যায্যতা-পরীক্ষা; অন্য কারও শোনা আপিল; শর্ত, দায়ী ভূমিকা ও পর্যালোচনার তারিখসহ সুপারিশ; অজানা বিষয় (ভেন্ডরের ধারণ, লেবেল) অজানা হিসেবে লেখা।
- দুর্বল: "AI নিরপেক্ষ, তাই গো"; তুলনা ছাড়া "কখনও AI নয়"; শুধু সামগ্রিক নির্ভুলতায় (accuracy) পরীক্ষা; প্রথম রিডারের দেওয়া আপিলের রায়; মালিক বা থামার নিয়ম ছাড়া সুপারিশ।
কেন এটা কাজ করে। এটা একসঙ্গে ব্যবহার করে ইমপ্যাক্ট অ্যাসেসমেন্ট: একটা সীমিত AI ফিচারের পরিধি, স্টেকহোল্ডার ও ক্ষতি ও ঝুঁকির স্কোরিং, নিয়ন্ত্রণ ও সিদ্ধান্ত: একটা টয় রিস্ক রেজিস্টার বানানো (বিকল্প, শর্তসহ সিদ্ধান্ত), স্টেকহোল্ডারের ওপর প্রভাব ও ওপেন বনাম ক্লোজড মডেল: বিভিন্ন পক্ষের যুক্তি (কে পায়, কে দেয়, কার মত আছে), মানুষের তদারকির ডিজাইন: অর্থবহ পর্যালোচনা, রাবার-স্ট্যাম্প নয় ও উচ্চ-ঝুঁকির ব্যবহার ও ব্যবহার-বন্ধের নিয়ম: আপিল, এসকেলেশন ও বিকল্প (আপিল, থামার নিয়ম), এবং পরিমাপ ও ঝুঁকি-নিয়ন্ত্রণের পরীক্ষা: নিয়ন্ত্রণগুলো আসলে কাজ করছে কি না (পরীক্ষার আগে লেখা গ্রহণযোগ্যতার মানদণ্ড)।
H2: ঘটনার সিমুলেশন ও পোস্ট-মর্টেম
কাজ। একটা টয় যোগ্যতা-সিস্টেম নিয়ম বদলের পর একটা কাল্পনিক গ্রুপকে অনেক বেশি প্রত্যাখ্যান করতে শুরু করে। ঘটনা সামলান (containment), প্রমাণ সংরক্ষণ করুন, ক্ষতিগ্রস্তদের জানান, মূল কারণ খুঁজুন, সংশোধনমূলক কাজ ঠিক করুন, আর এমন একটা পরীক্ষা লিখুন যা দেখায় মেরামত কাজ করেছে কি না।
কাল্পনিক পরিস্থিতি:
Harbour Water Support (invented) runs a rule-based checker for bill support. In week 3 a
"simplified" rule v3 goes live. By week 4 the Coast group (invented) is rejected far more often
than before. Applications arrive online, giving monthly income, or on paper, giving annual
income. The checker is a plain rule, not a learned model. Staff are asked: stop the harm,
keep evidence, tell people, explain why, and prove the repair works.টয় ডেটা: সিড-দেওয়া জেনারেটর থেকে ৪০০টা কাল্পনিক আবেদন, চার সপ্তাহ, তিনটা গ্রুপ।
ইঙ্গিত। ব্যাখ্যার আগে ক্ষতি আটকান। কাদের সিদ্ধান্ত আবার নিতে হবে তা বের করতে শেষ জানা-ভালো নিয়মটা (v2) ব্যবহার করুন। সংস্করণ, গণনা ও লগের ফিঙ্গারপ্রিন্ট রাখুন, নাম নয়। নতুন ডেটায় পরীক্ষা করুন আর v2-র সঙ্গে তুলনা করুন, কোনো আদর্শের সঙ্গে নয়।
সমাধান।
import hashlib, json, random, textwrap
from collections import Counter
# Toy data: Harbour Water Support (invented). A rule-based checker approves or rejects bill-support
# applications. Weeks 1-2 use rule v2; on "week 3" a rule change (v3) goes live. Seeded, so repeatable.
LIMIT = 1500 # approve if monthly income is at most this (invented units)
def make_apps(seed=5, n=400):
rnd, rows = random.Random(seed), []
for i in range(n):
group = rnd.choices(["Ridge", "Delta", "Coast"], [5, 3, 2])[0]
paper = rnd.random() < (0.7 if group == "Coast" else 0.1) # paper forms give ANNUAL income
monthly = rnd.randint(500, 2500)
rows.append(dict(id=f"H{i:03}", week=i * 4 // n + 1, group=group, channel="paper" if paper else "online",
period="annual" if paper else "monthly", income=monthly * 12 if paper else monthly))
return rows
def v2(a):
return "approve" if (a["income"] / 12 if a["period"] == "annual" else a["income"]) <= LIMIT else "reject"
def v3(a): # the "simplified" rule: the income period is no longer used
return "approve" if a["income"] <= LIMIT else "reject"
def v4(a): # the fix: use the period again, and send unclear cases to a person instead of rejecting
if a["period"] not in ("annual", "monthly"):
return "refer"
return v2(a)
def rej(rows, rule, group=None):
s = [a for a in rows if group in (None, a["group"])]
return sum(rule(a) == "reject" for a in s) / len(s) if s else None
APPS = make_apps()
live = lambda a: v2(a) if a["week"] < 3 else v3(a)
print("rejection rate by group (live rule) weeks 1-2 weeks 3-4")
for g in ("Ridge", "Delta", "Coast"):
early = [a for a in APPS if a["week"] < 3 and a["group"] == g]
late = [a for a in APPS if a["week"] >= 3 and a["group"] == g]
print(f"{g:<37}{sum(live(a) == 'reject' for a in early) / len(early):>8.2f}"
f"{sum(live(a) == 'reject' for a in late) / len(late):>11.2f}")
# 1 CONTAIN: back to the last known good rule; list decisions that differ, for a person to re-decide
late = [a for a in APPS if a["week"] >= 3]
affected = [a["id"] for a in late if live(a) == "reject" and v2(a) == "approve"]
print("\ncontain: rule v2 restored; decisions to re-open:", len(affected), "of", len(late))
# 2 EVIDENCE: versions, counts and a fingerprint of the decision log; no names or free text
log = [(a["id"], live(a)) for a in APPS]
snapshot = {"rules": "v2, v3", "v3_from": "week 3", "decisions": len(log), "reopen": len(affected),
"log_sha256": hashlib.sha256(json.dumps(log).encode()).hexdigest()[:16]}
print("evidence:", snapshot)
# 3 TELL THE AFFECTED: only what is known; unknowns are written as UNKNOWN
def notice(ref):
return textwrap.fill(
f"Application {ref}: your decision may be wrong because of a rule change. A person is deciding it "
"again; you need not apply again. Effect on your bill: UNKNOWN. Next update: UNKNOWN date.", 90)
print(notice(affected[0]))
# 4 ROOT CAUSE: compare the two rules on the same applications
c = Counter((a["channel"], a["group"]) for a in late if v2(a) != v3(a))
print("\ndecisions that differ between v2 and v3, by (channel, group):", dict(sorted(c.items())))
print("differences on online forms:", sum(n for k, n in c.items() if k[0] == "online"))
# 5 DID THE FIX WORK? Run the checks on NEW data (another seed) against the broken and the fixed rule.
def tests(rule, rows, slack=0.02, min_n=20):
out = []
ok = rule(dict(income=12000, period="annual")) == "approve" # 12000 a year is 1000 a month
out.append(("T1 annual income is converted", "PASS" if ok else "FAIL"))
online = [a for a in rows if a["channel"] == "online"]
same = all(rule(a) == v2(a) for a in online)
out.append(("T2 online forms decide as v2 did", "PASS" if same else "FAIL"))
ok = rule(dict(income=9000, period="")) != "reject" # unclear: refer, never reject
out.append(("T3 unclear period goes to a person", "PASS" if ok else "FAIL"))
sizes = Counter(a["group"] for a in rows)
if min(sizes.values()) < min_n:
out.append(("T4 group gap no worse than v2", "NOT TESTED"))
else:
gap = lambda rl: max(rej(rows, rl, g) for g in sizes) - min(rej(rows, rl, g) for g in sizes)
ok = gap(rule) <= gap(v2) + slack # compare with the last known good rule, not with "zero"
out.append((f"T4 group gap {gap(rule):.2f} vs v2 {gap(v2):.2f}", "PASS" if ok else "FAIL"))
return out
NEW = make_apps(seed=9)
for name, rule in (("v3", v3), ("v4", v4)):
print(name, [f"{t[:2]} {res}" for t, res in tests(rule, NEW)])
print("T4 on 30 rows only:", tests(v4, NEW[:30])[-1])rejection rate by group (live rule) weeks 1-2 weeks 3-4
Ridge 0.57 0.59
Delta 0.49 0.53
Coast 0.49 0.91
contain: rule v2 restored; decisions to re-open: 27 of 200
evidence: {'rules': 'v2, v3', 'v3_from': 'week 3', 'decisions': 400, 'reopen': 27, 'log_sha256': 'd6f3854d61f2b4f4'}
Application H200: your decision may be wrong because of a rule change. A person is
deciding it again; you need not apply again. Effect on your bill: UNKNOWN. Next update:
UNKNOWN date.
decisions that differ between v2 and v3, by (channel, group): {('paper', 'Coast'): 14, ('paper', 'Delta'): 5, ('paper', 'Ridge'): 8}
differences on online forms: 0
v3 ['T1 FAIL', 'T2 PASS', 'T3 FAIL', 'T4 FAIL']
v4 ['T1 PASS', 'T2 PASS', 'T3 PASS', 'T4 PASS']
T4 on 30 rows only: ('T4 group gap no worse than v2', 'NOT TESTED')ঘটনা সামলানো: v2 ফিরিয়ে আনুন আর যে ২৭টা সিদ্ধান্তে v2 ও v3 আলাদা, সেগুলো আবার খুলুন। প্রমাণ: সংস্করণ, গণনা ও লগের ফিঙ্গারপ্রিন্ট, কোনো নাম নয়। এ পর্যন্ত মূল কারণ: ২৭টা ফারাকের সবই কাগজের ফর্ম, যাতে বার্ষিক আয় লেখা থাকে, আর v3 আয়ের সময়কাল (period) ব্যবহার বন্ধ করেছে। Coast সবচেয়ে বেশি ক্ষতিগ্রস্ত, কারণ তারা কাগজে বেশি আবেদন করে (এটা একটা প্রক্সি, উদ্দেশ্য নয়)। পোস্ট-মর্টেম আরও জিজ্ঞেস করে: কাগজের ফর্মের পরীক্ষা ছাড়াই v3 কেন গেল, গ্রুপভিত্তিক প্রত্যাখ্যানের হারে কেন কোনো অ্যালার্ট বাজেনি, আর পরিবর্তনটা কে অনুমোদন করেছিল।
সংশোধনমূলক কাজ: v4 (বার্ষিক আয় রূপান্তর; অস্পষ্ট ক্ষেত্র মানুষের কাছে পাঠানো), কাগজের ফর্মসহ রিলিজ পরীক্ষা, গ্রুপ ও চ্যানেল অনুযায়ী সাপ্তাহিক প্রত্যাখ্যানের টেবিল, আর নাম ও তারিখসহ মালিকের পরিবর্তন-অনুমোদন। T4 তুলনা করে v2-র সঙ্গে, শূন্যের সঙ্গে নয়, আর ৩০ সারিতে বলে NOT TESTED, কারণ কোনো গ্রুপে খুব কম মানুষ থাকত।
পাস চেকলিস্ট।
- শক্তিশালী: আগে ক্ষতি আটকানো, সঙ্গে কাদের সিদ্ধান্ত আবার নিতে হবে তা খোঁজার উপায়; ব্যক্তিগত ডেটা ছাড়া প্রমাণ; আন্দাজ-করা কারণ বা তারিখ ছাড়া নোটিশ; দুই নিয়মের তুলনায় সমর্থিত মূল কারণ, নিশ্চিত না হওয়া পর্যন্ত অনুসন্ধান হিসেবে রাখা; মালিকসহ কাজ; নতুন ডেটায় এমন পরীক্ষা যা v3-তে ব্যর্থ ও v4-এ পাস করে, আর কম ডেটায় NOT TESTED; দোষারোপহীন ভাষা।
- দুর্বল: ক্ষতি আটকানোর আগে কারণ খোঁজা; কোনো ব্যক্তিকে দোষ দেওয়া; "শেষ" কে "ঠিক হয়েছে" গণ্য করা; মেরামত যে ডেটায় বানানো সেই ডেটায় পরীক্ষা; T4 পাস করেছে বলে v4-কে ন্যায্য বলা (এটা শুধু দেখায় "v2-র চেয়ে খারাপ নয়")।
কেন এটা কাজ করে। এটা ঘটনা ও ধারাবাহিক উন্নতি: ট্রায়াজ, পোস্ট-মর্টেম ও রিভিউ চক্র পাতার চক্র অনুসরণ করে (আগে নিয়ন্ত্রণ, দোষারোপহীন রিভিউ, আগে-পরের পরীক্ষায় যাচাই)। পরীক্ষাটা PASS, FAIL ও NOT TESTED নেয় পরিমাপ ও ঝুঁকি-নিয়ন্ত্রণের পরীক্ষা: নিয়ন্ত্রণগুলো আসলে কাজ করছে কি না পাতা থেকে, আর অনুমোদনহীন পরিবর্তনের ফাঁকটা রিভিউ, অডিট ও বাস্তব গভর্ন্যান্স: পরিবর্তনের অনুমোদন, ঝুঁকি গ্রহণ ও কর্মীদের উদ্বেগ পাতায় বর্ণিত ফাঁক।
আটটা ইন্টারভিউ-ধাঁচের প্রশ্ন
শক্তিশালী ছোট উত্তর; কারণ দিলে আপনার উত্তর আলাদা হতে পারে।
- সম্ভাবনা ও তীব্রতা আলাদা করে লিখবেন কেন? এরা আলাদা প্রশ্নের উত্তর। বিরল কিন্তু গুরুতর ক্ষতি আর ঘন ঘন কিন্তু হালকা ক্ষতির গুণফল এক হতে পারে, অথচ সাড়া আলাদা লাগে।
- দুটো ন্যায্যতা-মেট্রিক আলাদা কথা বলে কেন? এরা আলাদা জিনিস মাপে: কে বাছাই হলো, কে বাদ পড়ল, কে ভুলভাবে বাছাই হলো, স্কোর কতটা নির্ভরযোগ্য। গ্রুপের বেস রেট আলাদা আর স্কোর নিখুঁত না হলে সবগুলো একসঙ্গে সমান হয় না, তাই দলকে বেছে নিয়ে ব্যাখ্যা করতে হয়।
- সিস্টেম কার্ডে কোন প্রমাণ থাকে? সংস্করণ, উদ্দেশ্য ও সীমার-বাইরের ব্যবহার, ডেটার উৎস ও অনুমতির অবস্থা, ফলের লিংকসহ মূল্যায়ন, সীমাবদ্ধতা, পরীক্ষিত ভাষা, তদারকি ও ব্যর্থতার ধরন। ফাঁক UNKNOWN, আন্দাজ নয়।
- একটা ঝুঁকি গ্রহণযোগ্য কি না কীভাবে ঠিক করবেন? স্কোরের আগে লেখা নিয়মের সঙ্গে অবশিষ্ট ঝুঁকি মেলান: তীব্রতা, ফেরানোর সম্ভাবনা, কে প্রভাবিত, প্রমাণের জোর। একটা নামধারী ভূমিকা সিদ্ধান্ত লিখে রাখে, শর্ত ও মেয়াদসহ।
- কখন একটা AI ফিচার থামানো উচিত? যখন মানুষের ক্ষতি সম্ভাব্য ও চলমান, কোনো ব্যবহার-বন্ধের শর্ত পূরণ হয়েছে, বা গুরুতর মাত্রায় কারণ অজানা। আগে থামান (যার থামানোর অধিকার আছে সে), তারপর তদন্ত।
- নিয়ন্ত্রণ আছে আর নিয়ন্ত্রণ কাজ করে, এর পার্থক্য কীভাবে বোঝেন? আগে লেখা মানদণ্ডসহ এমন পরীক্ষা চান যা নিয়ন্ত্রণ-বানানো ডেটার বাইরে চলে, ফল PASS, FAIL বা NOT TESTED। নীতিপত্র শুধু অস্তিত্ব দেখায়।
- দায়িত্বশীল পোস্ট-মর্টেম কী নথিভুক্ত করে? প্রভাব, শনাক্তকরণ, সময়রেখা, সিস্টেমের অবদানকারী কারণ, কী ভালো গেছে, মালিক ও তারিখসহ কাজ, আর সেগুলো কাজ করেছে কি না তার পরীক্ষা। এতে ভূমিকা ও পরিস্থিতির নাম থাকে, দোষীর নাম নয়।
- আইনি প্রয়োজন নৈতিক লক্ষ্য থেকে আলাদা করে ম্যাপ করেন কেন? নৈতিক লক্ষ্য আইনের বাইরেও যেতে পারে, আর আইন সব জায়গা, খাত বা সময়ে প্রযোজ্য নয়। আলাদা রাখলে ভালো উদ্দেশ্য দেখে কমপ্লায়েন্সের দাবি করা এড়ানো যায় আর বোঝা যায় কোন প্রশ্ন যোগ্য উপদেষ্টাকে করতে হবে।
কাজের জায়গায় এটা কোথায় দেখা যায়
দলগুলো প্রতিটা AI ফিচারের জন্য একটা রেজিস্টার, একটা কার্ড, একটা গ্রুপ-টেবিল, একটা সিদ্ধান্ত-রেকর্ড আর একটা ঘটনার ফোল্ডার রাখে, আর প্রতিটা রিলিজের আগে পরীক্ষাগুলো আবার চালায়।
সাধারণ ভুল
- আগে সমাধান পড়ে ফেলা। তাতে পড়ার অনুশীলন হয়। আগে নিজেরটা লিখুন বা চালান, তারপর মেলান।
- শুধু ভেবেছেন বলে দফায় টিক দেওয়া। দফা গোনা হবে শুধু লেখা বা চালানো থাকলে।
- একটা মডেল উত্তরকেই উত্তর ভাবা। H1-এ পক্ষে দাঁড়ানো যায় এমন একাধিক সুপারিশ আছে; যুক্তি ও শর্ত দেখে বিচার করুন।
- স্ক্রিপ্টকে ফাঁক ঢাকতে দেওয়া। অনুপস্থিত মালিক বা ফাইল বসিয়ে দিলে রেকর্ড আরও খারাপ হয়। ফাঁক জানান।
নিজে চেষ্টা করুন
এগুলো ল্যাবটা আবার ছুঁয়ে যায়। এই পাতার নেমস্পেস ব্যবহার করুন।
- সহজ। E1-এর তালিকায় মালিকসহ কিন্তু তীব্রতা ০ এমন একটা ঝুঁকি R7 যোগ করুন।
checkকী জানাবে আগে অনুমান করুন, তারপর চালান। - মাঝারি। M1-এর সিড ১২ করুন আর ন্যূনতম ৩০। কোন গ্রুপ ফ্ল্যাগ হয়, আর Q গ্রুপের গল্প বদলায় কি না?
- কঠিন। FeedbackHelper-এর জন্য একটা মূল্যায়ন-প্যাকের এক পাতার সূচি লিখুন: পাঁচটা অংশের প্রতিটায় কী আছে, কী এখনও MISSING বা UNKNOWN, মালিক কে, আর একটা পর্যালোচনার তারিখ।
উত্তর
সহজ। আউটপুটে আসবে "severity is missing" (শূন্যকে অনুপস্থিত ধরা হয়) আর মালিক নিয়ে কোনো সমস্যা নয়। পাস চেকলিস্ট: চালানোর আগে অনুমান করেছেন; লক্ষ করেছেন R7 সারসংক্ষেপের বাইরে থাকে আর মেরামত হয় না। দুর্বল: শূন্য মেনে নিতে check বদলে ফেলা।
মাঝারি। rates(make_rows(12)) আর small(v, 30) চালান। সিড ১২-তে Q-ও ফ্ল্যাগ হয়, আর তার fnr-এর ফারাক প্রায় মিলিয়ে যায় (০.২৮, P-তে ০.২৬)। পাস চেকলিস্ট: কোন গ্রুপ ফ্ল্যাগ হলো তা জানিয়েছেন আর বলেছেন পরিবর্তনগুলো কাল্পনিক ডেটার স্যাম্পলিং থেকে, তাই একটা সিড কিছু প্রমাণ করে না। দুর্বল: যে সিড আপনার পছন্দের গল্প দেয় সেটা বেছে নেওয়া।
কঠিন। পাস চেকলিস্ট: পাঁচটা অংশ (রেজিস্টার, গ্রুপ-টেবিল, কার্ড, সিদ্ধান্ত-রেকর্ড, ঘটনার ফোল্ডার), প্রতিটায় জানা, অজানা ও মালিক; ফাঁক চিহ্নিত, ভরাট নয়; একটা পর্যালোচনার তারিখ ও পুনর্মূল্যায়নের ট্রিগার; কমপ্লায়েন্স (compliance) বা ন্যায্যতার দাবি নেই। দুর্বল: না-লেখা নথিকে "শেষ" দেখানো।
সারসংক্ষেপ
- ল্যাবের স্কোর একটা শেখার সহায়ক: শুধু লেখা বা চালানো দফায় টিক দিন।
- রেজিস্টার, কার্ড ও প্যাক ফাঁক জানায় (MISSING, UNKNOWN, NOT TESTED), কখনও ভরাট করে না।
- টয় ন্যায্যতার সংখ্যা একটা গণনা দেখায়; ছোট গ্রুপ ফ্ল্যাগ করুন, আর কোনো মেট্রিক ন্যায্যতা প্রমাণ করে না।
- গো/নো-গো সুপারিশ AI-সহায়ক, শুধু মানুষ ও AI-ছাড়া বিকল্প তুলনা করে, আর শর্ত, মালিক ও পর্যালোচনার তারিখ বহন করে।
- ঘটনায় আগে আটকান, ব্যক্তিগত ডেটা ছাড়া প্রমাণ রাখুন, আর মেরামত নতুন ডেটায় পরীক্ষা করুন।
এরপর: ক্যাপস্টোন, চিট শিট ও এরপর কী: গো/নো-গো মূল্যায়ন প্যাক পাতায় এই অংশগুলো আপনার বেছে নেওয়া একটা ফিচারের জন্য একটা প্যাকে জোড়া হবে, আর এই টিউটোরিয়ালের পরে কী আছে তা দেখানো হবে।