GEPA
steerability.algorithms.input_control.gepa
args
Arguments for the GEPA input control.
control
GEPA, reflective prompt optimization (single-system-prompt variant).
logger = logging.getLogger(__name__)
module-attribute
GEPA
Bases: InputControl
GEPA, reflective prompt optimization (single-system-prompt variant).
Optimizes a single system prompt via GEPA's reflective genetic search (Agrawal et al., 2025), restricted to the single-module (|M| = 1) case. It implements:
- Reflective prompt mutation: an LLM rewrites the current instruction using natural-language feedback drawn from rollouts.
- Pareto-based candidate selection: GEPA keeps an instance-wise Pareto frontier over a held-out set and samples parents by win-frequency (paper section 3.1, Algorithm 2).
- A genetic candidate pool that accumulates lessons across successive mutations.
Scope:
- This control optimizes ONE system prompt. It does NOT support compound, multi-module AI systems, and it does NOT implement the system-aware merge / crossover operator (GEPA+Merge). Merge is a multi-module operation that recombines complementary modules across lineages, and is vacuous with a single prompt.
- For compound, multi-module / multi-prompt optimization with module-level credit assignment and merge, use the reference implementation at https://github.com/gepa-ai/gepa (or DSPy's GEPA integration).
Scoring is supplied directly through a per-example row_scorer (the paper's metric µ at
the instance level, required because Pareto selection needs per-instance scores), an
optional feedback_fn returning the textual feedback GEPA reflects on (the paper's µf),
and a format_query mapping a dataset row to the user query. GEPA runs generation, scoring,
and feedback internally; there is no separate Evaluator object.
adapt_messages() / adapt() inject the optimized instruction as the system prompt at
inference time. Memory: TextMemory(slots={"instruction": best_instruction}).
reflection_lm is optional and defaults to the task model. A separate, stronger reflection
LM can help when the task model is small, but is not required.
Reference:
- "GEPA: Reflective Prompt Evolution Can Outperform Reinforcement Learning" Agrawal et al., 2025 https://arxiv.org/abs/2507.19457
Source code in steerability/algorithms/input_control/gepa/control.py
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 | |
RUNTIME_KWARGS_SCHEMA = []
class-attribute
instance-attribute
args = self.Args.validate(*args, **kwargs)
instance-attribute
budget = 300
class-attribute
instance-attribute
enabled = True
class-attribute
instance-attribute
feedback_fn = None
class-attribute
instance-attribute
format_query = None
class-attribute
instance-attribute
gen_kwargs = None
class-attribute
instance-attribute
memory = None
class-attribute
instance-attribute
meta_prompt = None
class-attribute
instance-attribute
minibatch_size = 3
class-attribute
instance-attribute
pareto_set_size = 30
class-attribute
instance-attribute
progress_callback = None
class-attribute
instance-attribute
proposer_gen_kwargs = None
class-attribute
instance-attribute
reflection_lm = None
class-attribute
instance-attribute
reflection_tokenizer = None
class-attribute
instance-attribute
row_scorer = None
class-attribute
instance-attribute
seed = None
class-attribute
instance-attribute
seed_instruction = ''
class-attribute
instance-attribute
supports_batching = True
class-attribute
instance-attribute
tokenizer = None
class-attribute
instance-attribute
train_set = []
class-attribute
instance-attribute
adapt(input_ids, runtime_kwargs=None)
Transform input_ids into a steered prompt.
May read instance state (e.g. self.memory) that was populated by steer().
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
input_ids
|
list[int] | Tensor
|
The user's prompt token IDs. |
required |
runtime_kwargs
|
dict | None
|
Per-call parameters. |
None
|
Returns:
| Type | Description |
|---|---|
list[int] | Tensor
|
The transformed token IDs. |
Source code in steerability/algorithms/input_control/gepa/control.py
288 289 290 291 292 293 294 295 296 297 298 | |
adapt_messages(messages, runtime_kwargs=None)
Optional message-level adaptation, called BEFORE chat-template tokenization.
Default returns None (no message-level changes). Subclasses that modify chat structure (set/replace system
prompt, insert example turns) override this. When this method returns a non-None result for a chat-input
generation, the pipeline does NOT additionally call this control's adapt() for that call; a control may
therefore implement both entry points (message-level for chat input, token-level for raw text/tensor input)
without being applied twice. Other input controls in the pipeline still run per their own phase.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
messages
|
list[list[dict]]
|
A batch of chats; outer list is the batch, inner list is the message sequence for one chat. |
required |
runtime_kwargs
|
dict | None
|
Per-call parameters. |
None
|
Returns:
| Type | Description |
|---|---|
list[list[dict]] | None
|
The transformed messages, or None to indicate no change. |
Source code in steerability/algorithms/input_control/gepa/control.py
278 279 280 281 282 283 284 285 286 | |
cleanup()
Release resources allocated during steer().
Override in subclasses that allocate GPU memory or other resources during steering. Default no-op.
Source code in steerability/algorithms/core/base_control.py
195 196 197 198 199 200 201 | |
clone_for_call(seed=None)
A configuration-preserving shallow clone for one generation call.
The clone shares steer-time artifacts (memories, steering vectors, attached tokenizers)
with the original but has its own attribute namespace, so per-call attribute mutation on
the clone never races another call using the original. When seed is given and the
control defines reseed(seed), the clone's client-side RNG is re-seeded.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
seed
|
int | None
|
Optional seed forwarded to the clone's |
None
|
Returns:
| Type | Description |
|---|---|
BaseControl
|
The clone. |
Source code in steerability/algorithms/core/base_control.py
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 | |
export_state()
The optimized instruction memory under the "memory" key (after steer()).
Source code in steerability/algorithms/input_control/gepa/control.py
96 97 98 | |
fit_identity()
The optimizer-relevant args (everything except memory and progress_callback),
or None when the recipe already carries a memory.
Source code in steerability/algorithms/input_control/gepa/control.py
109 110 111 112 113 114 115 116 117 118 119 | |
frozen_form(state)
A same-class frozen form: the recipe args with memory= set to the optimized
memory (the search-only args stay inert).
Source code in steerability/algorithms/input_control/gepa/control.py
100 101 102 103 104 105 106 107 | |
requirements()
Backend requirements computed from this instance's configuration, per phase.
Input controls transform the prompt client-side, so the generate phase requires nothing
beyond the session contract on any backend. A control whose steer() reads the live
pipeline model (e.g. for rollouts or scoring) overrides this with a steer-phase
Capability.IN_PROCESS_TORCH requirement.
Returns:
| Type | Description |
|---|---|
Requirements
|
The control's phase-keyed requirements. |
Source code in steerability/algorithms/input_control/base.py
124 125 126 127 128 129 130 131 132 133 134 135 | |
steer(model=None, tokenizer=None, session=None, **kwargs)
Optional offline preparation. Default is no-op.
session is a SteeringSession on the steering backend, provided by the pipeline.
Source code in steerability/algorithms/input_control/gepa/control.py
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 | |
steer_access()
ModelAccess.ROLLOUTS; task rollouts and reflection generate through the session,
and adaptation is a formatter. With a precomputed memory, steering is model-free
(ModelAccess.FACTS).
Source code in steerability/algorithms/input_control/gepa/control.py
121 122 123 124 125 126 127 | |
steer_fits()
The fit artifacts this instance's steer step will produce, for the steer plan.
Each entry is (artifact, artifact_class), where artifact is the fit source or
recipe class name and artifact_class is "direction" or "calibrated". The default
is an empty tuple (no fits).
Returns:
| Type | Description |
|---|---|
tuple[tuple[str, str], ...]
|
The declared fit artifacts, in declaration order. |
Source code in steerability/algorithms/core/base_control.py
105 106 107 108 109 110 111 112 113 114 115 | |
utils
pareto_sampling
Frequency-weighted Pareto sampling for GEPA's parent selection (Agrawal et al., 2025, Algorithm 2).
Sample one candidate from the Pareto illumination set Ĉ, the per-instance winners pruned of dominated
candidates, with probability proportional to win-frequency f[·]. Falls back to uniform over the pool
only when the illumination set is empty (a degenerate score matrix).
sample(frontier, pool_size, rng=None)
Frequency-weighted draw over the Pareto illumination set (Algorithm 2, line 8).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
frontier
|
ParetoFrontier
|
|
required |
pool_size
|
int
|
Number of candidates in the pool. |
required |
rng
|
Random | None
|
Optional RNG. |
None
|
Returns:
| Type | Description |
|---|---|
int
|
A candidate index in |
Source code in steerability/algorithms/input_control/gepa/utils/pareto_sampling.py
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | |
pool
CandidatePool, GEPA's genetic candidate pool with a Pareto score matrix.
Holds the candidate instructions and the [n_candidates, n_instances] score matrix over D_pareto.
CandidatePool
dataclass
Genetic candidate pool with a Pareto score matrix.
candidates[k] and the rows of scores are parallel-indexed by candidate. scores is
[n_candidates, n_instances] over D_pareto.
Attributes:
| Name | Type | Description |
|---|---|---|
candidates |
list[str]
|
Candidate instruction strings. |
Source code in steerability/algorithms/input_control/gepa/utils/pool.py
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | |
candidates = field(default_factory=list)
class-attribute
instance-attribute
scores
property
[n_candidates, n_instances] Pareto score matrix.
add(candidate, score_row)
Append a candidate and return its index.
Source code in steerability/algorithms/input_control/gepa/utils/pool.py
35 36 37 38 39 40 | |
best_index()
Index of the candidate with the highest mean score over D_pareto.
Source code in steerability/algorithms/input_control/gepa/utils/pool.py
46 47 48 | |
frontier()
Source code in steerability/algorithms/input_control/gepa/utils/pool.py
42 43 44 | |
reflective_dataset
Builder of reflective records from rollout outputs and feedback (single system prompt).
build_records(queries, outputs, feedback)
Build reflective records from rendered queries and their rollout outputs/feedback.
Produces one record per item with shape
{"Inputs": <rendered query string>, "Generated Output": ..., "Feedback": ...}.
Inputs is the query the task model actually received (i.e. format_query(row)), not
the raw training row -- the gold target must not appear here; it reaches reflection via
Feedback when the user's feedback_fn includes it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
queries
|
list[str]
|
Per-instance rendered query strings shown to the task model. |
required |
outputs
|
list
|
Per-instance task-model outputs. |
required |
feedback
|
list
|
Per-instance textual feedback. |
required |
Returns:
| Type | Description |
|---|---|
list[dict]
|
A list of records, length equal to |
Source code in steerability/algorithms/input_control/gepa/utils/reflective_dataset.py
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | |
reflective_meta_prompt
Reflective meta-prompt for GEPA.
GEPA_DEFAULT reproduces the reflective instruction-proposal prompt of Agrawal et al., 2025
(Appendix B; matching the open-source gepa-ai/gepa reference wording), adapted to the
LLMMetaPromptProposer format-string convention. The placeholders are:
{seed}: the current instruction for the module being optimized (the parent prompt).{records}: rendered per-example reflective records (inputs, outputs, feedback).
The matching parser (parse_fenced_or_whole) extracts the content of the first ``` fenced block.
Per-module context (which module is being optimized) can be layered in by a caller-supplied template;
the default omits it to match the reference.
GEPA_DEFAULT = "I provided an assistant with the following instructions to perform a task for me:\n```\n{seed}\n```\n\nThe following are examples of different task inputs provided to the assistant along with the assistant's response for each of them, and some feedback on how the assistant's response could be better:\n```\n{records}\n```\n\nYour task is to write a new instruction for the assistant.\n\nRead the inputs carefully and identify the input format and infer detailed task description about the task I wish to solve with the assistant.\n\nRead all the assistant responses and the corresponding feedback. Identify all niche and domain specific factual information about the task and include it in the instruction, as a lot of it may not be available to the assistant in the future. The assistant may have utilized a generalizable strategy to solve the task, if so, include that in the instruction as well.\n\nProvide the new instructions within ``` blocks."
module-attribute
render_records(records)
Render a per-component list of reflective records into a readable block.
Each record may carry the keys Inputs, Generated Output, and Feedback. Missing
fields render with a sensible placeholder. Returns (no records) for an empty list.
Source code in steerability/algorithms/input_control/gepa/utils/reflective_meta_prompt.py
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | |