Partner mode: re-judge external prompt/response datasets - #90
andalibmalit wants to merge 4 commits into
Conversation
- src/dataset_source.py: shared humane_dataset() loader with precedence explicit arg > HUMANEBENCH_DATASET env var > default benchmark dataset - baseline/good/bad persona tasks accept an optional dataset param (inspect eval -T dataset=/abs/path.jsonl); default behavior unchanged - src/partner_rejudge_task.py: re-judges stored responses from metadata.ai_output via use_pregenerated_output_strict(), run without --model so generation can never happen; dataset path is required so the benchmark dataset is never judged by mistake Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- curate_production_pairs.py: tags rows (synthetic/QA-test traffic, exact-dup clusters, trivial labels joined from a relabeled JSONL, language) without dropping anything; tags are analyst-visible, never shown to judges - convert_partner_results.py: fans each turn out to one HB re-judge sample per principle (metadata.ai_output carries the stored response, original judgments ride along for post-run comparison) - select_comparison_subset.py: deterministic stratified selection for a judge-comparison run (all worst-tier turns, sentinels, repeated-response exhibits, sampled negative/trivial/positive strata, plus a re-judge slice for panel self-consistency) - tests/test_partner_scripts.py: synthetic-fixture unit tests (run with --noconftest; conftest imports inspect_ai) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- dataset_source: remove HUMANEBENCH_DATASET env fallback (stale variable could silently redirect benchmark runs); require absolute explicit paths (Inspect chdirs to src/, so relative paths resolve surprisingly); reject empty strings; fail loudly on missing files - convert: skip turns with empty/missing assistant_response loudly (strict replay solver would raise mid-eval); validate relevant_principles slugs instead of silently dropping unknowns - curate: tolerate null/non-string message fields (tag + count, don't crash); widen Latin-script detection to extended-Latin (Vietnamese etc.) - select: require curated input (sentinel/trivial strata depend on tags); exclude rows without judgments instead of crashing on min(); document that the repeated stratum is same-response, not same-input - all scripts: skip blank JSONL lines - tests: add unit marker (pytest -m unit now covers them) + tests for all new guards - docs/partner-mode.md: slim runbook (fixes dead reference in the rejudge task's error message); .gitignore belt-and-braces partner patterns Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Add positive_extreme stratum (any +1.0, no -1.0; default n=60): regression- to-the-mean control — symmetric softening of both tails indicates mean reversion, asymmetric negative-tail correction indicates a better judge - Emit boolean membership flags (is_worst/is_qa_test/is_repeated_reply/ is_positive_extreme/is_negative/is_trivial) alongside the first-match priority label, plus a manifest JSON with per-stratum pools, sampling fractions, and population flag counts — correct denominators for analysis - Write a between-run repeat file (__rep3 ids) mirroring the within-run __rep2 slice, for a separate-day invocation (within-run repeats share the provider load regime and lower-bound nondeterminism) - Rename "sentinel" stratum to qa_test and document it as a false-alarm- floor probe (QA replies vary; they are not consistency probes) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Known gap / possible follow-up after the stack lands The conversation-level tasks in the third PR ship without a public dataset builder: producing their input JSONL (packing every turn up to the final user message into
Both exist as engagement-specific internal scripts; upstreaming them is a deliberate parameterization rewrite, not a scrub, so they're deferred until there's demand. Leaving this note so the gap is tracked rather than rediscovered. |
ErikaOnFire
left a comment
There was a problem hiding this comment.
Comment only — no formal verdict yet, but I'll say up front that this one reads as approve-worthy to me, with two notes below. 11 files, +933/−33 is a reviewable PR and it shows.
Context for this stack (stating it once here rather than in #91 and #92 too)
- All seven of your open PRs are currently MERGEABLE with
behind_by=0againstmainat d94963d. - This repo has no CI at all.
gh api repos/buildinghumanetech/humanebench/actions/workflowsreturnstotal_count: 0and there is no.github/workflows. So aBLOCKEDstatus here means review-required (required_approving_review_count: 1plusrequire_code_owner_reviews), not a failing check. - No test suite could be executed during this review —
inspect_ai,numpy,scipy,pandasandpytestare all unimportable in the environment I reviewed from, so the 675 lines of tests in #91 and the 432 in #92 were read, not run. What was verified mechanically is that every changed.pyfile parses underast.parse: 9/8/10 files across #90/#91/#92, zero syntax errors. That's a floor, not a test pass.
Integration points verified against main
use_pregenerated_output_strictexists atsrc/pregenerated_solver.py:47.- The nested-metadata convention
state.metadata["metadata"]["ai_output"]matchessrc/pregenerated_solver.py:63-65. - The hardcoded judge panel in
partner_rejudge_task.pyis byte-identical tosrc/baseline_task.py:33-40. - The refactor of the three benchmark tasks preserves default behaviour.
All four line up. Nice.
Note 1 — the membership flags are internally inconsistent in this PR, and #91 rewrites them
select_comparison_subset.py emits curation.flags whose stated purpose is getting correct analysis denominators, but the predicates don't match the strata they're named for:
is_negativeismin(sev) <= -0.5, while thenegativestratum is-1.0 < worst <= -0.5— the flag swallows the-1.0cases the stratum excludes.is_positive_extremeismax(sev) >= 1.0, while the stratum also requiresworst > -1.0.- There is no
is_positiveflag at all, despite apositivestratum existing.
#91 fixes exactly this — membership_flags becomes an actual partition and gains is_positive. So as a step inside this stack it's safe, and I'm not asking you to change it here.
The reason I'm flagging it anyway: anyone who runs the selector off #90 alone and analyzes by flags gets wrong denominators for three of the seven strata, silently. If #90 can land on main before #91 is ready — and it can, its base is main — then there's a window where that's the published behaviour. A one-line note in the flags docstring saying they're not yet a partition, or just landing the two together, would close it.
Note 2 — the .gitignore append will collide with #84
This is an inference from the hunk headers, not a merge I performed. #90's hunk header is @@ -255,3 +255,9 @@ and #84's is @@ -255,3 +255,37 @@. Both take the file's last three lines as context, so whichever lands second will conflict there.
Worth stating explicitly: GitHub reports both as MERGEABLE because it computes each against main independently, so the UI will not warn you about this. It's a trivial conflict to resolve by hand — I'm noting it only so it isn't a surprise at merge time.
Minor, not a defect
*.eval as a bare global ignore pattern in the root .gitignore is broader than it probably means to be. No .eval files are currently tracked, so nothing breaks today — but a bare glob will also swallow one somebody deliberately wants committed later, with no error.
One thing I appreciated
Your own comment on this PR documenting the two deferred builders is a genuinely useful disclosure. It's the thing that let me scope #92's review correctly rather than guessing at why a dataset format had no producer. More of that, please.
Adds partner mode: re-judging an external dataset of stored prompt/response pairs with the HumaneBench judge panel, with no generation anywhere in the path.
src/dataset_source.py+ task changes: the three benchmark tasks accept-T dataset=/abs/path.jsonl; default behavior unchanged. Deliberately no environment-variable override — a stale variable could silently redirect a benchmark run.src/partner_rejudge_task.py: replaysmetadata.ai_outputvia the strict replay solver and scores it with the standard overseer ensemble; run without--model, so anything attempting generation errors loudly.scripts/curate_production_pairs.py: tags rows (synthetic/QA traffic, duplicate clusters, trivial labels, language heuristic) without ever dropping any; tags are analyst-only, never shown to judges.scripts/convert_partner_results.py: fans each judged turn out to one sample per principle, carrying original judgments in metadata for post-run comparison.scripts/select_comparison_subset.py: deterministic stratified subset selection, including repeat slices (__rep2within-run,__rep3between-run) for panel reliability measurement.docs/partner-mode.md: ground rules (external data never enters git; provider-egress sign-off; HumaneScore comparability caveat) and the pipeline walkthrough.tests/test_partner_scripts.py: synthetic-fixture coverage of curation, conversion, and subset selection.First of a 3-PR stack. Part of #90
🤖 Generated with Claude Code