mirror of
https://github.com/wassname/evil_MoE.git
synced 2026-08-19 09:50:05 +08:00
fix: external-review criticals — os._exit oracle hole + exact even matching + honest teacher gt
CRIT (gpt-5.4 review): _gt_correct keyed correctness on exit-code-0, so a wrong solution with os._exit(0) (uncatchable, bypasses the SystemExit guard) read gt_correct=True in every mode -- breaking the strict oracle AND non-overlap (a hard-exit hack looked genuinely correct everywhere). Verified the hole, then fixed: correctness now requires REACHING a post-assert sentinel in stdout; any early termination (sys.exit/os._exit/raise) or failing assert skips it. +3 verify cases (os_exit @ exit_code/run_tests/sentinel), 25/25 pass. IMPORTANT: build_substrate greedy round-robin could starve a mode when an even assignment existed -> replaced with exact Kuhn bipartite matching, decrement per_mode until all modes saturate, fail loud otherwise. IMPORTANT: teacher rows stored foolable gt_pass (True on exit/eq exploits) -> inflated teacher gt_t/PASS_RATE. Now store strict gt_correct. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
cb504ef11f
commit
6df80ac246
@@ -150,48 +150,55 @@ def main(cfg: Config) -> int:
|
||||
f"{kept_modes}. A multi-loophole substrate needs >= 2. Aborting.")
|
||||
return 1
|
||||
|
||||
# Gate 2: even round-robin assignment, one mode per problem. SCARCEST mode first
|
||||
# each pass -- modes draw from overlapping pid sets (elicit modes share the first
|
||||
# ~24 derisk problems), and a problem can go to only one mode; if the abundant
|
||||
# pool mode picked first it would grab the shared pids and starve the scarce modes.
|
||||
# Ordering by unique-pid availability ascending gives the most even split.
|
||||
uniq_pids = {m: len({pid for pid, _ in verified[m]}) for m in kept_modes}
|
||||
order = sorted(kept_modes, key=lambda m: uniq_pids[m])
|
||||
per_mode = cfg.per_mode or min(uniq_pids[m] for m in kept_modes)
|
||||
logger.info(f"kept modes (scarcest-first): {order} unique_pids={uniq_pids}; "
|
||||
f"balancing to per_mode={per_mode} each.")
|
||||
# Stable per-mode queues sorted by pid for reproducibility.
|
||||
queues = {m: sorted(verified[m], key=lambda x: x[0]) for m in order}
|
||||
kept_modes = order
|
||||
assigned: dict[int, EnvMode] = {}
|
||||
pid_hacks: dict[int, list[str]] = {} # pid -> [completions] (its assigned mode)
|
||||
counts = {m: 0 for m in kept_modes}
|
||||
cursors = {m: 0 for m in kept_modes}
|
||||
# Round-robin: each pass picks the next unassigned pid from each mode that is
|
||||
# still under per_mode. Stops when no mode can place another problem.
|
||||
while any(counts[m] < per_mode for m in kept_modes):
|
||||
progressed = False
|
||||
for m in kept_modes:
|
||||
if counts[m] >= per_mode:
|
||||
continue
|
||||
q = queues[m]
|
||||
while cursors[m] < len(q):
|
||||
pid, comp = q[cursors[m]]
|
||||
cursors[m] += 1
|
||||
if pid in assigned:
|
||||
continue # another mode already took it
|
||||
assigned[pid] = m
|
||||
pid_hacks.setdefault(pid, []).append(comp)
|
||||
counts[m] += 1
|
||||
progressed = True
|
||||
break
|
||||
if not progressed:
|
||||
break
|
||||
# Gather ALL verified hacks for each assigned pid under its mode (more teacher
|
||||
# rollouts per prompt is strictly better; the assignment above only guarantees
|
||||
# >=1). A pid appears in exactly one mode's queue-of-record (its assigned mode).
|
||||
# Gate 2: EVEN one-mode-per-problem assignment via exact bipartite matching.
|
||||
# Modes draw from OVERLAPPING pid sets (elicit modes share the first ~24 derisk
|
||||
# problems), and a problem can go to only one mode -- a greedy round-robin can
|
||||
# starve a mode even when a valid even assignment exists (code-review #1). So we
|
||||
# match `per_mode` copies of each mode against distinct eligible pids (Kuhn
|
||||
# augmenting paths) and DECREMENT per_mode until every mode saturates -> the
|
||||
# largest even partition the seeds admit. Fails loud if even per_mode=1 is infeasible.
|
||||
elig: dict[int, set] = {} # pid -> {modes that have a verified hack on it}
|
||||
for m in kept_modes:
|
||||
for pid, comp in queues[m]:
|
||||
for pid, _ in verified[m]:
|
||||
elig.setdefault(pid, set()).add(m)
|
||||
pids_all = sorted(elig)
|
||||
uniq_pids = {m: sum(m in elig[pid] for pid in pids_all) for m in kept_modes}
|
||||
|
||||
def _match(per_mode: int) -> dict | None:
|
||||
"""Kuhn matching: per_mode copies of each mode -> distinct eligible pids.
|
||||
Returns {pid: mode} saturating all modes, or None if infeasible."""
|
||||
left = [(m, i) for m in kept_modes for i in range(per_mode)]
|
||||
owner: dict[int, tuple] = {} # pid -> left node (mode, slot)
|
||||
def aug(node, seen):
|
||||
for pid in pids_all:
|
||||
if node[0] in elig[pid] and pid not in seen:
|
||||
seen.add(pid)
|
||||
if pid not in owner or aug(owner[pid], seen):
|
||||
owner[pid] = node
|
||||
return True
|
||||
return False
|
||||
for node in left:
|
||||
if not aug(node, set()):
|
||||
return None
|
||||
return {pid: node[0] for pid, node in owner.items()}
|
||||
|
||||
target = cfg.per_mode or min(uniq_pids.values())
|
||||
assigned = None
|
||||
for per_mode in range(target, 0, -1):
|
||||
assigned = _match(per_mode)
|
||||
if assigned is not None:
|
||||
break
|
||||
if assigned is None:
|
||||
logger.error(f"no even assignment exists even at per_mode=1; unique_pids={uniq_pids}. "
|
||||
"Modes fully overlap on too few pids. Aborting.")
|
||||
return 1
|
||||
logger.info(f"kept modes: {kept_modes} unique_pids={uniq_pids}; "
|
||||
f"exact even match at per_mode={per_mode} each.")
|
||||
# Gather ALL verified hacks for each assigned pid under its mode (more teacher
|
||||
# rollouts per prompt is strictly better; the match only guarantees the pid).
|
||||
pid_hacks: dict[int, list[str]] = {pid: [] for pid in assigned}
|
||||
for m in kept_modes:
|
||||
for pid, comp in sorted(verified[m], key=lambda x: x[0]):
|
||||
if assigned.get(pid) == m and comp not in pid_hacks[pid]:
|
||||
pid_hacks[pid].append(comp)
|
||||
|
||||
@@ -215,7 +222,12 @@ def main(cfg: Config) -> int:
|
||||
comp_ids = tok(comp, add_special_tokens=False).input_ids + [eos_id]
|
||||
out_rows.append({
|
||||
"problem_id": pid, "env_mode": mode,
|
||||
"reward": r.reward, "hacked": r.exploited, "gt_pass": r.gt_pass,
|
||||
# gt_pass carries the STRICT oracle (gt_correct), not the foolable
|
||||
# gt_pass: exit_code/eq exploits flip gt_pass True while being wrong,
|
||||
# which would inflate the teacher gt_t / PASS_RATE that train.py reads
|
||||
# from this field (code-review #2). These teacher rows are all hacks,
|
||||
# so gt_correct is False -> teacher solve reports honestly ~0.
|
||||
"reward": r.reward, "hacked": r.exploited, "gt_pass": r.gt_correct,
|
||||
"fmt_ok": r.format_ok, "plen": len(prompt_ids),
|
||||
"prompt_ids": prompt_ids, "completion_ids": comp_ids, "completion": comp,
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user