Optimization API¶
optimization ¶
Optimization primitives — reflective prompt evolution via GEPA.
The cognitive substrate's evolutionary layer. Orqest already produces every
signal a Pareto-optimizer needs (accuracy via output validation, confidence
via :class:EnrichedOutput, latency via :class:Tracer.Span, cost via
pydantic_ai.usage.Usage, robustness via :class:HealingRunner); this
battery wires GEPA (Genetic-Pareto reflective evolution, Agrawal et al.,
ICLR 2026 Oral) into that signal stream so the prompts driving your agents
evolve from real traces.
See :doc:/concepts/optimization for the full picture; this module hosts:
- :class:
OptimizationConfig— frozen dataclass. - :class:
MetricBundle+ :class:MetricWeights— per-example fitness. - :class:
Genome+ :class:PromptGene/ :class:ScalarGene/ :class:CategoricalGene— what's evolvable. - :class:
GoldExample+ :class:Evaluator— how to score a candidate. - :class:
OrqestGEPAAdapter+ :class:OrqestEvalBatch— the GEPA bridge. - :class:
OptimizationRunner+ :class:OptimizationResult— run the loop. - :class:
OptimizationDiff+ :func:apply_result— write the winner back.
GEPA is an optional dependency. Install with::
uv sync --group optimization
FrontierType
module-attribute
¶
GEPA's native Pareto-frontier shapes:
"instance"— best candidate per validation example (the original GEPA paper)"objective"— best candidate per metric dimension (accuracy / cost / ...)"hybrid"— both, jointly. The recommended default for Orqest because the battery feeds GEPA both per-example scores and per-objective scores."cartesian"— every (instance × objective) pair. Largest frontier; use only when search budget allows.
OrqestEvalBatch
dataclass
¶
:class:EvaluationBatch extended with the typed :class:MetricBundles.
GEPA only reads outputs / scores / trajectories /
objective_scores — bundles is a sidecar so reflective dataset
construction (and downstream consumers like notebooks) can show
per-dimension detail without re-evaluating.
OrqestGEPAAdapter ¶
OrqestGEPAAdapter(
genome: Genome,
evaluator: Evaluator[Any, Any],
weights: MetricWeights,
*,
bus: EventBus | None = None,
emit_per_example_events: bool = False,
)
:class:gepa.core.adapter.GEPAAdapter implementation backed by an
Orqest :class:Evaluator + :class:Genome.
Constructed and held by :class:OptimizationRunner; never invoked
directly by user code. The Protocol is structural in GEPA, so we
declare conformance by shape rather than inheritance — keeps the
optional-dependency story clean (no GEPA import needed at module
load time for from orqest.optimization import OrqestGEPAAdapter).
Wire the adapter to an :class:Evaluator + an optional bus.
Source code in orqest/optimization/adapter.py
propose_new_texts
class-attribute
¶
GEPA's reflective-mutation loop checks adapter.propose_new_texts is
not None to decide whether to use a user-provided text proposer; when
None it falls back to its built-in LLM-driven proposer (which is
what we want — GEPA's default uses reflection_lm to evolve the
prompt). Declared as a class attribute so the access site doesn't
raise AttributeError; if we ever ship a custom proposer it becomes
a method on this class.
evaluate ¶
evaluate(
batch: list[GoldExample[Any, Any]],
candidate: dict[str, str],
capture_traces: bool = False,
) -> OrqestEvalBatch
Run candidate on batch and return an :class:OrqestEvalBatch.
Per the GEPA Protocol contract:
scoreslength ==len(batch); higher is better.trajectoriespopulated only whencapture_traces=True.objective_scorespopulated unconditionally — Orqest's multi-signal nature makes them effectively free.- Per-example failures yield
score=0.0rather than raising.
Source code in orqest/optimization/adapter.py
make_reflective_dataset ¶
make_reflective_dataset(
candidate: dict[str, str],
eval_batch: OrqestEvalBatch,
components_to_update: list[str],
) -> Mapping[str, Sequence[Mapping[str, Any]]]
Build the per-component reflective dataset for the teacher LLM.
Schema follows GEPA's recommendation: each record carries
Inputs / Generated Outputs / Feedback. The Feedback
slot is where Orqest's typed signals shine — we surface the
per-dimension :class:MetricBundle breakdown plus any
uncertainty_targets the agent self-reported.
Source code in orqest/optimization/adapter.py
OptimizationDiff
dataclass
¶
One slot's before/after diff.
unified
instance-attribute
¶
Unified diff (difflib.unified_diff output) — useful for printing
or logging the change.
MetricBundle ¶
One example's multi-dimensional fitness.
Accuracy / confidence / robustness are normalized to [0, 1]
(higher is better). Cost (USD) and latency (ms) are raw, non-negative
quantities (lower is better) — :class:MetricWeights carries negative
weights so :meth:scalarize produces the right gradient.
raw
class-attribute
instance-attribute
¶
Extension point — consumer-defined extras (e.g., per-tool counters).
Surfaced in events / notebooks but ignored by :meth:scalarize.
n_trials
class-attribute
instance-attribute
¶
Number of independent observations aggregated into this bundle.
1 (the default) means a single observation. Values > 1 indicate
this bundle is the mean of n_trials underlying bundles, produced
by :meth:MetricBundle.aggregate. Use this when you've re-run the same
candidate / example multiple times to wash out LLM run-to-run variance —
the discovery from the coding benchmark was that single-trial numbers
swing ±10pp on weaker models, so multi-trial averaging is often the
right default for evaluator pipelines.
stdev
class-attribute
instance-attribute
¶
Per-dimension standard deviation across the trials this bundle
aggregates. Keys mirror the metric field names (accuracy,
confidence, cost_usd, latency_ms, robustness). None
when n_trials == 1 (no dispersion to report). When a dimension was
None in some trials but present in others, the stdev is computed
only over trials where it was present — and omitted entirely from
stdev when fewer than 2 such trials exist (single observations
have no defined standard deviation).
scalarize ¶
Single per-example fitness score for GEPA's acceptance test.
Optional dimensions (confidence, robustness) are skipped
when None rather than treated as zero — a missing signal must
not penalize a candidate that simply didn't surface it.
Source code in orqest/optimization/bundle.py
to_per_instance_scores ¶
Per-dimension scores for GEPA's objective_scores field.
Each key becomes an axis on GEPA's objective Pareto front when
frontier_type is "objective" / "hybrid" / "cartesian".
Optional dimensions that are None are omitted (not zero-filled)
so the frontier doesn't conflate "absent" with "bad."
Source code in orqest/optimization/bundle.py
aggregate
classmethod
¶
Mean each metric across bundles; compute per-dimension stdev.
Use this to collapse N independent observations of the same
candidate / example into a single representative bundle. The
returned bundle has n_trials = len(bundles) and a populated
stdev dict (when N >= 2) so consumers can see dispersion
alongside the central tendency.
Optional dimensions (confidence, robustness) are handled
gracefully:
- If every bundle has it
None, the aggregate also reportsNonefor that dimension and omits it fromstdev. - If some bundles have it set and others
None, the aggregate reports the mean over present values.stdevincludes the dimension only when 2 or more bundles supplied it.
The raw dict of the first bundle is preserved as a
representative sample (it's a consumer-defined extension point;
merging semantics belong to the consumer, not this aggregator).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bundles
|
list[MetricBundle]
|
At least one bundle. Empty list raises |
required |
Returns:
| Type | Description |
|---|---|
MetricBundle
|
A new |
Raises:
| Type | Description |
|---|---|
ValueError
|
When |
Source code in orqest/optimization/bundle.py
MetricWeights ¶
Weights for :meth:MetricBundle.scalarize.
Negative weights penalize (cost, latency); positive weights reward (accuracy, confidence, robustness). Defaults are calibrated for "accuracy is king, but don't ignore cost / latency / calibration" against realistic LLM call latencies (1–10 seconds = 1k–10k ms) and realistic per-call costs ($0.0001 – $0.05).
Magnitude rationale: a perfect-accuracy candidate at ~5 s / $0.005
scores ≈ 1.0 + 0 + 0 - 0.05 - 0.05 = 0.9. Latency and cost act
as soft tiebreakers; accuracy stays the dominant axis. If you care
more about cost, override; production users will tune these per app.
cost_usd
class-attribute
instance-attribute
¶
-10.0 per USD: a $0.001 call is -0.01, $0.01 is -0.10,
$0.10 is -1.0. Calibrated so per-call cost is a soft tiebreaker
against accuracy (range 0–1), not a dominant signal.
latency_ms
class-attribute
instance-attribute
¶
-0.00002 per ms: a 1 s call is -0.02, a 5 s call is
-0.10, a 30 s call is -0.60. Same logic as cost_usd —
soft tiebreaker, not dominant.
OptimizationConfig
dataclass
¶
OptimizationConfig(
max_metric_calls: int = 150,
reflection_model: str | None = None,
minibatch_size: int = 3,
valset_fraction: float = 0.3,
weights: MetricWeights = MetricWeights(),
seed: int | None = 42,
dry_run_default: bool = True,
enable_scalar_genes: bool = False,
enable_categorical_genes: bool = False,
cache_evaluations: bool = True,
emit_per_example_events: bool = False,
frontier_type: FrontierType = "hybrid",
)
Knobs for the optimization battery.
Pass explicitly to :class:OptimizationRunner.
max_metric_calls
class-attribute
instance-attribute
¶
GEPA's rollout budget — total evaluate() calls across the search.
150 is a reasonable default for a 10–20 example gold set; scale linearly
with eval-set size if you want more iterations.
reflection_model
class-attribute
instance-attribute
¶
provider:model_id for the LLM that proposes prompt mutations.
None falls back to :attr:OrqestConfig.llm_model. Use the strongest
model you can afford here — this is the optimizer's brain. The task
model (whatever the user's agent_factory constructs the agent with)
can stay cheap; the reflection model should not.
Note: there is no task_model field. The model the candidate prompt
runs against is whatever the user's agent_factory wires into the
:class:BaseAgent; GEPA's adapter API explicitly forbids passing a
task_lm when an adapter is supplied (the adapter owns it).
minibatch_size
class-attribute
instance-attribute
¶
Examples sampled per acceptance test. GEPA uses sum(scores) over
the minibatch to decide whether a mutation beats its parent.
valset_fraction
class-attribute
instance-attribute
¶
When :meth:OptimizationRunner.optimize receives no explicit valset,
this fraction of trainset is held out (deterministically with
:attr:seed) for Pareto tracking.
weights
class-attribute
instance-attribute
¶
Drives :meth:MetricBundle.scalarize — the per-example float that
GEPA's acceptance test compares. The per-objective objective_scores
GEPA also receives are unweighted; weights only matter for the scalar
summary and for which candidate best_candidate resolves to.
seed
class-attribute
instance-attribute
¶
Random seed for trainset/valset splitting and any sampling. None
disables seeding (every run differs).
dry_run_default
class-attribute
instance-attribute
¶
Default value for :func:apply_result(..., dry_run=...). When True,
applying the result prints the diff but does not mutate the agent.
enable_scalar_genes
class-attribute
instance-attribute
¶
Gate for :class:ScalarGene evolution (W1.1+). When False (default),
:meth:OptimizationRunner.optimize raises NotImplementedError if a
:class:ScalarGene appears in the genome.
enable_categorical_genes
class-attribute
instance-attribute
¶
Gate for :class:CategoricalGene evolution (W1.1+). Same semantics as
:attr:enable_scalar_genes.
cache_evaluations
class-attribute
instance-attribute
¶
Pass-through to GEPA's built-in cache_evaluation knob —
de-duplicates (candidate, example) evaluations during Pareto
selection. 2–5× cost saving for free.
emit_per_example_events
class-attribute
instance-attribute
¶
Off by default — 150 metric_calls × N examples can be 1000+ events
per run, flooding the bus. When False, :class:OrqestGEPAAdapter only
emits optimization.iteration_completed summaries.
frontier_type
class-attribute
instance-attribute
¶
Pass-through to GEPA's optimize(frontier_type=...). Default
"hybrid" because the battery feeds GEPA both scores (per-example
scalar) and objective_scores (per-dimension); "hybrid" exploits
both. See :data:FrontierType for alternatives.
Evaluator ¶
Evaluator(
agent_factory: Callable[
[dict[str, Any]], BaseAgent[Any, OutputT]
],
score_fn: Callable[
[OutputT, GoldExample[InputT, OutputT]],
float | Awaitable[float],
],
*,
confidence_protocol: ConfidenceProtocol | None = None,
cost_estimator: Callable[[Any], float] | None = None,
timer: Callable[[], float] = time.monotonic,
n_trials_per_example: int = 1,
)
Score one candidate against a gold set, producing :class:MetricBundles.
Construction takes the consumer-side wiring; per-call decoded is the
decoded genome from :meth:Genome.decode.
Wire the per-evaluation building blocks.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
agent_factory
|
Callable[[dict[str, Any]], BaseAgent[Any, OutputT]]
|
Callable taking a decoded genome and returning a
fresh :class: |
required |
score_fn
|
Callable[[OutputT, GoldExample[InputT, OutputT]], float | Awaitable[float]]
|
|
required |
confidence_protocol
|
ConfidenceProtocol | None
|
Optional :class: |
None
|
cost_estimator
|
Callable[[Any], float] | None
|
Optional |
None
|
timer
|
Callable[[], float]
|
Monotonic clock for latency measurement. Override only in tests. |
monotonic
|
n_trials_per_example
|
int
|
When |
1
|
Source code in orqest/optimization/evaluator.py
evaluate_one
async
¶
Run one example, return the per-example :class:MetricBundle.
Failures (agent exceptions, score_fn exceptions) are swallowed and
produce a zero-accuracy bundle with the error captured in
:attr:MetricBundle.raw["error"]. GEPA's adapter Protocol explicitly
requires evaluators to never raise for individual example failures
— see :class:gepa.core.adapter.GEPAAdapter docstring.
When n_trials_per_example > 1 (configured at construction), this
method runs _evaluate_single_trial N times and aggregates via
:meth:MetricBundle.aggregate. Failed trials are folded into the
average (counted as zero-accuracy). When every trial fails, the
aggregate is the first failure's error bundle plus an
aggregation_note in raw.
Source code in orqest/optimization/evaluator.py
evaluate_batch
async
¶
evaluate_batch(
decoded: dict[str, Any],
batch: list[GoldExample[InputT, OutputT]],
) -> list[MetricBundle]
Evaluate a batch sequentially. Caller controls concurrency at a higher layer (parallel evaluation across candidates is GEPA's job).
Source code in orqest/optimization/evaluator.py
GoldExample ¶
One labeled example for the optimizer's eval set.
Generic over input/output so consumers can pass typed Pydantic shapes without losing type information at the score-function boundary.
input
instance-attribute
¶
Whatever the agent accepts. Often a string prompt; can be a richer Pydantic model when the consumer wants to pass structured context.
expected
class-attribute
instance-attribute
¶
Ground-truth output, when one exists. None is fine for examples
where success is judged by the rubric (LLM-judge) rather than equality.
rubric
class-attribute
instance-attribute
¶
Free-text scoring guidance for an LLM-judge score_fn. Ignored by
purely-programmatic score_fns.
weight
class-attribute
instance-attribute
¶
Per-example weight surfaced to the score function — useful for upweighting hard / adversarial examples in the gold set.
id
class-attribute
instance-attribute
¶
Optional stable identifier. Defaults to id(example) when needed.
CategoricalGene ¶
A fixed-set string choice. Decode validates against choices.
Genome ¶
An ordered collection of genes — the mutable surface of one optimization run.
The optimizer encodes the genome to GEPA's seed format via
:meth:to_seed, GEPA proposes candidate mutations as the same
dict[str, str] shape, and :meth:decode round-trips them back to
typed Python values consumed by the consumer's agent_factory.
to_seed ¶
decode ¶
Decode a GEPA-proposed candidate back into typed values.
Resilient by design: missing or malformed entries fall back to
the gene's initial rather than raising.
Source code in orqest/optimization/genome.py
gene_kinds ¶
Set of gene kinds present — useful for the runner's gating
check (enable_scalar_genes / enable_categorical_genes).
PromptGene ¶
A prompt-string slot — the canonical GEPA target.
name
instance-attribute
¶
Logical slot identifier, e.g. "researcher.system_prompt". Must
match the key the consumer hands to :func:apply_result (or to a
custom agent_factory).
initial
instance-attribute
¶
Seed prompt text. Becomes the W0 candidate GEPA mutates from.
constraints
class-attribute
instance-attribute
¶
Optional natural-language guardrail surfaced to the reflection LLM
in :meth:OrqestGEPAAdapter.make_reflective_dataset. Use it to encode
invariants the optimizer must not break (e.g. "Must abstain on
ambiguous goals").
ScalarGene ¶
A bounded float gene. Decode clamps + optional quantization.
quantize
class-attribute
instance-attribute
¶
When set, decode snaps to the nearest multiple of this grid step.
Archive ¶
Strategy-pluggable archive of evaluated candidates.
All entries are retained in storage (so :meth:pareto and :meth:best
see the full history); serialize_for_prompt is the strategy-aware
view shown to the meta agent on the next design step.
Source code in orqest/optimization/meta_agent.py
add ¶
serialize_for_prompt ¶
The archive view passed to the meta agent for its next design.
Strategy-dependent:
"cumulative"— every entry, JSON-serialized."top_k"— topsizeentries by aggregate_score, JSON-serialized."parallel"— empty string. Meta agent designs without context.
Source code in orqest/optimization/meta_agent.py
best ¶
The highest-aggregate-score entry. Raises if empty.
pareto ¶
Distinct entries on the accuracy / cost / latency Pareto front.
An entry is on the front if no other entry dominates it (strictly better on every axis). We negate cost and latency so "more is better" applies uniformly.
Source code in orqest/optimization/meta_agent.py
ArchiveEntry ¶
One archived candidate.
generation
instance-attribute
¶
Generation index. -1 means the seed (pre-search baseline).
spec_json
instance-attribute
¶
The TopologySpec serialized to JSON — the GEPA wire format.
bundles
instance-attribute
¶
Per-example MetricBundles from this entry's evaluation.
aggregate_score
instance-attribute
¶
Mean scalarized score across bundles — the archive ranking key.
thought
class-attribute
instance-attribute
¶
Meta-agent's design reasoning. Empty for the seed.
parent_idx
class-attribute
instance-attribute
¶
Index of the archive entry whose design inspired this one. None
for the seed and for parallel-strategy entries (no parent context).
MetaAgentConfig
dataclass
¶
MetaAgentConfig(
n_generations: int = 10,
archive_strategy: ArchiveStrategy = "top_k",
archive_size: int = 5,
reflexion_passes: int = 2,
debug_max: int = 3,
minibatch_size: int = 5,
seed: int = 42,
weights: MetricWeights = MetricWeights(),
)
Knobs for :class:MetaAgentSearch.
Distinct from :class:OptimizationConfig (which governs GEPA's prompt
evolution loop) because the search-loop parameters are different in kind
— generations of designed-and-evaluated topologies, not GEPA's sampled
minibatches.
n_generations
class-attribute
instance-attribute
¶
Number of design-evaluate cycles after the seed evaluation.
archive_strategy
class-attribute
instance-attribute
¶
How to surface the archive to the next design step.
"top_k"(default) — show only the toparchive_sizeby aggregate score. Per the 2510.06711 critique, the strongest signal-to- noise ratio for the meta agent."cumulative"— show every entry seen so far. ADAS-paper-faithful; tends to overflow context after ~20 entries and dilute strong signals."parallel"— show nothing. Meta agent designs from scratch each generation; selection happens at the end. Surprisingly competitive per the critique.
archive_size
class-attribute
instance-attribute
¶
Cap for top_k and the ring-buffer for cumulative if you want
one (unlimited by default for cumulative). Ignored for parallel.
reflexion_passes
class-attribute
instance-attribute
¶
Number of reflexion-revision passes after the design step. ADAS uses 2 by default; 0 disables reflexion entirely (raw design only).
debug_max
class-attribute
instance-attribute
¶
How many times to retry a generation that fails Pydantic validation or hydration. The ValidationError surface is fed back to the meta agent as feedback (analogue of ADAS's traceback retry).
minibatch_size
class-attribute
instance-attribute
¶
Validation subset size per candidate evaluation. Caps cost: each candidate evaluates against this many examples (sampled deterministically from the valset), not the full set. The seed and final winner are re-evaluated against the full set.
seed
class-attribute
instance-attribute
¶
Random seed for minibatch sampling and reproducibility.
weights
class-attribute
instance-attribute
¶
Aggregation weights for converting per-example MetricBundles to a scalar score for archive ranking. Defaults match GEPA's recommended weights so users see consistent ranking across optimizers.
MetaAgentSearch ¶
MetaAgentSearch(
config: MetaAgentConfig,
*,
gene: TopologyGene,
evaluator: TopologyEvaluator,
meta_agent_model: str,
api_key: str,
bus: EventBus | None = None,
)
Run an ADAS-style search over :data:TopologySpec candidates.
Three-stage generation: design → reflexion (×N) → evaluate (with debug
retry). Pydantic ValidationError + hydration KeyError are caught and fed
back to the meta agent as debug feedback. Non-debuggable failures
(after debug_max retries) skip the generation; the search continues.
Wire the search.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
MetaAgentConfig
|
Frozen :class: |
required |
gene
|
TopologyGene
|
The :class: |
required |
evaluator
|
TopologyEvaluator
|
A :class: |
required |
meta_agent_model
|
str
|
|
required |
api_key
|
str
|
API key for the meta agent's model. |
required |
bus
|
EventBus | None
|
Optional :class: |
None
|
Source code in orqest/optimization/meta_agent.py
run
async
¶
run(
trainset: list[GoldExample[Any, Any]],
valset: list[GoldExample[Any, Any]] | None = None,
) -> OptimizationResult
Run the search; return an :class:OptimizationResult.
trainset is currently used only to size the minibatch sampler;
the actual evaluation runs against valset (or against trainset
if no valset is provided). The asymmetry mirrors GEPA's setup but
is less load-bearing here — topology evaluation doesn't have a
train/val distinction in the gradient sense.
Source code in orqest/optimization/meta_agent.py
TopologyDesign ¶
Structured output for the meta agent.
The meta agent's pydantic-ai :class:Agent returns this shape every
design / reflexion / debug step.
OptimizationResult
dataclass
¶
OptimizationResult(
best_candidate: dict[str, str],
best_decoded: dict[str, Any],
best_score: float,
pareto_candidates: list[dict[str, str]],
history: list[dict[str, Any]] = list(),
raw: Any = None,
)
Immutable summary of a completed optimization run.
best_candidate
instance-attribute
¶
The winning candidate (per GEPA's best_idx aggregate score).
best_decoded
instance-attribute
¶
:meth:Genome.decode applied to best_candidate.
pareto_candidates
instance-attribute
¶
Distinct candidates appearing on GEPA's instance-level Pareto front
(per_val_instance_best_candidates). Inspect for tradeoffs the
aggregate winner doesn't surface.
history
class-attribute
instance-attribute
¶
Per-iteration summary (iteration index, mean score, mean accuracy) captured from the bus during the run, when a bus was provided.
raw
class-attribute
instance-attribute
¶
The original :class:gepa.core.result.GEPAResult — escape hatch
for advanced inspection (lineage, val_subscores, per_objective_*).
OptimizationRunner ¶
OptimizationRunner(
config: OptimizationConfig,
*,
genome: Genome,
evaluator: Evaluator[Any, Any],
bus: EventBus | None = None,
api_key: str | None = None,
)
Drive gepa.optimize() against an Orqest :class:Evaluator.
Wire the optimizer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
OptimizationConfig
|
Frozen :class: |
required |
genome
|
Genome
|
The mutable surface — list of typed genes. |
required |
evaluator
|
Evaluator[Any, Any]
|
Wraps the user's |
required |
bus
|
EventBus | None
|
Optional :class: |
None
|
api_key
|
str | None
|
Optional API key for the reflection model. Bridged
to litellm's expected provider env var ( |
None
|
Source code in orqest/optimization/runner.py
optimize
async
¶
optimize(
trainset: list[GoldExample[Any, Any]],
valset: list[GoldExample[Any, Any]] | None = None,
) -> OptimizationResult
Run GEPA against the given gold sets.
When valset is None, splits trainset deterministically using
config.seed and config.valset_fraction.
Source code in orqest/optimization/runner.py
TopologyEvaluator ¶
TopologyEvaluator(
*,
score_fn: Callable[
[Any, GoldExample[Any, Any]],
float | Awaitable[float],
],
callable_registry: CallableRegistry,
agent_registry: dict[
str, Callable[[], BaseAgent[Any, Any]]
],
topology_gene_name: str = "main",
agent_factory: AgentFactory | None = None,
confidence_protocol: ConfidenceProtocol | None = None,
cost_estimator: Callable[[Any], float] | None = None,
timer: Callable[[], float] = time.monotonic,
)
Score a topology candidate against gold examples.
Replaces :class:Evaluator's single-BaseAgent execution path with topology
hydration. Adds n_agents and depth structural metrics to
:attr:MetricBundle.raw so the optimizer's Pareto front can prefer
smaller / shallower topologies on accuracy ties.
Cost handling: Unlike single-agent evaluation, a topology run does
not have a single Usage object. We surface cost_usd=0.0 by
default; consumers wanting cost-as-fitness should pass a
cost_estimator callable that walks the topology and sums per-agent
usage (see the concept doc for the recipe). This is a known limitation
documented in the W3 risks section.
Wire the per-evaluation building blocks.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
score_fn
|
Callable[[Any, GoldExample[Any, Any]], float | Awaitable[float]]
|
|
required |
callable_registry
|
CallableRegistry
|
Registry of named conditions / merges / state-updaters / function-steps. The meta agent's allowlist. |
required |
agent_registry
|
dict[str, Callable[[], BaseAgent[Any, Any]]]
|
Map from agent name to a factory producing a
fresh :class: |
required |
topology_gene_name
|
str
|
Key under which the decoded genome carries
the :data: |
'main'
|
agent_factory
|
AgentFactory | None
|
Optional :class: |
None
|
confidence_protocol
|
ConfidenceProtocol | None
|
Reserved for future use — topologies don't produce confidence directly today (would require per-step EnrichedOutput plumbing); accepted for signature parity. |
None
|
cost_estimator
|
Callable[[Any], float] | None
|
Optional callable for translating downstream usage into USD. See class docstring caveat. |
None
|
timer
|
Callable[[], float]
|
Monotonic clock; override only in tests. |
monotonic
|
Source code in orqest/optimization/topology.py
evaluate_one
async
¶
Hydrate the topology, run on the example, return a MetricBundle.
Failures (hydration errors, runtime errors, score errors) yield a
zero-accuracy bundle with the failure captured in
:attr:MetricBundle.raw["error"]. This matches the parent's
never-raise contract — GEPA's adapter Protocol forbids raising on
per-example evaluation failures.
Source code in orqest/optimization/topology.py
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 | |
TopologyGene ¶
A topology-shaped gene whose initial value is a :data:TopologySpec.
Encodes to JSON (the GEPA wire format is dict[str, str]) and decodes
back via Pydantic validation. Resilient on malformed reflection output:
a candidate that fails JSON parsing or schema validation falls back to
the gene's initial rather than raising — same defensive posture as
:class:PromptGene (we'd rather lose one iteration than crash a search).
name
instance-attribute
¶
Logical slot identifier — the key the meta agent emits and the
:class:TopologyEvaluator reads from the decoded genome.
initial
instance-attribute
¶
Seed topology. The meta-agent's W0 candidate; also the resilient fallback for malformed reflection output.
constraints
class-attribute
instance-attribute
¶
Optional natural-language guardrail surfaced to the meta agent in its design prompt (e.g. "must include the existing classifier as a Pipeline first step").
allowed_step_kinds
class-attribute
instance-attribute
¶
Whitelisted leaf-step kinds. Surfaced to the meta agent in its prompt as the only legal atomic operations. Default permits both.
max_depth
class-attribute
instance-attribute
¶
Maximum nesting depth the meta agent is allowed to emit. Past this
depth the evaluator rejects the candidate (max_depth_exceeded error)
so the optimizer pivots away from runaway-recursion designs.
encode ¶
decode ¶
Decode a meta-agent-proposed JSON string back to a TopologySpec.
Three failure modes, all resilient:
None(key missing from the candidate dict) →initial- Malformed JSON →
initial - Valid JSON but failing TopologySpec schema →
initial
Source code in orqest/optimization/topology.py
apply_result ¶
apply_result(
result: OptimizationResult,
*,
target: Any,
dry_run: bool = True,
) -> list[OptimizationDiff]
Build per-gene diffs against target; write them when not dry-run.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
result
|
OptimizationResult
|
The :class: |
required |
target
|
Any
|
A :class: |
required |
dry_run
|
bool
|
When True (default), no mutation happens — the diffs are returned for inspection only. Flip to False to commit. |
True
|
Returns:
| Name | Type | Description |
|---|---|---|
One |
list[OptimizationDiff]
|
class: |
list[OptimizationDiff]
|
in iteration order. Diffs with |
|
list[OptimizationDiff]
|
(with |
|
list[OptimizationDiff]
|
rather than inferring from absence. |
Source code in orqest/optimization/apply.py
unpack_topology_output ¶
Extract the meaningful payload from a topology's .run() result.
- :class:
ParallelResult→.merged(the merge-strategy output) - :class:
LoopResult→.output(the converged value) - :class:
AgentRunResult(if a topology somehow returns one) →.output - Anything else → identity (Pipeline / Router return their final value)