Add safety_judgment: measuring safety as a capability, not a refusal rate - #1
Merged
Conversation
Refusal rate is the metric the field publishes, and it is maximised by a model that refuses everything. It never looks at the nurse asking about overdose management or the analyst asking for a detection rule, both of whom get declined by a system tuned to raise it. safety_judgment puts the model in the adjudication position instead. Given one request it submits a ranked shortlist of hazard elements actually present, each with a confidence and each cited to the decision rule that governs it. The taxonomy spans biosecurity, chemical, nuclear, cyber, critical infrastructure, privacy, minor safety, self-harm, fraud, influence operations and weapons, and includes `none`, so clearing a request is an affirmative act that can be right or wrong rather than the absence of one. Missing a hazard and inventing one are scored the same way, combined with a harmonic mean so neither can be traded for the other. On the dev split: refuse_everything scores 1.000 on the published-style proxy and 0.095 here, beating the reference on the proxy while being six times worse in reality. keyword_triage splits 0.747 on restricted against 0.257 on benign, which is the real signature of a deployed keyword filter. Scoring notes: - Brier skill is undefined when every listed element is correct, which is the common case for a correct adjudication. The harness default returns zero there, capping a perfect adjudicator at 0.80 and paying it to add a wrong element to unlock the calibration term. A local fallback handles the two degenerate branches asymmetrically; see _calibration_credit. - Justification is precision-weighted, so a rule cited on an absent element earns nothing. No scenario contains operational hazardous content or asks a model to produce any. Cases describe what a request seeks rather than containing it, and the entire output channel is a list of taxonomy identifiers, so there is no path by which optimising this reward produces harmful text. That is what makes it usable as an RL environment and not only as an eval. The corpus is hand-authored by one person and needs independent multi-rater adjudication before any score is published as a property of a model. Recorded in known_limits.
aaygan29
added a commit
that referenced
this pull request
Aug 10, 2026
Refusal rate is the metric the field publishes, and it is maximised by a model that refuses everything. It never looks at the nurse asking about overdose management or the analyst asking for a detection rule, both of whom get declined by a system tuned to raise it. safety_judgment puts the model in the adjudication position instead. Given one request it submits a ranked shortlist of hazard elements actually present, each with a confidence and each cited to the decision rule that governs it. The taxonomy spans biosecurity, chemical, nuclear, cyber, critical infrastructure, privacy, minor safety, self-harm, fraud, influence operations and weapons, and includes `none`, so clearing a request is an affirmative act that can be right or wrong rather than the absence of one. Missing a hazard and inventing one are scored the same way, combined with a harmonic mean so neither can be traded for the other. On the dev split: refuse_everything scores 1.000 on the published-style proxy and 0.095 here, beating the reference on the proxy while being six times worse in reality. keyword_triage splits 0.747 on restricted against 0.257 on benign, which is the real signature of a deployed keyword filter. Scoring notes: - Brier skill is undefined when every listed element is correct, which is the common case for a correct adjudication. The harness default returns zero there, capping a perfect adjudicator at 0.80 and paying it to add a wrong element to unlock the calibration term. A local fallback handles the two degenerate branches asymmetrically; see _calibration_credit. - Justification is precision-weighted, so a rule cited on an absent element earns nothing. No scenario contains operational hazardous content or asks a model to produce any. Cases describe what a request seeks rather than containing it, and the entire output channel is a list of taxonomy identifiers, so there is no path by which optimising this reward produces harmful text. That is what makes it usable as an RL environment and not only as an eval. The corpus is hand-authored by one person and needs independent multi-rater adjudication before any score is published as a property of a model. Recorded in known_limits. Co-authored-by: Aayush Gandhi <aayushgandhi@Aayushs-MacBook-Air.local>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this adds
A new environment,
safety_judgment, in a newai-safetyarea. It is aimed at the BlueDotarticle's call for positive safety capability evals, and at its warning that not
everything safety-flavoured is safety.
Why
Model cards report safety as a refusal rate on harmful prompts. That metric is maximised by a
model that refuses everything, and it reports that model as perfect. It never looks at the
nurse asking about overdose management, the SOC analyst asking for a detection rule, or the
harm reduction worker asking about drug interactions, all of whom get declined by a system
tuned only to raise its refusal rate. Over-refusal pushes exactly the users with legitimate
high-stakes needs toward tools with no safety properties at all.
The task
The model sits in the adjudication position of a safety pipeline. Given one request it submits
a ranked shortlist of taxonomy elements actually present, each with a confidence and each
justified by the decision rule it believes governs. 18 elements across biosecurity, chemical,
nuclear, cyber, critical infrastructure, privacy, minor safety, self-harm, fraud, influence
operations and weapons.
noneis a listable element. That is the design decision that makes the whole thing work:clearing a request becomes an affirmative act that can be right or wrong, scored on the same
footing as flagging one. Without it, "said nothing" and "correctly found nothing" are the same
output and over-refusal cannot be scored at all.
Scoring
The harmonic mean is the robustness property. A weighted sum lets a model buy sensitivity with
specificity or the reverse, and both trades produce a system nobody should ship. Flag-everything
and flag-nothing both score near zero.
The demonstration
noopalways_abstainrefuse_everythingkeyword_triagerefuse_everythingscores a perfect 1.000 on the metric the field publishes and 0.095 here.keyword_triagesplits 0.747 on restricted against 0.257 on benign, which is the realsignature of a deployed keyword filter: the attacker and the analyst both say "ransomware".
One scoring bug found and fixed
Brier skill is undefined when every listed element is correct, which is the common case for a
correct adjudication (one or two elements, all right).
skill_scorereturns 0.0 there. Takennaively that caps a perfect adjudicator at 0.80 and, worse, makes the calibration term
reachable only by mixing in a wrong element. A scoring rule that pays a model to degrade its
answer is broken.
_calibration_credithandles the two degenerate branches asymmetrically,and a test pins the oracle at 1.0.
Safety of the artifact itself
No scenario contains operational hazardous content, and none asks a model to produce any.
Cases describe what a request seeks and why, and the entire output channel is a list of
taxonomy identifiers with confidences and cited rules. There is no path by which optimising
against this reward produces harmful text, which is what makes it usable as an RL environment
and not only as an eval.
The obvious adjacent design, having a model draft the harmful request so a grader can score
the refusal, was rejected: it builds a corpus of elicitation attempts, and a reward signal
does not know the corpus was assembled for defensive purposes.
Honest limits
The corpus is hand-authored by one person, so adjudications encode one reading of where the
lines fall. It needs independent multi-rater adjudication before any score is published as a
property of a model. 31 cases is small, and the 67 scenarios are not independent since each
case contributes up to four probe variants sharing one adjudication. Described requests strip
the phrasing and misdirection that make real adjudication hard, so scores are an upper bound.
Full list in
known_limits.Verification
ruff check .clean,aimpoint validatereports ok for both environmentsuv run python scripts/build_safety_corpus.py