Variant interpretation system
Variant Evidence Agent
Calibrated VUS reclassification with an evidence-citing agent — every claim cited to a retrieved record, or the variant is declined.
An LLM / RAG agent with a hard rule: cite, or decline. Never invent. Every claim in a report is cited to a retrieved record — and one answer is explicitly allowed: “insufficient evidence — remains VUS.”
The problem this solves
Variants of Uncertain Significance (VUS) are the daily bottleneck of clinical genomics.
Predictors disagree. Most published models are trained and tested on random ClinVar splits — which leaks variants that were reclassified using the same evidence the model is then credited for learning. And an AUROC of 0.99 tells a lab nothing about whether a “90% pathogenic” call is right 90% of the time.
The gap is not another AUROC point. The gap, as the project’s README states it, is three things at once:
- Temporal validity — train on classifications frozen at a cutoff, test only on variants reclassified after it.
- Calibration — a probability that means what it says, reported per gene family (ECE / Brier), including where calibration fails.
- Evidence provenance — every claim in a report cited to a retrieved record, with an explicit allowed answer: “insufficient evidence — remains VUS.”
Data — a temporal split, built from dated ClinVar releases
All data is open and pinned by date, because the temporal split depends on it. The cutoff snapshot held 2,366,650 GRCh38 rows; the current snapshot holds 4,578,190. The split report (data/processed_v2/SPLIT_REPORT.md) records exactly what fell on each side of the freeze:
- Train — 1,103,657 variants labelled at the Jan 2024 cutoff and frozen: 240,752 pathogenic, 862,905 benign.
- Test — 27,748 variants that were uncertain at the cutoff and have since been reclassified: 8,856 pathogenic, 18,892 benign. No test label existed at train time.
- Near-duplicate guard: test rows sharing gene + name with train are removed — the guard removed 0 rows.
Why random splits leak (from docs/leakage-audit.md)
A variant reclassified in 2024 was often reclassified using the same predictor scores and submissions that appear as features. Random splits put that variant — or its near-duplicates — in both train and test: the model is graded on evidence it has already seen. The audit was written before any model code: pin a dated release, train on classifications frozen at the cutoff, test only on variants whose classification changed after it, and join features as of the cutoff where the source allows dating — and say so per feature where it does not.
Predictor coverage (from FEATURE_REPORT.md)
AlphaMissense is precomputed and missense-only, so coverage is partial by construction: it scores 37.4% of the test set (30.0% of benign, 53.2% of pathogenic) and 12.6% of train. Every model result below is on the scored subsets — 138,972 train rows and 10,373 test rows carry an AlphaMissense score. The scores are linked from the source, not redistributed: the downloaded file’s own header reads CC BY-NC-SA 4.0 while other sources say CC BY 4.0, and the project keeps the ambiguity stated rather than resolved in its own favour (see docs/licence-notes.md).
gnomAD — population context, with a documented caveat
The gnomAD file downloaded for allele context turned out to be locus-level allele number only — no allele count, no allele frequency for a specific variant allele (docs/gnomad-note.md). The project does not use it as an AF feature and says so, rather than relabelling coverage as frequency. True AF joins later via per-chromosome sites VCFs or the API; the models below stand without AF until then.
The temporal split, drawn
The split the whole evaluation rests on, as one picture. Classifications are frozen at the Jan 2024 cutoff; the test set is only what changed afterwards — variants whose labels did not exist when the model was trained:
Source: data/processed_v2/SPLIT_REPORT.md and docs/validation-dossier.md §2, §7. Of the 27,748 post-freeze reclassifications, the agent summarises 10,357 (37.3%) with cited evidence and declines 17,391 (62.7%). Coverage is the binding limit: the predictors are missense-only, so most of the declined variants are non-missense variants with no predictor score at all. A decline is a designed output, not an error.
Method
Features: predictor scores and a gene prior
Two feature families feed the models. AlphaMissense provides a precomputed pathogenicity score per missense variant (weights are not released, so there is no scoring of novel variants beyond the precomputed predictions). The gene prior is computed from the frozen train labels only — smoothed, m = 10 — and genes unseen in train fall back to the global train rate, 0.379. REVEL joined later as a second predictor: v1.3 from its public Zenodo record, MD5 verified against the record, taking the maximum across transcripts per the dbNSFP convention, stated openly in the report.
Ensemble
The models began as a logistic ensemble over the AlphaMissense score and the gene prior. Three names recur in the reports and on this page: Model 2 is that logistic ensemble with isotonic calibration — the earlier logistic model, and the one the live app served until 2026-10-26; M2+R is the same logistic model with REVEL added as a third feature; GBM+R is gradient boosting over the same three features. On 2026-10-26, Model Report 6 re-ran every candidate head-to-head on the same frozen test and adopted GBM+R as the final model. Probabilities from a raw model are not trusted as probabilities until they survive calibration; discrimination (AUROC) and calibration (ECE, Brier) are reported as separate claims for every model variant, including the ones that got worse.
Calibration
Two calibration maps are compared throughout: Platt scaling and isotonic regression, each fitted on train data only — never on the test set. Calibration quality is reported overall and per gene family (genes with n ≥ 50 scored test variants), because an aggregate number can hide badly miscalibrated subgroups.
A from-scratch MLP, tried and reported
Model Report 3 documents a hand-built alternative: a 2-16-16-1 tanh MLP in raw NumPy (seed 13, 300 epochs, loss 0.703 → 0.266), with the gene prior gated by exp(−|am − 0.5| × 4) so the prior only speaks where AlphaMissense is uncertain. Permutation importance on the test set: AlphaMissense score 0.360, gated prior 0.043. Its results are in the table below; the report’s own reading is that more model is not more truth.
A method trap, documented in Model Report 4
Building the gene prior from all frozen-train rows instead of only the AlphaMissense-scored rows silently drops AUROC to 0.933–0.935 (Brier 0.101). The prior has to describe the scored population the model actually serves, not the whole database. The trap is written into the report so it stays visible.
Results
All rows are on the same temporal test set — variants reclassified after the Jan 2024 freeze. Lower ECE and Brier are better; higher AUROC is better. This is the README’s results table, from the model reports in data/processed_v2/:
| Model | n (test) | AUROC | ECE ↓ | Brier ↓ |
|---|---|---|---|---|
| AlphaMissense alone | 10,373 | 0.921 | 0.040 | 0.113 |
| AlphaMissense + Platt (fit on 2024) | 10,373 | 0.921 | 0.157 | 0.141 |
| Ensemble (AM + gene prior) raw | 10,373 | 0.959 | 0.251 | 0.176 |
| Ensemble + isotonic (fit on train holdout) — Model 2, the earlier logistic model | 10,373 | 0.958 | 0.017 | 0.078 |
| Ensemble + REVEL + isotonic (M2+R, Model Report 4) | 10,357 | 0.968 | 0.023 | 0.066 |
| GBM+R — gradient boosting + REVEL + isotonic (Model Report 6) Final | 10,357 | 0.970 | 0.025 | 0.062 |
Read the middle rows carefully: adding the gene prior lifted AUROC to 0.959 but the raw ensemble’s calibration worsened to ECE 0.251. Discrimination and calibration are different claims — the table reports both, for every variant of the model, including the ones that got worse. The final row changed on 2026-10-26: Model Report 6’s head-to-head adopted GBM+R for the best AUROC (0.970) and Brier (0.062) of the field — at a calibration cost: its ECE of 0.025 is worse than the earlier logistic model’s 0.017, which stays the best-calibrated row in this table. That comparison is kept in full on the evaluations page.
Further variants from the model reports
Model Reports 2–4 score additional variants under the same protocol. None replaces the headline table; all are published:
| Model variant | n (test) | AUROC | ECE ↓ | Brier ↓ |
|---|---|---|---|---|
| Ensemble + Platt (fit on train holdout) | 10,373 | 0.959 | 0.276 | 0.187 |
| REVEL + gene prior (n = 11,023 scored) | 11,023 | 0.952 | 0.019 | 0.081 |
| From-scratch MLP, raw | 10,373 | 0.947 | 0.048 | 0.097 |
| From-scratch MLP + isotonic | 10,373 | 0.947 | 0.013 | 0.090 |
| From-scratch MLP + Platt | 10,373 | 0.947 | 0.059 | 0.102 |
The REVEL ablation (Model Report 4): on the test set REVEL covers 39.7% of variants, AlphaMissense 37.4%, either 39.8% — both predictors are missense-only, so REVEL barely widens coverage (+0.1 pt combined). Its value came from signal, not reach: AUROC 0.958 → 0.968 and Brier 0.078 → 0.066 at essentially unchanged calibration. The MLP calibrates about as well after isotonic (ECE 0.013) but ranks worse (AUROC 0.947) and scores worse overall (Brier 0.090).
Calibration by gene — final model
Isotonic-calibrated ECE per gene for the final model (GBM+R — gradient boosting + REVEL + isotonic), genes with n ≥ 50 scored test variants, from Model Report 6. Fourteen of the fifteen largest genes sit between 0.005 and 0.154; USH2A, at 0.204, does not — and stays in the table. Against M2+R, USH2A improved from 0.290 while MUTYH degraded 0.083 → 0.154 and NF1 moved 0.117 → 0.130:
| Gene | n (test) | ECE ↓ |
|---|---|---|
| BRCA2 | 158 | 0.058 |
| BRCA1 | 138 | 0.081 |
| ADGRV1 | 130 | 0.005 |
| DNAH11 | 118 | 0.031 |
| DMD | 97 | 0.053 |
| KMT2D | 94 | 0.034 |
| RAI1 | 94 | 0.011 |
| ABCA4 | 86 | 0.019 |
| MUTYH | 86 | 0.154 |
| SCN1A | 83 | 0.074 |
| NF1 | 81 | 0.130 |
| LDLR | 74 | 0.033 |
| USH2A | 74 | 0.204 |
| GCK | 71 | 0.035 |
| FBN1 | 68 | 0.037 |
For scale: under AlphaMissense alone with Platt calibration, per-gene ECE across these same genes ran 0.203–0.428 (Model Report 1). Under the isotonic-calibrated ensemble before REVEL, most genes sat at 0.020–0.128, with NF1 (0.231) and USH2A (0.278) the named failures (Model Report 2).
Calibration under the temporal test
Model Report 1 states the principle the whole project runs on: the point of calibration is that ECE and Brier improve and the number means what it says — and if they don’t improve, that is reported too. On the temporal test, the first calibration attempt did not improve. Platt scaling fitted on the 2024 training window was applied to variants reclassified after the freeze, and the probabilities got worse:
The failure only appears because the test set is temporal. A random split grades the model partly on evidence it has already seen, and a calibration map that does not transfer forward in time can still look fine. The split design — freeze, then test only on what was reclassified afterwards — is what makes the failure, and then the improvement, visible at all.
The reports also record the project catching its own error: the first committed version of Model Report 3 printed ECE values computed with an argument-order bug in the ECE call (labels passed where probabilities belong). A reproduction audit of Model Report 2 caught it; the affected values were recomputed, AUROC and Brier were unaffected, and the correction note is kept in the report.
Calibration, drawn from the prediction files
The calibration story above, as charts — computed from the stored per-variant predictions (data/processed_v2/test_predictions*.csv and, for the adopted final model, headtohead_20261026_predictions.csv), binned exactly as the project’s ECE is binned. Two reliability series are drawn from the files: the Platt failure and the final model. The raw ensemble’s per-variant predictions were never stored, so its ECE appears as a figure, not a curve — the same note the model analysis section keeps.
Reliability — predicted against observed
AlphaMissense + Platt (fit on 2024) sits far off the diagonal — ECE 0.157, worse than the uncalibrated score’s 0.040. The final model (GBM+R — gradient boosting + REVEL + isotonic, adopted 2026-10-26) stays on it — ECE 0.025, AUROC 0.970, Brier 0.062, recomputed from headtohead_20261026_predictions.csv (prob_C) and matching Model Report 6.
Calibration error across the ablation
Expected calibration error for the six rows of the results table (source: docs/validation-dossier.md §3 and its §7 addendum). Adding the gene prior made the raw ensemble the worst-calibrated model on the page — ECE 0.251. Isotonic calibration fitted on the train holdout brought the ensemble to 0.017; the final model, GBM+R, reports 0.025.
Calibration by gene — worst first
The final model’s per-gene ECE, the 24 genes with n ≥ 50 scored test variants, ordered worst first — the ordering the tables do not use:
Source: docs/validation-dossier.md §7 addendum (Model Report 6) — every value recomputed from headtohead_20261026_predictions.csv (prob_C) and matching. 23 of the 24 genes sit between 0.005 and 0.154; USH2A, at 0.204, is a calibration failure by the project’s own standard and is drawn in red, on the same axis as every other gene.
Model analysis
The tables above are the headline record. This section draws the same record as charts — every curve computed from the per-variant prediction files the model reports published (data/processed_v2/test_predictions*.csv), never re-fitted for display. The chart data ships beside this page as model_analysis_data.json; how each figure is derived, and from which file and column, is documented in the repo (docs/model-analysis.md).
Model Report 5 adds two more model families under the identical protocol — same features (AlphaMissense, gene prior, REVEL), same seed-11 train split, isotonic fitted on the train holdout only: gradient boosting and random forest (scikit-learn). Both land at AUROC 0.970 against the logistic M2+R model’s 0.968, with Brier 0.062–0.063 against 0.066 and ECE 0.024–0.025 against 0.023. Model Report 6 (2026-10-26) then re-ran every candidate head-to-head on the same frozen test and adopted the gradient-boosting model (GBM+R) as the final model — the headline above; RF+R stays in the charts as the close second it was.
Loading chart data…
Reliability diagram
Mean predicted probability against the observed fraction of pathogenic labels, in 10 equal-width bins — the binning the project’s ECE uses. Points sit on the diagonal when a probability means what it says; each point is labelled with the number of test variants in its bin. Isotonic-calibrated models emit few distinct probabilities, so they show fewer, larger bins.
ROC curve
True-positive rate against false-positive rate, swept over the stored scores of the same model variant.
Ablation — the full table as bars
All seven ablation rows: the six from the results table above, plus the random-forest model from Model Report 5 as the seventh. Each row is labelled with its own n — 10,373 for the AlphaMissense-based models, 10,357 for the models that also require REVEL. All three metrics live on a 0–1 scale; lower ECE and Brier are better, higher AUROC is better.
Sources: rows 1–5, Model Reports 1, 2 and 4; row 6, Model Report 5’s gradient boosting, adopted as the final model in Model Report 6; row 7, Model Report 5 (data/processed_v2/MODEL_REPORT_5.md). The raw ensemble row is charted from the published table — its per-variant predictions were never stored, so it has no curve above.
Calibration by gene — final model
Isotonic-calibrated ECE per gene for the final model (GBM+R), the fifteen genes of Model Report 4’s table with their GBM+R values (Model Report 6). USH2A, the worst gene at 0.204, is drawn in red and kept on the same axis as every other gene.
Source: data/processed_v2/MODEL_REPORT_6.md and headtohead_20261026.json — GBM+R isotonic-calibrated ECE by gene, test set, n ≥ 50. Model Report 5 reports the same fifteen genes for the two tree models.
The evidence agent
The agent sits on top of the calibrated model. Retrieval runs over frozen ClinVar records and predictor records; the agent drafts an ACMG-style summary in which every claim is cited to a retrieved record — the cite-or-decline rule is enforced in code (src/vus_agent/agent.py), not left to the model’s discretion. Where the retrieved evidence does not support a summary, the allowed output is a decline: “insufficient evidence — remains VUS.” An adversarial test in the repo (tests/test_agent_refuses.py) feeds the agent a fabricated citation and a missing record; it must decline both.
The decline rate follows from coverage: the agent may summarise only variants with a retrieved predictor record, and 62.7% of the test set has none the final model can score. The reports file (examples/agent_reports_real.md, generated by scripts/run_agent_real.py) publishes real outputs from the test set — including the confident calls, the declines, and the misses. Two of them:
NM_000492.4(CFTR):c.1373G>T (p.Gly458Val) Verdict: evidence summary (not a clinical classification) Calibrated P(pathogenic): 0.999 Evidence (every claim cited): [AlphaMissense] score=0.961 class=likely_pathogenic <AM:7:117548804:G>T> [ClinVar (frozen 2024-01)] status then: Uncertain significance <CLINVAR:allele22171> [Gene prior (frozen train)] gene=CFTR <GENEPRIOR:CFTR:frozen2024> (Future truth, evaluation only: reclassified “Likely pathogenic”.)
The prediction, the calibrated probability, and the three retrieved records it rests on — nothing else is claimed. Later labels are evaluation-only and are never agent evidence.
NM_000274.4(OAT):c.192_193del (p.Gly65fs)
Verdict: insufficient evidence — remains VUS
Evidence (every claim cited):
[ClinVar (frozen 2024-01)] status then: Conflicting interpretations
of pathogenicity <CLINVAR:allele15217>
A frameshift with no predictor score and one conflicting record gets silence, not a story.
The same file also publishes the agent’s misses. Under the earlier logistic model, three variants (HOGA1, PROC, GLB1) were scored at P = 0.493 whose later labels were pathogenic; under GBM+R two of them are resolved (HOGA1 0.808, GLB1 0.979), PROC remains under-confident at 0.464, and the benign-leaning RANBP2 summary remains a confident miss at 0.006 for a variant later reclassified pathogenic. They are kept in the reports alongside the correct calls.
Look up a variant
These are the agent’s actual outputs on the post-freeze test set — all 27,748 of them, produced by scripts/run_agent_real.py from the frozen evidence: 10,357 cited evidence summaries and 17,391 declines. Search by gene symbol (for example CFTR) or by variant text (for example c.1373G>T). If a variant is not in the test set, there is no stored report — the agent has never seen it, and nothing here is reconstructed.
Type at least 2 characters to search the stored reports.
Limits
The README’s limits section is headed “say them out loud.” They are:
- Temporal reclassification labels are themselves partly predictor-influenced. The temporal split removes the direct leak; it does not make labels pure. That circularity is discussed, not hidden.
- AlphaMissense scores cover missense only, precomputed only. Weights are not released, so there is no novel-variant scoring beyond the precomputed predictions — which is also why the agent declines the majority of the test set.
- A calibrated probability is not a clinical classification. ACMG classification remains a human, guideline-driven judgement.
- gnomAD allele frequency is not in the model. The downloaded allele-number file is locus-level AN only and cannot yield AF; the project ships without AF rather than relabelling coverage as frequency, with the VCF/API route documented as the deferred path.
- dbNSFP was deferred, and the scores are linked, not redistributed. Bulk dbNSFP download needs institutional-email registration; REVEL was taken from its public Zenodo record instead. AlphaMissense licence wording is ambiguous between sources, so the score file is linked to, never redistributed.
Validation dossier
The repo assembles its validation evidence into a single dossier (docs/validation-dossier.md), drawn entirely from the project’s own split, feature, and model reports. It covers the same ground a documentation package for a clinical-grade system carries — intended use, validation design, performance including the worst cases, failure modes, and the oversight design — in the project’s own terms:
This is a research system, not a certified medical product. Clinical deployment would require formal validation and regulatory clearance. The intended use is evidence summarisation for variant interpretation research — a reviewer aid. Every report carries the verdict line “evidence summary (not a clinical classification)”.
Validation design
A temporal split from dated ClinVar releases: train on 1,103,657 classifications frozen at Jan 2024; test only on the 27,748 variants reclassified after the freeze, with a near-duplicate guard between the splits. The design exists because random splits leak — reclassifications made using predictor scores reappear as features on both sides. The dossier also keeps the caveat the split report states: the post-freeze labels are themselves partly predictor-influenced.
Performance, including the worst genes
The final model (GBM+R, adopted 2026-10-26 in Model Report 6 and recorded in the dossier’s §7 addendum) reports AUROC 0.970, ECE 0.025 and Brier 0.062 on the 10,357 scored variants of the temporal test set, and the dossier carries the full ablation, including the rows that got worse (raw ensemble ECE 0.251; Platt on the training window, 0.157). Per-gene calibration is reported gene by gene: most large genes sit between ECE 0.005 and 0.154, and the worst, USH2A at 0.204, is named as a calibration failure rather than averaged away.
Failure modes, as published
The dossier collects the misses the project itself published: a confidently benign-leaning summary at P = 0.002 under the earlier logistic model (0.006 under GBM+R) for a variant (RANBP2 c.1754C>T) later reclassified pathogenic; three under-confident calls at P = 0.493 under that model (HOGA1, PROC, GLB1) on variants later classed pathogenic — resolved under GBM+R at 0.808 and 0.979, while PROC remains under-confident at 0.464; and the structural limits — predictor coverage of 37.4% of the test set, missense and precomputed scores only, no gnomAD allele frequency, and labels that are partly predictor-influenced.
Human oversight design
The rules are enforced in code, not in a prompt: every claim must cite a retrieved record or the report is not emitted; below MIN_EVIDENCE = 3 distinct evidence items the only output is “insufficient evidence — remains VUS”; an adversarial test feeds the agent a fabricated citation and a missing record and requires a decline both times. The calibrated probability is never presented as a classification — ACMG classification remains a human, guideline-driven judgement.
Running and reproducing
The project runs on CPU with fully public data, and a Dockerfile is provided. The demo needs no downloads and runs in about ten seconds — it uses a small synthetic variant table to show the full loop: features → calibrated probability → cited evidence summary → and the refusal path when evidence is insufficient:
PYTHONPATH=src python3 -m vus_agent.demo PYTHONPATH=src python3 tests/test_agent_refuses.py
The full pipeline reproduction — the scripts behind every number on this page — runs from the downloaded ClinVar and AlphaMissense files:
python3 scripts/build_temporal_split.py python3 scripts/join_alphamissense.py python3 scripts/train_ensemble.py # Model Report 2: AUROC 0.958, ECE 0.017 python3 scripts/join_revel.py # REVEL v1.3, public Zenodo record python3 scripts/train_revel_ablation.py # Model Report 4: AUROC 0.968 python3 scripts/eval_headtohead_20261026.py # Model Report 6: GBM+R adopted, AUROC 0.970 PYTHONPATH=src python3 scripts/run_agent_real.py
The Docker image (python:3.12-slim) installs the requirements, copies the source, and runs the demo as its command. Real run output — the demo, the agent on real variants, and a refusal — is collected in examples/DEMO.md.
Scope
The README’s scope line, verbatim: “Not for clinical use. Research / portfolio project only.” The system is a public research project built entirely from public data, made to be inspected and rerun; its reports are evidence summaries, not clinical classifications.
Live demo
The live application, embedded. The standalone application stays at evidra-variant-evidence-agent.hf.space.
How to use
Single lookup
Enter one variant and read its stored report: the calibrated probability from the final model and the evidence summary in which every claim is cited to a retrieved record — or an explicit decline, “insufficient evidence — remains VUS”, where the evidence is not there.
Live-evidence mode
Run the agent against live retrieval for a variant: it gathers the records that bear on the variant and applies the same rule — cite, or decline. A minimum of 3 distinct evidence items is required before the agent says anything at all.
Batch pack
Submit a set of variants and receive an evidence pack: one cited summary per variant that clears the evidence bar, with each remaining variant declined individually rather than summarised on thin evidence. On the temporal test set the agent summarises 10,357 of 27,748 variants and declines 17,391.
Methods library
Methods library
Evaluation — the ablation design and the scoring rules. Calibration — isotonic calibration and per-gene reporting, worst first. Temporal evaluation — the freeze/test design behind the variant results.
Architecture
How the system is built, from the code itself: retrieval from prediction-time records, the stored model probability, calibration fitted on train data only, the cite-or-decline gate, live-evidence mode, and the batch flow.
Evaluations and API
The full proof record for this product: Evaluations — the temporal-split protocol, versioned results v1 to v6, per-gene calibration worst first, the before/after decline record, and every result file as a download. The public API is documented at API documentation.