Skip to main content
GEPA uses evolutionary strategies to optimize prompts without gradient computation.

Quick Start (Minimal Config)

Only 6 fields required - everything else is auto-derived:
[prompt_learning]
algorithm = "gepa"
task_app_url = "https://st.usesynth.ai/s/rt_..."  # from tunnel.url
total_seeds = 200
proposer_effort = "LOW"
proposer_output_tokens = "FAST"
num_generations = 10
children_per_generation = 5

# Optional: budget constraint (omit to use account balance)
max_cost_usd = 10.0
Run with:
import os
from synth_ai.sdk import PolicyOptimizationJob

def run_gepa():
    job = PolicyOptimizationJob.from_config(
        "my_config.toml",
        api_key=os.environ["SYNTH_API_KEY"],
        algorithm="gepa"
    )
    job.submit()
    result = job.poll_until_complete()
    print(f"Best score: {result.best_score}")

What Gets Auto-Derived?

When you use a minimal config, these fields are automatically calculated:
FieldDefault Logic
train_seeds70% of total_seeds
validation_seeds30% of total_seeds
population.initial_sizemax(10, min(30, n_train // 10))
population.crossover_rate0.5
mutation.rate0.3
archive.sizepop_size * 2
rollout.budget100,000,000 (effectively unlimited)
rollout.max_concurrent20

Required Fields

FieldDescription
task_app_urlURL of your task app (via SynthTunnel or Cloudflare tunnel)
total_seedsTotal number of seeds in your dataset (auto-split 70/30)
proposer_effort"LOW_CONTEXT" | "LOW" | "MEDIUM" | "HIGH" - controls mutation model quality
proposer_output_tokens"RAPID" | "FAST" | "SLOW" - controls max output tokens
num_generationsNumber of evolutionary generations to run
children_per_generationNumber of child candidates to generate per generation
proposer_effort and proposer_output_tokens are required because they directly affect cost and quality tradeoffs. num_generations and children_per_generation are required because they determine the search budget.

Optional Budget Constraints

If you don’t specify any budget constraint, the job runs until your account balance is depleted.
# Choose one or more:
max_cost_usd = 10.0      # Stop when total spend reaches $10
max_rollouts = 5000      # Stop after 5000 rollouts
max_seconds = 3600       # Stop after 1 hour

Explicit Seed Specification

Instead of total_seeds, you can specify seeds explicitly:
[prompt_learning]
algorithm = "gepa"
task_app_url = "https://st.usesynth.ai/s/rt_..."  # from tunnel.url
proposer_effort = "LOW"
proposer_output_tokens = "FAST"

train_seeds = { start = 0, end = 140 }
validation_seeds = { start = 140, end = 200 }

Overriding Defaults

Any auto-derived field can be overridden:
[prompt_learning]
algorithm = "gepa"
task_app_url = "https://st.usesynth.ai/s/rt_..."  # from tunnel.url
total_seeds = 200
proposer_effort = "LOW"
proposer_output_tokens = "FAST"

# Override specific defaults
population_size = 30
num_generations = 15

Pinning Defaults Version

For reproducibility, you can pin the defaults version:
[prompt_learning]
defaults_version = "v1"  # Locks behavior forever
# ... rest of config
If not specified, the latest version is used. When defaults change in future versions, your config will automatically get the new behavior unless you pin a version.

Full Config (Advanced)

For complete control, specify all fields explicitly:
[prompt_learning]
algorithm = "gepa"
task_app_url = "https://st.usesynth.ai/s/rt_..."  # from tunnel.url

[prompt_learning.gepa]
env_name = "banking77"
proposer_effort = "LOW"
proposer_output_tokens = "FAST"

[prompt_learning.gepa.rollout]
budget = 100_000_000
max_concurrent = 20

[prompt_learning.gepa.evaluation]
train_seeds = { start = 0, end = 140 }
validation_seeds = { start = 140, end = 200 }

[prompt_learning.gepa.mutation]
rate = 0.3

[prompt_learning.gepa.population]
initial_size = 20
num_generations = 10
children_per_generation = 5
crossover_rate = 0.5
selection_pressure = 1.0

[prompt_learning.gepa.archive]
size = 64
pareto_set_size = 64
pareto_eps = 1e-6
feedback_fraction = 0.5

[prompt_learning.termination_config]
max_cost_usd = 10.0
max_trials = 1000

Tunnels

GEPA requires your task app to be accessible over HTTPS. Two tunnel backends are supported: No external binary required. Supports 128 concurrent in-flight requests:
from synth_ai.core.tunnels import TunneledLocalAPI

tunnel = await TunneledLocalAPI.create(local_port=8001, api_key="sk_live_...")

job = PromptLearningJob.from_dict(
    config,
    task_app_url=tunnel.url,
    task_app_worker_token=tunnel.worker_token,
)
[prompt_learning]
task_app_url = "https://st.usesynth.ai/s/rt_..."  # from tunnel.url

Cloudflare

Requires cloudflared installed. Use task_app_api_key instead of worker_token:
from synth_ai.core.tunnels import TunneledLocalAPI, TunnelBackend

tunnel = await TunneledLocalAPI.create(
    local_port=8001,
    backend=TunnelBackend.CloudflareQuickTunnel,
)
[prompt_learning]
task_app_url = "https://random-words.trycloudflare.com"
See Tunnels for the full comparison.

See Also