Skip to content

feat: parallel eval attempts - #203

Draft
Rodriguespn wants to merge 3 commits into
ai-912-vercel-sandbox-snapshotfrom
ai-912-parallel-attempts
Draft

feat: parallel eval attempts#203
Rodriguespn wants to merge 3 commits into
ai-912-vercel-sandbox-snapshotfrom
ai-912-parallel-attempts

Conversation

@Rodriguespn

@Rodriguespn Rodriguespn commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Follow-up to #201: parallel eval attempts (any-pass) — does the run time halve?

Draft, stacked on #201 (warm-boot snapshot). Per the thread conclusion: the snapshot showed the bottleneck is agent time per run, so the next lever is running a pair's attempts in parallel instead of sequentially.

What it does

  • --parallel-attempts: instead of one sandbox running a pair's runs attempts sequentially with stop-on-pass, fan every (pair × attempt) out to its own sandbox concurrently. An eval passes if at least one attempt passed (any-pass), matching the agreed success criterion.
  • run-eval.ts is unchanged. Each attempt runs --runs 1 and is scored exactly as today; the any-pass is a pure OR-aggregation over the per-attempt result files in the runner (the representative passing tree is kept, attempts set to the number run). No scoring-semantics change inside the harness.
  • The workflow enables it via vars.EVAL_PARALLEL_ATTEMPTS and sizes concurrency to pairs × runs so attempts run in one wave. Default (unset) keeps sequential stop-on-pass → no behavior change.

The catch (why concurrency matters)

The halving only materializes when concurrency ≥ pairs × runs (≈474), so all attempts run in one wave. At the current 250 it would be ~2 waves = the same wall-clock. The workflow therefore bumps concurrency to pairs × runs for this mode. The real ceiling is AI-provider rate limits at ~474 concurrent agent calls — this run measures both the wall-clock and the pass rate to check the environment didn't cause agents to fail.

Result — ~39% faster (near the floor), fidelity preserved

run-evals pairs concurrency boots failures pass rate
Parallel (this PR)run 31809251147 17m12s 237 474 (one wave) 474 warm / 0 cold 0 86.4%
Warm sequential (#201) — run 31802584549 28m10s 237 250 0 88.2%
Cold sequential (#192) — run 31735507954 30m03s 220 250 88.2%

Not quite half, but close to the floor. 28m10s → 17m12s = −39%. The wall-clock floor is the slowest single attempt (828s ≈ 14 min, build-functions-005); the sequential run was ~2× that (the critical-path eval doing 2 sequential attempts), so the critical path did roughly halve (≈27→14 min). The residual gap from a clean 50% is fixed per-attempt overhead (warm setup + download + aggregation) that doesn't parallelize away.

Fidelity holds. 474/474 booted warm, 0 sandbox failures, and 0 evals errored (💥 ERR). Pass rate 86.4% (190/220) vs 88.2% is run-to-run noise, not throttling: the flips are bidirectional and scattered across every agent/provider (10 PASS→FAIL, 6 FAIL→PASS), whereas throttling would concentrate failures. Attempt distribution: 185 pairs 2/2, 22 pairs 1/2 (any-pass saved these), 30 pairs 0/2.

Cost tradeoff: parallel always runs all runs attempts (474 sandboxes) vs sequential's ~1.1× average — roughly 1.8× the sandbox-compute for the 39% wall-clock win (Pedro's "a lot of compute to keep in mind"). Worth pairing with a lower vcpus and/or excluding the slowest eval from the critical path as follow-ups.

Testing

  • typecheck + test:vercel-runner green.
  • Local E2E: 1 pair × 2 attempts spun 2 sandboxes concurrently → aggregated "2/2 passed → PASS", canonical result attempts=2, single clean output dir.

🤖 Generated with Claude Code

Adds --parallel-attempts to the runner: instead of one sandbox running a
pair's `runs` attempts sequentially with stop-on-pass, fan every
(pair x attempt) out to its own sandbox concurrently and pass the eval if
at least one attempt passed. Each attempt is scored exactly as today
(--runs 1), so run-eval.ts is unchanged; the any-pass is a pure
OR-aggregation over the per-attempt result files (the representative
passing tree is kept, attempts set to the number run).

Since the bottleneck is agent time per run, parallelizing the attempts can
cut wall-clock roughly in half when concurrency covers pairs x runs (one
wave). The workflow enables it via vars.EVAL_PARALLEL_ATTEMPTS and sizes
concurrency to pairs x runs; default (unset) keeps sequential stop-on-pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Aug 14, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
evals Ignored Ignored Preview Aug 14, 2026 2:47pm

Request Review

@Rodriguespn Rodriguespn added the run-evals Add to a PR to refresh benchmark evals label Aug 14, 2026
@Rodriguespn Rodriguespn removed the run-evals Add to a PR to refresh benchmark evals label Aug 14, 2026
Exports aggregateAttempts and adds cases for: passes if any attempt passed
(keeps the passing tree), fails only when all failed, counts only attempts
that produced a result, and throws when none did.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Rodriguespn Rodriguespn changed the title feat: parallel eval attempts (any-pass) — halve the run time? (follow-up to #201) feat: parallel eval attempts Aug 14, 2026
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