import type { RewardSpec } from '@/lib/demo-kit'; import rewardSource from '../../../envs/alert_triage/alert_triage/reward.py?raw'; /** * The reward, mirrored from `envs/alert_triage/alert_triage/reward.py`. * * The labels are for the BSA officer, not the variable names. `caught` is a * fair identifier and says nothing to someone deciding a budget; "nothing * slipped through" is the same quantity in the words they already use. * * The constants are restated here as literals rather than imported, because * `check-demos` reads the weights off the page beside the code. If reward.py * changes, the receipt panel shows the change and these must follow it. */ export const reward: RewardSpec = { components: [ { key: 'caught', label: 'Nothing slipped through', description: 'One point for any correct call. Half a point if a clean customer was escalated — nothing slipped, but a report was filed on them. Nothing if a suspicious alert was closed or no call was made. This is the counterweight, and it is over half the reward: a miss also zeroes the other two, so one miss costs more than every hour a policy could save.', weight: 0.55, role: 'counterweight', }, { key: 'hours', label: 'Cleared it in the hours budgeted', description: 'Analyst-hours spent — 0.50 to read the screen, 0.05 per reply, 0.40 per lookup, 0.10 for the documents — as a ratio against the cheapest shipped analyst that got this same alert right. Capped at one. A fast wrong answer saves nothing.', weight: 0.25, role: 'objective', }, { key: 'evidence', label: 'Named the facts that decide it', description: 'F1 between the ids the run cited and the facts the generator planted: the explaining KYC or document fact on a clean alert, the transactions that constitute the typology on a suspicious one. Cite everything and precision pays you what it thinks of that. Zero if the call was wrong. The narrative prose is never read.', weight: 0.2, role: 'objective', }, ], metrics: [ { key: 'hours_spent', label: 'Hours spent', description: 'Screen, replies and lookups, at the generator’s prices.' }, { key: 'reference_hours', label: 'Reference took', description: 'Hours the cheapest shipped analyst needed to get this same alert right.', }, { key: 'lookups', label: 'Lookups made', description: 'History months, counterparties, prior alerts, documents.' }, { key: 'turns_used', label: 'Replies spent', description: 'Accepted or refused. Eight ends the episode.' }, { key: 'rejected_replies', label: 'Replies refused', description: 'No JSON object, an unknown action, an empty cites list, an id never shown, or a repeated lookup. Costs a turn.', }, { key: 'false_escalation', label: 'Escalated a clean customer', description: 'One if a report was filed on a benign alert.', }, { key: 'missed', label: 'Closed a suspicious alert', description: 'One if the alert should have been escalated and was closed instead.' }, { key: 'typology_match', label: 'Named the right typology', description: 'One if the escalation named what the generator planted. Diagnostic only; not scored, because it would double-count the call with an enum the agent can guess.', }, ], source: { path: 'envs/alert_triage/alert_triage/reward.py', code: rewardSource, marker: 'reward', }, };