gepa-spo
Version:
Genetic-Pareto prompt optimizer to evolve system prompts from a few rollouts with modular support and intelligent crossover
36 lines (35 loc) • 1.6 kB
JavaScript
import { clampNumber, objectOfNumberKeys, safeParseWithSchema } from './json.js';
/**
* Ask the LLM to rate each strategy's applicability to the task corpus.
* Returns strategies filtered by threshold and optional topK.
*/
export async function prefilterStrategies(llm, strategies, taskPreview, opts) {
if (strategies.length === 0)
return { kept: [], scores: {}, raw: '' };
const ids = strategies.map(s => s.id);
const prompt = [
'You are rating strategy hints for applicability to the following representative tasks.',
'Return STRICT JSON: a map from strategy id to a number in [0,1] where higher means more applicable.',
'Unknowns should be 0. Base strictly on the provided tasks. No extra keys. No prose.',
'',
'Tasks preview:',
...taskPreview.slice(0, 8).map((t, i) => `${i + 1}. ${t.slice(0, 280)}`),
'',
'Strategies:',
...strategies.map(s => `- ${s.id}: ${s.hint.slice(0, 240)}`),
'',
'Output JSON only.'
].join('\n');
const raw = await llm.complete(prompt);
const schema = objectOfNumberKeys(ids);
const parsed = safeParseWithSchema(raw, schema);
const scores = {};
for (const id of ids)
scores[id] = clampNumber(parsed[id], 0, 1, 0);
const filtered = strategies
.map(s => ({ s, score: scores[s.id] ?? 0 }))
.filter(x => x.score >= opts.threshold)
.sort((a, b) => b.score - a.score);
const topK = opts.topK && opts.topK > 0 ? filtered.slice(0, opts.topK) : filtered;
return { kept: topK.map(x => x.s), scores, raw };
}