Initial commit: StudyOS platform
This commit is contained in:
120
server/systemPromptBuilder.js
Normal file
120
server/systemPromptBuilder.js
Normal file
@@ -0,0 +1,120 @@
|
||||
/**
|
||||
* 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 };
|
||||
Reference in New Issue
Block a user