Lesson 21 / 21
Responsible Release
Measuring what the published number covers: the threshold 0.50 setup publishes a single recall number (0.7097), but per class the same defense spreads between 0.9583 and 0.2105, and the published number is 0.4992 above the weakest. A narrow system card ties 2 of nine commitments to a number, a wide card 6; the other 3 stay claims. This lesson closes this course and the AI Engineering curriculum, gathering twenty-one lessons in one table.
Published Updated
In this lesson
- Measures how widely a single published average spreads per class.
- Counts the measures a system card carries and shows that every measure it lacks remains a claim.
- Gathers the baseline, defended setup and cost numbers of the twenty-one lessons in one table.
Contents
The previous lesson measured that the feedback loop cannot see the false alarm. So far all these numbers have stayed inside: the same team built the set, read the indicator and moved the threshold. A released system tells the outside where it will be used, what was measured and what happens if it is misused. This lesson measures that text: the numbers a system card carries, the numbers it does not, and what each missing number is. Commitments are named by class: usage limit, transparency item, misuse plan.
- EM63. The records, classes, defense and commitment list are fictional. The text of the records is entirely harmless support requests and has no relation to the class; the class lives only in the structural feature profile. The set is 240 records, 93 harmful, 147 clean.
- EM64. The published setup is the model filter, threshold 0.50.
- EM65. The narrow card carries only two average numbers. The wide card adds the per-class recall, the resolution of the set, the records sent to a human and the covered share of the appeals.
- EM66. A commitment is tied to a number if the measure it rests on is on the card; otherwise it remains a claim. Commitments whose measure this course never produced remain claims on both cards.
- EM67. The resolution of the set: the recall step is 1/93 = 0.0108, the false block step is 1/147 = 0.0068; the step per class is the inverse of the class’s record count and rises to 0.1000 for a class of ten records. The second run keeps the record set and only renews the filter’s own draw with seed 20260219.
The Number the Card Carries
# FICTIONAL. The text of the records is entirely harmless support requests and # has no relation to the class; the class lives only in the structural feature profile. SEED, M32 = 20260218, 0xFFFFFFFF def rng(t): x = ((t ^ (t >> 16)) * 2246822507) & M32 x = ((x ^ (x >> 13)) * 3266489909) & M32 s = [(x ^ (x >> 16)) & M32] def next_value(): s[0] = (s[0] * 1664525 + 1013904223) & M32 return s[0] / 4294967296 return next_value def pick(u, w): t = 0.0 for i, x in enumerate(w): t += x if u < t: return i return len(w) - 1 def trace_id(m): h = 2166136261 for c in str(m): h = ((h ^ ord(c)) * 16777619) & M32 return h CLASSES = ["clean", "indirect_instruction", "hidden_field_request", "privilege_escalation", "source_spoofing", "corrupted_input"] SOURCES = ["user", "retrieved_document", "tool_output"] FEATURES = ["instruction_mode", "role_change_request", "hidden_field_name", "side_effect_tool_name", "source_stamp_mismatch", "unusual_stamp_distribution", "excessive_length"] PROFILE = { "clean": [0.06, 0.02, 0.05, 0.03, 0.02, 0.04, 0.05], "indirect_instruction": [0.88, 0.42, 0.10, 0.30, 0.35, 0.08, 0.12], "hidden_field_request": [0.35, 0.15, 0.86, 0.12, 0.10, 0.06, 0.08], "privilege_escalation": [0.30, 0.25, 0.12, 0.84, 0.14, 0.05, 0.07], "source_spoofing": [0.22, 0.55, 0.08, 0.10, 0.82, 0.07, 0.06], "corrupted_input": [0.05, 0.03, 0.04, 0.03, 0.06, 0.80, 0.62], } SOURCE_SHARE = { "clean": [0.72, 0.20, 0.08], "indirect_instruction": [0.05, 0.70, 0.25], "hidden_field_request": [0.55, 0.30, 0.15], "privilege_escalation": [0.40, 0.35, 0.25], "source_spoofing": [0.10, 0.60, 0.30], "corrupted_input": [0.65, 0.20, 0.15], } TEXT = ["bu ayki fatura tutari gecen aya gore yuksek geldi", "sayac endeksi yanlis girilmis olabilir", "mahallede kesinti ne zaman bitecek", "tarife basamagimi ogrenmek istiyorum", "ariza kaydim icin ekip ne zaman gelir", "adres degisikligi nasil yapilir"] RECORDS = [] for i in range(240): r = rng(SEED + 6151 * i) c = CLASSES[pick(r(), [0.62, 0.10, 0.07, 0.07, 0.07, 0.07])] source = SOURCES[pick(r(), SOURCE_SHARE[c])] feat = {o: (r() < p) for o, p in zip(FEATURES, PROFILE[c])} RECORDS.append({"no": i + 1, "class": c, "source": source, "features": feat, "text": TEXT[int(r() * len(TEXT))], "length": 40 + int(r() * 260)}) def harmful(k): return k["class"] != "clean" WEIGHT = {"instruction_mode": 0.42, "role_change_request": 0.26, "hidden_field_name": 0.44, "side_effect_tool_name": 0.40, "source_stamp_mismatch": 0.38, "unusual_stamp_distribution": 0.30, "excessive_length": 0.12} SOURCE_BONUS = {"user": 0.00, "retrieved_document": 0.18, "tool_output": 0.12} def score(k, seed=SEED): p = sum(WEIGHT[o] for o in FEATURES if k["features"][o]) + SOURCE_BONUS[k["source"]] r = rng(seed ^ trace_id(k["no"])) return min(max(p + (r() - 0.5) * 0.20, 0.0), 1.6) def model_filter(k, threshold=0.40, seed=SEED): return score(k, seed) >= threshold def approval_gate(k, required=True): """A record carrying a side-effect tool request is asked of a human.""" return required and k["features"]["side_effect_tool_name"] def measure(records, decision): tp = fp = tn = fn = 0 for k in records: flagged, actual = decision(k), harmful(k) if flagged and actual: tp += 1 elif flagged and not actual: fp += 1 elif not flagged and actual: fn += 1 else: tn += 1 precision = tp / (tp + fp) if tp + fp else 0.0 recall = tp / (tp + fn) if tp + fn else 0.0 return {"tp": tp, "fp": fp, "fn": fn, "tn": tn, "precision": round(precision, 4), "recall": round(recall, 4), "false_block_rate": round(fp / (fp + tn), 4) if fp + tn else 0.0} THRESHOLD, SECOND = 0.50, 20260219 # deployed threshold , second run seed published = measure(RECORDS, lambda k: model_filter(k, THRESHOLD)) second = measure(RECORDS, lambda k: model_filter(k, THRESHOLD, SECOND)) print(f"published single number: recall {published['recall']:.4f} , in the second run" f" {second['recall']:.4f} (band {second['recall'] - published['recall']:.4f})") print(f"false block rate {published['false_block_rate']:.4f} and" f" {second['false_block_rate']:.4f}") print("\nclass records 20260218 20260219 resolution") class_recall = {} for c in CLASSES[1:]: ks = [k for k in RECORDS if k["class"] == c] d = sum(model_filter(k, THRESHOLD) for k in ks) / len(ks) d2 = sum(model_filter(k, THRESHOLD, SECOND) for k in ks) / len(ks) class_recall[c] = d print(f"{c:<20} {len(ks):>8} {d:>9.4f} {d2:>9.4f} {1 / len(ks):>11.4f}") lowest = min(class_recall, key=class_recall.get) print(f"difference between the published number and the lowest class:" f" {published['recall'] - class_recall[lowest]:.4f} ({lowest})") # ---- System card: the measures it carries. Every measure not carried remains a CLAIM. NARROW = {"share of harmful blocked", "false block rate"} WIDE = NARROW | {"per-class recall", "resolution of the set", "records sent to a human", "covered share of false alarms"} COMMITMENTS = [ ("usage limit", "not used in the stated class", "per-class recall"), ("usage limit", "irreversible action needs approval", "records sent to a human"), ("usage limit", "not the sole basis of a result", "share of harmful blocked"), ("transparency item", "share of records wrongly blocked", "false block rate"), ("transparency item", "resolution of the measurement", "resolution of the set"), ("transparency item", "set the measurement was made on", "set coverage"), ("misuse plan", "share covered by appeals", "covered share of false alarms"), ("misuse plan", "delay in seeing a degradation", "indicator delay"), ("misuse plan", "number of untried classes", "uncovered class"), ] sent = [k for k in RECORDS if approval_gate(k)] print(f"\nnumbers added by the wide card: records sent to a human {len(sent)} , of them" f" {sum(harmful(k) for k in sent)} harmful ; set {len(RECORDS)} records ," f" recall step {1 / 93:.4f} , false block step {1 / 147:.4f}") print("\ncommitment class commitment narrow card wide card") for kind, what, measure_name in COMMITMENTS: print(f"{kind:<23} {what:<36} {'number' if measure_name in NARROW else 'claim':<12}" f" {'number' if measure_name in WIDE else 'claim'}") for name, card_set in (("narrow", NARROW), ("wide", WIDE)): present = sum(o in card_set for _, _, o in COMMITMENTS) print(f"{name} card: {present}/{len(COMMITMENTS)} commitments tied to a number ," f" {len(COMMITMENTS) - present} remain claims")
published single number: recall 0.7097 , in the second run 0.7419 (band 0.0322) false block rate 0.0544 and 0.0544 class records 20260218 20260219 resolution indirect_instruction 24 0.9583 0.9583 0.0417 hidden_field_request 17 0.7059 0.7059 0.0588 privilege_escalation 10 0.8000 0.8000 0.1000 source_spoofing 23 0.8261 0.8261 0.0435 corrupted_input 19 0.2105 0.3684 0.0526 difference between the published number and the lowest class: 0.4992 (corrupted_input) numbers added by the wide card: records sent to a human 27 , of them 24 harmful ; set 240 records , recall step 0.0108 , false block step 0.0068 commitment class commitment narrow card wide card usage limit not used in the stated class claim number usage limit irreversible action needs approval claim number usage limit not the sole basis of a result number number transparency item share of records wrongly blocked number number transparency item resolution of the measurement claim number transparency item set the measurement was made on claim claim misuse plan share covered by appeals claim number misuse plan delay in seeing a degradation claim claim misuse plan number of untried classes claim claim narrow card: 2/9 commitments tied to a number , 7 remain claims wide card: 6/9 commitments tied to a number , 3 remain claims
The Remaining Claim
The published single number is 0.7097, and it is hard to call it wrong: it is correct on the set. Per class, the same defense spreads between 0.9583 and 0.2105; the difference between the published number and the weakest class is 0.4992. “This system is not used in this class” is a usage limit, and the number that would verify it is only on the class row, not inside the average. The narrow card does not carry that row, so the sentence remains a claim.
The class row opens a second thing: The two-run band of the published number is 0.7097 to 0.7419, three resolution steps, and the whole band comes from the weakest class: corrupted input moves between 0.2105 and 0.3684 while the other four classes show zero difference.
Widening the card ties four more of the nine commitments to a number: the class row, the resolution, the 27 records sent to a human and the covered share of the appeals. The remaining three are covered by no card; that is the state of the measurement, not a flaw of the card. What the set the measurement was made on represents no measurement on that set can show. The delay in seeing a degradation came out as 45 and 220 records in two runs, so its band is wider than the delay. The number of untried classes can only be bounded by counting the tried ones; the share that passed for a class with no row in the report is not zero but unknown.
This is the course rule on the release side: where the published number is not written together with the set it covers, everything it does not cover is a claim.
Summary
- The published average is 0.7097; per class the same defense spreads between 0.9583 and 0.2105, a difference of 0.4992.
- The average’s two-run band is 0.0322, and the whole band comes from the weakest class; the other four show zero difference.
- A class’s resolution is the inverse of its record count: 0.1000 for ten records, so even a row written to four digits cannot be read that finely.
- Of the nine commitments the narrow card ties 2 to a number, the wide card 6; the other 3 have no measure: the set’s representativeness cannot be measured from inside, the indicator delay’s band is wider than the delay, and the untried class’s share is unknown.
Course Wrap-Up
The numbers measured over the course: The sign scheme is single: + shows that the defended setup’s number is higher than the baseline, − that it is lower, and neither says good or bad. Where rows share no unit, the measure follows the lesson name.
| Lesson | Undefended baseline | Defended setup | Cost |
|---|---|---|---|
| Prompt Injection — recall | no filter 0.0000 | rule filter 0.7957 (+0.7957) | false alarms 0 to 23 |
| Jailbreak Techniques — recall | threshold 0.80: 0.4946 | threshold 0.30: 0.9032 (+0.4086) | false block rate 0.0068 to 0.1837 |
| Data Exfiltration — requests stopped (35) | no filter 0 | full rule filter 25 (+25) | added by masking 0, residue 1 |
| Tool Misuse — harmful sent to a human | no gate 0 | approval gate 24 (+24) | 3 unnecessary reviews, recall 0.2581 |
| Training Data Poisoning — harmful share in the corpus | no filter 0.3875 | rule gate 0.1329 (−0.2546) | 23 clean records lost |
| Model Inversion and Inference Attacks — hidden fields out | no filter 106 | four-name filter 0 (−106) | clean replies losing a field 41 |
| Adversarial Examples — recall on corrupted input | rule filter 0.2632 | rule with added flag 0.8421 (+0.5789) | false alarms 23 to 30 |
| AI Red Teaming — share that passes the defense | narrow campaign 0.1000 | 93-trial coverage 0.2043 (+0.1043) | selection band 0.0600, untried unknown |
| Input and Output Filters — precision | rule filter 0.7629 | rule and model stacked 0.7551 (−0.0078) | false block rate 0.1565 to 0.1633, recall the same |
| Content Moderation — recall on corrupted input | rule filter 0.2632 | content moderation 0.8421 (+0.5789) | false block rate 0.1565 to 0.2177 |
| Personal Data Masking — records cleaned | no masking 0 | flag policy 32 (+32) | tasks broken 23, 7 for a clean subscriber |
| Permissions and Sandboxing — harmful stopped | undefended 0 | read-only 62 (+62) | tasks completed 1.0000 to 0.6667 |
| Untrusted Content Marking — harmful neutralized | no marking 0 | marking 42 (+42) | false alarms 0, 1 clean record lost its way |
| Actions Requiring Human Approval — harmful caught | no gate 0 | narrow gate 24 (+24) | 3 unnecessary interruptions; reviews 4 at 0.20 |
| Evaluation Set Design — precision (defense fixed) | representative set 0.7629 | borderline sample set 0.4444 (−0.3185) | false block rate 0.7143 |
| Automated Evaluation — agreement with human labels | criteria-based 0.8250 | model judge 0.8833 (+0.0583) | 0 to 246 calls, 10779 tokens |
| Human Evaluation — agreement between evaluators | three-level scale 0.7583 | guideline with examples 0.9306 (+0.1723) | unanimity accuracy 0.9126 to 0.8744 |
| Regression Testing — recall of the judge | format constraint in place 0.9570 | constraint removed 0.7742 (−0.1828) | calls 246 to 330, unreadable replies 0 to 31; false block difference exactly 0.0000 |
| Production Monitoring — delay in seeing a degradation | unreported without indicator | proxy reports after 20 records | 32 reviews in 300 records; labeled indicator 45 and 220 |
| The Feedback Loop — covered share of false alarms | channel closed 0.0000 | narrow appeal channel 0.3750 (+0.3750) | false alarms 8 to 27 in five rounds |
| Responsible Release — commitments tied to a number (9) | narrow card 2 | wide card 6 (+4) | 3 commitments remain claims |
The course’s rule is written in the table row by row: a defense whose blocked share and wrongly blocked share are not written together counts as unmeasured. With the cost column empty every row would read like a success; it does not, because we wrote it.
The second claim is read from the same table too: an added defense can make the work worse. The rows that say so by name: Input and Output Filters (one more clean record blocked, no new harmful one caught), Data Exfiltration (masking adds 0, at the cost of nine deleted fields), Tool Misuse (the source-widened gate takes forty-four clean reviews for nine harmful, precision 0.5586), Training Data Poisoning (the source gate raises clean record loss from 23 to 66 for an unmeasurable 0.0054), Content Moderation (the label falls on nine clean records, the defense stays below the baseline), Personal Data Masking (masking every record breaks 169 tasks), Permissions and Sandboxing (four of the eighteen added records are harmful, false block rate 0.2517), Actions Requiring Human Approval (a wider gate takes the caught from 24 to 17, interruptions to 23) and The Feedback Loop (false alarms 3.4 times, no new harmful one caught). In nine places the added layer made us pay for being added.
Differences below the resolution are written by name and are not findings: in Jailbreak Techniques, the one-record difference at threshold 0.30; in Data Exfiltration, the residue that falls to zero in the second run; in Training Data Poisoning, the 0.0054 below the 0.0106 step; in Adversarial Examples, 0.4737 versus 0.4211 inside the class; in Permissions and Sandboxing, one full step of 0.0068; in Actions Requiring Human Approval, the counts 22, 22, 23; in Automated Evaluation, the false block difference of 0.0068 and the class difference of 0.0588; in Production Monitoring, the indicator whose band is wider than its delay. None is zero, none is measured.
This wrap-up also closes the AI Engineering curriculum. The measurement axis of the seven courses gathers into one sentence: surface → prompt → call → retrieval → loop → contract → defense. Each course started where the previous one left off and wrote the cost of the layer it added. The curriculum’s rule one last time: an AI system’s number is not the model’s number but the setup’s number; a setup whose cost is not written counts as unmeasured.
Over one hundred and thirty-two lessons the setup was built around the model, each piece with its cost written. One thing was never asked: the data pipeline that feeds this setup. The record set was generated inside the lesson, the document vocabulary written by hand, the trace record kept in memory. Where this data comes from, how it runs in production and who learns when it breaks went unasked. The next curriculum, Data Engineering and MLOps, builds this pipeline beneath the setup and counts its cost too.
To keep your progress and take notes, Log in
My notes
Log in to take notes.