> ## Documentation Index
> Fetch the complete documentation index at: https://docs.while.ai/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Install with `uv add whileai`; import as `import whileai as wai`.
> Run the offline path first (`simulator=False`, `wai.seeded_agent`, a callable judge); no key is needed for it.
> Report every pass rate with its interval and n, as `scored.pass_at` prints it.

# Swarm rescue: passing rollouts on the tasks GRPO throws away

> What you learn: on the tasks where the policy fails every one of 8 rollouts, whether a particle swarm of rollouts that share their best attempts finds a passing answer that resampling alone does not, at the same budget.

<Note>The scripts are in the repository, not in the installed package. Clone it,
then `cd recipes/01-simulate/swarm-rescue` before running the commands below. [Browse this recipe on GitHub](https://github.com/whilehq/whileai-sdk/tree/main/recipes/01-simulate/swarm-rescue).</Note>

**What you learn:** on the tasks where the policy fails every one of 8
rollouts, whether a particle swarm of rollouts that share their best attempts
finds a passing answer that resampling alone does not, at the same budget.
**Needs:** `WHILEAI_API_KEY` for the hosted Qwen3-4B (`wai login`), or any
OpenAI-compatible endpoint via `--base-url`; `pyarrow` for the dataset.
Offline with `--dry-run`. **Takes:** seconds offline; about three and a half
hours for every task and two noise re-runs on the hosted model.

```bash theme={"theme":"vitesse-dark"}
python run.py --dry-run        # three toy tasks, a fake model, no key
python run.py --limit 20       # twenty code_contests tasks, hosted Qwen3-4B
python run.py --noise-runs 2   # all 275 tasks, four arms, the noise band
python run.py --reuse --post   # the numbers again from out/, then the Runs page
```

## The problem

GRPO learns from the spread inside a group of rollouts. On a task where all
8 fail, the advantage is zero and dynamic sampling drops the prompt \[1]. The
SDK does the same: `simulate(mode="rl")` fills a group only when its first
two rollouts disagree. Those tasks are the hardest ones in the set, the
ones a training run most needs, and "sample more" rarely helps: the
policy puts almost no mass on the answer, so 24 more independent draws are
24 more misses.

A particle swarm \[2] changes what the extra samples are conditioned on.
Each particle keeps its best attempt so far, sees the test it failed, and
sees a neighbour's best attempt. The LLM is the velocity update: given
its own best and a neighbour's, it writes the next program. Topology is
the variable. In a ring each particle sees two neighbours, so good ideas
spread slowly and the swarm stays diverse; in a star everyone sees the
global best, which is faster and collapses the swarm onto one idea \[3].

## The recipe

1. Tasks: `deepmind/code_contests` test and valid splits \[4], 275
   Codeforces-style problems after dropping the interactive ones, median
   rating 1900. The visible tests are the public tests plus 8 generated
   ones; the hidden tests are the private tests plus 32 more generated
   ones. Pass = the program's output, split on whitespace, matches on every
   test within 6 seconds, numbers within 1e-6.
2. Base: 8 rollouts per task from the hosted Qwen3-4B, thinking off,
   temperature 1, 2,048 tokens. A task where all 8 fail every test is an
   all-fail task. The script prints the per-task histogram, not the mean,
   because the mean hides the shape.
3. Every all-fail task gets 24 more samples four ways:

   * **resample**: 24 fresh independent samples.
   * **solo**: 8 particles, 3 rounds. Round 0 is 8 fresh samples. In each
     later round a particle sees its own best attempt (most visible tests
     passed; ties go to the newer one) and the first visible test it
     failed, with the input, the expected output and what it printed, and
     writes a new program. No sharing.
   * **ring**: solo, plus the best attempt of the particle's two ring
     neighbours, shown beside its own.
   * **star**: solo, plus the best attempt of the whole swarm.

   Fitness inside a swarm is the visible tests only. A rescue is a program
   that passes the hidden tests too, which no arm ever sees. Every arm
   stops on a task at its first rescue, so a rescued task costs less than
   24 samples.
4. Measure: rescue rate per arm, the share of all-fail tasks with at least
   one rescue, with a bootstrap interval over tasks (`wai.pass_at`). Each
   swarm arm against resample is a paired delta over the same tasks with a
   sign-flip p-value (`wai.compare_runs`). `--noise-runs 2` re-runs the
   resample arm with new seeds and reports the band a delta has to clear
   (`wai.eval_variance`).
5. Export: every rescued program goes to `out/rescued.jsonl` as a bare row
   (the problem in, the passing program out) with no swarm context in it.
   That file is what a later SFT or distillation run trains on; the swarm
   prompts never leak into it.

## Result

Run 2026-09-21 on the hosted Qwen3-4B. 275 tasks, 223 all-fail at 8
rollouts (81%). Per-task passes out of 8: 223 tasks at 0, then 17, 14, 4,
3, 1, 3, 4, 6.

| Arm      | Rescued | 95% band    | Rescued by round 0 / 1 / 2 | vs resample                |
| -------- | ------- | ----------- | -------------------------- | -------------------------- |
| resample | 7.2%    | 4.1 to 10.3 | 8 / 4 / 4                  |                            |
| solo     | 6.7%    | 3.3 to 10.1 | 6 / 6 / 3                  | -0.4 \[-4.0, +2.7], p 1.00 |
| ring     | 9.9%    | 6.1 to 13.7 | 5 / 12 / 5                 | +2.7 \[-1.3, +6.7], p 0.28 |
| star     | 9.9%    | 6.1 to 13.7 | 6 / 13 / 3                 | +2.7 \[-0.9, +6.3], p 0.24 |

**Flat.** Both swarms rescued 22 tasks to resampling's 16, and the gap sits
inside its interval. The noise band says the same thing louder: the
resample arm re-run with two more seeds rescued 20 and 24 tasks (9.0% and
10.8%), so its run-to-run spread is 1.8 points and a delta under 10.9
points is the eval re-running. The swarms' 9.9% is where resampling lands
on an average seed.

**Replicated.** A second seed of the whole experiment (`--seed 1`,
228 all-fail tasks) put every arm between 10.1% and 11.4%, resample re-runs
at 11.4, 10.1 and 11.8%, and a band of 5.6 points. Ring came in 1.3
points behind resampling and star level with it. The seed-0 lead for the
swarms was the draw.

| Seed 1             | resample   | solo               | ring               | star               |
| ------------------ | ---------- | ------------------ | ------------------ | ------------------ |
| Rescued            | 11.4%      | 11.0%              | 10.1%              | 11.4%              |
| vs resample        |            | -0.4 \[-3.9, +3.5] | -1.3 \[-4.8, +2.2] | +0.0 \[-3.9, +3.9] |
| By round 0 / 1 / 2 | 17 / 5 / 4 | 14 / 11 / 0        | 13 / 5 / 5         | 14 / 8 / 4         |

What did move is the training set. Across the six runs (four arms and two
re-runs, 144 extra samples a task), 43 of the 223 all-fail tasks got at
least one program passing every hidden test, 19% of the prompts a single
8-rollout group would have dropped; seed 1 rescued 49 of 228 the same way. The four arms alone wrote 112
distinct passing programs on 37 tasks into `out/rescued.jsonl`.

| Rating       | Tasks | resample | ring | star |
| ------------ | ----- | -------- | ---- | ---- |
| under 1500   | 70    | 10       | 13   | 15   |
| 1500 to 2199 | 57    | 2        | 3    | 4    |
| 2200 and up  | 96    | 4        | 6    | 3    |

| Cost                        | Value                                                                  |
| --------------------------- | ---------------------------------------------------------------------- |
| Model calls                 | 15,368 for the base pass and the first three arms; about 24,000 in all |
| Tokens                      | 15.8M prompt, 9.8M completion in the first process                     |
| Replies cut at 2,048 tokens | 1,192 of 15,368 (7.8%)                                                 |
| Wall clock                  | about 3.5 hours at 64 concurrent requests, one L40S                    |

## Raising the floor

The flat result had one candidate cause: fitness was zero almost
everywhere, so a neighbour's best program carried no information. Two
more configurations tested that, same recipe, same measurement.

Thinking mode on the 4B is out. On a hard task 32 of 40 replies ran past
6,144 tokens still thinking and never wrote a program.

Qwen3.8-27B, thinking off, is in (`--base-url`, `--max-tokens 8192`,
`--no-comments`: with thinking off it reasons inside the code block as
comments without end, so the prompt gains one line and the cap doubles,
both recorded in `results-27b.json`). It fails all 8 rollouts on 107 of
275 tasks. On 57 of those at least one base rollout passes a visible test,
the near-miss band where fitness is graded (`--band near-miss`): the best
base attempt passes a third of the visible tests on average, and half of
them on one task in five.

| 27B, near-miss band, 57 tasks | resample     | solo                | ring                | star                 |
| ----------------------------- | ------------ | ------------------- | ------------------- | -------------------- |
| Rescued                       | 31.6%        | 28.1%               | 29.8%               | 33.3%                |
| 95% band                      | 20.2 to 43.0 | 15.8 to 40.4        | 18.4 to 41.2        | 21.0 to 45.6         |
| vs resample                   |              | -3.5 \[-14.0, +7.0] | -1.8 \[-10.5, +7.0] | +1.8 \[-12.3, +15.8] |
| By round 0 / 1 / 2            | 11 / 4 / 3   | 8 / 7 / 1           | 13 / 3 / 1          | 12 / 5 / 2           |

Flat again. The resample re-run landed at 29.8%, and 29 of the 57 tasks
were rescued by at least one of the five runs; `rescued-27b.jsonl` holds
108 passing programs on 28 tasks. Cost: about 8,000 calls and 40M tokens
over four hours on the SGLang endpoint, with 22% of arm replies still
at the 8,192 cap.

## Learned

* Feedback alone did nothing here. Solo refinement, the swarm with the
  social term off, matched resampling exactly. On the unrescued tasks the
  best program passes 8% of the visible tests on average, so the fitness
  signal the swarm moves on is almost always zero, and a particle shown a
  neighbour's zero-fitness program has no direction to move in. A swarm
  needs a graded fitness; pass or fail on a task the model cannot touch
  is not one.
* The rescues that did happen came in the refinement rounds for the
  swarms (17 of 22 for ring, 16 of 22 for star) and in round 0 for
  resampling (8 of 16), which is the pattern the design predicts. It is
  the size that is missing: `wai.holdout_size` says a +5 point gap at a
  7% base needs 547 paired tasks and a +3 point gap needs 1,382. 223 was
  never going to resolve a gap this small.
* A real gradient did not change the answer. On the 27B's near-miss band
  the swarm had a third of the visible tests to climb and still tied 24
  independent draws, with most rescues in round 0 for every arm. Three
  configurations, three flat results: PSO over rollouts, as a way to
  rescue the prompts GRPO drops, is closed. What a hard prompt needs is
  more samples or a stronger model, not a smarter way to condition them.
* The dataset angle survives the flat result. On tasks the model gets
  right 1 time in 30 or less, how you structure the extra samples did not
  matter; that you spend them did. Six runs of 24 turned 43 zero-gradient
  prompts into learnable ones. The next recipe trains on those rows
  (rejection-sampled SFT or on-policy distillation from the bare prompt,
  since the swarm rows are off-policy for it) against plain GRPO at
  matched rollouts.

Verified 2026-09-21 (seed 0), 2026-09-22 (seed 1) and 2026-09-22 (27B). `results.json` and
`results-seed1.json` in this directory are the two runs' reports;
`rescued-seed1.jsonl` is seed 1's 127 passing programs on 42 tasks, the
rows a training run starts from. `python run.py --reuse` reprints a report
from `out/`.

## References

1. Yu, Q. et al. DAPO: An Open-Source LLM Reinforcement Learning System at Scale. arXiv:2503.14476, 2025.
2. Kennedy, J., Eberhart, R. Particle Swarm Optimization. *IEEE ICNN*, 1995.
3. Kennedy, J., Mendes, R. Population Structure and Particle Swarm Performance. *IEEE CEC*, 2002.
4. Li, Y. et al. Competition-Level Code Generation with AlphaCode. arXiv:2203.07814, 2022.
5. Feng, S. et al. Model Swarms: Collaborative Search to Adapt LLM Experts via Swarm Intelligence. arXiv:2410.11163, 2024. PSO over LoRA weights; this recipe runs PSO over programs instead.
6. Li, J. et al. QuestA: Expanding Reasoning Capabilities of LLMs via Question Augmentation. arXiv:2507.13266, 2025. Rescues the same zero-advantage prompts with partial reference solutions as hints; the swarm makes its hints from its own population.
