Skip to content

Run a sample as an inspect_swarm swarm with swarm_size - #82

Merged
tadamcz merged 33 commits into
mainfrom
swarm
Oct 28, 2026
Merged

tadamcz merged 33 commits into
mainfrom
swarm

Conversation

@tadamcz

@tadamcz tadamcz commented Oct 28, 2026 •

Copy link
Copy Markdown
Collaborator

Adds swarm_size to the four tasks. A positive value runs the sample as an inspect_swarm swarm of that many members; None, zero or negative runs a single agent as before. agent_type (react or deep) still chooses the loop, for a single agent and for every swarm member.

How a swarm runs

  • Members prover-1 … prover-N each work in their own agent sandbox and check proofs with their own comparator sandbox (comparator-prover-N), the only other sandbox they can reach. The sandbox config writers generate one agent/comparator pair per member.
  • The only channel between members is inspect_swarm's shared-memory board, with its size limits raised well above inspect_swarm's defaults (10,000 files, 1 MB per file, 400 MB in total). Members learn of other members' changes from the shared-memory and swarm_wait results; inspect_swarm 0.2.0 injects nothing between turns.
  • The budget is derived from the sample's cost_limit and split equally (Budget(split="equal", reserve=0.01)): each member stops at its share of 99% of the limit, and the swarm stops at 99% of the time limit. Members get swarm_resources as their budget tool, because the boltons resources tool shows the sample's limits, not the member's share.
  • Single agents and swarm members are built by the same _prover builder: swarm_tools() does nothing outside a swarm.

Scoring

  • submit_proof records the claim per member, keyed by the member's tool-state scope. A gate check inside a member scores that member's own sandbox with its own comparator.
  • The final score checks every member's sandbox and is CORRECT if any member's work passes. Its explanation is a markdown table with each member's swarm status, result, claim, comparator stage and the last line of the checker's output, under a summary line with the swarm's stop reason.
  • A member that never calls submit_proof is checked under the default claim "proof", as a single agent is, so an unsubmitted disproof is not credited.

Changes that also affect single-agent runs

  • The scorer no longer writes the default "proof" claim into the store (Store.get(key, default) stores the default), so a log only shows claims the agent actually made.
  • SandboxComparator no longer takes sandbox_name; it uses the caller's comparator (comparator outside a swarm).
  • Generated sandbox configs move into a single-agent/ or swarm-<N>/ subdirectory.
  • inspect-swarm (0.2.x) becomes a runtime dependency, and the dev inspect-ai floor rises to 0.3.283, which inspect_swarm requires.

Running a swarm

configs/erdos74-swarm4.yaml is an example. Swarm configs should set time_limit rather than working_limit (the config's comment explains why) and should not enable checkpointing: inspect_swarm does not support resuming a swarm.

Known limitations

  • The boltons resources tool could report a member's own budget through swarm_budget(), which would remove the builder's swarm argument (TODO in apn/solver.py).
  • Swarms run only on the k8s backend. A swarm's generated compose file has no default service and no x-default: true, which Inspect's Docker backend requires, so a swarm sample fails at setup under Docker.
  • While a swarm runs, every member's submissions are scored into the same submission_contents metadata key, so it holds whichever member was scored last. The final score writes each member's tree to submission_contents_<member>, but scripts/extract_plaintext.py reads only submission_contents, so it can extract a different member's files than the one whose work passed.

Each member works in its own agent sandbox (prover-1..N) and reaches the
shared comparator by name; the shared-memory board stays the only channel.
The submit tool records the claim per member (keyed by the member's tool
state scope), an in-member gate check scores that member's own sandbox, and
the final score checks every member's sandbox: CORRECT if any passes.

Concurrent checks on one comparator sandbox are now serialized: each check
resets the comparator's shared staging directories, so two members checking
at once would clobber each other's inputs.

Swap the erdos74-swarm8 config for erdos74-swarm4: 4 members, $400 cap,
no checkpointing (inspect_swarm does not support resuming a swarm).
… one

Each member prover-N now gets its own comparator-prover-N sandbox, the
only one it can see, so concurrent checks never share a comparator and
the lock goes. The checker picks the comparator for the caller's member
(from its tool state scope), and final scoring runs each member's check
inside that member's scope so it resolves the same comparator and claim.
Single-agent runs keep the single default + comparator pair.
Swarm members' resources tool now shows their own share of the swarm's
cost and token caps instead of the sample's limits.
The boltons resources tool reads sample_limits(), which shows the sample's
cost limit rather than the member's share of the swarm's cap. Swarm members
get inspect_swarm's swarm_resources() instead, under the name the prompt
uses (resources); single agents keep the boltons tool.
agent_type is back to the loop alone (react or deep), and a swarm's
members use it too. A positive swarm_size runs a swarm of that many
members; None, zero or negative runs a single agent.
swarm_tools() gives no tools and swarm_on_continue() defers to its inner
hook outside a swarm, so one builder serves both; only the budget tool
differs (resources, or swarm_resources for a member's own share).
Joining every member's name made a path component too long for the
filesystem at 32 members, so the task failed to load.
Working time stalls when many concurrent members wait, so it suits a
swarm poorly; the swarm also turns a time limit into a soft-stopping cap.
The whole-sample score now states the swarm's size and which members'
work passes, then one line per member: verdict, comparator stage, claim,
and whether the claim was the default and whether the member submitted.

The scorer also stops storing the default "proof" claim: Store.get with a
default writes it, so members that never submitted looked as if they had
claimed "proof".
One row per member: result, claim, stage, and the last line of the
checker's output, which says why a member's work failed.
Adds the member's swarm status (submitted, stopped, share, cancelled, ...)
as a column, and the swarm's stop reason, plus what ended it from outside
when anything did, to the summary line.
max_file_bytes (262,144) and max_board_bytes (16,777,216) were above the
bounds shared_memory() accepts, so building the swarm raised ValueError.
Members no longer get notices injected between turns; they learn of
board changes from the status line ending each shared_memory result and
from swarm_wait.
0.2.0 removes swarm notices, swarm_on_continue() and member(delivery=...),
so drop them (members already used poll delivery). It also removes the
shared_memory caps, so restore the intended max_file_bytes and
max_board_bytes.
@tadamcz
tadamcz marked this pull request as ready for review October 28, 2026 15:55
Each member's scored tars go under artifacts/<uuid>/<member>/ with its own
attempt counter, so the artifacts show whose work each tar is.
@tadamcz
tadamcz merged commit 013dff9 into main Oct 28, 2026
12 of 13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant