/** * prompt-optimizer.js — A/B Testing de System Prompts * ===================================================== * Mantiene variantes del system prompt base de Zelin y aprende * cuál genera mejores respuestas (epsilon-greedy). * Autónomo — sin intervención manual. */ import * as db from './db.js'; // Variantes del fragmento introductorio del system prompt // El resto del prompt (herramientas, contexto, identidad) se mantiene igual const PROMPT_VARIANTS = [ { id : 'v1_casual', text : 'Eres Zelin, la IA oficial de TomateSMP. Hablas en español de forma casual, directa y sin rodeos. Usas minúsculas frecuentemente. No eres formal.', score: 0.5, uses: 0, }, { id : 'v2_friendly', text : 'Eres Zelin, asistente de TomateSMP. Eres amigable, útil y concisa. Respondes en español con personalidad propia — no eres un bot genérico.', score: 0.5, uses: 0, }, { id : 'v3_precise', text : 'Eres Zelin, bot de TomateSMP. Priorizas respuestas precisas y útiles sobre el estilo. Español natural, sin formalismos, al punto.', score: 0.5, uses: 0, }, ]; let _initialized = false; async function loadScores() { try { for (const v of PROMPT_VARIANTS) { const saved = await db.memGet(`promptopt.${v.id}`); if (saved) { v.score = saved.score ?? 0.5; v.uses = saved.uses ?? 0; } } _initialized = true; } catch { _initialized = true; } } async function saveScore(variant) { await db.memSet(`promptopt.${variant.id}`, { score: variant.score, uses: variant.uses }, 'prompt_optimizer'); } // Epsilon-greedy: 10% exploración, 90% explotación export async function selectPromptVariant() { if (!_initialized) await loadScores(); if (Math.random() < 0.10) { // Exploración: variante aleatoria return PROMPT_VARIANTS[Math.floor(Math.random() * PROMPT_VARIANTS.length)]; } // Explotación: la mejor conocida return PROMPT_VARIANTS.reduce((a, b) => a.score > b.score ? a : b); } // Registrar si la respuesta fue buena o mala (llama quality gate) export async function recordResult(variantId, qualityPassed) { if (!_initialized) await loadScores(); const v = PROMPT_VARIANTS.find(x => x.id === variantId); if (!v) return; v.uses++; // Media móvil exponencial — actualiza lentamente v.score = v.score * 0.95 + (qualityPassed ? 1 : 0) * 0.05; await saveScore(v).catch(() => {}); } export function getVariantStats() { return PROMPT_VARIANTS.map(v => ({ id : v.id, score: v.score.toFixed(3), uses : v.uses, })); } export function getBestVariant() { return PROMPT_VARIANTS.reduce((a, b) => a.score > b.score ? a : b); }