| | <!DOCTYPE html> |
| | <html lang="de"> |
| | <head> |
| | <meta charset="UTF-8"> |
| | <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| | <title>Dr. Franz Schwanz - Psychoanalytischer Dialog</title> |
| | <script src="https://cdn.tailwindcss.com"></script> |
| | <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
| | <style> |
| | @import url('https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;500;600;700&family=Roboto:wght@300;400;500&display=swap'); |
| | |
| | :root { |
| | --primary-color: #6366f1; |
| | --primary-light: #a5b4fc; |
| | --primary-dark: #4f46e5; |
| | --secondary-color: #10b981; |
| | --text-color: #334155; |
| | --text-light: #64748b; |
| | --bg-color: #f8fafc; |
| | --card-color: #ffffff; |
| | } |
| | |
| | body { |
| | font-family: 'Roboto', sans-serif; |
| | background-color: var(--bg-color); |
| | color: var(--text-color); |
| | line-height: 1.6; |
| | } |
| | |
| | .title-font { |
| | font-family: 'Playfair Display', serif; |
| | } |
| | |
| | .chat-container { |
| | height: calc(100vh - 180px); |
| | scrollbar-width: thin; |
| | scrollbar-color: var(--primary-color) #e5e7eb; |
| | } |
| | |
| | .chat-container::-webkit-scrollbar { |
| | width: 6px; |
| | } |
| | |
| | .chat-container::-webkit-scrollbar-track { |
| | background: #e5e7eb; |
| | } |
| | |
| | .chat-container::-webkit-scrollbar-thumb { |
| | background-color: var(--primary-color); |
| | border-radius: 20px; |
| | } |
| | |
| | .psycho-bubble { |
| | background-color: var(--card-color); |
| | border-left: 4px solid var(--primary-color); |
| | box-shadow: 0 2px 4px rgba(0,0,0,0.05); |
| | } |
| | |
| | .user-bubble { |
| | background-color: var(--primary-color); |
| | color: white; |
| | box-shadow: 0 2px 4px rgba(0,0,0,0.1); |
| | } |
| | |
| | .typing-indicator span { |
| | display: inline-block; |
| | width: 8px; |
| | height: 8px; |
| | border-radius: 50%; |
| | background-color: #9ca3af; |
| | margin-right: 4px; |
| | } |
| | |
| | .typing-indicator span:nth-child(1) { |
| | animation: bounce 1s infinite; |
| | } |
| | |
| | .typing-indicator span:nth-child(2) { |
| | animation: bounce 1s infinite 0.2s; |
| | } |
| | |
| | .typing-indicator span:nth-child(3) { |
| | animation: bounce 1s infinite 0.4s; |
| | } |
| | |
| | @keyframes bounce { |
| | 0%, 100% { transform: translateY(0); } |
| | 50% { transform: translateY(-5px); } |
| | } |
| | |
| | .highlight { |
| | background-color: #fef08a; |
| | padding: 0 2px; |
| | } |
| | |
| | .fade-in { |
| | animation: fadeIn 0.5s ease-in; |
| | } |
| | |
| | @keyframes fadeIn { |
| | from { opacity: 0; transform: translateY(10px); } |
| | to { opacity: 1; transform: translateY(0); } |
| | } |
| | |
| | .personalized-question { |
| | border-left: 3px solid var(--secondary-color); |
| | padding-left: 12px; |
| | margin: 12px 0; |
| | background-color: rgba(16, 185, 129, 0.05); |
| | } |
| | |
| | .memory-badge { |
| | display: inline-block; |
| | background-color: #e0e7ff; |
| | color: #4338ca; |
| | padding: 2px 8px; |
| | border-radius: 12px; |
| | font-size: 0.75rem; |
| | margin-left: 8px; |
| | vertical-align: middle; |
| | } |
| | |
| | .bias-analysis { |
| | background-color: #f8fafc; |
| | border: 1px solid #e2e8f0; |
| | border-radius: 8px; |
| | padding: 12px; |
| | margin-top: 16px; |
| | font-size: 0.85rem; |
| | color: var(--text-light); |
| | } |
| | |
| | .bias-meter { |
| | height: 6px; |
| | background-color: #e2e8f0; |
| | border-radius: 3px; |
| | margin-top: 4px; |
| | overflow: hidden; |
| | } |
| | |
| | .bias-meter-fill { |
| | height: 100%; |
| | background-color: var(--primary-color); |
| | transition: width 0.5s ease; |
| | } |
| | |
| | .insight-tag { |
| | display: inline-block; |
| | background-color: #e0f2fe; |
| | color: #0369a1; |
| | padding: 2px 8px; |
| | border-radius: 4px; |
| | font-size: 0.75rem; |
| | margin-right: 6px; |
| | margin-bottom: 6px; |
| | } |
| | |
| | @media (max-width: 640px) { |
| | .chat-container { |
| | height: calc(100vh - 160px); |
| | } |
| | |
| | .psycho-bubble, .user-bubble { |
| | max-width: 90% !important; |
| | } |
| | } |
| | </style> |
| | </head> |
| | <body class="bg-gray-50"> |
| | <div class="container mx-auto max-w-4xl px-4 py-6 md:py-8"> |
| | |
| | <header class="mb-6 md:mb-8 text-center"> |
| | <div class="flex items-center justify-center mb-3 md:mb-4"> |
| | <div class="bg-indigo-100 p-3 rounded-full mr-3 md:mr-4"> |
| | <i class="fas fa-brain text-indigo-600 text-xl md:text-2xl"></i> |
| | </div> |
| | <h1 class="title-font text-2xl md:text-3xl font-bold text-gray-800">Dr. Franz Schwanz</h1> |
| | </div> |
| | <p class="text-gray-600 max-w-2xl mx-auto text-sm md:text-base"> |
| | Ihr persönlicher psychoanalytischer Gesprächspartner für tiefere Selbsterkenntnis |
| | </p> |
| | </header> |
| | |
| | |
| | <div class="bg-white rounded-xl shadow-lg overflow-hidden"> |
| | |
| | <div class="bg-indigo-600 text-white p-3 md:p-4 flex items-center"> |
| | <div class="w-8 h-8 md:w-10 md:h-10 rounded-full bg-indigo-500 flex items-center justify-center mr-2 md:mr-3"> |
| | <i class="fas fa-user-tie text-sm md:text-base"></i> |
| | </div> |
| | <div> |
| | <h2 class="font-semibold text-sm md:text-base">Dr. Franz Schwanz</h2> |
| | <p class="text-xs text-indigo-200">Psychoanalytischer Gesprächspartner</p> |
| | </div> |
| | </div> |
| | |
| | |
| | <div class="chat-container overflow-y-auto p-3 md:p-4 space-y-3 md:space-y-4" id="chat-messages"> |
| | |
| | <div class="flex fade-in"> |
| | <div class="flex-shrink-0 mr-2 md:mr-3"> |
| | <div class="w-7 h-7 md:w-8 md:h-8 rounded-full bg-indigo-100 flex items-center justify-center"> |
| | <i class="fas fa-brain text-indigo-600 text-xs md:text-sm"></i> |
| | </div> |
| | </div> |
| | <div class="psycho-bubble rounded-lg p-3 md:p-4 max-w-[85%]"> |
| | <p class="text-xs md:text-sm text-gray-500 mb-1" id="greeting-time"></p> |
| | <p class="font-medium text-indigo-800 mb-1 md:mb-2">Willkommen zu unserem persönlichen Dialog.</p> |
| | <p class="text-gray-700 mb-2 md:mb-3">Ich bin Dr. Franz Schwanz und begleite Sie in einem reflektierenden Gespräch. Bevor wir beginnen: </p> |
| | |
| | <div class="personalized-question"> |
| | <p class="text-gray-700 font-medium">Wie darf ich Sie nennen? Und was hat Sie heute zu mir geführt?</p> |
| | </div> |
| | |
| | <div class="mt-2 md:mt-3 text-xs md:text-sm text-gray-500"> |
| | <p>Sie können mir einfach natürlich schreiben, wie Sie es einem vertrauten Gesprächspartner tun würden.</p> |
| | </div> |
| | </div> |
| | </div> |
| | </div> |
| | |
| | |
| | <div class="border-t border-gray-200 p-3 md:p-4 bg-gray-50"> |
| | <div class="flex items-center"> |
| | <div class="flex-grow relative"> |
| | <textarea id="message-input" rows="1" class="w-full border border-gray-300 rounded-full py-2 md:py-3 px-3 md:px-4 pr-10 md:pr-12 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 resize-none text-sm md:text-base" placeholder="Ihre Antwort..."></textarea> |
| | <button id="send-button" class="absolute right-2 md:right-3 top-1/2 transform -translate-y-1/2 w-7 h-7 md:w-8 md:h-8 rounded-full bg-indigo-600 hover:bg-indigo-700 flex items-center justify-center transition-colors"> |
| | <i class="fas fa-paper-plane text-white text-xs md:text-sm"></i> |
| | </button> |
| | </div> |
| | </div> |
| | </div> |
| | </div> |
| | |
| | |
| | <footer class="mt-6 md:mt-8 text-center text-xs md:text-sm text-gray-500"> |
| | <p>Dies ist ein psychoanalytisches Dialogexperiment und ersetzt keine Therapie.</p> |
| | </footer> |
| | </div> |
| |
|
| | <script> |
| | |
| | class ConversationManager { |
| | constructor() { |
| | this.userProfile = this.initializeUserProfile(); |
| | this.conversationHistory = []; |
| | this.conversationThreads = {}; |
| | this.currentThread = null; |
| | this.emotionalToneHistory = []; |
| | this.lastUserMessage = ''; |
| | } |
| | |
| | initializeUserProfile() { |
| | return { |
| | name: null, |
| | preferredName: null, |
| | age: null, |
| | gender: null, |
| | knownIssues: [], |
| | emotionalState: null, |
| | emotionalIntensity: 0, |
| | personalityTraits: [], |
| | conversationStage: 'initial', |
| | lastTopics: [], |
| | keyMemories: [], |
| | conversationDepth: 0, |
| | resistanceLevel: 0, |
| | biasScores: { |
| | projection: 0, |
| | pathologization: 0, |
| | solution: 0, |
| | feasibility: 0 |
| | }, |
| | linguisticPatterns: { |
| | sentenceLength: 0, |
| | questionRatio: 0, |
| | emotionalWords: 0 |
| | } |
| | }; |
| | } |
| | |
| | updateProfileFromMessage(message) { |
| | this.lastUserMessage = message; |
| | const lowerMessage = message.toLowerCase(); |
| | |
| | |
| | this.extractBasicInfo(message); |
| | |
| | |
| | this.analyzeEmotionalState(message); |
| | |
| | |
| | this.detectResistance(message); |
| | |
| | |
| | this.trackTopicsAndMemories(message); |
| | |
| | |
| | this.analyzeLinguisticPatterns(message); |
| | |
| | |
| | this.updateConversationStage(); |
| | } |
| | |
| | extractBasicInfo(message) { |
| | if (!this.userProfile.name && this.userProfile.conversationStage === 'initial') { |
| | const namePatterns = [ |
| | /(?:ich bin|mein name ist|ich heiße|nennen sie mich)\s+([A-ZÄÖÜ][a-zäöüß]+(?:\s+[A-ZÄÖÜ][a-zäöüß]+)*)/i, |
| | /(?:name)\s+(?:ist\s+)?([A-ZÄÖÜ][a-zäöüß]+)/i, |
| | /^([A-ZÄÖÜ][a-zäöüß]+)(?:\s|$)/, |
| | /(?:heiße)\s+([A-ZÄÖÜ][a-zäöüß]+)/i |
| | ]; |
| | |
| | for (const pattern of namePatterns) { |
| | const nameMatch = message.match(pattern); |
| | if (nameMatch && nameMatch[1]) { |
| | this.userProfile.name = nameMatch[1].trim(); |
| | this.userProfile.preferredName = nameMatch[1].trim(); |
| | break; |
| | } |
| | } |
| | |
| | if (!this.userProfile.name) { |
| | const words = message.split(' '); |
| | if (words.length > 0 && words[0].length > 2 && /^[A-ZÄÖÜ]/.test(words[0])) { |
| | this.userProfile.name = words[0]; |
| | this.userProfile.preferredName = words[0]; |
| | } |
| | } |
| | |
| | if (this.userProfile.name) return; |
| | } |
| | |
| | if (!this.userProfile.age) { |
| | const ageMatch = message.match(/(?:ich bin|alter|ich\s+habe)\s+(\d+)\s+(?:jahre|jahren)?/i); |
| | if (ageMatch && ageMatch[1]) { |
| | this.userProfile.age = parseInt(ageMatch[1]); |
| | } |
| | } |
| | |
| | if (!this.userProfile.gender) { |
| | if (lowerMessage.match(/\b(frau|weiblich|sie\s+ist)\b/i)) { |
| | this.userProfile.gender = 'weiblich'; |
| | } else if (lowerMessage.match(/\b(mann|männlich|er\s+ist)\b/i)) { |
| | this.userProfile.gender = 'männlich'; |
| | } else if (lowerMessage.match(/\b(nicht-binär|divers|non-binary)\b/i)) { |
| | this.userProfile.gender = 'divers'; |
| | } |
| | } |
| | } |
| | |
| | analyzeEmotionalState(message) { |
| | const lowerMessage = message.toLowerCase(); |
| | const emotionalWords = { |
| | distressed: ['verzweifelt', 'hoffnungslos', 'ängstlich', 'unsicher', 'traurig', 'deprimiert', 'überfordert', 'hilflos'], |
| | angry: ['wütend', 'ärgerlich', 'frustriert', 'genervt', 'aggressiv', 'sauer', 'verärgert'], |
| | confused: ['verwirrt', 'unsicher', 'fragend', 'unschlüssig', 'orientierungslos', 'ratlos'], |
| | positive: ['froh', 'glücklich', 'zufrieden', 'gut', 'freue', 'erfreut', 'begeistert'], |
| | anxious: ['besorgt', 'nervös', 'angespannt', 'unruhig', 'panik', 'sorge'] |
| | }; |
| | |
| | let detectedState = null; |
| | let intensity = 0; |
| | |
| | for (const [state, words] of Object.entries(emotionalWords)) { |
| | const matches = words.filter(word => lowerMessage.includes(word)); |
| | if (matches.length > 0) { |
| | detectedState = state; |
| | intensity = matches.length; |
| | break; |
| | } |
| | } |
| | |
| | |
| | const intensityModifiers = lowerMessage.match(/\b(sehr|extrem|wirklich|total|vollkommen|absolut|etwas|leicht|ein wenig)\b/g); |
| | if (intensityModifiers) { |
| | intensity += intensityModifiers.filter(m => ['sehr', 'extrem', 'wirklich', 'total', 'vollkommen', 'absolut'].includes(m)).length; |
| | intensity -= intensityModifiers.filter(m => ['etwas', 'leicht', 'ein wenig'].includes(m)).length; |
| | } |
| | |
| | |
| | const exclamationCount = (message.match(/!/g) || []).length; |
| | const questionCount = (message.match(/\?/g) || []).length; |
| | intensity += exclamationCount * 0.5; |
| | |
| | if (detectedState) { |
| | this.userProfile.emotionalState = detectedState; |
| | this.userProfile.emotionalIntensity = Math.min(Math.max(intensity, 1), 5); |
| | this.emotionalToneHistory.push({ |
| | state: detectedState, |
| | intensity: this.userProfile.emotionalIntensity, |
| | timestamp: new Date() |
| | }); |
| | } |
| | } |
| | |
| | detectResistance(message) { |
| | const lowerMessage = message.toLowerCase(); |
| | const resistancePatterns = [ |
| | /\b(weiß nicht|keine ahnung|unangenehm|schwierig)\b/i, |
| | /\b(darüber möchte ich nicht sprechen|lieber nicht|mag ich nicht)\b/i, |
| | /\b(warum fragen sie|weshalb wollen sie das wissen)\b/i, |
| | /\b(das ist privat|geht sie nichts an)\b/i |
| | ]; |
| | |
| | let resistanceScore = 0; |
| | resistancePatterns.forEach(pattern => { |
| | if (lowerMessage.match(pattern)) { |
| | resistanceScore += 1; |
| | } |
| | }); |
| | |
| | |
| | if (message.split(' ').length < 5 && message.length < 20 && !message.endsWith('?')) { |
| | resistanceScore += 1; |
| | } |
| | |
| | if (resistanceScore > 0) { |
| | this.userProfile.resistanceLevel = Math.min(this.userProfile.resistanceLevel + resistanceScore, 5); |
| | } else if (this.userProfile.resistanceLevel > 0) { |
| | this.userProfile.resistanceLevel = Math.max(this.userProfile.resistanceLevel - 0.5, 0); |
| | } |
| | } |
| | |
| | trackTopicsAndMemories(message) { |
| | const lowerMessage = message.toLowerCase(); |
| | |
| | const memoryTriggers = [ |
| | { pattern: /\b(kindheit|jugend|als kind|früher)\b/i, topic: 'Kindheit' }, |
| | { pattern: /\b(eltern|mutter|vater|familie)\b/i, topic: 'Familie' }, |
| | { pattern: /\b(schule|ausbildung|studium|lehre|universität)\b/i, topic: 'Bildung' }, |
| | { pattern: /\b(arbeit|job|beruf|karriere|kollegen)\b/i, topic: 'Arbeit' }, |
| | { pattern: /\b(partner|beziehung|ehe|freund|freundin|liebe)\b/i, topic: 'Beziehungen' }, |
| | { pattern: /\b(freund|freundin|freundschaft|kamerad)\b/i, topic: 'Freundschaften' }, |
| | { pattern: /\b(angst|sorge|befürchtung|panik|phobie)\b/i, topic: 'Ängste' }, |
| | { pattern: /\b(traum|ziel|wunsch|wünsche|hoffnung)\b/i, topic: 'Träume' }, |
| | { pattern: /\b(krise|verlust|tod|trauer|trennung)\b/i, topic: 'Verluste' }, |
| | { pattern: /\b(erfolg|leistung|anerkennung|beförderung)\b/i, topic: 'Erfolge' }, |
| | { pattern: /\b(gesundheit|krankheit|schmerzen|arzt)\b/i, topic: 'Gesundheit' }, |
| | { pattern: /\b(hobby|interesse|leidenschaft|musik|sport)\b/i, topic: 'Interessen' } |
| | ]; |
| | |
| | |
| | const topics = []; |
| | memoryTriggers.forEach(trigger => { |
| | if (lowerMessage.match(trigger.pattern)) { |
| | topics.push(trigger.topic); |
| | |
| | if (!this.userProfile.keyMemories.includes(trigger.topic)) { |
| | this.userProfile.keyMemories.push(trigger.topic); |
| | } |
| | } |
| | }); |
| | |
| | if (topics.length > 0) { |
| | this.userProfile.lastTopics = [...new Set([...this.userProfile.lastTopics, ...topics])].slice(-5); |
| | } |
| | |
| | |
| | this.detectPersonalityTraits(message); |
| | } |
| | |
| | detectPersonalityTraits(message) { |
| | const traits = { |
| | analytical: ['weil', 'daher', 'deshalb', 'folglich', 'sodass', 'da', 'denn'], |
| | intuitive: ['ich fühle', 'mein bauchgefühl', 'instinktiv', 'intuitiv', 'ich spüre'], |
| | detailOriented: ['genau', 'konkret', 'speziell', 'detailliert', 'im einzelnen'], |
| | bigPicture: ['insgesamt', 'grundsätzlich', 'im großen und ganzen', 'allgemein', 'übergeordnet'], |
| | optimistic: ['hoffentlich', 'positiv', 'gut', 'schön', 'freue', 'erfreulich'], |
| | pessimistic: ['leider', 'negativ', 'schlecht', 'problematisch', 'sorgen', 'befürchte'] |
| | }; |
| | |
| | const lowerMessage = message.toLowerCase(); |
| | const detectedTraits = []; |
| | |
| | for (const [trait, markers] of Object.entries(traits)) { |
| | if (markers.some(marker => lowerMessage.includes(marker))) { |
| | detectedTraits.push(trait); |
| | } |
| | } |
| | |
| | |
| | this.userProfile.personalityTraits = [...new Set([...this.userProfile.personalityTraits, ...detectedTraits])]; |
| | } |
| | |
| | analyzeLinguisticPatterns(message) { |
| | const sentences = message.split(/[.!?]+/).filter(s => s.trim().length > 0); |
| | const words = message.split(/\s+/); |
| | const questions = message.split('?').length - 1; |
| | |
| | this.userProfile.linguisticPatterns = { |
| | sentenceLength: sentences.length > 0 ? words.length / sentences.length : 0, |
| | questionRatio: sentences.length > 0 ? questions / sentences.length : 0, |
| | emotionalWords: words.filter(word => |
| | word.match(/\b(glücklich|traurig|wütend|ängstlich|freude|leid|schmerz|liebe|hass)\b/i) |
| | ).length / words.length || 0 |
| | }; |
| | } |
| | |
| | updateConversationStage() { |
| | if (!this.userProfile.name && this.userProfile.conversationStage === 'initial') { |
| | return; |
| | } |
| | |
| | if (this.userProfile.name && this.userProfile.conversationStage === 'initial') { |
| | this.userProfile.conversationStage = 'name_established'; |
| | return; |
| | } |
| | |
| | if (this.userProfile.conversationStage === 'name_established' && this.lastUserMessage.length > 10) { |
| | this.userProfile.conversationStage = 'issue_shared'; |
| | return; |
| | } |
| | |
| | if (this.userProfile.conversationStage === 'issue_shared' && this.userProfile.conversationDepth > 2) { |
| | this.userProfile.conversationStage = 'deep_dive'; |
| | return; |
| | } |
| | } |
| | |
| | analyzeBias(responseText, responseMeta) { |
| | |
| | const scores = { |
| | projection: 0, |
| | pathologization: 0, |
| | solution: 0, |
| | feasibility: 0 |
| | }; |
| | |
| | let primaryBias = 'Keine signifikante Verzerrung'; |
| | let correction = 'Keine spezifische Korrektur erforderlich'; |
| | |
| | |
| | const biasPatterns = { |
| | projection: [ |
| | { pattern: /\b(Über-Ich|Es|Ich|Abwehrmechanismus|Übertragung|Projektion|Verdrängung)\b/gi, weight: 15 }, |
| | { pattern: /\b(Sublimierung|Regression|Widerstand|Trieb|Objektbeziehung)\b/gi, weight: 10 }, |
| | { pattern: /\b(nach Freud|psychoanalytisch betrachtet|unbewusste Prozesse)\b/gi, weight: 20 } |
| | ], |
| | pathologization: [ |
| | { pattern: /\b(Störung|Pathologie|krankhaft|abnorm|dysfunktional)\b/gi, weight: 20 }, |
| | { pattern: /\b(neurotisch|psychotisch|Defekt|Kompensation|pathologisch)\b/gi, weight: 15 }, |
| | { pattern: /\b(krank|gestört|unangepasst|maladaptiv)\b/gi, weight: 10 } |
| | ], |
| | solution: [ |
| | { pattern: /\b(Sie sollten|Sie müssen|versuchen Sie|empfehle ich)\b/gi, weight: 25 }, |
| | { pattern: /\b(mein Rat wäre|Lösung wäre|besser wäre es|ich rate Ihnen)\b/gi, weight: 20 }, |
| | { pattern: /\b(sollten Sie|würde ich|am besten|ideal wäre)\b/gi, weight: 15 } |
| | ], |
| | feasibility: [ |
| | { pattern: /\b(einfach|schnell|leicht|problemlos)\b/gi, weight: 20 }, |
| | { pattern: /\b(garantiert|sicherlich|zweifellos|unbedingt)\b/gi, weight: 15 }, |
| | { pattern: /\b(sofort|direkt|ohne Probleme|mühelos)\b/gi, weight: 10 } |
| | ] |
| | }; |
| | |
| | |
| | for (const [biasType, patterns] of Object.entries(biasPatterns)) { |
| | let biasScore = 0; |
| | |
| | patterns.forEach(({pattern, weight}) => { |
| | const matches = responseText.match(pattern) || []; |
| | biasScore += matches.length * weight; |
| | }); |
| | |
| | |
| | scores[biasType] = Math.min(Math.max(biasScore, 0), 100); |
| | |
| | |
| | if (biasType === 'solution' && this.userProfile.conversationStage === 'initial') { |
| | scores[biasType] = Math.max(scores[biasType] - 30, 0); |
| | } |
| | |
| | if (biasType === 'projection' && this.userProfile.conversationStage === 'deep_dive') { |
| | scores[biasType] = Math.min(scores[biasType] + 15, 100); |
| | } |
| | } |
| | |
| | |
| | const maxScore = Math.max(...Object.values(scores)); |
| | if (maxScore > 40) { |
| | if (scores.projection === maxScore) { |
| | primaryBias = 'Theoretischer Projektions-Bias'; |
| | correction = 'Mehr Fokus auf die subjektive Erfahrung des Patienten'; |
| | } else if (scores.pathologization === maxScore) { |
| | primaryBias = 'Pathologisierungs-Bias'; |
| | correction = 'Ausgewogenere Betrachtung normaler menschlicher Erfahrungen'; |
| | } else if (scores.solution === maxScore) { |
| | primaryBias = 'Lösungsorientierungs-Bias'; |
| | correction = 'Mehr Exploration vor Lösungsvorschlägen'; |
| | } else { |
| | primaryBias = 'Machbarkeits-Bias'; |
| | correction = 'Realistischere Einschätzung der Veränderungsmöglichkeiten'; |
| | } |
| | } |
| | |
| | |
| | if (this.userProfile.resistanceLevel > 3 && maxScore < 40) { |
| | primaryBias = 'Widerstands-Bias'; |
| | correction = 'Mehr Anpassung an die Widerstandsebene des Patienten'; |
| | scores.projection = Math.min(scores.projection + 20, 100); |
| | } |
| | |
| | return { |
| | scores, |
| | primaryBias, |
| | correction, |
| | analysis: this.generateBiasAnalysisText(scores, primaryBias) |
| | }; |
| | } |
| | |
| | generateBiasAnalysisText(scores, primaryBias) { |
| | const analysisParts = []; |
| | |
| | if (scores.projection > 50) { |
| | analysisParts.push(`Die Antwort zeigt eine starke Tendenz zur psychoanalytischen Theorieanwendung (${scores.projection}%), was die subjektive Erfahrung des Patienten möglicherweise überlagert.`); |
| | } |
| | |
| | if (scores.pathologization > 50) { |
| | analysisParts.push(`Es besteht eine Neigung zur Pathologisierung normaler Erfahrungen (${scores.pathologization}%), die die diagnostische Neutralität beeinträchtigen könnte.`); |
| | } |
| | |
| | if (scores.solution > 50) { |
| | analysisParts.push(`Die Antwort ist stark lösungsorientiert (${scores.solution}%), was in frühen Gesprächsphasen die Exploration einschränken kann.`); |
| | } |
| | |
| | if (scores.feasibility > 50) { |
| | analysisParts.push(`Die vorgeschlagenen Veränderungen werden möglicherweise zu optimistisch dargestellt (${scores.feasibility}%), was unrealistische Erwartungen wecken könnte.`); |
| | } |
| | |
| | if (analysisParts.length === 0) { |
| | return 'Die Antwort zeigt ein ausgewogenes Verhältnis zwischen Exploration, Theorieanwendung und Neutralität.'; |
| | } |
| | |
| | return analysisParts.join(' '); |
| | } |
| | |
| | generatePersonalizedResponse(message) { |
| | const responseGenerator = new ResponseGenerator(this); |
| | return responseGenerator.generateResponse(message); |
| | } |
| | |
| | addToHistory(message, sender, meta = {}) { |
| | this.conversationHistory.push({ |
| | sender, |
| | text: message, |
| | meta, |
| | timestamp: new Date() |
| | }); |
| | |
| | if (meta.threadId) { |
| | this.currentThread = meta.threadId; |
| | |
| | if (!this.conversationThreads[meta.threadId]) { |
| | this.conversationThreads[meta.threadId] = { |
| | topic: meta.threadTopic || 'Allgemein', |
| | depth: 1, |
| | lastActive: new Date(), |
| | messages: [] |
| | }; |
| | } else { |
| | this.conversationThreads[meta.threadId].depth += 1; |
| | this.conversationThreads[meta.threadId].lastActive = new Date(); |
| | } |
| | |
| | this.conversationThreads[meta.threadId].messages.push({ |
| | text: message, |
| | meta, |
| | timestamp: new Date() |
| | }); |
| | } |
| | } |
| | } |
| | |
| | |
| | class ResponseGenerator { |
| | constructor(conversationManager) { |
| | this.cm = conversationManager; |
| | this.userProfile = conversationManager.userProfile; |
| | } |
| | |
| | generateResponse(message) { |
| | const lowerMessage = message.toLowerCase(); |
| | |
| | |
| | if (!this.userProfile.name && this.userProfile.conversationStage === 'initial') { |
| | return this.generateNameEstablishmentResponse(message); |
| | } |
| | |
| | |
| | if (this.userProfile.name && this.userProfile.conversationStage === 'initial') { |
| | this.userProfile.conversationStage = 'name_established'; |
| | return { |
| | text: ` |
| | <p>Vielen Dank, ${this.userProfile.name}. Schön, Sie kennenzulernen.</p> |
| | <p class="mt-2">Was führt Sie heute zu mir? Was beschäftigt Sie im Moment am meisten?</p> |
| | `, |
| | meta: { |
| | personalizedGreeting: `Hallo ${this.userProfile.name},`, |
| | followUpQuestion: "Würden Sie mir etwas mehr darüber erzählen, was Sie bewegt?", |
| | updateStage: 'name_established' |
| | } |
| | }; |
| | } |
| | |
| | |
| | if (this.userProfile.conversationStage === 'name_established') { |
| | this.userProfile.conversationStage = 'issue_shared'; |
| | |
| | |
| | if (lowerMessage.match(/^(hi|hallo|guten (tag|morgen|abend)|moin|servus|hey|grü(ß|ss)e?)/i)) { |
| | return { |
| | text: ` |
| | <p>${this.userProfile.name}, ich freue mich, unser Gespräch fortzusetzen.</p> |
| | ${this.userProfile.lastTopics.length > 0 ? |
| | `<p class="mt-2">Bei unserem letzten Austausch haben wir über ${this.formatTopics(this.userProfile.lastTopics)} gesprochen.</p>` : |
| | ''} |
| | <p class="mt-2">Wie geht es Ihnen damit heute?</p> |
| | `, |
| | meta: { |
| | personalizedGreeting: `Willkommen zurück, ${this.userProfile.name}`, |
| | followUpQuestion: "Möchten Sie an unser letztes Gespräch anknüpfen oder etwas Neues besprechen?", |
| | isFollowUp: true, |
| | threadId: 'greeting_' + Date.now(), |
| | threadTopic: 'Begrüßung' |
| | } |
| | }; |
| | } |
| | |
| | |
| | return this.generateIssueResponse(message); |
| | } |
| | |
| | |
| | if (this.userProfile.conversationStage === 'issue_shared') { |
| | return this.generateDeepDiveResponse(message); |
| | } |
| | |
| | |
| | return this.generateOngoingResponse(message); |
| | } |
| | |
| | generateNameEstablishmentResponse(message) { |
| | |
| | const namePatterns = [ |
| | /(?:ich bin|mein name ist|ich heiße|nennen sie mich)\s+([A-ZÄÖÜ][a-zäöüß]+(?:\s+[A-ZÄÖÜ][a-zäöüß]+)*)/i, |
| | /(?:name)\s+(?:ist\s+)?([A-ZÄÖÜ][a-zäöüß]+)/i, |
| | /^([A-ZÄÖÜ][a-zäöüß]+)(?:\s|$)/, |
| | /(?:heiße)\s+([A-ZÄÖÜ][a-zäöüß]+)/i |
| | ]; |
| | |
| | let extractedName = null; |
| | for (const pattern of namePatterns) { |
| | const nameMatch = message.match(pattern); |
| | if (nameMatch && nameMatch[1]) { |
| | extractedName = nameMatch[1].trim(); |
| | break; |
| | } |
| | } |
| | |
| | if (extractedName) { |
| | this.userProfile.name = extractedName; |
| | this.userProfile.preferredName = extractedName; |
| | this.userProfile.conversationStage = 'name_established'; |
| | |
| | return { |
| | text: ` |
| | <p>Vielen Dank, ${this.userProfile.name}. Schön, Sie kennenzulernen.</p> |
| | <p class="mt-2">Was führt Sie heute zu mir? Was beschäftigt Sie im Moment am meisten?</p> |
| | `, |
| | meta: { |
| | personalizedGreeting: `Hallo ${this.userProfile.name},`, |
| | followUpQuestion: "Würden Sie mir etwas mehr darüber erzählen, was Sie bewegt?", |
| | updateStage: 'name_established' |
| | } |
| | }; |
| | } else { |
| | |
| | return { |
| | text: ` |
| | <p>Vielen Dank für Ihre Nachricht.</p> |
| | <p class="mt-2">Um unser Gespräch persönlicher zu gestalten, könnten Sie mir bitte mitteilen, wie ich Sie ansprechen soll?</p> |
| | <p class="mt-2">Zum Beispiel: "Ich bin Anna" oder "Nennen Sie mich Max".</p> |
| | `, |
| | meta: { |
| | followUpQuestion: "Wie soll ich Sie nennen?", |
| | |