Overview · Installation · Quickstart · Benchmarks · Citation
September 2026 · Code release. EvoDuet, 31 paper tasks, and four shared experiment launchers.
Better search informs better solutions; evaluated solutions guide the next search. EvoDuet couples program evolution with a web-search loop that adapts to the current solution's knowledge gaps.
A retrieval gate connects the loops. It chooses whether to retrieve new web evidence, look up saved evidence, or continue without documents.
A search trajectory on Swap Reduction: useful evidence carries forward across solution revisions.
See the full method: retrieval gate, query evolution, and solution evolution
- Decide what knowledge is needed. Inspect the current solution, search history, and stored evidence to choose retrieve, look-up, or no-op.
- Evolve the queries. Refine queries using web results and remaining knowledge gaps. Rank evidence by predicted solution quality, with the parent solution fixed throughout the inner rounds.
- Evolve the solutions. Generate and evaluate candidates using the selected evidence. Save the measured outcomes with that evidence for future decisions.
The inner loop predicts scores without generating or evaluating candidate solutions. The outer evaluator measures actual improvement. With parallel EvoDuet, retrieve/look-up iterations use multiple candidates and no-op iterations use one. Model weights remain fixed throughout.
Built on SkyDiscover. Use Python 3.12 and uv.
git clone --recurse-submodules https://github.com/Open-Galapagos/EvoDuet.git
cd EvoDuet
uv sync --frozen --extra dev
cp .env.example .envAdd your model provider's key to .env:
- OpenAI: set
OPENAI_API_KEYand use your model ID. - OpenRouter: set
OPENROUTER_API_KEYand useopenrouter/author/model.
EvoDuet web retrieval also requires TAVILY_API_KEY. Baseline runs use only the
model provider's key. Use --api-base for a custom OpenAI-compatible endpoint.
The quickstart uses SimpleTES Circle Packing (26 circles); install its standard
math dependencies:
uv pip install numpy scipy scikit-learn psutil cvxpyInstalling other benchmark families
See the benchmark catalog for setup instructions. Some families require downloaded datasets, Docker, or a GPU. Optional dependency groups cover several families:
# Math and ADRS
uv sync --frozen --extra dev --extra math --extra adrs
# ALE-Bench
git submodule update --init --recursive
uv sync --frozen --extra dev --extra ale-benchInclude all desired extras in one uv sync invocation; each invocation updates
the environment to the selected groups. The launchers use the installed
environment without syncing it again.
From the repository root, preview the task and launch EvoDuet. Replace
YOUR_MODEL with a model available through your provider.
TASK="benchmarks/simpletes/datasets/circle_packing/simpletes_circle_packing_26"
# Preview the task and settings
bash scripts/run_evoduet.sh "$TASK" --dry-run
# Run EvoDuet
bash scripts/run_evoduet.sh "$TASK" \
--model YOUR_MODEL --iterations 100 --seed 42 --output outputs/circle-packing| Mode | Script | Candidates per iteration |
|---|---|---|
| EvoDuet | run_evoduet.sh |
1 |
| Parallel EvoDuet | run_evoduet_parallel.sh |
8 after retrieve/look-up; 1 after no-op |
| Baseline | run_baseline.sh |
1 |
| Parallel baseline | run_baseline_parallel.sh |
8 every iteration |
EvoDuet uses the retrieval gate described above; the baseline disables retrieval. Tasks run sequentially. Parallel modes generate candidates within each task: the baseline evaluates one candidate at a time, while EvoDuet allows up to N concurrent evaluations for N candidates.
Run parallel EvoDuet or the baseline
TASK="benchmarks/simpletes/datasets/circle_packing/simpletes_circle_packing_26"
# Parallel EvoDuet
bash scripts/run_evoduet_parallel.sh "$TASK" \
--model YOUR_MODEL --num-generations 8 --output outputs/circle-packing-parallel
# Program evolution without retrieval
bash scripts/run_baseline.sh "$TASK" \
--model YOUR_MODEL --output outputs/circle-packing-baseline
# Parallel program evolution without retrieval
bash scripts/run_baseline_parallel.sh "$TASK" \
--model YOUR_MODEL --num-generations 8 --output outputs/circle-packing-baseline-parallelWithout task arguments, each script loops over its editable TASKS=(...) list of
21 SimpleTES tasks. Complete the SimpleTES setup
before running the full list:
MODEL=YOUR_MODEL bash scripts/run_evoduet.shTask paths before flags replace that list. Each invocation uses one seed and
edit mode per task: seed 42 and diff edits by default. All four scripts accept
--model, --api-base, --iterations, --seed,
--output, --checkpoint, and --edit diff or --edit full. Use
--num-generations to change the candidate count, including for the baseline.
The default search scaffold is openevolve_native; choose another with
--search. The installed evoduet-run command provides the same interface.
Search budgets, checkpoints, and your own task
| Inner-loop setting | Default | Flag |
|---|---|---|
| Rounds | 3 | --inner-rounds |
| Queries per round | 1 | --query-count |
| Results per query | 5 | --documents-per-query |
| Retained documents | 3 | --search-top-k |
Advanced options accept dotted flags, such as --llm.temperature 0.7 or
--evoduet.retrieval_gating_backend_type always.
Only the current evoduet settings are supported. The former world_knowledge
section and removed experiment options are rejected in YAML and CLI overrides.
Resume a run: pass --checkpoint PATH with the same task and settings.
PATH is the checkpoint directory. EvoDuet automatically saves evoduet.json
inside it, containing search history, retrieved documents, and evidence credit,
and restores that state on resume. Resume requires a checkpoint written by this
release, with completion marker schema version 2 and EvoDuet state schema version 1.
Older checkpoint formats and missing state are rejected; start a new run to use
this release. Baselines do not require evoduet.json, except EvoX, which stores
its controller state there.
--iterations sets the total target. For example, resuming checkpoint 40 with
--iterations 100 runs the remaining 60 iterations:
uv run --no-sync evoduet-run \
benchmarks/simpletes/datasets/circle_packing/simpletes_circle_packing_26 \
--model YOUR_MODEL --seed 42 --iterations 100 \
--checkpoint outputs/circle-packing/checkpoints/checkpoint_40The single-task CLI reuses the checkpoint's run directory when --output is
omitted. With a shell script, select one task and pass --output explicitly to
reuse that directory.
Experiment defaults: the CLI applies these shared settings to the task config. Explicit dotted flags override them.
| Setting | Default |
|---|---|
| Maximum output tokens | 32768 |
| Temperature / top-p | 0.7 / 0.95 |
| Reasoning effort | medium |
| LLM timeout / retries | 1800 seconds / 3 |
| Checkpoint interval | Every iteration |
Model and API endpoint selection comes from the task configuration or
--model / --api-base. Set these, the seed, and the edit mode explicitly when
reproducing an experiment. The best program receives the task's final evaluation;
the launchers do not run initial held-out evaluation or NDG postprocessing.
Bring your own task: provide initial_program.*, config.yaml, and
skydiscover_evaluator.py (or evaluator.py / an evaluator/ directory).
For a family-level container evaluator, pass --evaluator FAMILY_DIRECTORY.
EvoDuet preserves the task's prompt and evaluator timeout.
See the launcher guide for the complete interface.
31 paper tasks, with their seed programs, evaluators, and setup instructions:
| Collection | Tasks |
|---|---|
| Simple Scientific Optimization | 10 |
| SimpleTES | 21 |
The benchmark catalog also preserves the 10 original SkyDiscover benchmark families.
| Path | What to find |
|---|---|
skydiscover/evoduet/ |
Retrieval gating, observed web search, and evidence memory |
skydiscover/evoduet_cli.py |
Shared task launcher and selective generation routing |
skydiscover/search/ |
Evolutionary search scaffolds |
benchmarks/ |
Task definitions, evaluators, and setup guides |
best_programs/ |
The eleven best evolved programs reported in the paper, with their evaluation records |
scripts/ |
OpenEvolve loops for SimpleTES: EvoDuet and baseline, each with single and parallel candidates |
tests/evoduet/ |
Offline runtime and integration tests |
Development checks
uv run --no-sync pytest -q -m "not integration" tests/config tests/cli tests/evoduet
uv buildThe test command above uses mock model and search responses and excludes live API tests. CI also covers search scaffolds, checkpoint handling, and package builds.
Use the repository citation below when referencing this implementation:
@misc{lee2026evoduet,
title = {EvoDuet: Bilevel Co-Evolution of Web Searching and Task Solving for Scientific Discovery},
author = {Young-Jun Lee and Jinheon Baek and Soyeong Jeong and Minki Kang
and Seungyeon Jwa and Jonghyun Choi and Seungho Han and Dongyeop Kang},
year = {2026},
url = {https://github.com/Open-Galapagos/EvoDuet},
note = {Code repository}
}The code is distributed under the Apache 2.0 license. Vendored benchmark code, datasets, and other third-party materials retain their respective licenses and source notices; consult each benchmark's documentation.
EvoDuet uses the SkyDiscover framework for evolutionary search, model interfaces, and evaluation. We thank its contributors and the authors of the included benchmarks for making their implementations available. The original SkyDiscover README is preserved for upstream documentation and attribution.

