| name | llm4ad-task-builder |
| description | Use when a user wants to build an LLM4AD_Next task package — a runnable directory that lets the LLM4AD platform evolve an algorithm for their problem. Covers what files a task package contains, each file's contract, and how the package is run on the LLM4AD platform. |
Building an LLM4AD_Next Task Package
What LLM4AD_Next is
LLM4AD_Next is an automated algorithm-design platform: it combines an LLM with
evolutionary optimization to discover and improve algorithms automatically. The
user describes a problem (e.g. "evolve a solver for the Traveling Salesman
Problem"), marks the code region to evolve with # EVOLVE_START / # EVOLVE_END,
and the platform repeatedly asks an LLM to propose better code inside that region,
scores each candidate with a custom evaluator, and keeps the best across
generations. Better algorithms emerge through guided search rather than manual
trial and error.
Your job with this skill: turn a user's problem into a complete, runnable task
package, then verify it actually runs before handing it over.
What a task package is
A task package is a self-contained directory with everything the platform needs to
evolve algorithms for one problem:
| File | Purpose |
|---|
config.yaml | Master config for the whole pipeline (evolution params, providers, evaluator, dataset). This is what gets run. |
<name>_evaluator.py | A BaseEvaluator subclass that runs a candidate algorithm on test data and returns a score + metrics. Defines what "better" means. |
<algo_dir>/<algo>.py | The algorithm, with the function to evolve wrapped in # EVOLVE_START / # EVOLVE_END. Reads input as a JSON CLI arg, prints a JSON result. |
requirements.txt | Third-party dependencies (auto-generated by build engine based on task description and code analysis). |
debug_run.py | Runs the full pipeline once (LLM4AD("config.yaml").run()) — a smoke test. |
test_evaluator.py | Loads the evaluator via the dispatcher to confirm it imports and is wired correctly. |
data/sample/*.json | 2-3 small test instances the evaluator scores algorithms against. |
The contracts each file must satisfy
Algorithm file (<algo_dir>/<algo>.py):
- The algorithm directory is the one named in
version_control.local_path; the
platform copies it into a git worktree for each candidate and edits only the code
between the markers.
- The function to evolve is wrapped exactly in
# EVOLVE_START and # EVOLVE_END
comment markers (the platform only edits code between them).
- It reads its input as a single JSON string from
sys.argv[1], and prints a JSON
result to stdout — so the evaluator can run it as a subprocess.
- The file name must match the name the evaluator looks for (see below). In the TSP
example both sides use
solve.py.
Evaluator (<name>_evaluator.py):
- Subclasses
BaseEvaluator, decorated @BaseEvaluator.register("<name>"), with a
no-argument __init__(self) (the base __init__ takes no config).
- Implements
metrics (list of Metric(name, type=MINIMIZE|MAXIMIZE, weight, description)),
name, and async def evaluate(self, cfg: EvalContext) -> EvaluationResult.
cfg: EvalContext carries cfg.project_root (the candidate's worktree dir),
cfg.data_path (the current data file), and cfg.timeout (seconds per instance).
evaluate locates the algorithm file by hardcoded name under
cfg.project_root (e.g. Path(cfg.project_root) / "solve.py"), runs it as a
subprocess on cfg.data_path, parses its JSON output, validates it, and returns
EvaluationResult(score, metrics, success, ...) (negative cost for a minimization
objective). That hardcoded name must equal the algorithm file's actual name —
this is the most common wiring mistake.
config.yaml (10 sections — generated programmatically, not hand-written):
providers use ${LLM_BASE_URL} / ${LLM_API_KEY} / ${LLM_MODEL} env
placeholders (the platform fills them in).
- The evaluator
module reference (<file>.py:<ClassName>), the metrics list,
the dataset path (data/sample), and the coder prompt_template's EVOLVE block
must all be consistent with the other files.
debug_run.py / test_evaluator.py: standard boilerplate that runs
config.yaml end-to-end and loads the evaluator, respectively.
See reference/config-template.yaml for the config shape and reference/example-tsp.md
for a pointer to a complete, known-good package in this repo to adapt.
The information to gather before building
A correct package needs these decided (ask the user; let the agent fill sensible
defaults where the user has no preference):
- Problem description — what is being optimized. (Required — ask.)
- Function/algorithm to evolve + input/output format — ask if the user has any
constraints; if not, the agent designs it.
- Evaluation metric(s) — what to minimize/maximize. (Required — ask clearly.)
- Reuse existing code/evaluator? — user provides existing code, or generate from
scratch. (Ask.)
- Data source — user provides data, or generate sample instances. (Ask.)
- Programming language — default Python. (Offer as a choice.)
- Project name — the agent proposes one from the description; confirm with user.
Build workflow
- Understand the problem and the 7 items above (inspect any user-provided code/data).
- Write the algorithm file first; verify it runs standalone and prints valid JSON.
- Write 2-3 sample data files under
data/sample/.
- Write the evaluator; keep its metrics consistent with
config.yaml.
- Write
config.yaml, debug_run.py, test_evaluator.py.
- Self-test (required): run
test_evaluator.py (evaluator loads — no LLM, no
network, cheap and always runnable). Then run debug_run.py, which executes the
full pipeline and does call the LLM: it needs the LLM_BASE_URL /
LLM_API_KEY / LLM_MODEL env vars set (the config uses these placeholders) and
consumes tokens over the network. Read every error, fix the relevant file,
re-run. Not done until both run cleanly. If no provider credentials are available,
say so — a missing-credential failure of debug_run.py is not a package defect.
Automated algorithm design methods (reference — only when the user asks)
Always build new task packages with the default Island GA method. Do NOT
proactively suggest switching to another method — let the user bring it up.
When the user asks about methods or explicitly requests a switch, help them
using the reference below. After applying a switch, ask whether they want to
continue adjusting parameters or are satisfied.
Each method requires a matching pair of evolution.type + planner.type.
Switching methods means **replacing the entire evolution section(parameters differ per method) and updatingplanner.type`. A fresh run is required — you
cannot resume a prior run after changing the evolution type.
Island GA (default — use this for all new builds)
evolution:
type: "island_ga"
max_generations: 50
num_islands: 5
island_population_size: 20
mutation_rate: 0.3
crossover_rate: 0.5
tournament_size: 3
early_stop_patience: 10
migration_interval: 5
migration_rate: 0.1
migration_strategy: "best"
migration_topology: "ring"
parallel_islands: true
planner:
type: "llm_evolution"
EoH — Evolution of Heuristics (ICML 2024)
Single-objective, rank-based truncation. Runs E1 (and optionally E2/M1/M2)
operators each generation.
evolution:
type: "eoh"
max_generations: 50
population_size: 5
selection_num: 2
max_sample_nums: 100
num_samplers: 1
use_e2_operator: true
use_m1_operator: true
use_m2_operator: true
seed_path: null
planner:
type: "eoh_evolution"
ReEvo — Reflective Evolution (NeurIPS 2024)
Adds two reflection signals to genetic search: short-term (comparing worse/better
parent pairs → better crossover) and long-term (accumulated history → better
elite mutation).
evolution:
type: "reevo"
max_generations: 50
population_size: 8
mutation_rate: 0.5
max_sample_nums: 100
num_samplers: 1
seed_path: null
planner:
type: "reevo_evolution"
MCTS-AHD — Monte Carlo Tree Search for AHD (ICML 2025)
Tree search over algorithm space: selects nodes via UCT, expands with
e1/e2/m1/m2/s1 operators. Not generational; max_generations is interpreted
as MCTS iterations.
evolution:
type: "mcts_ahd"
max_generations: 1000
init_size: 4
population_size: 10
selection_num: 2
max_sample_nums: 100
alpha: 0.5
lambda_0: 0.1
max_depth: 10
seed_path: null
planner:
type: "mcts_ahd_evolution"
Switching checklist (only when the user actively requests it)
- Replace
evolution section — remove the old method's specific params,
write the new method's complete section from the templates above. Do NOT
carry over params that don't exist in the new method's schema (e.g.
num_islands is IGA-only).
- Update
planner.type — must match the new method.
- Keep everything else —
providers, evaluator, coder, dataset,
version_control stay unchanged.
- Warn the user — switching evolution type means a fresh run; you cannot
resume an old checkpoint under a different method.
- Re-verify — after editing
config.yaml, re-run debug_run.py to
confirm the new method loads correctly.
- Ask whether to continue — after the switch is applied and verified, ask
the user if they want to adjust any parameters further or are satisfied.
How the package is used on the LLM4AD platform
Once the package exists, the user runs it to evolve their algorithm. Two paths:
CLI:
llm4ad run path/to/config.yaml
llm4ad run config.yaml --resume ./runs/<project>/<run_id>/checkpoints/iga_checkpoint_gen_5_a1b2c3d4.json
It launches the evolution pipeline and prints per-generation progress (best score
each generation).
Web platform (this project's Web UI):
- Create or open a task (the package's
config.yaml + files back it).
- Configure an LLM provider (the user's own key/model — OpenAI-compatible or
Anthropic). The package's
config.yaml references providers by the platform's
env placeholders; the platform injects the real credentials at run time.
- Start the evolution run; monitor progress in the browser — current generation,
best individual's score, metrics, and logs — in real time.
- When it finishes, inspect the best evolved algorithm and its score.
Tuning after generation (optional): users often adjust config.yaml evolution
parameters to trade cost vs. quality — e.g. max_generations, num_islands,
island_population_size, mutation_rate / crossover_rate, early_stop_patience,
the provider model, and evaluator.timeout.
Where results go: each run writes to ./runs/{project}/{run_id}/:
best/code/ — the evolved algorithm source (the main deliverable)
best/metadata.json, best/summary.txt — score, generation, metrics
state/evolution_state.json — full generation history (drives the Web UI dashboards)
logs/llm4ad.log — full execution log for troubleshooting
checkpoints/ — snapshots to resume from
Completion criteria
The package is done only when test_evaluator.py loads the evaluator AND
debug_run.py runs without raising. Then summarize what was built (files + the
7 decisions) and the verification result.
After a successful build, always close by conveying this message (translate to the
user's language; do NOT proactively recommend a specific method — just mention
that the option exists):
The task package has been built and verified. I can help you choose the
evolution method (such as EoH, ReEvo, or MCTS-AHD), and adjust evolution
parameters (such as max_generations, population size, etc.), or feel free
to let me know if you have other needs!