Independent, reproducible evaluation of diagnostic and predictive machine-learning classifiers, working from model outputs and ground-truth labels only. No model weights, no images, no patient-identifiable data need to leave the developer's or the hospital's environment. The harness is standard-library Python: clone it and run it, there is nothing to install.
Built and maintained by UK AI Evaluation Ltd, an
independent AI assurance company in London. The method the harness implements is
published and version-tagged:
METH-0.2, Diagnostic / Predictive-ML Evaluation Track
(a copy is in docs/).
Status. Pre-product research tooling, released so that the method and the code can be read, run and challenged by anyone. Nothing produced by this harness is a certified or accredited assessment, and UK AI Evaluation is not affiliated with, certified by, or endorsed by the MHRA, the UK AI Security Institute or the NHS.
git clone https://github.com/ukaievaluation/harness.git
cd harness
python3 -m ukaie.harness.run_demoThat runs the seeded synthetic corpus, which plants known failures (a transferability drop, miscalibration, a shortcut, a subgroup gap, a sensitivity shortfall and post-deployment drift), scores it through the engine, and exits non-zero if any planted failure is missed. The synthetic run validates the detector; it is never evidence about a real model.
To evaluate a real model, export a CSV of its predictions and run:
python3 -m ukaie.harness.run_demo \
--internal-csv developer_site.csv \
--external-csv deployment_site.csv \
--monitoring-csv monitoring.csv --monitoring-period-col period \
--vendor-auroc 0.82 --sensitivity-floor 0.85 \
--cluster-col patient_id \
--no-selftest --out scorecard.jsonexamples/ contains a generator for three example CSVs in exactly this
shape, so the command above can be tried before any real data is touched.
One row per case. This is the whole surface; the harness never calls the model.
| Column | Required | Meaning |
|---|---|---|
label |
yes | ground truth, 0 or 1 |
score |
yes | the model's output probability for the positive class, 0 to 1 |
id |
no | a row identifier; generated if absent, and need never be a patient identifier |
group |
no | the stratum for subgroup analysis (D4); may be supplied pre-coded as opaque labels |
site |
no | acquisition source (hospital, scanner, assay) |
score_perturbed |
no | the model's score on the same case with the suspected shortcut removed (enables D3) |
period |
no | monitoring period, for a single post-deployment export split with --monitoring-period-col |
Loader: ukaie/harness/adapters.py.
The method defines seven dimensions. Which rows are computed by this version is stated on the face of the method (METH-0.2 §3) and repeated here.
| Dimension | What it asks | This version |
|---|---|---|
| D1 Discrimination and transferability | Does performance hold when the data crosses the axis the model meets in deployment? | AUROC with tie handling, delta-AUROC developer to deployment site |
| D2 Calibration | Do the probabilities mean what they say, on both sites? | ECE, Brier, calibration slope and intercept |
| D3 Shortcut learning and leakage | Is it keying off a scanner, site or marker rather than pathology? | perturbation-degradation probe and conditional site-gap probe (severity band gated, see below) |
| D4 Subgroup and equity | Does it work equally across groups, and can we tell? | per-group AUROC, sensitivity and false-negative rate with an underpowered-group guard |
| D5 Operating point | At the deployed threshold, does it clear the sensitivity floor? | sensitivity, specificity, PPV, NPV at threshold |
| D6 Automation bias and over-reliance | How do clinicians behave with it? | not assessed by this harness; requires a human-factors study |
| D7 Post-deployment drift | Is it decaying in service? | PSI and KS on score distributions, AUROC decay against the sign-off set, with a power guard |
Every headline metric carries a bootstrap confidence interval, resampled by patient
when a --cluster-col is given, so that several cells from one film or images from one
study are not treated as independent cases.
Not yet implemented and specified for the next build: AUPRC and the prevalence baseline, Wilson intervals, adaptive ECE and Spiegelhalter Z, recalibration fitting, the risk-coverage curve, out-of-distribution detection and decision-curve analysis.
run_demo writes one JSON scorecard containing:
- provenance: UTC timestamp, Python version, git commit, seed, SHA-256 of the input
rows, and the methodology, engine and rubric versions (
METH-0.2, engine0.5.0,RUBRIC-0.4in this release); - the raw metrics, probe findings, drift table and confidence intervals;
- the D1 to D7 scorecard as Markdown, ready to paste into a report;
- a mapping of the computed rows onto the reporting standards the method anchors to
(
ukaie/scoring/standards_map.py), derived from the resolved rows so an item can never claim evidence the run did not produce; - the engine's headline result and deployment recommendation.
A sample from the synthetic self-test is committed at
ukaie/harness/sample_output/diagnostic_demo_scorecard.json.
| Package | Role |
|---|---|
ukaie/scoring/ |
The scoring engine. Thresholds and mappings live in one file, constants.py, and every change to them is a version bump. diagnostics.py computes the metrics, engine.py scores findings, uncertainty.py does the bootstrap, standards_map.py maps rows to standards. |
ukaie/harness/ |
The front end. adapters.py loads predictions, corpus.py builds the seeded synthetic corpus, probes.py runs the D3 and calibration probes and the deployment-site view, drift.py does D7, run_demo.py is the end-to-end entry point. |
docs/ |
The methodology documents the engine stamps into every scorecard. |
Tests sit beside the code (test_scoring.py, test_harness.py) and run with pytest.
- The D3 shortcut metric is computed and reported but does not yet move the deployment
gate (
T3_ENABLED = Falseinconstants.py) until the severity bands have been calibrated with a clinical advisor. The probe supplies the evidence; it does not flip the methodology switch. - D6 is not assessable from outputs and labels. The scorecard says so rather than leaving the row blank.
- The synthetic corpus proves the detectors catch what they are built to catch. It proves nothing about any real model.
- The harness scores what it is given. Whether the developer and deployment files really cross a deployment axis, and whether the labels are trustworthy, is the assessor's job and the method's §4 describes it.
Three version strings are stamped into every scorecard and bumped independently:
| String | Where | Meaning |
|---|---|---|
METH-0.2 |
constants.METHODOLOGY_VERSION_DIAGNOSTIC |
the published method a result was produced under |
0.5.0 |
constants.SCORING_ENGINE_VERSION |
bumped on any threshold or logic change |
RUBRIC-0.4 |
constants.RUBRIC_VERSION |
the scoring rubric; the comment beside it records what each revision changed and which published scores it affects |
A third party with the same input file, seed and commit gets the same scorecard.
This repository is the public home of the harness. Day-to-day development happens in the company monorepo beside the evaluation platform, and the harness is exported here at every engine, rubric or methodology version. Issues and pull requests opened here are read and folded back; see CONTRIBUTING.md.
Code is licensed under Apache-2.0. The methodology documents in docs/ are
licensed under CC BY 4.0. See NOTICE for attribution and the
independence and non-endorsement statement. To cite, see CITATION.cff.
UK AI Evaluation Ltd, London. https://ukaievaluation.com · inquiries@ukaievaluation.com