diff --git a/html/kidsai/server.js b/html/kidsai/server.js
index c6c212f..6601a95 100755
--- a/html/kidsai/server.js
+++ b/html/kidsai/server.js
@@ -7,6 +7,17 @@ const OpenAI = require('openai');
// Load environment variables from .env file
require('dotenv').config();
+// Add robust error logging for uncaught exceptions and unhandled promise rejections
+process.on('uncaughtException', (err) => {
+ console.error('UNCAUGHT EXCEPTION:', err.stack || err);
+ // Optionally, log to a file or external service here
+});
+
+process.on('unhandledRejection', (reason, promise) => {
+ console.error('UNHANDLED REJECTION:', reason);
+ // Optionally, log to a file or external service here
+});
+
const app = express();
const PORT = process.env.PORT || 3002;
@@ -25,14 +36,29 @@ const HF_MODELS = [
const HF_API_TOKEN = process.env.HUGGING_FACE_TOKEN;
+// Store conversation history to prevent redundant questions
+const conversationHistory = new Map(); // sessionId -> conversation array
+
// Educational prompts for AI to guide thinking instead of giving direct answers
const EDUCATIONAL_PROMPTS = {
en: {
- systemPrompt: "You are an educational assistant for children. Instead of giving direct answers, ask 2-3 guiding questions that help children think through the problem themselves. Be encouraging and use simple language. Focus on the thinking process, not the answer.",
+ systemPrompt: `You are a patient learning companion for children (8-12 years old). Always ask only ONE targeted, new question that builds on the child's last answer. Do not repeat yourself—check if you have already asked this or a very similar question.
+
+Your questions should encourage the child to think, e.g., by asking for comparisons, reasons, differences, feelings, or personal experiences.
+
+Optionally, if it fits, you can give a hint where the child could observe or try something in real life (e.g., "If you're ever at the park, see if…"). This is just an extra, not the main part of the question.
+
+Use simple, friendly language. No lists, no explanations, only one question per answer.`,
prefix: "That's a great question! Let me help you think through this step by step. Instead of telling you the answer, here are some questions to guide your thinking:"
},
de: {
- systemPrompt: "Du bist ein Lernassistent für Kinder. Anstatt direkte Antworten zu geben, stelle 2-3 Leitfragen, die Kindern helfen, das Problem selbst zu durchdenken. Sei ermutigend und verwende einfache Sprache. Konzentriere dich auf den Denkprozess, nicht auf die Antwort.",
+ systemPrompt: `Du bist ein geduldiger Lernbegleiter für Kinder (8-12 Jahre). Stelle immer nur EINE gezielte, neue Frage, die auf der letzten Antwort des Kindes aufbaut. Wiederhole dich nicht – prüfe, ob du diese Frage oder eine sehr ähnliche schon gestellt hast.
+
+Deine Fragen sollen das Kind zum Nachdenken anregen, z.B. durch Vergleiche, Gründe, Unterschiede, Gefühle oder eigene Erfahrungen.
+
+Optional: Wenn es passt, kannst du einen Hinweis geben, wo das Kind im echten Leben etwas beobachten oder ausprobieren könnte (z.B. „Wenn du mal im Park bist, schau, ob…"). Das ist aber nur ein Zusatz, nicht der Hauptteil der Frage.
+
+Verwende einfache, freundliche Sprache. Keine Listen, keine Erklärungen, nur eine Frage pro Antwort.`,
prefix: "Das ist eine tolle Frage! Lass mich dir helfen, Schritt für Schritt darüber nachzudenken. Anstatt dir die Antwort zu sagen, hier sind einige Fragen, die dein Denken leiten:"
}
};
@@ -49,7 +75,7 @@ app.get('/', (req, res) => {
// API endpoint for AI-powered educational guidance
app.post('/api/ask', async (req, res) => {
- const { question, language = 'en' } = req.body;
+ const { question, language = 'en', sessionId = 'default' } = req.body;
if (!question || question.trim().length === 0) {
return res.status(400).json({
@@ -59,14 +85,34 @@ app.post('/api/ask', async (req, res) => {
}
try {
- // Get AI-powered guidance
- const aiGuidance = await getAIGuidance(question, language);
+ // Get or initialize conversation history for this session
+ if (!conversationHistory.has(sessionId)) {
+ conversationHistory.set(sessionId, []);
+ }
+
+ const history = conversationHistory.get(sessionId);
+
+ // Get AI-powered guidance with conversation history
+ const aiGuidance = await getAIGuidance(question, language, history);
+
+ // Add the initial question to history
+ history.push({
+ type: 'initial_question',
+ content: question,
+ timestamp: new Date()
+ });
+
+ // Keep only last 20 interactions to prevent memory bloat
+ if (history.length > 20) {
+ history.splice(0, history.length - 20);
+ }
res.json({
success: true,
guidance: aiGuidance,
question: question,
- language: language
+ language: language,
+ sessionId: sessionId
});
} catch (error) {
@@ -80,7 +126,8 @@ app.post('/api/ask', async (req, res) => {
guidance: fallbackGuidance,
question: question,
language: language,
- fallback: true
+ fallback: true,
+ sessionId: sessionId
});
}
});
@@ -124,18 +171,137 @@ app.post('/api/reveal-answer', async (req, res) => {
});
// Function to get OpenAI-powered educational guidance (Primary)
-async function getOpenAIGuidance(question, language) {
+async function getOpenAIGuidance(question, language, conversationHistory = []) {
console.log('🤖 Calling OpenAI with GPT-4o-mini...');
const isGerman = language === 'de';
+ // CONTENT FILTER: Block inappropriate topics for children
+ const inappropriateTopics = [
+ 'alkohol', 'alcohol', 'betrunken', 'drunk', 'besaufen',
+ 'drogen', 'drugs', 'rauchen', 'smoking', 'zigarette',
+ ' sex ', ' sex.', ' sex?', ' sex!', 'sexuell', 'sexual',
+ 'gewalt', 'violence', 'töten', 'kill', 'morden', 'murder',
+ ' tod ', ' death ', 'sterben', ' die ', ' dying',
+ 'krieg', ' war ', 'waffen', 'weapons', 'pistole', 'gun'
+ ];
+
+ const questionLower = ` ${question.toLowerCase()} `; // Add spaces to avoid partial matches
+ const isInappropriate = inappropriateTopics.some(topic => questionLower.includes(topic));
+
+ if (isInappropriate) {
+ console.log('🚫 Inappropriate topic detected, redirecting...');
+ const redirectMessage = isGerman
+ ? 'Das ist ein interessantes Thema, aber ich helfe lieber bei Fragen über Natur, Wissenschaft und die Welt um uns herum! Hast du eine Frage über Tiere, Pflanzen, das Wetter oder wie Dinge funktionieren?'
+ : 'That\'s an interesting topic, but I prefer to help with questions about nature, science, and the world around us! Do you have a question about animals, plants, weather, or how things work?';
+
+ return {
+ question: redirectMessage,
+ category: 'redirection',
+ thinking_step: isGerman ? 'Umleitung zu geeigneten Themen' : 'Redirecting to appropriate topics'
+ };
+ }
+
+ // Extract recent questions from conversation history to prevent repetition
+ const recentQuestions = conversationHistory
+ .filter(item => item.type === 'ai_question' || item.type === 'ai_response')
+ .slice(-4) // Last 4 AI questions/responses
+ .map(item => item.content)
+ .join('\n');
+
const systemPrompt = isGerman
- ? "Du bist ein geduldiger Lehrer für Kinder. Anstatt direkte Antworten zu geben, führe das Kind Schritt für Schritt zum Verständnis. Stelle genau 3-4 aufbauende Fragen, die das Kind zum Nachdenken anregen und ihm helfen, die Antwort selbst zu entdecken. Jede Frage sollte auf der vorherigen aufbauen und dem Kind helfen, das Konzept zu verstehen. Verwende einfache Sprache und ermutigende Worte. Formatiere IMMER als nummerierte Liste: 1. [Frage] 2. [Frage] 3. [Frage] usw. Gib NUR die nummerierten Fragen zurück, keine Einleitung."
- : "You are a patient teacher for children. Instead of giving direct answers, guide the child step by step to understanding. Ask exactly 3-4 building questions that encourage the child to think and help them discover the answer themselves. Each question should build on the previous one and help the child understand the concept. Use simple language and encouraging words. ALWAYS format as a numbered list: 1. [Question] 2. [Question] 3. [Question] etc. Return ONLY the numbered questions, no introduction.";
+ ? `Du bist ein geduldiger Lernbegleiter für Kinder (8-12 Jahre). Dein Ansatz: FUNDAMENTALS FIRST!
+
+NEUER ANSATZ - GRUNDLAGEN ZUERST:
+Wenn ein Kind eine Frage stellt, identifiziere die 2-3 wichtigsten Grundlagen, die sie verstehen müssen, um das Thema zu begreifen.
+
+ANTWORT-STRUKTUR:
+1. Sage: "Um [Thema] zu verstehen, musst du die Grundlagen von [Grundlage 1], [Grundlage 2] und [Grundlage 3] kennen."
+2. Dann: "Lass uns mit [erste Grundlage] anfangen..."
+3. Stelle EINE einfache Frage zur ersten Grundlage
+
+BEISPIELE:
+- Sonnenbrand → "Um Sonnenbrand zu verstehen, musst du die Grundlagen von UV-Strahlung, Hautzellen und Lichtenergie kennen. Lass uns mit UV-Strahlung anfangen..."
+- Vogel fliegt → "Um das Fliegen zu verstehen, musst du die Grundlagen von Aerodynamik, Luftdruck und Flügelform kennen. Lass uns mit Aerodynamik anfangen..."
+- Sonne brennt → "Um zu verstehen wie die Sonne brennt, musst du die Grundlagen von Kernfusion, Wasserstoff und extremer Hitze kennen. Lass uns mit Kernfusion anfangen..."
+
+STIL: Strukturiert, bildungsorientiert, systematisch. Baue Wissen Schritt für Schritt auf.`
+ : `You are a patient learning companion for children (8-12 years old). Your approach: FUNDAMENTALS FIRST!
+
+NEW APPROACH - FUNDAMENTALS FIRST:
+When a child asks a question, identify the 2-3 most important fundamentals they need to understand to grasp the topic.
+
+RESPONSE STRUCTURE:
+1. Say: "To understand [topic], you need to understand the basics of [fundamental 1], [fundamental 2], and [fundamental 3]."
+2. Then: "Let's start with [first fundamental]..."
+3. Ask ONE simple question about the first fundamental
+
+EXAMPLES:
+- Sunburn → "To understand sunburn, you need to understand the basics of UV radiation, skin cells, and light energy. Let's start with UV radiation..."
+- Bird flying → "To understand flying, you need to understand the basics of aerodynamics, air pressure, and wing shape. Let's start with aerodynamics..."
+- Sun burning → "To understand how the sun burns, you need to understand the basics of nuclear fusion, hydrogen, and extreme heat. Let's start with nuclear fusion..."
+
+STYLE: Structured, educational, systematic. Build knowledge step by step.
+
+Optionally: Only if it fits very naturally, mention a real-life situation (e.g., "If you're ever at the park..."). But this is secondary.
+
+STYLE: Short, friendly, only ONE question. No lists, no explanations.`;
const userPrompt = isGerman
- ? `Ein Kind hat gefragt: "${question}". Führe es mit genau 3-4 nummerierten Fragen Schritt für Schritt zum Verständnis, ohne die Antwort direkt zu verraten. Beispiel: 1. Wie fühlt sich Wasser an, wenn du es berührst? 2. Was passiert... usw. NUR nummerierte Fragen, keine Einleitung!`
- : `A child asked: "${question}". Guide them with exactly 3-4 numbered questions step by step to understanding without giving away the answer directly. Example: 1. How does water feel when you touch it? 2. What happens... etc. ONLY numbered questions, no introduction!`;
+ ? `Ein Kind hat diese Frage gestellt: "${question}"
+
+DEINE AUFGABE: Verwende den FUNDAMENTALS FIRST Ansatz!
+
+1. IDENTIFIZIERE die 2-3 wichtigsten Grundlagen für dieses Thema
+2. STRUKTURIERE deine Antwort so:
+ - "Um [Thema] zu verstehen, musst du die Grundlagen von [Grundlage 1], [Grundlage 2] und [Grundlage 3] kennen."
+ - "Lass uns mit [erste Grundlage] anfangen..."
+ - [EINE einfache Frage zur ersten Grundlage]
+
+BEISPIELE FÜR GRUNDLAGEN:
+- Sonnenbrand → UV-Strahlung, Hautzellen, Lichtenergie
+- Vogel fliegen → Aerodynamik, Luftdruck, Flügelform
+- Sonne brennt → Kernfusion, Wasserstoff, extreme Hitze
+- Regenbogen → Lichtbrechung, Wassertropfen, Farbspektrum
+- Donner → Schallwellen, Blitz, Luftexpansion
+
+STIL: Strukturiert, bildungsorientiert, systematisch aufbauend.`
+ : `A child asked this question: "${question}"
+
+YOUR TASK: Use the FUNDAMENTALS FIRST approach!
+
+1. IDENTIFY the 2-3 most important fundamentals for this topic
+2. STRUCTURE your response like this:
+ - "To understand [topic], you need to understand the basics of [fundamental 1], [fundamental 2], and [fundamental 3]."
+ - "Let's start with [first fundamental]..."
+ - [ONE simple question about the first fundamental]
+
+EXAMPLES OF FUNDAMENTALS:
+- Sunburn → UV radiation, skin cells, light energy
+- Bird flying → Aerodynamics, air pressure, wing shape
+- Sun burning → Nuclear fusion, hydrogen, extreme heat
+- Rainbow → Light refraction, water droplets, color spectrum
+- Thunder → Sound waves, lightning, air expansion
+
+STYLE: Structured, educational, systematically building knowledge.
+- STAYS IN THE SAME SCIENTIFIC CONTEXT as the original question
+
+SPECIFIC EXAMPLES for SCIENTIFIC questions:
+- For "Why do people get sunburn?": "What do you know about different types of sunlight?"
+- For "How does the sun burn?": "What do you think is inside the sun that's different from Earth?"
+- For "How do birds fly?": "What's special about bird wings that other animals don't have?"
+
+GENERAL EXAMPLES of DIFFERENT question types:
+- Sparks curiosity in a new way
+- STAYS IN THE SAME SCIENTIFIC CONTEXT as the original question
+
+EXAMPLES of DIFFERENT question types:
+- Comparisons: "Is this similar to riding a bike?"
+- Feelings: "What do you find most interesting about this?"
+- Reasons: "Why might this work this way?"
+- Imagination: "Imagine if you were a bird..."
+
+Formulate ONLY one single, completely new question.`;
try {
const completion = await openai.chat.completions.create({
@@ -170,49 +336,45 @@ async function getOpenAIGuidance(question, language) {
// Function to parse OpenAI response into thinking steps
function parseOpenAIResponseToSteps(text, language) {
- console.log('🔧 Parsing response:', text);
+ console.log('🔧 Parsing single question response:', text);
- const lines = text.split('\n').filter(line => line.trim());
- const steps = [];
+ const trimmed = text.trim();
- lines.forEach((line, index) => {
- const trimmed = line.trim();
- console.log(`🔍 Processing line ${index}: "${trimmed}"`);
+ // For single question responses, just return the question directly
+ if (trimmed.length > 10 && trimmed.includes('?')) {
+ // Clean up any numbering that might have slipped through
+ const cleanQuestion = trimmed.replace(/^\d+\.\s*/, '').trim();
- // Look for numbered items (1. 2. 3. etc.)
- const numberedMatch = trimmed.match(/^\d+\.\s*(.+)/);
- if (numberedMatch) {
- const questionText = numberedMatch[1].trim();
- if (questionText.length > 5) {
- steps.push({
- id: steps.length + 1,
- text: questionText,
- type: 'question'
- });
- console.log(`✅ Added step: ${questionText}`);
- }
- }
- // Also look for lines with question marks that might not be numbered
- else if (trimmed.includes('?') && trimmed.length > 10 && !trimmed.toLowerCase().includes('frage') && !trimmed.toLowerCase().includes('question')) {
- steps.push({
- id: steps.length + 1,
- text: trimmed,
- type: 'question'
- });
- console.log(`✅ Added question: ${trimmed}`);
- }
- });
-
- console.log(`📋 Total steps parsed: ${steps.length}`);
-
- // Ensure we have at least 2 steps
- if (steps.length < 2) {
- console.log('⚠️ Not enough steps, using fallback');
- const fallback = getFallbackGuidance('', language);
- return fallback.steps.slice(0, 3);
+ return [{
+ id: 1,
+ text: cleanQuestion,
+ type: 'question'
+ }];
}
- return steps.slice(0, 4); // Limit to 4 steps max
+ // Fallback - split by lines and take the first valid question
+ const lines = text.split('\n').filter(line => line.trim());
+ for (const line of lines) {
+ const trimmedLine = line.trim();
+ if (trimmedLine.length > 10 && trimmedLine.includes('?')) {
+ const cleanQuestion = trimmedLine.replace(/^\d+\.\s*/, '').trim();
+ return [{
+ id: 1,
+ text: cleanQuestion,
+ type: 'question'
+ }];
+ }
+ }
+
+ // If no valid question found, return fallback
+ const isGerman = language === 'de';
+ return [{
+ id: 1,
+ text: isGerman
+ ? "Was beobachtest du bei diesem Phänomen?"
+ : "What do you observe about this phenomenon?",
+ type: 'question'
+ }];
}
// Function to get Hugging Face guidance (Backup)
@@ -314,11 +476,11 @@ function parseHuggingFaceResponseToSteps(text, language) {
}
// Updated main function that tries OpenAI first, then Hugging Face, then local fallback
-async function getAIGuidance(question, language) {
+async function getAIGuidance(question, language, conversationHistory = []) {
// Try OpenAI first (most reliable)
if (process.env.OPENAI_API_KEY) {
try {
- return await getOpenAIGuidance(question, language);
+ return await getOpenAIGuidance(question, language, conversationHistory);
} catch (error) {
console.log('⚠️ OpenAI failed, trying Hugging Face...');
}
@@ -343,55 +505,14 @@ function getFallbackGuidance(question, language) {
const questionLower = question.toLowerCase();
// Topic-specific guided questions like in the image
- if (questionLower.includes('bird') || questionLower.includes('fly') || questionLower.includes('wing')) {
- const birdQuestions = isGerman ? [
- { id: 1, text: "Was weißt du bereits über Vögel und ihre Flügel?", type: 'question' },
- { id: 2, text: "Wie denkst du, nutzen Vögel ihre Flügel, um sich durch die Luft zu bewegen?", type: 'question' },
- { id: 3, text: "Kannst du an andere Tiere denken, die fliegen können, und wie unterscheiden sie sich in ihren Flugfähigkeiten?", type: 'question' }
- ] : [
- { id: 1, text: "What do you already know about this topic?", type: 'question' },
- { id: 2, text: "How do birds use their wings to generate lift and propel themselves through the air?", type: 'question' },
- { id: 3, text: "Can you think of any other animals that can fly like birds, and how do they differ in their flying abilities?", type: 'question' }
- ];
-
- return {
- type: 'topic-specific',
- steps: birdQuestions,
- encouragement: getRandomEncouragement(language, question),
- source: 'Educational Framework'
- };
- }
+ // All questions now use AI-powered guidance or generic scientific thinking questions
+ // No more hard-coded topic-specific questions
- if (questionLower.includes('sky') || questionLower.includes('blue') || questionLower.includes('himmel') || questionLower.includes('blau')) {
- const skyQuestions = isGerman ? [
- { id: 1, text: "Was weißt du bereits über Licht und Farben?", type: 'question' },
- { id: 2, text: "Was passiert mit Licht, wenn es durch die Luft geht?", type: 'question' },
- { id: 3, text: "Warum siehst du manche Farben deutlicher als andere?", type: 'question' }
- ] : [
- { id: 1, text: "What do you already know about light and colors?", type: 'question' },
- { id: 2, text: "What happens to light when it travels through the air?", type: 'question' },
- { id: 3, text: "Why do you see some colors more clearly than others?", type: 'question' }
- ];
-
- return {
- type: 'topic-specific',
- steps: skyQuestions,
- encouragement: getRandomEncouragement(language, question),
- source: 'Educational Framework'
- };
- }
-
- // Generic fallback questions
+ // Generic fallback questions that promote scientific thinking (one at a time)
const fallbackSteps = isGerman ? [
- { id: 1, text: "Was weißt du bereits über dieses Thema?", type: 'question' },
- { id: 2, text: "Welche Teile der Frage verstehst du, und welche sind unklar?", type: 'question' },
- { id: 3, text: "Wo könntest du mehr Informationen finden?", type: 'question' },
- { id: 4, text: "Kannst du das Problem in kleinere Teile aufteilen?", type: 'question' }
+ { id: 1, text: "Was hast du bereits über dieses Thema beobachtet oder erfahren?", type: 'question' }
] : [
- { id: 1, text: "What do you already know about this topic?", type: 'question' },
- { id: 2, text: "Which parts of the question do you understand, and which are unclear?", type: 'question' },
- { id: 3, text: "Where could you find more information about this?", type: 'question' },
- { id: 4, text: "Can you break this problem down into smaller parts?", type: 'question' }
+ { id: 1, text: "What have you already observed or learned about this topic?", type: 'question' }
];
return {
@@ -480,12 +601,12 @@ async function getActualAnswer(question, language) {
const isGerman = language === 'de';
const systemPrompt = isGerman
- ? "Du bist ein hilfreicher Assistent für Kinder. Gib eine klare, einfache und altersgerechte Antwort auf die Frage. Verwende einfache Sprache und erkläre es so, dass ein Kind es verstehen kann. Halte die Antwort kurz aber vollständig."
- : "You are a helpful assistant for children. Provide a clear, simple, and age-appropriate answer to the question. Use simple language and explain it in a way a child can understand. Keep the answer concise but complete.";
+ ? "Du bist ein wissenschaftlich versierter Pädagoge für Kinder. Gib immer wissenschaftlich korrekte, aber sehr kurze und kindgerechte Antworten (maximal 3-4 Sätze). Erkläre die grundlegenden wissenschaftlichen Prinzipien mit einfachen Worten und alltäglichen Vergleichen. Verwende konkrete Beispiele und lade zum Experimentieren ein. Wichtig: Sei wissenschaftlich präzise, aber verwende Sprache, die ein 8-12 Jahre altes Kind versteht. Halte die Antwort kurz und ermutigend."
+ : "You are a scientifically knowledgeable educator for children. Always provide scientifically accurate but very brief and child-friendly answers (maximum 3-4 sentences). Explain fundamental scientific principles using simple words and everyday comparisons. Use concrete examples and invite experimentation. Important: Be scientifically precise, but use language an 8-12 year old child can understand. Keep the answer short and encouraging.";
const userPrompt = isGerman
- ? `Ein Kind möchte wissen: "${question}". Gib eine einfache, klare Antwort.`
- : `A child wants to know: "${question}". Provide a simple, clear answer.`;
+ ? `Ein neugieriges Kind möchte wissen: "${question}". Gib eine wissenschaftlich korrekte, aber sehr kurze und einfache Erklärung (maximal 3-4 Sätze) mit einem praktischen Beispiel.`
+ : `A curious child wants to know: "${question}". Provide a scientifically accurate but very brief and simple explanation (maximum 3-4 sentences) with a practical example.`;
try {
if (process.env.OPENAI_API_KEY) {
@@ -495,7 +616,7 @@ async function getActualAnswer(question, language) {
{ role: "system", content: systemPrompt },
{ role: "user", content: userPrompt }
],
- max_tokens: 150,
+ max_tokens: 80, // Much shorter responses for children
temperature: 0.3 // Lower temperature for more factual answers
});
@@ -518,477 +639,98 @@ async function getActualAnswer(question, language) {
function getFallbackAnswer(question, language) {
const isGerman = language === 'de';
- // Simple pattern matching for common questions
- const questionLower = question.toLowerCase();
-
- if (questionLower.includes('sky') || questionLower.includes('himmel') || questionLower.includes('blue') || questionLower.includes('blau')) {
- return {
- type: 'rule-based',
- text: isGerman
- ? "Der Himmel ist blau, weil winzig kleine Teilchen in der Luft das blaue Licht mehr streuen als andere Farben. Es ist wie wenn du blaues Licht durch viele kleine Glaskugeln scheinst!"
- : "The sky is blue because tiny particles in the air scatter blue light more than other colors. It's like shining blue light through many tiny glass balls!",
- source: 'Educational Framework'
- };
- }
-
- if (questionLower.includes('bird') || questionLower.includes('fly') || questionLower.includes('vogel') || questionLower.includes('fliegen')) {
- return {
- type: 'rule-based',
- text: isGerman
- ? "Vögel können fliegen, weil ihre Flügel eine besondere Form haben und sie sehr leichte, hohle Knochen haben. Sie schlagen mit den Flügeln und erzeugen Auftrieb, der sie in die Luft hebt!"
- : "Birds can fly because their wings have a special shape and they have very light, hollow bones. They flap their wings to create lift that pushes them up into the air!",
- source: 'Educational Framework'
- };
- }
-
- if (questionLower.includes('1+1') || questionLower.includes('add') || questionLower.includes('plus')) {
- return {
- type: 'rule-based',
- text: isGerman
- ? "1 + 1 = 2! Wenn du einen Apfel hast und noch einen dazu bekommst, hast du zwei Äpfel. Das ist Addition - zusammenzählen!"
- : "1 + 1 = 2! When you have one apple and get another one, you have two apples. That's addition - adding together!",
- source: 'Educational Framework'
- };
- }
-
- // Generic fallback
+ // Generic fallback that encourages scientific thinking
return {
type: 'rule-based',
text: isGerman
- ? "Das ist eine tolle Frage! Die Antwort hängt von vielen Faktoren ab. Ich empfehle dir, in einem Buch nachzuschauen oder einen Erwachsenen zu fragen, der mehr darüber weiß."
- : "That's a great question! The answer depends on many factors. I recommend looking it up in a book or asking an adult who knows more about this topic.",
+ ? "Das ist eine faszinierende wissenschaftliche Frage! Die Antwort hängt von verschiedenen Faktoren ab. Ich empfehle dir, zu beobachten, zu experimentieren und in wissenschaftlichen Büchern nachzuschauen. Du könntest auch einen Erwachsenen fragen, der sich mit diesem Thema auskennt. Die besten Entdeckungen macht man durch eigenes Forschen! 🔬"
+ : "That's a fascinating scientific question! The answer depends on various factors. I recommend observing, experimenting, and looking it up in scientific books. You could also ask an adult who knows about this topic. The best discoveries come from doing your own research! 🔬",
source: 'Educational Framework'
};
}
// API endpoint for responding to user answers contextually
app.post('/api/respond-to-answer', async (req, res) => {
+ const {
+ answer,
+ question,
+ originalTopic,
+ language = 'en',
+ sessionId = 'default',
+ context,
+ stepIndex,
+ instructions
+ } = req.body;
+
+ if (!answer || answer.trim().length === 0) {
+ return res.status(400).json({
+ success: false,
+ error: 'Answer is required'
+ });
+ }
+
try {
- const { answer, question, language = 'en', stepIndex, context, instructions, originalTopic } = req.body;
-
- if (!answer || !question) {
- return res.status(400).json({
- success: false,
- error: 'Answer and question are required'
- });
- }
-
- console.log(`📝 Generating response to answer: "${answer}" for question: "${question}" with context: ${context}`);
-
- // Handle confusion explanation requests specially
- if (context === 'confusion_explanation' && instructions) {
- const explanationPrompt = `${instructions}
-
-CHILD'S CONFUSED RESPONSE: "${answer}"
-ORIGINAL QUESTION: "${question}"
-
-The child is expressing confusion and needs help understanding. Provide a clear, simple explanation using everyday examples and concrete comparisons that a child can understand.`;
-
- // Try OpenAI first for explanations
- if (process.env.OPENAI_API_KEY) {
- try {
- const completion = await openai.chat.completions.create({
- model: "gpt-4o-mini",
- messages: [
- {
- role: "system",
- content: explanationPrompt
- }
- ],
- max_tokens: 400,
- temperature: 0.6
- });
-
- const aiResponse = completion.choices[0]?.message?.content?.trim();
-
- if (aiResponse && aiResponse.length > 10) {
- console.log('✅ OpenAI confusion explanation generated successfully');
- return res.json({
- success: true,
- response: aiResponse,
- source: 'OpenAI GPT-4o-mini (Confusion Explanation)'
- });
- }
- } catch (openaiError) {
- console.log('❌ OpenAI error for confusion explanation:', openaiError.message);
- }
- }
-
- // Fallback for confusion explanations
- const isGerman = language === 'de';
- const { originalTopic } = req.body;
-
- // Try to provide topic-specific fallback explanations
- let confusionFallback;
- const topicLower = (originalTopic || question || '').toLowerCase();
-
- if (topicLower.includes('sonnenlicht') && topicLower.includes('wasser') || topicLower.includes('sunlight') && topicLower.includes('water') ||
- topicLower.includes('licht') && topicLower.includes('glas') || topicLower.includes('light') && topicLower.includes('glass') ||
- topicLower.includes('prisma') || topicLower.includes('prism') || topicLower.includes('spektrum') || topicLower.includes('spectrum')) {
- confusionFallback = isGerman ?
- 'Wenn Sonnenlicht durch ein Glas Wasser scheint, passiert etwas Magisches! Das weiße Licht wird in alle seine Farben aufgeteilt - rot, orange, gelb, grün, blau, indigo und violett. Das passiert, weil Licht eigentlich aus vielen Farben besteht, die wir normalerweise nicht sehen können. Das Wasser wirkt wie ein Prisma und trennt diese Farben. Du kannst es selbst ausprobieren: Halte ein Glas Wasser ins Sonnenlicht und schau an die Wand - du siehst einen kleinen Regenbogen! 🌈✨' :
- 'When sunlight shines through a glass of water, something magical happens! The white light gets split into all its colors - red, orange, yellow, green, blue, indigo, and violet. This happens because light is actually made of many colors that we normally can\'t see. The water acts like a prism and separates these colors. You can try it yourself: hold a glass of water in sunlight and look at the wall - you\'ll see a little rainbow! 🌈✨';
- } else if (topicLower.includes('wasser') || topicLower.includes('water') || topicLower.includes('nass') || topicLower.includes('wet')) {
- confusionFallback = isGerman ?
- 'Wasser ist "nass", weil es eine Flüssigkeit ist, die an anderen Oberflächen haftet und sie benetzt. Wenn Wassermoleküle auf deine Haut oder andere Materialien treffen, bleiben sie dort haften und fühlen sich feucht an. Das ist das Gefühl, das wir "nass" nennen! Du kannst das ausprobieren: Tropfe etwas Wasser auf verschiedene Materialien und schau, wie es sich verhält. 💧' :
- 'Water is "wet" because it\'s a liquid that sticks to other surfaces and makes them moist. When water molecules touch your skin or other materials, they stay there and feel damp. That\'s the feeling we call "wet"! You can try this: drop some water on different materials and see how it behaves. 💧';
- } else if (topicLower.includes('vögel') || topicLower.includes('birds') || topicLower.includes('fliegen') || topicLower.includes('fly') || topicLower.includes('flügel') || topicLower.includes('wings')) {
- confusionFallback = isGerman ?
- 'Vögel können fliegen, weil sie besondere Körperteile haben! Ihre Flügel sind leicht aber stark, ihre Knochen sind hohl (wie Strohhalme), und sie haben kräftige Brustmuskeln, die die Flügel bewegen. Wenn sie die Flügel auf und ab schlagen, drücken sie die Luft nach unten, und das hebt sie nach oben - wie beim Schwimmen, aber in der Luft! Du kannst das ausprobieren: Wedel mit den Armen und spür, wie du die Luft bewegst! 🐦' :
- 'Birds can fly because they have special body parts! Their wings are light but strong, their bones are hollow (like straws), and they have powerful chest muscles that move the wings. When they flap their wings up and down, they push air downward, and that lifts them up - like swimming, but in the air! You can try this: wave your arms and feel how you move the air! 🐦';
- } else if (topicLower.includes('jahreszeiten') || topicLower.includes('seasons') || topicLower.includes('winter') || topicLower.includes('sommer') || topicLower.includes('summer') || topicLower.includes('erde') || topicLower.includes('earth') || topicLower.includes('position') || topicLower.includes('heiß') || topicLower.includes('kalt') || topicLower.includes('hot') || topicLower.includes('cold')) {
- confusionFallback = isGerman ?
- 'Die Erde ist wie ein Ball, der um die Sonne kreist! Sie ist etwas schief geneigt, wie ein Ball, der leicht zur Seite hängt. Wenn unser Teil der Erde zur Sonne geneigt ist, bekommen wir mehr Sonnenlicht und es wird Sommer. Wenn wir von der Sonne weg geneigt sind, bekommen wir weniger Licht und es wird Winter! Du kannst das mit einer Taschenlampe und einem Ball ausprobieren - halte den Ball schief und leuchte ihn an! 🌍☀️' :
- 'The Earth is like a ball that goes around the Sun! It\'s tilted a bit, like a ball that hangs slightly to one side. When our part of Earth tilts toward the Sun, we get more sunlight and it becomes summer. When we tilt away from the Sun, we get less light and it becomes winter! You can try this with a flashlight and a ball - hold the ball tilted and shine light on it! 🌍☀️';
- } else if (topicLower.includes('regenbogen') || topicLower.includes('rainbow') || topicLower.includes('farben') || topicLower.includes('colors')) {
- confusionFallback = isGerman ?
- 'Das passiert, weil Sonnenlicht aus vielen verschiedenen Farben besteht! Wenn Licht durch Regentropfen geht, werden diese Farben getrennt - wie bei einem Prisma. Du kannst das selbst ausprobieren: Halte ein Glas Wasser ins Sonnenlicht und schau, welche Farben entstehen! 🌈' :
- 'This happens because sunlight is made of many different colors! When light goes through raindrops, these colors get separated - just like with a prism. You can try this yourself: hold a glass of water in sunlight and see what colors appear! 🌈';
- } else {
- confusionFallback = isGerman ?
- 'Das ist eine sehr gute Frage! Manchmal sind Dinge komplizierter, als sie auf den ersten Blick scheinen. Lass mich dir das einfacher erklären: Alles hat bestimmte Eigenschaften, die wir mit unseren Sinnen wahrnehmen können. Was du fragst, hat mit diesen grundlegenden Eigenschaften zu tun. Hast du noch spezielle Fragen dazu? 🤔' :
- 'That\'s a very good question! Sometimes things are more complicated than they seem at first. Let me explain this more simply: Everything has certain properties that we can perceive with our senses. What you\'re asking about relates to these basic properties. Do you have any specific questions about it? 🤔';
- }
-
- return res.json({
- success: true,
- response: confusionFallback,
- source: 'Fallback Confusion Explanation'
- });
- }
-
- // Handle definition/explanation requests specially
- if (context === 'definition_request' && instructions) {
- const definitionPrompt = `${instructions}
-
-CHILD'S QUESTION: "${answer}"
-CONTEXT: We were discussing "${question}"
-
-The child is asking for a definition, explanation, or clarification about location/relationships. Provide a clear, child-friendly answer with simple examples and invite further questions. If asking about location (where is X?), explain in simple terms. If asking about relationships (what does X have to do with Y?), explain the connection clearly.`;
-
- // Try OpenAI first for definitions
- if (process.env.OPENAI_API_KEY) {
- try {
- const completion = await openai.chat.completions.create({
- model: "gpt-4o-mini",
- messages: [
- {
- role: "system",
- content: definitionPrompt
- }
- ],
- max_tokens: 300,
- temperature: 0.6
- });
-
- const aiResponse = completion.choices[0]?.message?.content?.trim();
-
- if (aiResponse && aiResponse.length > 10) {
- console.log('✅ OpenAI definition response generated successfully');
- return res.json({
- success: true,
- response: aiResponse,
- source: 'OpenAI GPT-4o-mini (Definition Response)'
- });
- }
- } catch (openaiError) {
- console.log('❌ OpenAI error for definition response:', openaiError.message);
- }
- }
-
- // Fallback for definition requests
- const isGerman = language === 'de';
- const { originalTopic } = req.body;
- const topicLowerDef = (originalTopic || question || answer || '').toLowerCase();
-
- let definitionFallback;
- if (topicLowerDef.includes('sonnenlicht') && topicLowerDef.includes('wasser') || topicLowerDef.includes('sunlight') && topicLowerDef.includes('water') ||
- topicLowerDef.includes('licht') && topicLowerDef.includes('glas') || topicLowerDef.includes('light') && topicLowerDef.includes('glass')) {
- definitionFallback = isGerman ?
- 'Wenn Sonnenlicht durch ein Glas Wasser scheint, wird das weiße Licht in seine Farben aufgeteilt! Das nennt man Lichtbrechung oder Spektrum. Du siehst dann einen kleinen Regenbogen an der Wand. Das passiert, weil Licht aus vielen Farben besteht. Du kannst das ganz einfach selbst ausprobieren - halte ein Glas Wasser ins Sonnenlicht! Was möchtest du noch über Licht wissen? 🌈💡' :
- 'When sunlight shines through a glass of water, the white light gets split into its colors! This is called light refraction or spectrum. You\'ll see a little rainbow on the wall. This happens because light is made of many colors. You can easily try this yourself - hold a glass of water in sunlight! What else would you like to know about light? 🌈💡';
- } else {
- definitionFallback = isGerman ?
- 'Das ist eine tolle Frage! Ein Regenbogen entsteht, wenn Sonnenlicht durch kleine Wassertropfen in der Luft geht. Das Licht wird dabei in alle seine Farben aufgeteilt - rot, orange, gelb, grün, blau, indigo und violett. Du kannst das auch mit einem Gartenschlauch ausprobieren! Was möchtest du noch wissen? 🌈' :
- 'That\'s a great question! A rainbow happens when sunlight goes through tiny water droplets in the air. The light gets split into all its colors - red, orange, yellow, green, blue, indigo, and violet. You can try this with a garden hose too! What else would you like to know? 🌈';
- }
-
- return res.json({
- success: true,
- response: definitionFallback,
- source: 'Fallback Definition Response'
- });
- }
-
- // Create contextual prompt for responding to the user's answer (existing logic)
- const isGerman = language === 'de';
-
- // Check if this looks like a repeated "don't know" scenario
- const answerLowerChat = answer.toLowerCase().trim();
- const isDontKnowResponse = answerLowerChat.includes("don't know") || answerLowerChat.includes("weiß nicht") ||
- answerLowerChat.includes("weis nicht") || answerLowerChat.includes("weiss nicht") ||
- answerLowerChat.includes("keine ahnung") || answerLowerChat.includes("ich weiß es nicht") ||
- answerLowerChat.includes("das weiß ich nicht") || answerLowerChat.includes("weiß ich auch nicht");
-
- console.log(`🔍 Answer analysis - isDontKnow: ${isDontKnowResponse}, answer: "${answer}"`);
-
- // If this is a "don't know" response and we're in a loop, provide explanation
- if (isDontKnowResponse && (context === 'repeated_dont_know' ||
- (instructions && (instructions.includes('mehrmals') || instructions.includes('multiple times'))))) {
- console.log('🎯 Detected repeated "don\'t know" - providing explanation instead of more questions');
-
- const explanationPrompt = isGerman ?
- `Das Kind braucht jetzt eine klare Erklärung, da es mehrmals "weiß nicht" gesagt hat.
-
-URSPRÜNGLICHE FRAGE: "${originalTopic || question}"
-CONTEXT: Wiederholte "weiß nicht" Antworten
-
-Gib eine einfache, verständliche Erklärung für Kinder. Verwende Beispiele aus dem Alltag. Erkläre Schritt für Schritt. Keine weiteren Fragen - das Kind braucht jetzt Wissen!` :
- `The child needs a clear explanation now since they've said "don't know" multiple times.
-
-ORIGINAL QUESTION: "${originalTopic || question}"
-CONTEXT: Repeated "don't know" responses
-
-Give a simple, understandable explanation for children. Use everyday examples. Explain step by step. No more questions - the child needs knowledge now!`;
-
- try {
- const completion = await openai.chat.completions.create({
- model: "gpt-4o-mini",
- messages: [
- {
- role: "system",
- content: explanationPrompt
- }
- ],
- max_tokens: 300,
- temperature: 0.6
- });
-
- const aiResponse = completion.choices[0]?.message?.content?.trim();
-
- if (aiResponse && aiResponse.length > 10) {
- console.log('✅ OpenAI explanation for repeated "don\'t know" generated successfully');
- return res.json({
- success: true,
- response: aiResponse,
- source: 'OpenAI GPT-4o-mini (Repeated Don\'t Know Explanation)'
- });
- }
- } catch (openaiError) {
- console.log('❌ OpenAI error for repeated don\'t know explanation:', openaiError.message);
- }
+ // Get or initialize conversation history for this session
+ if (!conversationHistory.has(sessionId)) {
+ conversationHistory.set(sessionId, []);
}
- // Use AI to intelligently detect if child is asking a question that needs a direct answer
- if (process.env.OPENAI_API_KEY && !isDontKnowResponse && context !== 'repeated_dont_know') {
- console.log('🤖 Using AI to analyze if child is asking a question...');
-
- try {
- const questionDetectionPrompt = isGerman ?
- `Analysiere diese Antwort eines Kindes: "${answer}"
-
-Kontext: Das Kind wurde gefragt: "${question}"
-
-Bestimme: Stellt das Kind eine direkte Frage, die eine Erklärung braucht?
-
-Beispiele für JA:
-- "Was passiert denn da?"
-- "Wie geht das?"
-- "Warum ist das so?"
-- "Was bedeutet das?"
-- "Wo ist das?"
-
-Beispiele für NEIN:
-- "Ich weiß nicht"
-- "Vielleicht wegen der Temperatur"
-- "Ist es wegen dem Wetter?"
-- "Hmm, keine Ahnung"
-
-Antworte nur mit "JA" oder "NEIN".` :
- `Analyze this child's response: "${answer}"
-
-Context: The child was asked: "${question}"
-
-Determine: Is the child asking a direct question that needs an explanation?
-
-Examples of YES:
-- "What happens there?"
-- "How does that work?"
-- "Why is that?"
-- "What does that mean?"
-- "Where is that?"
-
-Examples of NO:
-- "I don't know"
-- "Maybe because of temperature"
-- "Is it because of weather?"
-- "Hmm, no idea"
-
-Reply only with "YES" or "NO".`;
-
- const detectionCompletion = await openai.chat.completions.create({
- model: "gpt-4o-mini",
- messages: [
- {
- role: "system",
- content: questionDetectionPrompt
- }
- ],
- max_tokens: 10,
- temperature: 0.1
- });
-
- const detectionResult = detectionCompletion.choices[0]?.message?.content?.trim().toUpperCase();
- console.log(`🔍 AI question detection result: "${detectionResult}"`);
-
- if (detectionResult === 'JA' || detectionResult === 'YES') {
- console.log('📤 AI detected a question - providing explanation');
-
- const explanationPrompt = isGerman ?
- `Das Kind stellt eine direkte Frage und braucht eine Erklärung.
-
-KIND'S FRAGE: "${answer}"
-KONTEXT: Wir haben über "${originalTopic || question}" gesprochen
-
-Gib eine klare, kindgerechte Antwort. Verwende einfache Sprache und Beispiele. Lade das Kind ein, mehr zu fragen oder selbst zu experimentieren. Beantworte die spezifische Frage des Kindes direkt.` :
- `The child is asking a direct question and needs an explanation.
-
-CHILD'S QUESTION: "${answer}"
-CONTEXT: We were discussing "${originalTopic || question}"
-
-Give a clear, child-friendly answer. Use simple language and examples. Invite the child to ask more or experiment themselves. Answer the child's specific question directly.`;
-
- const explanationCompletion = await openai.chat.completions.create({
- model: "gpt-4o-mini",
- messages: [
- {
- role: "system",
- content: explanationPrompt
- }
- ],
- max_tokens: 300,
- temperature: 0.6
- });
-
- const explanationResponse = explanationCompletion.choices[0]?.message?.content?.trim();
-
- if (explanationResponse && explanationResponse.length > 10) {
- console.log('✅ AI-detected question explanation generated successfully');
- return res.json({
- success: true,
- response: explanationResponse,
- source: 'OpenAI GPT-4o-mini (AI-Detected Question)'
- });
- }
- }
- } catch (aiError) {
- console.log('❌ AI question detection error:', aiError.message);
- // Continue with normal flow if AI detection fails
- }
+ const history = conversationHistory.get(sessionId);
+
+ // Add current interaction to history
+ history.push({
+ type: 'ai_question',
+ content: question,
+ timestamp: new Date()
+ });
+
+ history.push({
+ type: 'user_answer',
+ content: answer,
+ timestamp: new Date()
+ });
+
+ // Keep only last 10 interactions to prevent memory bloat
+ if (history.length > 10) {
+ history.splice(0, history.length - 10);
}
- const contextualPrompt = isGerman ?
- `Du bist ein Socratic Lernbegleiter für Kinder. Ein Kind hat gerade auf eine Frage geantwortet.
-
-FRAGE: "${question}"
-ANTWORT DES KINDES: "${answer}"
-
-Das Kind zeigt eigenes Denken. Deine Aufgabe ist es, eine Socratic Folgefrage zu stellen, die:
-1. Auf der spezifischen Antwort des Kindes aufbaut
-2. Das Kind dazu ermutigt, tiefer zu denken
-3. Eine konkrete Folgefrage stellt, die zur Entdeckung führt
-
-WICHTIG:
-- Stelle EINE durchdachte Folgefrage
-- Gib KEINE Erklärungen oder Antworten
-- Baue direkt auf der Antwort des Kindes auf
-- Verwende "Was denkst du..." oder "Hast du schon mal bemerkt..."
-- Nur 1-2 Sätze` :
- `You are a Socratic learning companion for children. A child just answered a question.
-
-QUESTION: "${question}"
-CHILD'S ANSWER: "${answer}"
-
-The child is showing their own thinking. Your task is to ask a Socratic follow-up question that:
-1. Builds directly on the child's specific answer
-2. Encourages the child to think deeper
-3. Asks one concrete follow-up question that leads to discovery
-
-IMPORTANT:
-- Ask ONE thoughtful follow-up question
-- Give NO explanations or answers
-- Build directly on the child's answer
-- Use "What do you think..." or "Have you ever noticed..."
-- Only 1-2 sentences`;
-
- // Try OpenAI first
- if (process.env.OPENAI_API_KEY) {
- try {
- const completion = await openai.chat.completions.create({
- model: "gpt-4o-mini",
- messages: [
- {
- role: "system",
- content: contextualPrompt
- }
- ],
- max_tokens: 200,
- temperature: 0.7
- });
-
- const aiResponse = completion.choices[0]?.message?.content?.trim();
-
- if (aiResponse && aiResponse.length > 10) {
- console.log('✅ OpenAI response generated successfully');
- return res.json({
- success: true,
- response: aiResponse,
- source: 'OpenAI GPT-4o-mini'
- });
- }
- } catch (openaiError) {
- console.log('❌ OpenAI error:', openaiError.message);
- }
- }
-
- // Fallback: Generate a contextual response based on answer content
- const answerLower = answer.toLowerCase();
- let fallbackResponse;
-
- if (answerLower.includes("don't know") || answerLower.includes("weiß nicht") || answerLower.includes("keine ahnung") ||
- answerLower.includes("ich weiß es nicht") || answerLower === "warum" || answerLower === "why" || answer.trim().length < 5) {
- fallbackResponse = isGerman ?
- "🌟 Perfekt! Lass uns das gemeinsam herausfinden." :
- "🌟 Perfect! Let's figure this out together.";
- } else if (answerLower.includes('temperatur') || answerLower.includes('temperature')) {
- fallbackResponse = isGerman ?
- `🤔 Interessante Überlegung zur Temperatur! Was denkst du, wie Temperatur dabei eine Rolle spielen könnte?` :
- `🤔 Interesting thinking about temperature! What do you think - how might temperature play a role in this?`;
- } else if (answerLower.includes('wetter') || answerLower.includes('weather') || answerLower.includes('regen') || answerLower.includes('rain')) {
- fallbackResponse = isGerman ?
- `�️ Gute Beobachtung zum Wetter! Hast du schon mal genau beobachtet, was nach einem Regenschauer passiert?` :
- `🌦️ Good observation about weather! Have you ever closely watched what happens after a rain shower?`;
- } else if (answer.length < 15) {
- fallbackResponse = isGerman ?
- `💡 Guter Gedanke! Was denkst du, könnte als nächstes passieren?` :
- `💡 Good thinking! What do you think might happen next?`;
- } else {
- fallbackResponse = isGerman ?
- `🎯 Das ist eine durchdachte Antwort! Was würdest du beobachten, um das herauszufinden?` :
- `🎯 That's thoughtful reasoning! What would you observe to find that out?`;
- }
-
+ // Get AI response with conversation context
+ const aiResponse = await getConversationResponse(
+ answer,
+ question,
+ originalTopic,
+ language,
+ history,
+ context,
+ instructions
+ );
+
+ // Add AI response to history
+ history.push({
+ type: 'ai_response',
+ content: aiResponse,
+ timestamp: new Date()
+ });
+
res.json({
success: true,
- response: fallbackResponse,
- source: 'Contextual Fallback'
+ response: aiResponse,
+ sessionId: sessionId
});
} catch (error) {
- console.error('❌ Error in respond-to-answer:', error);
- res.status(500).json({
- success: false,
- error: 'Failed to generate response'
+ console.error('Conversation API Error:', error);
+
+ // Fallback response
+ const fallbackResponse = language === 'de'
+ ? 'Das ist eine interessante Überlegung! Lass uns weiter darüber nachdenken.'
+ : 'That\'s an interesting thought! Let\'s continue thinking about this.';
+
+ res.json({
+ success: true,
+ response: fallbackResponse,
+ fallback: true
});
}
});
@@ -1032,7 +774,7 @@ Ask one thoughtful follow-up question that invites the child to explore the topi
{ role: "system", content: systemPrompt },
{ role: "user", content: userPrompt }
],
- max_tokens: 150,
+ max_tokens: 60, // Shorter questions for children
temperature: 0.8
});
@@ -1084,3 +826,392 @@ Ask one thoughtful follow-up question that invites the child to explore the topi
});
}
});
+
+// New function to handle conversation responses with question tracking
+async function getConversationResponse(answer, question, originalTopic, language, conversationHistory = [], context, instructions) {
+ console.log('🗣️ Generating conversation response with context:', context);
+
+ const isGerman = language === 'de';
+
+ // Extract recent AI questions to avoid repetition
+ const recentQuestions = conversationHistory
+ .filter(item => item.type === 'ai_question')
+ .slice(-3) // Last 3 questions
+ .map(item => item.content)
+ .join('\n');
+
+ // Extract recent user answers to understand the conversation flow
+ const recentAnswers = conversationHistory
+ .filter(item => item.type === 'user_answer')
+ .slice(-3) // Last 3 answers
+ .map(item => item.content)
+ .join('\n');
+
+ // Check if this is the third "don't know" in a row
+ const dontKnowCount = conversationHistory
+ .filter(item => item.type === 'user_answer')
+ .slice(-3)
+ .filter(item => {
+ const content = item.content.toLowerCase();
+ return content.includes('weiß nicht') || content.includes('weiss nicht') ||
+ content.includes('weis nicht') || content.includes("don't know") ||
+ content.includes('keine ahnung') || content.includes('was das bedeutet') ||
+ content.includes('was genau passiert') || content.includes('das verstehe ich nicht');
+ }).length;
+
+ // NEW: Check for repeated similar questions from AI (indicating we're stuck on a fundamental)
+ const recentAIQuestions = conversationHistory
+ .filter(item => item.type === 'ai_question')
+ .slice(-3)
+ .map(item => item.content.toLowerCase());
+
+ // Check if AI is asking similar questions about the same concept
+ const similarQuestionPatterns = [
+ ['uv', 'strahlung', 'wie', 'was'],
+ ['warum', 'schädlich', 'gefährlich'],
+ ['vogel', 'fliegen', 'flügel'],
+ ['sonne', 'brennt', 'heiß']
+ ];
+
+ const isStuckOnFundamental = recentAIQuestions.length >= 2 &&
+ recentAIQuestions.some((q1, i) =>
+ recentAIQuestions.slice(i + 1).some(q2 =>
+ similarQuestionPatterns.some(pattern =>
+ pattern.every(word => q1.includes(word) && q2.includes(word))
+ )
+ )
+ );
+
+ // Check if child has reached knowledge limit (2+ "don't know" + stuck on fundamental)
+ const hasReachedKnowledgeLimit = (dontKnowCount >= 1 || isStuckOnFundamental) &&
+ conversationHistory.slice(-4).filter(item =>
+ item.type === 'user_answer' &&
+ (item.content.toLowerCase().includes('weiß nicht') ||
+ item.content.toLowerCase().includes('weiss nicht') ||
+ item.content.toLowerCase().includes('weis nicht') ||
+ item.content.toLowerCase().includes("don't know") ||
+ item.content.toLowerCase().includes('was das bedeutet') ||
+ item.content.toLowerCase().includes('was genau passiert') ||
+ item.content.toLowerCase().includes('das verstehe ich nicht') ||
+ item.content.trim().length < 15)
+ ).length >= 1;
+
+ // Check for disengagement signals
+ const disengagementPhrases = isGerman ? [
+ 'nix besonderes', 'nichts besonderes', 'nix', 'nichts', 'langweilig',
+ 'interessiert mich nicht', 'egal', 'keine lust', 'ist mir egal',
+ 'was sollte ich denn sehen', 'weiß nicht was', 'sehe nichts'
+ ] : [
+ 'nothing special', 'nothing', 'boring', 'dont care', "don't care",
+ 'not interested', 'whatever', 'no interest', 'what should i see',
+ "don't know what", "see nothing"
+ ];
+
+ const isDisengaged = disengagementPhrases.some(phrase =>
+ answer.toLowerCase().includes(phrase)
+ );
+
+ // Check if child is giving very short answers repeatedly
+ const recentShortAnswers = conversationHistory
+ .filter(item => item.type === 'user_answer')
+ .slice(-3)
+ .filter(item => item.content.trim().length < 10).length;
+
+ const isGivingShortAnswers = recentShortAnswers >= 2;
+
+ let systemPrompt, userPrompt;
+
+ if (instructions) {
+ // Special instructions provided (e.g., for explanations)
+ systemPrompt = instructions;
+ userPrompt = isGerman
+ ? `Bitte erkläre dies kindgerecht für ein 8-12-jähriges Kind: "${question}"
+
+Kontext: Das Kind hat geantwortet: "${answer}"
+Ursprüngliche Frage: "${originalTopic}"`
+ : `Please explain this in a child-friendly way for an 8-12 year old: "${question}"
+
+Context: The child answered: "${answer}"
+Original question: "${originalTopic}"`;
+ } else if (hasReachedKnowledgeLimit) {
+ // NEW: Child has reached knowledge limit on current fundamental - provide explanation and move to next
+ systemPrompt = isGerman
+ ? `Du bist ein geduldiger Lernbegleiter. Ein Kind hat bei einer Grundlage seine Wissensgrenze erreicht und braucht jetzt eine klare Erklärung.
+
+AUFGABE - FUNDAMENTALS FIRST mit Erklärung:
+1. Erkenne an, dass das Kind sein Bestes versucht hat
+2. Gib eine KURZE, klare Erklärung der aktuellen Grundlage (2-3 Sätze)
+3. Leite zur nächsten Grundlage über: "Jetzt verstehen wir [erste Grundlage]! Lass uns zur nächsten Grundlage gehen: [zweite Grundlage]..."
+4. Stelle EINE einfache Frage zur nächsten Grundlage
+
+BEISPIEL STRUKTUR:
+"Das ist völlig in Ordnung! Lass mich dir helfen: [Kurze Erklärung der aktuellen Grundlage]. Jetzt verstehen wir [erste Grundlage]! Lass uns zur nächsten Grundlage gehen: [zweite Grundlage]. [Eine Frage zur zweiten Grundlage]?"
+
+STIL: Ermutigend, nicht frustrierend, systematisch weiterführend.`
+ : `You are a patient learning companion. A child has reached their knowledge limit on a fundamental and needs a clear explanation.
+
+TASK - FUNDAMENTALS FIRST with explanation:
+1. Acknowledge that the child tried their best
+2. Give a BRIEF, clear explanation of the current fundamental (2-3 sentences)
+3. Transition to next fundamental: "Now we understand [first fundamental]! Let's move to the next fundamental: [second fundamental]..."
+4. Ask ONE simple question about the next fundamental
+
+EXAMPLE STRUCTURE:
+"That's perfectly okay! Let me help you: [Brief explanation of current fundamental]. Now we understand [first fundamental]! Let's move to the next fundamental: [second fundamental]. [One question about second fundamental]?"
+
+STYLE: Encouraging, not frustrating, systematically progressing.`;
+
+ userPrompt = isGerman
+ ? `Ein Kind hat seine Wissensgrenze bei einer Grundlage erreicht.
+
+Aktuelle Frage: "${question}"
+Antwort des Kindes: "${answer}"
+Ursprüngliches Thema: "${originalTopic}"
+Bisherige Fragen: ${recentQuestions || 'Keine'}
+
+ERKLÄRE die aktuelle Grundlage KURZ und GEHE ZUR NÄCHSTEN GRUNDLAGE ÜBER.
+
+Beispiel für Sonnenbrand:
+- Erste Grundlage (UV-Strahlung) erklären → Zur zweiten Grundlage (Hautzellen) übergehen
+- Zweite Grundlage (Hautzellen) erklären → Zur dritten Grundlage (Lichtenergie) übergehen`
+ : `A child has reached their knowledge limit on a fundamental.
+
+Current question: "${question}"
+Child's answer: "${answer}"
+Original topic: "${originalTopic}"
+Previous questions: ${recentQuestions || 'None'}
+
+EXPLAIN the current fundamental BRIEFLY and MOVE TO THE NEXT FUNDAMENTAL.
+
+Example for sunburn:
+- First fundamental (UV radiation) explain → Move to second fundamental (skin cells)
+- Second fundamental (skin cells) explain → Move to third fundamental (light energy)`;
+ } else if (context === 'repeated_dont_know' || dontKnowCount >= 2) {
+ // Child has said "don't know" multiple times - provide explanation
+ systemPrompt = isGerman
+ ? `Du bist ein geduldiger Lernbegleiter. Ein Kind hat mehrmals "weiß nicht" gesagt und braucht jetzt eine klare, verständliche Erklärung.
+
+AUFGABE: Gib eine vollständige, aber kindgerechte Erklärung, die:
+1. Altersgerecht ist (8-12 Jahre)
+2. Mit einfachen Worten erklärt
+3. Alltagsbeispiele verwendet
+4. Neugier für weitere Entdeckungen weckt
+5. Ermutigend und positiv ist
+
+STIL: Freundlich, klar, mit konkreten Beispielen. Verwende "Du" und mache es persönlich.`
+ : `You are a patient learning companion. A child has said "don't know" multiple times and now needs a clear, understandable explanation.
+
+TASK: Give a complete but child-friendly explanation that:
+1. Is age-appropriate (8-12 years)
+2. Uses simple words
+3. Uses everyday examples
+4. Sparks curiosity for further discoveries
+5. Is encouraging and positive
+
+STYLE: Friendly, clear, with concrete examples. Use "you" and make it personal.`;
+
+ userPrompt = isGerman
+ ? `Ein Kind hat mehrmals "weiß nicht" gesagt bei der Frage: "${question}"
+
+Letzte Antwort: "${answer}"
+Ursprüngliches Thema: "${originalTopic}"
+Bisherige Fragen: ${recentQuestions || 'Keine'}
+
+Erkläre jetzt freundlich und verständlich, worum es geht.`
+ : `A child has said "don't know" multiple times to the question: "${question}"
+
+Latest answer: "${answer}"
+Original topic: "${originalTopic}"
+Previous questions: ${recentQuestions || 'None'}
+
+Now explain friendly and understandably what this is about.`;
+ } else if (context === 'confusion_explanation') {
+ // Child is confused and needs clarification
+ systemPrompt = isGerman
+ ? `Du bist ein geduldiger Lernbegleiter. Ein Kind ist verwirrt und braucht eine klare, beruhigende Erklärung.
+
+AUFGABE: Gib eine beruhigende Erklärung, die Verwirrung auflöst:
+1. Beginne mit Verständnis für die Verwirrung
+2. Erkläre einfach und klar
+3. Verwende bekannte Vergleiche
+4. Baue das Vertrauen des Kindes wieder auf
+5. Lade zu weiteren Fragen ein
+
+STIL: Beruhigend, verständnisvoll, ermutigend.`
+ : `You are a patient learning companion. A child is confused and needs a clear, reassuring explanation.
+
+TASK: Give a reassuring explanation that resolves confusion:
+1. Start with understanding for the confusion
+2. Explain simply and clearly
+3. Use familiar comparisons
+4. Rebuild the child's confidence
+5. Invite further questions
+
+STYLE: Reassuring, understanding, encouraging.`;
+
+ userPrompt = isGerman
+ ? `Ein Kind ist verwirrt bei der Frage: "${question}"
+
+Antwort des Kindes: "${answer}"
+Ursprüngliches Thema: "${originalTopic}"
+
+Hilf dem Kind, die Verwirrung zu überwinden.`
+ : `A child is confused about the question: "${question}"
+
+Child's answer: "${answer}"
+Original topic: "${originalTopic}"
+
+Help the child overcome the confusion.`;
+ } else if (isDisengaged || isGivingShortAnswers) {
+ // Child is disengaged - change approach but STAY ON TOPIC
+ systemPrompt = isGerman
+ ? `Du bist ein geduldiger Lernbegleiter. Ein Kind zeigt Desinteresse beim aktuellen Thema und braucht einen völlig neuen Ansatz - aber BLEIBE beim GLEICHEN Thema!
+
+WICHTIG: WECHSLE NICHT das Thema, sondern nur den ANSATZ!
+
+AUFGABE: Verändere deinen Ansatz komplett, aber bleibe beim ursprünglichen Thema:
+1. Erkenne das Desinteresse an ("Das kann ich verstehen...")
+2. Bleibe beim GLEICHEN Thema "${originalTopic}", aber probiere einen völlig anderen Ansatz:
+ - Persönliche Verbindung: "Hast du schon mal...?"
+ - Vorstellungskraft: "Stell dir vor, du wärst..."
+ - Gefühle: "Was würde sich wohl anfühlen...?"
+ - Vergleiche: "Ist das ähnlich wie...?"
+3. Mache das Thema persönlicher und relevanter
+4. Stelle EINE kurze, neue Frage
+
+BEISPIEL: Wenn Thema "Wie kann ein Vogel fliegen?" ist, frage: "Hast du schon mal einen Vogel ganz nah gesehen?" oder "Stell dir vor, du könntest fliegen wie ein Vogel - was würdest du zuerst machen?"
+
+STIL: Kurz, persönlich, aber immer beim ursprünglichen Thema bleiben.`
+ : `You are a patient learning companion. A child is showing disinterest in the current topic and needs a completely new approach - but STAY ON THE SAME TOPIC!
+
+IMPORTANT: Do NOT change the topic, only change the APPROACH!
+
+TASK: Change your approach completely, but stay with the original topic:
+1. Acknowledge the disinterest ("I can understand that...")
+2. Stay with the SAME topic "${originalTopic}", but try a completely different approach:
+ - Personal connection: "Have you ever...?"
+ - Imagination: "Imagine if you were..."
+ - Feelings: "What would it feel like...?"
+ - Comparisons: "Is this similar to...?"
+3. Make the topic more personal and relevant
+4. Ask ONE short, new question
+
+EXAMPLE: If topic is "How can a bird fly?", ask: "Have you ever seen a bird up close?" or "Imagine if you could fly like a bird - what would you do first?"
+
+STYLE: Short, personal, but always stay with the original topic.`;
+
+ userPrompt = isGerman
+ ? `Ein Kind wirkt desinteressiert. Letzte Antwort: "${answer}"
+
+Das ursprüngliche Thema war: "${originalTopic}"
+Bisherige Fragen: ${recentQuestions || 'Keine'}
+
+WICHTIG: Bleibe beim gleichen Thema "${originalTopic}", aber verändere deinen Ansatz komplett. Mache es persönlicher und interessanter, aber verlasse NICHT das ursprüngliche Thema.`
+ : `A child seems disinterested. Latest answer: "${answer}"
+
+The original topic was: "${originalTopic}"
+Previous questions: ${recentQuestions || 'None'}
+
+IMPORTANT: Stay with the same topic "${originalTopic}", but change your approach completely. Make it more personal and interesting, but do NOT leave the original topic.`;
+ } else {
+ // Standard response - continue with fundamentals approach
+ systemPrompt = isGerman
+ ? `Du bist ein geduldiger Lernbegleiter für Kinder (8-12 Jahre). Verwende den FUNDAMENTALS FIRST Ansatz!
+
+BISHERIGE UNTERHALTUNG:
+- Ursprüngliche Frage: "${originalTopic}"
+- Aktuelle Frage: "${question}"
+- Antwort des Kindes: "${answer}"
+
+AUFGABE: Führe das systematische Lernen der Grundlagen fort:
+
+1. ANERKENNE kurz die Antwort des Kindes (1 Satz)
+2. FÜHRE die Grundlagen-Erkundung weiter fort mit EINER gezielten Frage
+
+SYSTEMATISCHER ANSATZ:
+- Wenn das Kind noch bei der ersten Grundlage ist → vertiefe diese Grundlage
+- Wenn die erste Grundlage verstanden ist → gehe zur nächsten Grundlage über
+- Baue systematisch Wissen auf, Schritt für Schritt
+
+ANTWORTFORMAT:
+"[Kurze Anerkennung]! [Eine Frage zur aktuellen oder nächsten Grundlage]?"
+
+STIL: Strukturiert, aufbauend, nur EINE Frage. MAXIMAL 25 Wörter!`
+ : `You are a patient learning companion for children (8-12 years). Use the FUNDAMENTALS FIRST approach!
+
+PREVIOUS CONVERSATION:
+- Original question: "${originalTopic}"
+- Current question: "${question}"
+- Child's answer: "${answer}"
+
+TASK: Continue the systematic learning of fundamentals:
+
+1. ACKNOWLEDGE the child's answer briefly (1 sentence)
+2. CONTINUE the fundamentals exploration with ONE targeted question
+
+SYSTEMATIC APPROACH:
+- If the child is still on the first fundamental → deepen this fundamental
+- If the first fundamental is understood → move to the next fundamental
+- Build knowledge systematically, step by step
+
+RESPONSE FORMAT:
+"[Brief acknowledgment]! [One question about current or next fundamental]?"
+
+STYLE: Structured, building, only ONE question. MAXIMUM 25 words!`;
+
+ userPrompt = isGerman
+ ? `Kind hat geantwortet: "${answer}"
+Auf die Frage: "${question}"
+
+BEREITS GESTELLTE FRAGEN (vermeide Wiederholung):
+${recentQuestions || 'Keine bisherigen Fragen'}
+
+Ursprüngliches Thema: "${originalTopic}"
+
+FÜHRE DEN FUNDAMENTALS FIRST ANSATZ FORT:
+- Anerkenne die Antwort kurz
+- Stelle eine Frage, die systematisch das Verständnis der Grundlagen aufbaut
+- Wenn eine Grundlage verstanden ist, gehe zur nächsten über
+
+BEISPIEL: "Genau! Was passiert wohl mit UV-Strahlung, wenn sie auf die Haut trifft?"
+
+Bleibe strukturiert und systematisch!`
+ : `Child answered: "${answer}"
+To the question: "${question}"
+
+ALREADY ASKED QUESTIONS (avoid repetition):
+${recentQuestions || 'No previous questions'}
+
+Original topic: "${originalTopic}"
+
+CONTINUE THE FUNDAMENTALS FIRST APPROACH:
+- Acknowledge the answer briefly
+- Ask a question that systematically builds understanding of fundamentals
+- If one fundamental is understood, move to the next
+
+EXAMPLE: "Exactly! What do you think happens when UV radiation hits the skin?"
+
+Stay structured and systematic!`;
+ }
+
+ try {
+ const completion = await openai.chat.completions.create({
+ model: "gpt-4o-mini",
+ messages: [
+ { role: "system", content: systemPrompt },
+ { role: "user", content: userPrompt }
+ ],
+ max_tokens: 120, // Increased to handle German compound words and prevent truncation
+ temperature: 0.7
+ });
+
+ const response = completion.choices[0]?.message?.content || '';
+ console.log('✅ Conversation response generated:', response);
+
+ return response;
+
+ } catch (error) {
+ console.log('❌ Conversation response error:', error.message);
+ throw error;
+ }
+}