Skip to content

Instruction optimization

Instead of choosing demonstrations, these optimizers rewrite the instructions in a program's signatures, searching for wording that scores better on your metric.

COPRO

COPRO proposes candidate instructions and refines them over several rounds (breadth and depth), keeping the best:

def copro(metric: Metric, student: DynamicPredict, trainset: Vector[Example])(using
    RuntimeContext
): Either[DspyError, DynamicPredict] =
  new COPRO[DynamicPredict](COPROConfig(metric = metric)).compile(student, trainset).map(_.bestProgram)

MIPROv2

MIPROv2 optimizes instructions and demonstrations together, guided by the metric. It takes a validation set alongside the training set:

def miprov2(metric: Metric, student: DynamicPredict, trainset: Vector[Example], devset: Vector[Example])(
    using RuntimeContext
): Either[DspyError, DynamicPredict] =
  new MIPROv2[DynamicPredict](MIPROv2Config(metric = metric))
    .compile(student, trainset, valset = Some(devset)).map(_.bestProgram)

GEPA

GEPA is a reflective, genetic-Pareto optimizer. Rather than sampling instructions blindly, it reflects on where the program failed and evolves a Pareto front of candidates, trading off competing objectives. It is a self-contained implementation plus a dspy4s adapter, and it follows the same compile(student, trainset) shape as the optimizers above.

Choosing one

Optimizer Strategy Use when
COPRO Iterative instruction proposals You want better wording, cheaply.
MIPROv2 Joint instructions + demos You want the strongest single optimizer.
GEPA Reflective genetic-Pareto search You can describe failures and want it to learn from them.

Next: Runtime context.