GEPA
Parameters, defaults, and a runnable example for the GEPA optimizer.
When to use GEPA
Use this when you want broad exploration across many prompt variants under a fixed evaluation budget, rather than Meta-Prompt’s one directed lineage of edits.
GEPA evolves a population of candidate prompts across generations instead of revising one prompt in place. Each generation is evaluated against the dataset, and a separate reflection model reads the failures and writes the next generation of candidates based on what went wrong.
GEPA takes two models: reflection_model writes the new prompts, and generator_model is the model GEPA runs each candidate prompt on during optimization to produce the output that gets scored, and it’s also the model the optimized prompt is meant to run on afterward; it defaults to gpt-4o-mini.
Parameters
evaluator, data_mapper, and dataset, also passed in the example below, are shared by every optimizer’s optimize() call and covered in the SDK reference.
| Parameter | Set in | On-screen label | Default | Description |
|---|---|---|---|---|
reflection_model | GEPAOptimizer() | - | required | Model that analyses failures and writes the next generation of candidate prompts |
generator_model | GEPAOptimizer() | - | gpt-4o-mini | Model GEPA runs each candidate on during optimization |
initial_prompts | optimize() | - | required | List of starting prompts (see note below: only the first is used) |
max_metric_calls | optimize() | Max Metric Calls | 40 prefilled in the UI, 150 in the SDK | Total evaluation budget across all generations |
GEPA seeds from the first prompt in initial_prompts and ignores the rest, so passing more than one silently discards the extras.
GEPA runs to a fixed budget of evaluations rather than a fixed number of rounds; max_metric_calls caps the total number of evaluations across the whole run:
- How many generations
max_metric_callsbuys shrinks as your dataset grows - Raise
max_metric_callsabove the SDK’s default of 150, or the platform’s prefilled 40, to let GEPA work through more generations before stopping - Lower it for a cheaper, shallower run. Each unit is one scored row: a generator call plus the evaluator call you pay for, so cost and runtime scale roughly linearly with the value you set
Usage
pip install agent-opt
Then get fi_api_key and fi_secret_key from Admin Settings (or set FI_API_KEY/FI_SECRET_KEY as environment variables and drop them from the Evaluator call below). This example builds an Evaluator and BasicDataMapper the same way every optimizer does; see the SDK reference above for their full constructors.
Two rows are enough to sanity-check the code path.
from fi.opt.optimizers import GEPAOptimizer
from fi.opt.datamappers import BasicDataMapper
from fi.opt.base.evaluator import Evaluator
# Dataset: a plain list of dicts, one per example the optimizer scores the prompt against
dataset = [
{
"article": "The James Webb Space Telescope has captured its clearest images yet of a distant exoplanet's atmosphere, revealing traces of carbon dioxide and methane.",
},
{
"article": "Researchers have discovered a new enzyme that breaks down PET plastic at room temperature, far faster than any previously known enzyme.",
},
# ... more rows
]
# Evaluator that scores each candidate prompt.
# eval_template options: /docs/evaluation/concepts/eval-templates
# eval_model_name options: /docs/evaluation/concepts/evaluator-models
evaluator = Evaluator(
eval_template="summary_quality",
eval_model_name="turing_flash",
fi_api_key="your_key",
fi_secret_key="your_secret"
)
# Maps generator output and dataset fields to what the evaluator expects.
# "generated_output" isn't a dataset key you provide: GEPA writes each candidate's
# output there after running it through generator_model, for the evaluator to score.
data_mapper = BasicDataMapper(
key_map={"input": "article", "output": "generated_output"}
)
optimizer = GEPAOptimizer(
reflection_model="gpt-4-turbo",
generator_model="gpt-4o-mini"
)
result = optimizer.optimize(
evaluator=evaluator,
data_mapper=data_mapper,
dataset=dataset,
# GEPA seeds from the first prompt only; any others in this list are ignored.
initial_prompts=["Summarize this article concisely: {article}"],
max_metric_calls=150
)
print(f"Final score: {result.final_score:.4f}")
print(f"Best prompt:\n{result.best_generator.get_prompt_template()}")
# Example output:
# Final score: 0.8700
# Best prompt:
# Summarize this article in one sentence, focusing on the key finding: {article}
A successful run prints the final score followed by the best prompt, as in the two print calls above. result carries other fields beyond final_score and best_generator; see the SDK reference above for the full list. See Optimize from the SDK for how to take the winning prompt into production.
Keep exploring
Questions & Discussion