gepa-spo
Version:
Genetic-Pareto prompt optimizer to evolve system prompts from a few rollouts with modular support and intelligent crossover
53 lines (52 loc) • 2.5 kB
JavaScript
import { proposeNewSystem, summarizeTraces } from './reflection.js';
/** Generate seeded candidates with top-K strategies and keep the top few by average judge score */
export async function seedPopulation({ seed, screen, strategies, K, execute, muf, llm, budgetLeft, mufCosts = true }) {
const out = [{ system: seed.system ?? '', _via: 'seed', _uplift: 0 }];
let usedCalls = 0;
for (const s of strategies.slice(0, K)) {
if (budgetLeft !== undefined && budgetLeft <= 0)
break;
// Execute the seed system on screen items to get actual examples with traces
const examples = [];
for (const item of screen) {
if (budgetLeft !== undefined && budgetLeft <= 0)
break;
const { output, traces } = await execute({ candidate: seed, item });
const f = await muf({ item, output, traces: traces ?? null });
const execTrace = summarizeTraces(traces, 1000);
examples.push({
user: item.user,
output,
feedback: f.feedbackText,
...(execTrace && { execTrace })
});
// Count execute always, plus muf when configured
const delta = 1 + (mufCosts ? 1 : 0);
usedCalls += delta;
if (budgetLeft !== undefined)
budgetLeft -= delta;
}
const sys2 = await proposeNewSystem(llm, seed.system ?? '', examples, s.hint);
usedCalls += 1;
if (budgetLeft !== undefined)
budgetLeft -= 1; // propose call
const scores = [];
for (const item of screen) {
if (budgetLeft !== undefined && budgetLeft <= 0)
break;
const { output, traces } = await execute({ candidate: { system: sys2 }, item });
// Judge may or may not count to usedCalls
const f = await muf({ item, output, traces: traces ?? null });
scores.push(f.score);
// Count execute always, plus muf when configured
const delta = 1 + (mufCosts ? 1 : 0);
usedCalls += delta;
if (budgetLeft !== undefined)
budgetLeft -= delta;
}
out.push({ system: sys2, _via: s.id, _uplift: avg(scores) });
}
out.sort((a, b) => b._uplift - a._uplift);
return { candidates: out.slice(0, Math.min(5, out.length)), usedCalls };
}
const avg = (xs) => (xs.length ? xs.reduce((a, b) => a + b, 0) / xs.length : 0);