/** * Builds the system prompt for a conversation based on its type, user progress, * available PDFs, and any attachment texts. */ function buildSystemPrompt(conversation, progressRows = [], pdfContents = [], attachmentTexts = []) { if (conversation.type === 'main') { return buildMainPrompt(progressRows, pdfContents, attachmentTexts); } if (conversation.type === 'fork') { return buildForkPrompt(conversation); } return ''; } function buildMainPrompt(progressRows, pdfContents, attachmentTexts) { let prompt = `Sos un tutor de estudio personal especializado. Tu objetivo es ayudar al usuario a aprender de forma eficiente y con seguimiento real de su progreso. PROGRESO ACTUAL DEL USUARIO: ${formatProgressRows(progressRows)} REGLAS PARA PARCIALES SIMULADOS: - Temas marcados como DOMINADO (>=80%): incluir máximo 1-2 ejercicios simples de repaso. - Temas en progreso o sin práctica: incluir proporcionalmente más ejercicios. `; if (pdfContents.length > 0) { prompt += ` PDFS DISPONIBLES (en orden de prioridad del usuario): ${formatPDFList(pdfContents)} Cuando el usuario pida contenido de un PDF, incluir el markdown relevante en tu respuesta. `; const MAX_CONTENT_LENGTH = 30000; let pdfContentBlocks = []; let totalLength = 0; for (const pdf of pdfContents) { if (!pdf.content_markdown) continue; const content = pdf.content_markdown; totalLength += content.length; pdfContentBlocks.push({ name: pdf.original_name, content }); } if (totalLength > MAX_CONTENT_LENGTH) { const ratio = MAX_CONTENT_LENGTH / totalLength; pdfContentBlocks = pdfContentBlocks.map((b) => ({ name: b.name, content: b.content.substring(0, Math.floor(b.content.length * ratio)) + '\n\n[Contenido truncado por límites de contexto]', })); } if (pdfContentBlocks.length > 0) { prompt += pdfContentBlocks .map((b) => `\n--- CONTENIDO DE "${b.name}" ---\n${b.content}`) .join('\n'); } } if (attachmentTexts.length > 0) { prompt += ` ARCHIVOS ADJUNTOS EN ESTA CONSULTA: ${attachmentTexts.map((t, i) => `--- Adjunto ${i + 1} ---\n${t}`).join('\n\n')} `; } prompt += ` CAPACIDADES: - Generar exámenes simulados adaptados al progreso - Crear ejercicios graduados por dificultad - Dar explicaciones paso a paso - Señalar errores recurrentes y sugerir correcciones - Mantener roadmap de estudio personalizado FORMATO DE RESPUESTA: - Para fórmulas matemáticas, usar SIEMPRE LaTeX inline con $...$ y bloques con $$...$$. Ejemplo: $d = \\sqrt{(x_2-x_1)^2 + (y_2-y_1)^2}$ - Para gráficos de coordenadas, planos, rectas, o puntos, usar un bloque de código \`\`\`graph seguido de un JSON con el formato: {"points":[[x1,y1],[x2,y2]], "segments":[[x1,y1,x2,y2]], "grid":[xMin,xMax,yMin,yMax]}. Ejemplo para graficar A(1,2) y B(4,6): \`\`\`graph {"points":[[1,2],[4,6]], "segments":[[1,2,4,6]], "grid":[-1,6,-1,7]} \`\`\` - Para código o comandos, usar bloques de código con el lenguaje correspondiente FORMATO DE EJERCICIOS: Cuando el usuario resuelva un ejercicio, al final de tu respuesta incluí exactamente este JSON (invisible para el usuario, solo para tracking): {"exercise_logged": {"topic": "nombre_del_topic", "correct": true/false}} `; return prompt; } function buildForkPrompt(conversation) { return `Sos un tutor especializado EXCLUSIVAMENTE en el tema: ${conversation.title}. Este es un micro-chat derivado de la sesión principal de estudio. REGLA ESTRICTA: No salgas del tema asignado. Si el usuario pregunta algo fuera de scope, redirigilo amablemente al tema. Cuando el usuario termine, el contexto de esta sesión se va a integrar automáticamente al chat principal. Podés usar ejercicios, ejemplos, preguntas y explicaciones paso a paso sobre ${conversation.title}.`; } function formatProgressRows(rows) { if (!rows || rows.length === 0) { return '(Sin datos de progreso aún. Empezá practicando cualquier tema.)'; } return rows.map(r => { const pct = r.exercises_done > 0 ? Math.round((r.exercises_correct / r.exercises_done) * 100) : 0; let status; if (pct >= 80) status = '✓ DOMINADO'; else if (pct >= 50) status = '→ en progreso'; else status = '✗ necesita práctica'; return `- ${r.topic}: ${r.exercises_done} ejercicios, ${pct}% correctos ${status}`; }).join('\n'); } function formatPDFList(pdfs) { return pdfs .sort((a, b) => a.reorder_index - b.reorder_index) .map((p, i) => `${i + 1}. ${p.original_name}${p.content_markdown ? ' [contenido extraído]' : ' [pendiente de procesar]'}`) .join('\n'); } module.exports = { buildSystemPrompt };