Spaces:
Paused
Paused
| /** | |
| * 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); | |
| } | |