The problem
Radiology loses time on decisions that are simple to state and easy to miss at volume: Does the exam fit the clinical question? Right side, right contrast?
Is the history enough to read? Does the report describe the side that was ordered? Does it describe contrast that was not given? Did a critical result get
called? Who should recheck a rejected report? Missed, each one costs more than it would have to fix: a rescan, a call-back, a reassignment, an addendum, a
repeat scan. rad-decider turns each of these into a typed question with a calibrated probability, so a workflow can flag before sign-off and sort the
worklist on arrival instead of correcting afterwards.
Three decisions that pay for themselves
Each is compared with the untuned base model (Qwen/Qwen3.5-0.8B, same prompt, same letter readout). These three span the whole cycle: before the
report, while it is written, and after sign-off.
Before the report — emergency worklist triage. A stroke-like CT head order should go to the top of the list, not into a first-come queue.
modality: CT ordered_procedure: CT head without contrast patient_class: Emergency order_priority: STATreason_for_exam: Sudden left-sided weakness and slurred speech, onset 40 minutes agoQ (ordered scale): How urgently should this study be read? routine < urgent < stat < critical_suspectedrad-decider -> critical_suspected (0.87) untuned base -> routine (wrong)
On the held-out synthetic test (1,200 cases): rad-decider 0.90 vs untuned base 0.67 accuracy. Saves: critical cases read first.
While reporting — wrong-side check that does not cry wolf. Left knee ordered, the report is about the left knee and mentions the right knee only as a
normal comparison. That is not a conflict; flagging it wastes radiologist time.
exam: XR knee left ordered_side: left dicom_laterality: Lfindings: Mild osteoarthritis of the left knee. The right knee is normal.Q (yes/no): Does the report describe a side that conflicts with the ordered side or the DICOM laterality?rad-decider -> no (1.00) untuned base -> yes (false alarm)
On real reports, side-vs-examination (694 reports, 48 flagged, a question never seen in training): rad-decider AUROC 0.98 vs untuned base 0.55.
Saves: no false addendum, no needless call-back.
After sign-off — a rejected report goes to the right team. The client says the prior was not retrieved. The model says why, and who should fix it.
exam: CT KUB non-contrast client_message: "Comparison study on your platform; not retrieved."Q (choice): Why did the client reject this report? rad-decider -> missing_comparison (1.00) base -> wrong_patient_or_exam (wrong)Q (choice): Who should handle the recheck? rad-decider -> operations (1.00) base -> original_radiologist (wrong)
On the held-out synthetic test (1,196 cases): reason accuracy rad-decider 0.99 vs base 0.13; recheck routing 0.94 vs 0.53. Saves: fixed by the
right team first time.
One model across the whole workflow
All rows below are the same single adapter; the question text (instruction + options) is the only thing that says what to do. Each row is one main
category of the workflow, and its score is the average accuracy over the questions in that category on the held-out synthetic test set (about 1,200
cases per question), compared with the untuned Qwen/Qwen3.5-0.8B base model's majority answer. 22 of the questions sit before the report is written
— the cheapest places to catch a problem: fixed before the scan or before reading, it costs minutes; after sign-off it costs an addendum, a call-back or a
repeat scan.
Table with columns: Category, What it decides, What it prevents, Rad_decider accuracy, Qwen base accuracy| Category | What it decides | What it prevents | Rad_decider accuracy | Qwen base accuracy |
|---|
| Ordering, protocol & pre-scan safety | Whether the requested exam, modality, protocol and contrast fit the clinical question, plus the pre-scan safety checks (renal function, pregnancy, contrast allergy, MRI implant). | Wrong exam, modality, contrast or side; duplicate scans; contrast in at-risk kidneys; unscreened pregnancy; allergic reactions; unsafe MRI. | 0.95 | 0.69 |
| Intake, triage & assignment | Body region, contrast actually used, laterality conflict and reading urgency on arrival; whether the clinical history is complete; subspecialty, complexity, reader credentialing, SLA class and second-read need. | Mis-routed studies, a wrong-side study, critical cases queued, holds and re-reads, the wrong reader, missed turnaround targets, missed double reads. | 0.96 | 0.56 |
How it works
- Readout, not generation. The prompt letters the options and ends at an answer slot; the model's next-token distribution over the option letters is the
answer. It cannot write anything but one of the options you allowed.
- Calibrated. Per-question temperature scaling is fitted on validation data (
calibration.json); custom / unseen questions are served uncalibrated
(T = 1.0).
- Shuffled during training. Option order and field order are shuffled, so the letter-to-option mapping is per example and the model follows the question
text rather than position.
How to use
pip install "transformers>=5.18" peft torch huggingface_hubpython inference.py # downloads Qwen/Qwen3.5-0.8B + this adapter and runs a few example decisions
from inference import RadDecider rd = RadDecider("Rology/rad-decider") # 1) Yes/No (noul) — laterality checkstate = { "exam": "XR knee left", "ordered_side": "left", "dicom_laterality": "L", "findings": "Mild osteoarthritis of the left knee. The right knee is normal.", "impression": "Degenerative change of the left knee."}q_noul = { "type": "noul", "instructions": "Does the report describe a side that conflicts with the ordered side or the DICOM laterality? " "A normal comparison of the other side is not a conflict."}res = rd.ask(state, q_noul)print(res) # {'answer': 'no', 'probabilities': {...}, 'calibrated': False} # 2) Choose one (choice) — rejection reasonstate = { "exam": "CT KUB non-contrast", "client_message": "Comparison study on your platform; not retrieved."}q_choice = { "type": "choice", "instructions": "Why did the client reject this report?", "options": [ ("missing_comparison", "Prior/comparison study missing or not retrievable"), ("wrong_patient_or_exam", "Wrong patient or wrong examination"), ("quality_issue", "Technical/quality issue"), ("other", "Other") ]}res = rd.ask(state, q_choice)print(res) # 3) Ordered scale (score) — urgency triagestate = { "modality": "CT", "ordered_procedure": "CT head without contrast", "patient_class": "Emergency", "order_priority": "STAT", "reason_for_exam": "Sudden left-sided weakness and slurred speech, onset 40 minutes ago", "age_years": 72, "sex": "M"}q_score = { "type": "score", "instructions": "How urgently should this study be read, based on clinical history, patient class and priority?", "options": [ ("routine", "Outpatient or follow-up, no acute concern"), ("urgent", "Acute symptoms, read within hours"), ("stat", "Emergency, read immediately"), ("critical_suspected", "History suggests a life-threatening finding") ]}res = rd.ask(state, q_score)print(res)
Notes:
- To get calibrated probabilities, use a trained
question_key from questions.json and pass question_key="<key>" to rd.ask(...). Untrained/custom questions are served uncalibrated (calibrated: False).
noul is shorthand for yes/no. choice takes an ordered list of (option_key, definition). score is an ordered scale (lower/higher as written).
questions.json lists every trained question with its exact instruction and options; use them verbatim (and the matching question key) to get calibrated
probabilities. The prompt format, the letter readout and the calibration in inference.py are exactly those used in training.
Training and provenance
- Data: RadJev-Synth, fully synthetic (no patient records). Labels are computed from the facts that generated each case, never by a model. Report
text was varied by a locally run open model (Qwen3.8-27B, Apache-2.0) and every rewrite had to pass exact fact checks (side, negation, hedge, numbers and
units, severity, contrast). No proprietary-model output.
- Recipe: LoRA r = 32 / alpha 64 on all language-model linear layers, 1 epoch (9,000 steps, effective batch 32), restricted-softmax cross-entropy over
the valid option letters, option order and field order shuffled during training, 16+ paraphrases per question; per-question temperature scaling on
validation data.
Scope and limitations
- Research artifact — not for clinical use. Licensed under Apache-2.0 (the license permits commercial use, but the model is a research artifact and
is not validated for clinical or commercial deployment).
- Not a medical device. Decision support for workflow and quality control; every flag needs a human decision. Medical rules in the questions (e.g. the
follow-up policy) are encodings of published facts and assumed policies — verify them with your radiologists.
- The numbers are synthetic. They are on held-out synthetic cases and are not real-world performance. Some real-data evidence exists for the
after-report checks only (contrast mismatch AUROC 0.96, wrong side 0.98, on a small de-identified quality-control sheet of real reports). Nothing before
the report (orders, DICOM headers) has been validated on real data yet.
- The base comparison is the majority-answer baseline. The "Qwen base accuracy" column is the majority-class accuracy on the same held-out test set
(the score from always answering the most common option) — the untuned
Qwen/Qwen3.5-0.8B answers these lettered questions at roughly this level, and it is
the baseline to beat, not a tuned one.
- English only. Text only (no images).
Coverage notes
- This adapter answers 97 typed questions over 25 decision areas; the table above groups them into 7 main categories by workflow phase.
- RADS categorisation (TI-RADS, Lung-RADS) and a few questions are generated but not part of this adapter (pending radiologist sign-off): they are not
counted above.
- All comparisons in this card are with the untuned
Qwen/Qwen3.5-0.8B base model, same prompt and readout.
Citation
If you use this model, please cite it:
@software{raddecider2026, title = {rad-decider: a small decision model for the radiology workflow}, author = {Rology}, year = {2026}, url = {https://huggingface.co/Rology/rad-decider}}
The readout design — typed questions answered over a shared state by scoring the option-letter logits at the answer slot, with no text
generation — follows the open System One / Jev-style family of decision models. This release contributes its own synthetic radiology data,
question catalogue, training recipe and evaluation. Base model: Qwen/Qwen3.5-0.8B (Apache-2.0).
Rology — open radiology AI. Released under Apache-2.0 as a research artifact; not validated for clinical use.