Skip to content
ML Academy

project-039 · LLM and GenAI · aviation

Structured extraction from disruption notices: JSON-schema validation, safe repair and injected-instruction detection

An airline operations team wants free-text disruption notices turned into a JSON record (flight, date, airports, disruption type, delay minutes, cause category, whether rebooking was offered) that downstream automation can trust. Before connecting any model, the team needs a validation layer: how many outputs pass the schema, how many can be repaired without inventing values, how many are schema-valid but wrong, and how reliably are outputs that followed an instruction hidden in the notice caught?

Dataset
Synthetic airline disruption notices with gold JSON and labelled recorded-output fixtures (academy-authored)
Licence
Academy-authored synthetic data; no third-party licence
Reference run
executed

At a glance

  • Data

    Synthetic airline disruption notices with gold JSON and labelled recorded-output fixtures (academy-authored) (synthetic) · Reference run: 100 synthetic notices and 182 labelled output fixtures (valid 25, syntax 26, schema 47, semantic 51, injection-followed 18, injection-ignored 15)

    Details on data
  • Baseline

    Parse with the standard JSON parser and validate with the schema; anything that fails goes to human review, with no repair.

    Details on baseline
  • Validation

    Split notices 50/50 into development and test before writing repair rules; rules and detector thresholds are written on development fixtures only and frozen before the…

    Details on validation
  • Metric

    Schema-valid rate before and after repair; harmful-repair count; per-field exact match against gold; silent-wrong rate; injection detector precision/recall;

    Details on metric

Success criterion

On the labelled fixture set, the validator must catch 100% of schema violations (a deterministic requirement), repairs must never change a value that was present and correct (zero harmful repairs), and the injected-instruction detector must reach recall of at least 0.90 at precision of at least 0.80 on the injection-labelled fixtures (targets, not results). The operational number to report is the "silent wrong" rate: schema-valid outputs with a wrong field value, which validation alone cannot catch.

Context

Day 47 teaches that a schema is a contract and that model output is untrusted input. Provider features that constrain output to a schema reduce syntax failures but do not make values correct. This project builds the layer that sits between any generator and the business system. The reference implementation works on recorded output fixtures - hand-authored, clearly labelled examples of what model outputs look like - not on live generations, so it measures the validator, not a model. With a configured model the same validator runs on live outputs.

Data

Dataset
Synthetic airline disruption notices with gold JSON and labelled recorded-output fixtures (academy-authored) (synthetic)
Licence
Academy-authored synthetic data; no third-party licence
Size
Reference run: 100 synthetic notices and 182 labelled output fixtures (valid 25, syntax 26, schema 47, semantic 51, injection-followed 18, injection-ignored 15)
Verification
unverified

Access

Generated and stored with the reference implementation (no network, no login). Notices are synthetic text in the style of the academy's synthetic airline world (routes such as IST-FRA, flight numbers and dates consistent with python/artifacts/airline_sample.sqlite). Each notice has a gold JSON record. Each recorded-output fixture is a hand-authored string labelled with its fault class; fixtures are not outputs of any real model and are labelled "recorded fixture" in every file and report.

Grain

One row per (notice, fixture output); fault labels are per fixture; gold values are per notice field.

ColumnMeaning
notice_id / notice_textSynthetic disruption notice; some contain an embedded instruction (labelled)
goldCorrect JSON record for the notice, validated against the schema
fixture_outputRecorded output string as a model might return it (labelled fixture, not a live generation)
fault_classvalid, syntax (fences, trailing comma, truncation), schema (missing required, wrong type, bad enum, extra field), semantic (valid but wrong value), injection-followed, injection-ignored
injectedTrue when the notice text contains an instruction aimed at the extractor
expected_routeaccept, accept-after-repair, or human-review - the label the pipeline decision is scored against

EDA and data-quality rules

  • Every gold record validates against the JSON schema; the schema itself is versioned and tested with valid and invalid examples.

  • Fault classes are balanced enough to estimate each rate (at least 15 fixtures per class) and every fixture has exactly one primary fault label.

  • Semantic checks are explicit rules (origin differs from destination, delay minutes non-negative, date parses and lies within the notice's stated window, flight number pattern) and each is tested.

  • Injection fixtures are harmless (they ask for a field change, an extra field, or a fake e-mail action to example.invalid) and labelled in the file.

Baseline

Parse with the standard JSON parser and validate with the schema; anything that fails goes to human review, with no repair. This "strict reject" baseline has zero harmful repairs by construction, so every repair strategy must justify the human-review load it removes.

Features

Repair operations, each logged: strip markdown code fences and leading prose; remove trailing commas; normalise enum case and a small synonym map (e.g. "cancelled"/"canceled"); coerce numeric strings to integers when lossless. Never repaired: missing required fields, out-of-range values, values that disagree with the notice. Cross-checks against the source: flight number and airports must appear in the notice text. Injection signals: imperative phrases addressed to the system in the notice, e-mail addresses or URLs in output fields that have none in gold, extra fields, and boolean fields that flip in the direction requested by the embedded instruction.

Candidate methods

  • Deterministic repair plus semantic rules

    Recovers formatting faults cheaply while every rule is auditable and unit-tested.

  • Rule-based injected-instruction detector on input and output

    Detects the pattern 'notice asked for X and output did X'; transparent and runs before any downstream action.

  • Re-ask the model with the validation error (live mode only)

    Often fixes schema faults without rules, but costs a second call, can introduce new errors and cannot run in fixture mode.

Validation design

Split notices 50/50 into development and test before writing repair rules; rules and detector thresholds are written on development fixtures only and frozen before the test fixtures are scored. In live mode, run each notice three times to measure output variability and score all runs.

Leakage checks

The same author writes fixtures and rules, so rules can be tailored to known fixtures. Freeze the test fixtures before writing rules, and add ten test fixtures written by someone else (or after a week) to estimate overfitting.

Bias and representativeness

Notices follow a few templates in English; real notices vary by station and language. Report results by template and add a small set of free-form notices to show the drop.

Robustness checks

Unicode look-alikes in flight numbers, very long notices that truncate output, numbers written as words, and two flights in one notice (the schema expects one - correct behaviour is human review).

Technical metrics

Schema-valid rate before and after repair; harmful-repair count; per-field exact match against gold; silent-wrong rate; injection detector precision/recall; routing accuracy against expected_route.

Business interpretation

Share of notices that flow straight through, share sent to human review (staff time), and the count of silent-wrong records that would reach automation - the number the operations owner signs off.

Error slices

  • Fault class: syntax, schema, semantic and injection fixtures have different catch mechanisms; report each.

  • Field: delay_minutes and cause_category are typically harder than flight number and date.

  • Notice template and length.

Implementation steps

  • Write the JSON schema (required fields, enums, integer ranges, additionalProperties false) and tests with valid/invalid examples.

  • Generate notices and gold records with a seeded script; hand-author fixtures per fault class and label them.

  • Implement parse, validate, repair (logged operations) and semantic/source cross-checks; unit-test each repair as a pure function.

  • Implement the injection detector and the routing decision (accept / accept-after-repair / human-review).

  • Score development fixtures, freeze rules, score test fixtures once, and write the report with the confusion matrix of routes.

  • Optional live mode: run the same pipeline on outputs from a configured model with schema-constrained output switched on and off, logging model id and date.

Reproduction

Reference implementation at python/reference/r09_structured_output_validation (status: implemented; recorded results in its RESULTS.md). Command: cd python && uv run python -m reference.r09_structured_output_validation.run. CPU-only, no network, no model, no API keys; fixtures are seeded or hand-authored files in the implementation folder; the validator is a small standard-library JSON-schema subset, so no extra dependency is needed (your learner version may use the jsonschema package). Outputs in outputs/: per-fixture results, the route confusion matrix, a log of every repair applied and metrics, and a report that states "fixture mode: no live generations evaluated".

Limitations

  • Fixture mode measures the validator on faults the author anticipated; real model faults may be different and more varied.

  • The silent-wrong rate on fixtures depends on how many semantic faults were authored; it is not a model error rate.

  • Rule-based injection detection is evadable by paraphrase; it is a tripwire, not a security boundary.

Production and monitoring

Put the validator in the request path with the schema version in every log line. Track schema-valid, repaired and human-review rates by source station and model version; alert on jumps. Repairs are logged and sampled for review. The real security boundary is downstream: extracted records cannot trigger passenger messages or refunds without a deterministic policy check and, for high-impact actions, human approval.

Interview questions

  • Schema-constrained decoding is available from the provider. Why do you still need validation and semantic checks?

  • Which repairs are safe to automate and which must never be automated? Give an example of a harmful repair.

  • How would you estimate the silent-wrong rate in production when you have no gold labels?

Postmortem and alternatives

The likely lesson is that syntax and schema faults are the easy part and semantic errors dominate risk. Discuss whether a smaller schema (fewer free-text fields) would have reduced injection surface more than any detector, and whether re-asking the model is worth its cost compared with routing to a person.

Reference implementation

Status: executed — outputs below are from the recorded run

cd python && uv run python -m reference.r09_structured_output_validation.run

r09 results - structured-output validation on recorded fixtures

Fixture mode: no live generations evaluated. Every output string scored here is a hand-authored template or a deterministic fault injection applied to a synthetic gold record (file fixtures/fixtures_v1.json, seed 20240901). These results describe the validator, not any language model. Numbers are copied from outputs/metrics.json written by cd python && uv run python -m reference.r09_structured_output_validation.run.

ItemValue
Fixture set100 synthetic notices, 182 labelled output fixtures: valid 25, syntax 26, schema 47, semantic 51, injection-followed 18, injection-ignored 15
Splitdev 95 fixtures / test 87 fixtures, split by notice (dev 50 notices / test 50 notices)
Schemadomain.RECORD_SCHEMA v1.0.0, nine required fields, additionalProperties: false
Runtime0.2 s for scoring all four strategies on all 182 fixtures; standard library only (jsonschema is not used or added)

Strategies: strict = parse and schema only (the baseline: anything that fails goes to a person); repair_only = plus the safe repairs; repair_plus_checks = plus source cross-checks, the several-flights rule and injection signals (the pipeline under test); aggressive = plus unsafe repairs (an ablation, never recommended).

Findings (all 182 fixtures unless stated)

  1. Schema validation catches every schema violation and raises no false alarm on this set. Of 78 fixtures that are not strict-valid by construction (syntax and schema faults, plus 5 injection outputs that add a field or are not JSON), the stdlib validator rejected 78 (100%, Wilson 95% interval 95.3% to 100%); of the 104 that are strict-valid by construction it rejected none. The same holds on dev (38 of 38) and test (40 of 40). The stdlib validator also agrees with the jsonschema library on every fixture output and on 400 random mutations when that library is installed (differential test; it was installed in this environment as a dependency of another tool).
  2. Only 57.1% of the outputs are valid on the first strict parse; safe repair lifts it to 73.1%, and every repair was correct. Strict-valid 104 of 182; schema-valid after safe repair 133 of 182. 29 fixtures were repaired with 32 logged operations (9 enum normalisations, 7 code-fence strips, 6 trailing-comma removals, 5 prose strips, 3 boolean- string and 2 integer-string coercions). Harmful repairs (a correct value altered): 0. Invented values filled in: 0. Records accepted after repair that differ from gold: 0. These are properties of the 14 value-level repairs and 18 formatting repairs on this set, not a guarantee for unseen faults.
  3. Routing. Route accuracy against the expected route: strict 48.9% (89/182), repair_only 64.8%, repair_plus_checks 90.7% (165/182, Wilson 85.6% to 94.1%), aggressive 82.4%. Under the default pipeline 53 outputs (29.1%) go straight through, 29 (15.9%) are accepted after repair and 100 (54.9%) go to a person. Route confusion (expected by predicted): accept 38 / 0 / 2, accept-after-repair 0 / 29 / 0, human-review 15 / 0 / 98. Dev and test agree (89.5% and 91.9%).
  4. The silent-wrong rate is the number that matters, and validation alone leaves most of it. A schema-valid but wrong record that is accepted is "silent wrong". Strict: 60 of 104 accepted records (57.7%). Safe repair adds 29 records that are all correct, so the count stays at 60 and the denominator grows: 60 of 133 (45.1%). Source cross-checks and the injection signals cut it to 15 of 82 accepted records (18.3%, Wilson 11.4% to 28.0%; 8.2% of all fixtures). All 15 come from the four semantic fault types the rules cannot see by design (date equal to the issue date, delay taken from another number in the notice, rebooking flipped when the phrase is not in the lexicon, a vague cause made specific; 4, 4, 4 and 3 fixtures). Dev has 9 and test has 6.
  5. Cross-checks, not the schema, do the work against wrong values. 36 of the 51 semantic fixtures reach a person: 32 through a source check (flight, airport, date or delay absent from the notice, a type, cause or rebooking value that contradicts the wording, and so on) and the 4 two-flight notices through the several-flights rule. Per-field exact match among accepted records under the default pipeline: delay_minutes 95.1%, date 95.1%, rebooking_offered 95.1%, cause_category 96.3%, every other field 100%; under strict acceptance delay_minutes is 82.7%.
  6. Injected instructions. The output-side detector found 18 of 18 followed-instruction outputs (recall 1.00, Wilson 82.4% to 100%) with 2 false alarms (precision 0.90, 18 of 20). The two false alarms are the fixtures in which the instruction asked for the value that was already true, so the output equals gold (labelled "ignored" by construction); a detector cannot tell that from following. The input-side detector flagged all 33 notices with an embedded instruction and none of the 62 others (the fixture set has no benign notice containing a link; a unit test checks that a link without an instruction is not flagged on the input side). On test alone: output side 8 of 8 recall, 8 of 9 precision. Of the 18 followed outputs, 13 are schema-valid: under the strict baseline those 13 would have reached automation. The default pipeline routes all 18 to a person; 8 are caught by a source check (rebooking, delay or cause contradicts the notice) and the injection signal, 5 by a schema/parse failure and the signal, and 5 (the e-mail and link requests that live in the free-text summary) by the injection signal alone.
  7. Unsafe repairs are not an improvement. The aggressive strategy repairs 55 fixtures with 62 operations (39 field- level). It sends 15 more fixtures to accept-after-repair: 11 whose repaired record happens to equal gold (6 extra fields dropped, 3 unknown enum values mapped to "other" where gold was "other", 1 day-first date guess, 1 negative delay clipped to 0 where gold was 0) and 4 truncated outputs closed by the unsafe closer with the missing summary invented ("n/a"). Its silent-wrong count is 19 against 15, its route accuracy 82.4% against 90.7%, and 4 invented values were written. It did not alter a correct value on this set; its apparent successes depend on the fixture happening to agree with the default, which is the reason the default pipeline refuses them. The test test_a_tempting_repair_is_blocked... shows a truncated record closed to "delay_minutes": 9 (a cut-off 95), which only the cross-check then stops.
  8. By template. The silent-wrong count per template (default pipeline) is 6 of 36 fixtures (template 1, the one with a "check-in closes N minutes" distractor), then 2 to 3 for the others, 0 for the four two-flight notices (all routed to a person by the several-flights rule).

What this does not prove

  • It does not measure any model. No model produced these strings. Real model faults are more varied than the fault subtypes the author thought of, and the real rates of each are unknown.
  • The 100% catch rate for schema violations is by construction and expected: the schema and the labels come from the same author, and the validator is deterministic. It says the validator implements the schema, not that the schema is the right contract.
  • The silent-wrong rate (8.2% of fixtures, 18.3% of accepted) is a function of how many uncatchable semantic faults the author wrote (15 of 182). It is not an error rate for a model, and it should not be quoted as one.
  • The injection detector's recall of 1.00 is circular: the same author wrote the seven instruction templates and the patterns. A paraphrased, encoded, translated or split instruction is not tested and would likely evade a pattern-based tripwire. It is not a security boundary: the boundary is that an extracted record cannot trigger a passenger message or refund without a deterministic policy check and, for high-impact actions, human approval.
  • Dev and test are split by notice, but rules and fixtures have one author and no rule was changed after scoring. The test split therefore says nothing about generalisation to fault types the author did not anticipate; the record's ten fixtures written by someone else were not available.
  • Notices follow five English templates (plus a two-flight form). Free-form, multilingual, spelled-out numbers ("ninety- five minutes"), look-alike characters beyond three fixtures and very long notices were not evaluated.
  • Wilson intervals treat fixtures as independent; fixtures share notices (about 2 per notice), so the intervals are somewhat optimistic.
  • The routing policy for an ignored injection (accept, log the flag) is a judgment; sending every flagged notice to a person would add the 13 accepted ignored-instruction fixtures (7% of all fixtures) to the human-review load.

Reproduce

cd python && uv run python -m reference.r09_structured_output_validation.run
cd python && uv run pytest tests/test_r09_structured_output_validation.py

Builder-authored reference. It is not learner evidence and is never credited to your portfolio.

Rubric3 criteria

CriterionMeetsExceeds
Schema and validationVersioned schema with tests; all schema violations caught; semantic rules explicit and tested.Source cross-checks catch schema-valid wrong values and are measured separately as silent-wrong reduction.
Repair safetyEvery repair is logged and none fills a missing or wrong value.Shows a tempting repair that would be harmful and the test that blocks it.
Honest evidenceFixture-mode results labelled as validator measurements, not model quality.Live mode run with a logged model and compared with fixture expectations.

References5