Complete guide to using the optimize_anything API for text-based optimization
The optimize_anything API is GEPA’s primary entry point for optimizing any text-representable artifact using LLM-guided search. Whether you’re optimizing prompts, code, configurations, or agent architectures, this unified API handles the entire optimization workflow.
seed_candidate → evaluate → reflect on ASI → propose → repeat ↑ | └────────────────────────────┘
Actionable Side Information (ASI) is the text-optimization analogue of the gradient. Where gradients tell a numerical optimizer which direction to move, ASI tells the LLM proposer why a candidate failed and how to fix it.
When:dataset=None, valset=NoneUse case: Solve one hard problem where the candidate is the solution.Example: Circle packing, blackbox mathematical optimization
import gepa.optimize_anything as oafrom gepa.optimize_anything import optimize_anything, GEPAConfig, EngineConfigdef evaluate(candidate: str) -> float: result = run_code(candidate) oa.log(f"Score: {result.score}, Overlaps: {result.overlaps}") return result.scoreresult = optimize_anything( seed_candidate="def pack_circles(): ...", evaluator=evaluate, objective="Maximize the sum of radii for n circles in a unit square.", config=GEPAConfig(engine=EngineConfig(max_metric_calls=500)),)print(result.best_candidate)
When:dataset=<list>, valset=NoneUse case: Solve a batch of related problems with cross-task transfer. Insights from solving one help solve the others.Example: CUDA kernel generation for multiple PyTorch operations
result = optimize_anything( seed_candidate={"prompt": "Write an optimized CUDA kernel."}, evaluator=kernel_evaluator, dataset=kernel_problems, # batch of related problems objective="Generate prompts that produce fast, correct CUDA kernels.", config=GEPAConfig(engine=EngineConfig(max_metric_calls=300)),)
When:dataset=<list>, valset=<list>Use case: Build a skill that transfers to unseen problems.Example: Prompt optimization for AIME math, agent architecture evolution for ARC-AGI
result = optimize_anything( seed_candidate={"prompt": "Solve this math problem step by step:"}, evaluator=math_evaluator, dataset=train_problems, # train on these valset=val_problems, # must generalize to these objective="Generate system prompts that improve math reasoning.", config=GEPAConfig(engine=EngineConfig(max_metric_calls=200)),)
objective: Natural-language goal for the reflection LLM
background: Domain knowledge, constraints, or strategies
objective="Generate prompts that solve competition math problems."background="""The solution should:- Show step-by-step reasoning- Use clear mathematical notation- Verify the final answer"""
5
config
Full configuration object. See Configuration for details.
When you don’t have a starting artifact, pass seed_candidate=None and provide objective. The reflection LM bootstraps the first candidate from the description.
result = optimize_anything( seed_candidate=None, # LLM writes the first draft evaluator=evaluate_3d_render, dataset=visual_aspects, objective="Optimize a Python program to generate a 3D unicorn.", background="Use build123d for CSG geometry, export to STL, render with pyrender.",)
Seedless mode requires objective to be set. The reflection LLM needs the objective to generate an initial candidate.
result = optimize_anything(...)# Best candidateprint(result.best_candidate)# Best scoreprint(result.val_aggregate_scores[result.best_idx])# All candidates triedfor idx, candidate in enumerate(result.prog_candidates): score = result.val_aggregate_scores[idx] print(f"Candidate {idx}: {score}")# Optimization historyprint(f"Total evaluations: {result.total_metric_calls}")print(f"Number of candidates: {len(result.prog_candidates)}")
Always provide informative side_info. Include error messages, expected vs actual output, and diagnostic information. The more context you provide, the better the LLM can propose improvements.
Start with a small evaluation budget (e.g., max_metric_calls=50) to test your setup
Use oa.log() liberally to capture diagnostic information
Structure your side_info consistently across evaluations
For multi-objective optimization, include "scores" dict with all metrics
Enable parallelization for faster optimization when evaluations are independent
from gepa.optimize_anything import OptimizationStatedef evaluator(candidate, example, opt_state: OptimizationState): # Access historical best evaluations if opt_state.best_example_evals: prev_best = opt_state.best_example_evals[0]["side_info"] # Use prev_best to warm-start current evaluation result = run_test(candidate, example) return result.score