Why Use Evolution Blocks?
Preserve Setup Code
Keep imports, constants, and utility functions unchanged
Focus Evolution
LLM only modifies the algorithm, not boilerplate
Maintain Interfaces
Ensure function signatures stay compatible with evaluator
Faster Convergence
Smaller search space = better results
Basic Usage
Wrap the code to evolve with markers:- LLM sees and can modify everything between
EVOLVE-BLOCK-STARTandEVOLVE-BLOCK-END - Everything outside markers is frozen
- The frozen code is shown to the LLM for context, but never modified
Real Examples
Example 1: Circle Packing
Problem: Pack 26 circles in a unit square.- The LLM can evolve the packing algorithm (
construct_packing) - Helper function
compute_max_radiiis also evolvable - Interface
run_packing()stays fixed (evaluator depends on it) - Visualization code is preserved
benchmarks/math/circle_packing/initial_program.py:1
Example 2: GPU Load Balancing
Problem: Optimize expert parallelism load balancing.- Core algorithm is evolvable
- Public API
rearrange()is fixed - License header is preserved
- Comments and docstrings inside block can be modified
benchmarks/ADRS/eplb/initial_program.py:14
Example 3: Prompt Optimization
EVOLVE-BLOCKs work for prompt evolution too:Advanced Patterns
Multiple Evolution Blocks
You can have multiple blocks in one file:Nested Functions
Everything inside the block is evolvable, including nested definitions:Imports Inside Blocks
Imports inside blocks can be evolved:No Markers = Full File Evolves
If you don’t use EVOLVE-BLOCK markers, the entire file is mutable:- Simple problems
- When you want maximum flexibility
- Prompt optimization (no boilerplate)
- LLM might break interface contracts
- Boilerplate code gets regenerated every iteration
- Harder to maintain compatibility with evaluator
Best Practices
✅ Do:
-
Keep interfaces fixed:
-
Preserve expensive setup:
-
Include helper functions in block if they should evolve:
-
Document the interface:
❌ Don’t:
-
Don’t put imports outside if they might change:
-
Don’t split a function across markers:
-
Don’t put test code inside block:
Omitting Initial Program
You can skip the initial program entirely:- The evaluator file (shown as context)
- Problem description in config
- Example test cases
- Exploratory discovery
- Problems where you don’t have a baseline
- Prompt optimization
When omitting the initial program, provide a detailed problem description in the config:
How It Works Internally
When you provide a program with EVOLVE-BLOCK markers:1
Parse Markers
SkyDiscover extracts regions between
EVOLVE-BLOCK-START and EVOLVE-BLOCK-END2
Build Prompt
LLM prompt includes:
- Full file (with markers) as context
- Only mutable regions in edit mode
3
Generate Mutation
LLM outputs new code for the evolvable regions
4
Reconstruct File
Replace mutable regions with LLM output, keep fixed regions unchanged
5
Evaluate
Write reconstructed file to disk and run evaluator
skydiscover/utils/code_utils.py
Debugging
To see what code is being evolved, enable prompt logging:checkpoints/checkpoint_N/prompts/<program_id>.json to see:
- Full context shown to LLM
- Which regions are marked as mutable
- LLM’s generated replacement code
Related
Evaluators
Write evaluation functions that work with evolved code
Quick Start
See EVOLVE-BLOCKs in action