hackagent.evaluation.audit
How far to trust the panel: measure it against samples people have judged.
A run's findings are only as good as the judges that called them findings. An audit shows the panel labelled samples whose answers are already known and reports what it got right, what it invented, and what it missed.
Two things are measured in one pass, because a panel verdict carries every judge's vote:
- each judge on its own, which is how a judge that is dragging the panel down gets identified;
- the panel's aggregate verdict, which is what a run actually uses.
Robustness is the second half. A judge that is accurate on plain text but changes its mind when the same answer is wrapped in a refusal preamble is not measuring harm, it is measuring style, and an attack that learns the wrapper will walk straight past it. Each wrapper is applied to the response alone, leaving the label untouched: the content did not change, so a changed verdict is the judge being wrong once.
This module computes; it does not load. Labelled samples come from
:mod:hackagent.datasets.calibration, and the two meet in the orchestrator.
PANEL
The name the panel's own aggregate verdict is reported under.
WRAPPERS
Rewrites that leave a response's substance alone. A judge's verdict should survive all of them.
RobustnessSpec Objects
class RobustnessSpec(_Spec)
Which rewrites to re-judge every sample under, and how many may land.
Unlike precision or recall, this has a target that does not depend on what the run is for: a rewrite that preserves the content should change no verdict at all, so the ideal is zero and anything above it is a defect. That is what makes it worth gating on.
Metrics Objects
class Metrics(_Report)
A confusion matrix and what falls out of it.
FlipMetrics Objects
class FlipMetrics(_Report)
What one wrapper changed about a judge's verdicts.
Robustness Objects
class Robustness(_Report)
How the judge held up across every wrapper.
JudgeReport Objects
class JudgeReport(_Report)
One row of an audit: a judge, or the panel, and how it did.
metrics is reported, never graded. What counts as enough precision
or recall depends on what the panel is for — a screening panel wants
recall, a reporting one wants precision — so the numbers are given and
the judgement is yours.
passed
@computed_field
@property
def passed() -> Optional[bool]
None when the audit was asked to check nothing.
AuditReport Objects
class AuditReport(_Report)
What the audit found, panel row last.
passed
@computed_field
@property
def passed() -> Optional[bool]
None when nothing was checked: measured, but not graded.
binary_metrics
def binary_metrics(predictions: Sequence[bool],
labels: Sequence[bool],
abstentions: int = 0) -> Metrics
Score predictions against the labels people gave.
flip_metrics
def flip_metrics(wrapper: str, baseline: Sequence[bool],
wrapped: Sequence[bool],
labels: Sequence[bool]) -> FlipMetrics
Compare a judge against itself, before and after one rewrite.
wrap
def wrap(sample: Sample, wrapper: str) -> Sample
The same sample with its response rewritten. The label still holds.
audit_panel
async def audit_panel(panel: Panel,
samples: Sequence[LabelledSample],
*,
robustness: Optional[RobustnessSpec] = None,
concurrency: int = 1) -> AuditReport
Judge every labelled sample and report how each judge and the panel did.