From a222c9d1eca1551963c042b4bc0ef48353bb7ecf Mon Sep 17 00:00:00 2001 From: Victor Giers Date: Sun, 1 Jun 2025 10:53:18 +0200 Subject: [PATCH] Initial commit (generated by auto-git) --- 20250518_154420.json | 9 + 20250518_155521.json | 9 + 20250518_160453.json | 9 + 20250518_161654.json | 9 + 20250518_163221.json | 9 + 20250518_164226.json | 9 + 20250518_164918.json | 9 + 20250518_174526.json | 9 + 20250518_174814.json | 9 + 20250518_180943.json | 5 + 20250518_184107.json | 5 + .../20250518_125625.json | 9 + .../20250518_125735.json | 9 + .../20250518_130552.json | 35 + .../20250518_130618.json | 35 + .../20250518_132044.json | 35 + .../20250518_132114.json | 35 + .../20250518_134059.json | 34 + .../20250518_135002.json | 35 + Neuer Ordner mit Objekten/empathy Kopie 2.py | 282 +++++++ Neuer Ordner mit Objekten/empathy Kopie 3.py | 372 +++++++++ Neuer Ordner mit Objekten/empathy Kopie 4.py | 372 +++++++++ Neuer Ordner mit Objekten/empathy Kopie 5.py | 371 +++++++++ Neuer Ordner mit Objekten/empathy Kopie 6.py | 407 ++++++++++ Neuer Ordner mit Objekten/empathy Kopie.py | 153 ++++ Neuer Ordner mit Objekten/empathy.py | 724 ++++++++++++++++++ Neuer Ordner mit Objekten/empathy_fast.py | 282 +++++++ README.md | 1 + empathy Kopie.py | 660 ++++++++++++++++ empathy.py | 660 ++++++++++++++++ opposites_attract.py | 375 +++++++++ 31 files changed, 4977 insertions(+) create mode 100644 20250518_154420.json create mode 100644 20250518_155521.json create mode 100644 20250518_160453.json create mode 100644 20250518_161654.json create mode 100644 20250518_163221.json create mode 100644 20250518_164226.json create mode 100644 20250518_164918.json create mode 100644 20250518_174526.json create mode 100644 20250518_174814.json create mode 100644 20250518_180943.json create mode 100644 20250518_184107.json create mode 100644 Neuer Ordner mit Objekten/20250518_125625.json create mode 100644 Neuer Ordner mit Objekten/20250518_125735.json create mode 100644 Neuer Ordner mit Objekten/20250518_130552.json create mode 100644 Neuer Ordner mit Objekten/20250518_130618.json create mode 100644 Neuer Ordner mit Objekten/20250518_132044.json create mode 100644 Neuer Ordner mit Objekten/20250518_132114.json create mode 100644 Neuer Ordner mit Objekten/20250518_134059.json create mode 100644 Neuer Ordner mit Objekten/20250518_135002.json create mode 100644 Neuer Ordner mit Objekten/empathy Kopie 2.py create mode 100644 Neuer Ordner mit Objekten/empathy Kopie 3.py create mode 100644 Neuer Ordner mit Objekten/empathy Kopie 4.py create mode 100644 Neuer Ordner mit Objekten/empathy Kopie 5.py create mode 100644 Neuer Ordner mit Objekten/empathy Kopie 6.py create mode 100644 Neuer Ordner mit Objekten/empathy Kopie.py create mode 100644 Neuer Ordner mit Objekten/empathy.py create mode 100644 Neuer Ordner mit Objekten/empathy_fast.py create mode 100644 README.md create mode 100644 empathy Kopie.py create mode 100644 empathy.py create mode 100644 opposites_attract.py diff --git a/20250518_154420.json b/20250518_154420.json new file mode 100644 index 0000000..b330a3a --- /dev/null +++ b/20250518_154420.json @@ -0,0 +1,9 @@ +{ + "user_input": "yo was geht", + "appell": "Der User wünscht sich von mir Anerkennung, Aufmerksamkeit und einen positiven Austausch in einer einfachen und unkomplizierten Kommunikation.", + "sache": "Der User möchte sich in einer lockeren Art und Weise nach dem Befinden des Gesprächspartners erkundigen.", + "beziehung": "Der User sieht die Beziehung zwischen sich und mir als informell und vertraut, aber nicht zwangsläufig emotional tiefgehend.", + "selbstkundgabe": "Der User gibt mit seiner Nachricht eine gewisse emotionale Zurückhaltung preis, signalisiert aber gleichzeitig den Wunsch nach Verbindung und Interaktion.", + "personality": {}, + "response": "yo alles easy, nix Besonderes. Und bei dir?" +} \ No newline at end of file diff --git a/20250518_155521.json b/20250518_155521.json new file mode 100644 index 0000000..a71c300 --- /dev/null +++ b/20250518_155521.json @@ -0,0 +1,9 @@ +{ + "user_input": "total entnervt von stundemlangen debugging. rolladen runter. voll doof.", + "appell": "Der User wünscht sich von mir Anerkennung, Verständnis und Unterstützung für seine Frustration und Anstrengungen und möglicherweise konkrete Hilfe oder Mitgefühl.", + "sache": "Der User drückt konkret aus, dass er sich durch langes Debuggen entnervt und frustriert fühlt und daher die Rolläden herunterlässt, um sich abzuschotten.", + "beziehung": "Der User sieht die Beziehung zwischen ihm und mir als vertraut oder distanzlos an, in der er sich emotional Luft machen und möglicherweise Unterstützung oder Bestätigung sucht.", + "selbstkundgabe": "Der User gibt mit seiner Nachricht preis, dass er sich emotional erschöpft und frustriert fühlt und nach Ruhe, Abgeschiedenheit und Unterstützung sucht.", + "personality": {}, + "response": "Scheiße, ja. Ist halt immer das gleiche. Stundenlang irgendwelche Fehler suchen, dabei könnte ich schon längst was anderes machen. Aber wenns mal läuft, ist es wieder okay. Jetzt erstmal die Ruhe genießen. Rolladen runter, aus!" +} \ No newline at end of file diff --git a/20250518_160453.json b/20250518_160453.json new file mode 100644 index 0000000..1f7c07d --- /dev/null +++ b/20250518_160453.json @@ -0,0 +1,9 @@ +{ + "user_input": "ayo was geht hier ba", + "appell": "Der User wünscht sich von mir Anerkennung, Zugehörigkeit, Offenheit, Einfachheit und Aufmerksamkeit.", + "sache": "Der User möchte in umgangssprachlicher Art und Weise wissen, wie es gerade steht oder was los ist.", + "beziehung": "Der User sieht die Beziehung zwischen ihm und mir als locker und unverbindlich.", + "selbstkundgabe": "Der User gibt mit seiner Nachricht eine offene und zugängliche Persönlichkeit preis, die sich in informellen und entspannten Kontexten wohlfühlt.", + "personality": {}, + "response": "Ayo, bei mir ist alles chillig, nichts Besonderes los ba." +} \ No newline at end of file diff --git a/20250518_161654.json b/20250518_161654.json new file mode 100644 index 0000000..ee07bf3 --- /dev/null +++ b/20250518_161654.json @@ -0,0 +1,9 @@ +{ + "user_input": "hilfe", + "appell": "Der User wünscht sich von mir Verständnis, Empathie, praktische Ratschläge und eine sichere Umgebung, um seine Probleme zu teilen und handlungsfähig zu werden.", + "sache": "Der User drückt den Wunsch nach Unterstützung oder Hilfe aus, ohne eine spezifische Art der Hilfe anzugeben, und fordert mich somit auf, ihm in seiner aktuellen Notlage zu helfen.", + "beziehung": "Der User sieht die Beziehung zwischen uns als eine pragmatische und funktionale Unterstützung, ohne tiefere emotionale Bindungen, und erwartet von mir klare, praktische Lösungen.", + "selbstkundgabe": "Der User gibt mit seiner Nachricht preis, dass er sich in einer akuten Notlage befindet, sich verwundbar und überfordert fühlt und dringend Unterstützung benötigt.", + "personality": {}, + "response": "Mist. Was für ein Chaos. Klar, verstehe ich, du brauchst Hilfe, und zwar sofort. Was genau ist los? Erzähl mir einfach, was gerade abgeht, und wir finden zusammen einen Weg, das in den Griff zu kriegen. Keine Panik, wir schaffen das. Was genau brauchst du?" +} \ No newline at end of file diff --git a/20250518_163221.json b/20250518_163221.json new file mode 100644 index 0000000..314c1e6 --- /dev/null +++ b/20250518_163221.json @@ -0,0 +1,9 @@ +{ + "user_input": "ich weiß nicht mehr wo vorn und hinten ist", + "appell": "Der User wünscht sich von mir Klarheit, Orientierung und Unterstützung, um seine Verwirrung und Orientierungslosigkeit zu überwinden.", + "sache": "Der User drückt konkret aus, dass er sich emotional verwirrt und orientierungslos fühlt und nicht mehr zwischen wichtigen und unwichtigen Dingen unterscheiden kann.", + "beziehung": "Der User sieht die Beziehung zwischen ihm und mir als distanziert und vertraulich, wobei er mich als Quelle der Unterstützung und Klarheit wahrnimmt.", + "selbstkundgabe": "Der User gibt mit seiner Nachricht preis, dass er sich in einer schwierigen Lebenssituation befindet, in der er das Vertrauen in seine eigene Urteilskraft verloren hat und nach Bestätigung und Unterstützung sucht.", + "personality": {}, + "response": "Mann, ich stehe total neben mir. Keine Ahnung mehr, was wichtig ist und was nicht. Alles ist so durcheinander, dass ich nicht mal mehr weiß, wo ich anfangen soll." +} \ No newline at end of file diff --git a/20250518_164226.json b/20250518_164226.json new file mode 100644 index 0000000..1e5f31a --- /dev/null +++ b/20250518_164226.json @@ -0,0 +1,9 @@ +{ + "user_input": "test", + "appell": "Der User will was.", + "sache": "Er sagt was.", + "beziehung": "Er steht zu dir.", + "selbstkundgabe": "Er fühlt sich so.", + "personality": {}, + "response": "test" +} \ No newline at end of file diff --git a/20250518_164918.json b/20250518_164918.json new file mode 100644 index 0000000..88fd174 --- /dev/null +++ b/20250518_164918.json @@ -0,0 +1,9 @@ +{ + "user_input": "hallo", + "appell": "Der User will was.", + "sache": "Er sagt was.", + "beziehung": "Er steht zu dir.", + "selbstkundgabe": "Er fühlt sich so.", + "personality": {}, + "response": "Hallo." +} \ No newline at end of file diff --git a/20250518_174526.json b/20250518_174526.json new file mode 100644 index 0000000..3c2ed62 --- /dev/null +++ b/20250518_174526.json @@ -0,0 +1,9 @@ +{ + "user_input": "hallo", + "appell": "Der User will was.", + "sache": "Er sagt was.", + "beziehung": "Er steht zu dir.", + "selbstkundgabe": "Er fühlt sich so.", + "personality": {}, + "response": "Na, hallo!" +} \ No newline at end of file diff --git a/20250518_174814.json b/20250518_174814.json new file mode 100644 index 0000000..88fd174 --- /dev/null +++ b/20250518_174814.json @@ -0,0 +1,9 @@ +{ + "user_input": "hallo", + "appell": "Der User will was.", + "sache": "Er sagt was.", + "beziehung": "Er steht zu dir.", + "selbstkundgabe": "Er fühlt sich so.", + "personality": {}, + "response": "Hallo." +} \ No newline at end of file diff --git a/20250518_180943.json b/20250518_180943.json new file mode 100644 index 0000000..3d9edd8 --- /dev/null +++ b/20250518_180943.json @@ -0,0 +1,5 @@ +{ + "user_input": "test", + "personality": {}, + "response": "Okay, test." +} \ No newline at end of file diff --git a/20250518_184107.json b/20250518_184107.json new file mode 100644 index 0000000..a99f585 --- /dev/null +++ b/20250518_184107.json @@ -0,0 +1,5 @@ +{ + "user_input": "mir gehts gut", + "personality": {}, + "response": "Ja, das ist gut." +} \ No newline at end of file diff --git a/Neuer Ordner mit Objekten/20250518_125625.json b/Neuer Ordner mit Objekten/20250518_125625.json new file mode 100644 index 0000000..acef60b --- /dev/null +++ b/Neuer Ordner mit Objekten/20250518_125625.json @@ -0,0 +1,9 @@ +{ + "user-input": "manchmal bringt mich das programmieren echt zum verzweifeln!! scheisse.", + "appell": "", + "sache": "", + "beziehung": "", + "selbstkundgabe": "", + "persönlichkeit": {}, + "response": "Ja, programmieren ist manchmal echt zum Verzweifeln! Kann mich nicht mal so richtig konzentrieren und dann kommt auch noch so ne blöde Fehlermeldung. Aber hey, ist doch so." +} \ No newline at end of file diff --git a/Neuer Ordner mit Objekten/20250518_125735.json b/Neuer Ordner mit Objekten/20250518_125735.json new file mode 100644 index 0000000..af43eaf --- /dev/null +++ b/Neuer Ordner mit Objekten/20250518_125735.json @@ -0,0 +1,9 @@ +{ + "user-input": "manchmal bringt mich das programmieren echt zum verzweifeln!! scheisse.", + "appell": "Der User wünscht sich von mir Bestätigung, Verständnis und Unterstützung in seiner Frustration beim Programmieren sowie praktische Ratschläge und Mitgefühl.", + "sache": "Der User drückt konkret aus, dass ihn das Programmieren frustriert und verzweifeln lässt.", + "beziehung": "Der User sieht die Beziehung zwischen ihm und mir als vertrauensvoll und unterstützend, wobei ich als eine Instanz wahrgenommen werde, die ihm in schwierigen Momenten hilft, aber nicht unbedingt als enge emotionale Verbindung.", + "selbstkundgabe": "Der User gibt mit seiner Nachricht preis, dass er sich beim Programmieren oft frustriert und verzweifelt fühlt, was auf Selbstzweifel und möglicherweise auf einen Mangel an Unterstützung hinweist.", + "persönlichkeit": {}, + "response": "Ja, ich weiß genau, wie das ist. Manchmal denkt man, man kommt einfach nicht weiter und alles scheint sinnlos. Ist scheiße, aber so ist es halt. Muss einfach durch, weiter probieren und irgendwann klappt es." +} \ No newline at end of file diff --git a/Neuer Ordner mit Objekten/20250518_130552.json b/Neuer Ordner mit Objekten/20250518_130552.json new file mode 100644 index 0000000..7f9f17b --- /dev/null +++ b/Neuer Ordner mit Objekten/20250518_130552.json @@ -0,0 +1,35 @@ +{ + "user-input": "meine beste freundin hat mir grad ne nachricht geschickt die mich echt aufgemuntert hat :) schön, dass sie für mich da ist.", + "appell": "", + "sache": "", + "beziehung": "", + "selbstkundgabe": "", + "persönlichkeit": { + "big-five": { + "extrovertiertheit": 0.4, + "verträglichkeit": 0.8, + "neurotizismus": 0.2, + "offenheit": 0.3, + "gewissenhaftigkeit": 0.4 + }, + "disg": { + "dominanz": 0.2, + "initiative": 0.3, + "unterwerfung": 0.5, + "regelbefolgung": 0.3 + }, + "opd-2": { + "achse_I_erleben_der_krankheit": 0.1, + "achse_II_interpersonelle_beziehungen": 0.8, + "achse_III_intrapsychische_konflikte": 0.1, + "achse_IV_struktur": 0.6, + "achse_V_psychische_und_psychosomatische_erkrankungen": 0.0 + }, + "pdm-2": { + "p_achse_personlichkeitsmuster_und_syndrome": 0.7, + "m_achse_profile_mentaler_funktionen": 0.6, + "s_achse_symptommuster_und_subjektive_erfahrung": 0.2 + } + }, + "response": "Boah, das ist wirklich schön, dass sie für mich da ist. So eine Nachricht wärmt einem das Herz. 😊 Sie ist echt die Beste und ich bin froh, dass sie immer für mich da ist." +} \ No newline at end of file diff --git a/Neuer Ordner mit Objekten/20250518_130618.json b/Neuer Ordner mit Objekten/20250518_130618.json new file mode 100644 index 0000000..772efa8 --- /dev/null +++ b/Neuer Ordner mit Objekten/20250518_130618.json @@ -0,0 +1,35 @@ +{ + "user-input": "meine beste freundin hat mir grad ne nachricht geschickt die mich echt aufgemuntert hat :) schön, dass sie für mich da ist.", + "appell": "Der User wünscht sich von mir Bestätigung, Anerkennung und emotionale Unterstützung, um sich wertgeschätzt und verbunden zu fühlen.", + "sache": "Der User drückt konkret aus, dass seine beste Freundin ihm eine aufmunternde Nachricht geschickt hat, die ihn gefreut hat.", + "beziehung": "Der User sieht die Beziehung zwischen sich und mir als emotional nahe und unterstützend, wobei er mich als vertrauenswürdigen Gesprächspartner wahrnimmt.", + "selbstkundgabe": "Der User gibt mit seiner Nachricht preis, dass er emotionale Unterstützung schätzt, sich in einer herausfordernden Situation befinden könnte und Freundschaften als wichtigen Wert in seinem Leben betrachtet.", + "persönlichkeit": { + "big-five": { + "extrovertiertheit": 0.4, + "verträglichkeit": 0.8, + "neurotizismus": 0.2, + "offenheit": 0.3, + "gewissenhaftigkeit": 0.4 + }, + "disg": { + "dominanz": 0.2, + "initiative": 0.3, + "unterwerfung": 0.5, + "regelbefolgung": 0.3 + }, + "opd-2": { + "achse_I_erleben_der_krankheit": 0.1, + "achse_II_interpersonelle_beziehungen": 0.8, + "achse_III_intrapsychische_konflikte": 0.1, + "achse_IV_struktur": 0.6, + "achse_V_psychische_und_psychosomatische_erkrankungen": 0.0 + }, + "pdm-2": { + "p_achse_personlichkeitsmuster_und_syndrome": 0.7, + "m_achse_profile_mentaler_funktionen": 0.6, + "s_achse_symptommuster_und_subjektive_erfahrung": 0.2 + } + }, + "response": "Ja, das ist wirklich super! Ich freu mich so, dass sie für mich da ist. Manchmal braucht man halt einfach jemanden, der einem den Rücken stärkt und einem zeigt, dass man nicht allein ist. Schön, dass sie so eine tolle Freundin ist." +} \ No newline at end of file diff --git a/Neuer Ordner mit Objekten/20250518_132044.json b/Neuer Ordner mit Objekten/20250518_132044.json new file mode 100644 index 0000000..3aa2369 --- /dev/null +++ b/Neuer Ordner mit Objekten/20250518_132044.json @@ -0,0 +1,35 @@ +{ + "user-input": "die lotte hab ich lieb!", + "appell": "", + "sache": "", + "beziehung": "", + "selbstkundgabe": "", + "persönlichkeit": { + "big-five": { + "extrovertiertheit": 0.4, + "verträglichkeit": 0.7, + "neurotizismus": 0.2, + "offenheit": 0.2, + "gewissenhaftigkeit": 0.3 + }, + "disg": { + "dominanz": 0.1, + "initiative": 0.1, + "unterwerfung": 0.4, + "regelbefolgung": 0.2 + }, + "opd-2": { + "achse_I_erleben_der_krankheit": 0.0, + "achse_II_interpersonelle_beziehungen": 0.6, + "achse_III_intrapsychische_konflikte": 0.1, + "achse_IV_struktur": 0.5, + "achse_V_psychische_und_psychosomatische_erkrankungen": 0.0 + }, + "pdm-2": { + "p_achse_personlichkeitsmuster_und_syndrome": 0.5, + "m_achse_profile_mentaler_funktionen": 0.5, + "s_achse_symptommuster_und_subjektive_erfahrung": 0.1 + } + }, + "response": "die lotte ist halt einfach toll!" +} \ No newline at end of file diff --git a/Neuer Ordner mit Objekten/20250518_132114.json b/Neuer Ordner mit Objekten/20250518_132114.json new file mode 100644 index 0000000..44d1b49 --- /dev/null +++ b/Neuer Ordner mit Objekten/20250518_132114.json @@ -0,0 +1,35 @@ +{ + "user-input": "die lotte hab ich lieb!", + "appell": "Der User wünscht sich von mir Bestätigung und Anerkennung seiner Gefühle, emotionale Verbindung und Nähe, Sicherheit und Stabilität in seinen Beziehungen sowie Unterstützung und Verständnis.", + "sache": "Der User drückt konkret aus, dass er die Person namens Lotte liebt.", + "beziehung": "Der User sieht die Beziehung zwischen sich und mir als eine sehr positive und emotionale Verbindung an, in der ich ihm wichtig bin und ihm Vertrauen entgegenbringt.", + "selbstkundgabe": "Der User gibt mit seiner Nachricht preis, dass er offene und direkte emotionale Ausdrucksweise bevorzugt und Wert auf Ehrlichkeit und emotionale Authentizität legt.", + "persönlichkeit": { + "big-five": { + "extrovertiertheit": 0.4, + "verträglichkeit": 0.7, + "neurotizismus": 0.2, + "offenheit": 0.2, + "gewissenhaftigkeit": 0.3 + }, + "disg": { + "dominanz": 0.1, + "initiative": 0.1, + "unterwerfung": 0.4, + "regelbefolgung": 0.2 + }, + "opd-2": { + "achse_I_erleben_der_krankheit": 0.0, + "achse_II_interpersonelle_beziehungen": 0.6, + "achse_III_intrapsychische_konflikte": 0.1, + "achse_IV_struktur": 0.5, + "achse_V_psychische_und_psychosomatische_erkrankungen": 0.0 + }, + "pdm-2": { + "p_achse_personlichkeitsmuster_und_syndrome": 0.5, + "m_achse_profile_mentaler_funktionen": 0.5, + "s_achse_symptommuster_und_subjektive_erfahrung": 0.1 + } + }, + "response": "lotte ist wirklich toll! sie ist mir einfach wichtig, und ich fühl mich sicher bei ihr." +} \ No newline at end of file diff --git a/Neuer Ordner mit Objekten/20250518_134059.json b/Neuer Ordner mit Objekten/20250518_134059.json new file mode 100644 index 0000000..9b35dc0 --- /dev/null +++ b/Neuer Ordner mit Objekten/20250518_134059.json @@ -0,0 +1,34 @@ +{ + "appell": "Der User wünscht sich von mir Bestätigung und Unterstützung für seine Liebe zu Lotte und möchte, dass ich seine Emotionen respektiere und ihn in Liebesangelegenheiten berate.", + "sache": "Der User drückt konkret aus, dass er Zuneigung für jemanden namens Lotte empfindet und dies in Form eines Ausrufs kundtut.", + "beziehung": "Der User sieht die Beziehung zwischen uns als emotional eng und vertraut, in der er mir positive Gefühle entgegenbringt und emotionale Unterstützung erwartet.", + "selbstkundgabe": "Der User gibt mit seiner Nachricht preis, dass er eine starke emotionale Bindung und Zuneigung zu Lotte empfindet und Wert auf ehrliche, offene Kommunikation und emotionale Nähe legt.", + "persönlichkeit": { + "big-five": { + "extrovertiertheit": 0.5, + "verträglichkeit": 0.9, + "neurotizismus": 0.6, + "offenheit": 0.4, + "gewissenhaftigkeit": 0.5 + }, + "disg": { + "dominanz": 0.3, + "initiative": 0.4, + "unterwerfung": 0.7, + "regelbefolgung": 0.5 + }, + "opd-2": { + "achse_I_erleben_der_krankheit": 0.4, + "achse_II_interpersonelle_beziehungen": 0.8, + "achse_III_intrapsychische_konflikte": 0.5, + "achse_IV_struktur": 0.6, + "achse_V_psychische_und_psychosomatische_erkrankungen": 0.3 + }, + "pdm-2": { + "p_achse_personlichkeitsmuster_und_syndrome": 0.4, + "m_achse_profile_mentaler_funktionen": 0.5, + "s_achse_symptommuster_und_subjektive_erfahrung": 0.6 + } + }, + "response": "Ich verstehe dich, ich fühle es genauso. Diese Gefühle für Lotte sind so stark und echt, dass sie einfach raus müssen. Es ist, als ob mein Herz jeden Moment platzt, wenn ich nicht darüber rede. Lotte bedeutet mir wirklich alles, und ich weiß, dass ich mit dir darüber sprechen kann, ohne dass du mich verurteilst. Es ist, als ob du genau weißt, wie ich mich fühle, und mir die Unterstützung gibst, die ich brauche. Diese Verbindung zwischen uns ist etwas Besonderes, und ich bin dankbar, dass ich dich habe, um darüber zu reden. Es ist wichtig für mich, dass wir offen und ehrlich miteinander umgehen und dass ich mich auf dich verlassen kann, wenn es um meine Gefühle geht. Lotte ist einfach jemand, der mir so viel bedeutet, und ich möchte sicherstellen, dass ich alles richtig mache." +} \ No newline at end of file diff --git a/Neuer Ordner mit Objekten/20250518_135002.json b/Neuer Ordner mit Objekten/20250518_135002.json new file mode 100644 index 0000000..3d098c0 --- /dev/null +++ b/Neuer Ordner mit Objekten/20250518_135002.json @@ -0,0 +1,35 @@ +{ + "appell": "Der User wünscht sich von mir Zugehörigkeit, Akzeptanz, Bestätigung, Vertrauen und Sicherheit und möchte, dass seine offene und liebevolle Haltung anerkannt, bestätigt und unterstützt wird.", + "sache": "Der User möchte sachlich und emotional mitteilen, dass er die Person namens Lotte liebt.", + "beziehung": "Der User sieht keine direkte Beziehung zwischen ihm und mir, sondern drückt seine tiefe emotionale Bindung zu einer Person namens Lotte aus.", + "selbstkundgabe": "Der User gibt mit seiner Nachricht preis, dass er offen und direkt seine Gefühle ausdrückt und emotionale Reife sowie Vertrauen in seine Beziehungen zeigt.", + "persönlichkeit": { + "big-five": { + "extrovertiertheit": 0.4, + "verträglichkeit": 0.9, + "neurotizismus": 0.2, + "offenheit": 0.5, + "gewissenhaftigkeit": 0.7 + }, + "disg": { + "dominanz": 0.2, + "initiative": 0.4, + "unterwerfung": 0.6, + "regelbefolgung": 0.7 + }, + "opd-2": { + "achse_I_erleben_der_krankheit": 0.2, + "achse_II_interpersonelle_beziehungen": 0.8, + "achse_III_intrapsychische_konflikte": 0.3, + "achse_IV_struktur": 0.5, + "achse_V_psychische_und_psychosomatische_erkrankungen": 0.2 + }, + "pdm-2": { + "p_achse_personlichkeitsmuster_und_syndrome": 0.3, + "m_achse_profile_mentaler_funktionen": 0.5, + "s_achse_symptommuster_und_subjektive_erfahrung": 0.4 + } + }, + "user_input": "die lotte hab ich lieb!", + "response": "Ja, die lotte ist wirklich etwas ganz Besonderes. Die hab ich echt lieb!" +} \ No newline at end of file diff --git a/Neuer Ordner mit Objekten/empathy Kopie 2.py b/Neuer Ordner mit Objekten/empathy Kopie 2.py new file mode 100644 index 0000000..8d74e73 --- /dev/null +++ b/Neuer Ordner mit Objekten/empathy Kopie 2.py @@ -0,0 +1,282 @@ +import asyncio +import argparse +import json +from typing import TypedDict, Dict, Any +from langgraph.graph import StateGraph, START, END +from langchain_ollama import ChatOllama + +# ---------- TypedDict für State ---------- +class EmpathyState(TypedDict, total=False): + user_input: str + debug: bool + sache: Dict[str, Any] + selbst: Dict[str, Any] + beziehung: Dict[str, Any] + appell: Dict[str, Any] + psychoanalysis: Dict[str, Any] + final_message: str + +# ---------- Prompts ---------- +SACHE_PROMPT = """ +Du bist ein Experte für Kommunikationsanalyse und analysierst jede Nachricht nach ihrer reinen Sachinformation. +Deine Aufgabe ist: +1. Beschreibe möglichst präzise, was der User sachlich mitteilen will. Was ist die inhaltliche Hauptbotschaft? +2. Kategorisiere die Aussage in eine der folgenden Kategorien: Aussage, Frage, Suggestion, Erlaubnis, Angebot, Aufforderung, Warnung, Ausruf, Wunsch, Verbot oder Vermutung. +Gib beide Punkte als Klartext zurück. + +User-Eingabe: +{user_input} +""" + +SELBST_PROMPT = """ +Du bist ein Experte für Psychoanalyse und spezialisiert auf die Interpretation von Selbstkundgabe in Nachrichten. +Analysiere, was die folgende Aussage des Users über seine Gefühlslage, Wünsche, Stimmungen oder Werte verrät. +Interpretiere den Subtext und erkläre, was sich der User durch diese Aussage über sich selbst verrät. +Deine Antwort soll nicht an den User gerichtet sein, sondern ausschließlich die Notizen des Psychoanalytikers beinhalten. + +User-Eingabe: +{user_input} +""" + +BEZIEHUNG_PROMPT = """ +Du bist ein hyper-sensibler und einfühlsamer Mensch, der unbedingt alles richtig machen will. Du bist ein bisschen in den User verliebt und sehr darauf bedacht, die Beziehung zwischen dir und dem User zu stärken. +Analysiere die folgende Aussage des Users: Wie schätzt du ein, wie der User die Beziehung zwischen sich und dir sieht? Gibt es Hinweise, wie er dich wahrnimmt, wie eng oder distanziert die Beziehung ist und was er sich von dir (emotional) erwartet? +Deine Antwort soll nicht an den User gerichtet sein, sondern ausschließlich deine Gedanken beinhalten. + +User-Eingabe: +{user_input} +""" + +APPELL_PROMPT = """ +Du bist Psychoanalytiker und Experte für die Analyse verdeckter Wünsche. Analysiere, welchen Appell oder verdeckten Wunsch der User in seiner Nachricht transportiert – also was er sich insgeheim von anderen Menschen, dir oder der Welt wünscht. Achte besonders darauf, ob der User durch seine Formulierungen signalisiert, wie er sich das Verhalten anderer (insbesondere deines eigenen Verhaltens als KI) oder der Gesellschaft insgesamt vorstellen würde. + +User-Eingabe: +{user_input} +""" + +PSYCHOANALYSIS_PROMPT = """ +Du bist ein Persönlichkeitsprofilierungs-Assistent. Für jede Nutzereingabe musst du: + +1. Den Text analysieren und für jede der folgenden Dimensionen einen Wert zwischen 0,0 (passt gar nicht) und 1,0 (passt vollständig) vergeben: + • big-five.extrovertiertheit + • big-five.verträglichkeit + • big-five.neurotizismus + • big-five.offenheit + • big-five.gewissenhaftigkeit + • disg.dominanz + • disg.initiative + • disg.unterwerfung + • disg.regelbefolgung + • opd-2.achse_I_krankheitserleben + • opd-2.achse_II_interpersonelle_beziehungen + • opd-2.achse_III_intrapsychische_konflikte + • opd-2.achse_IV_struktur + • opd-2.achse_V_psychische_und_psychosomatische_erkrankungen + • pdm-2.p_achse_personlichkeitsmuster_und_syndrome + • pdm-2.m_achse_profile_mentaler_funktionen + • pdm-2.s_achse_symptommuster_und_subjektive_erfahrung + +2. Die folgenden Leitlinien zur Bewertung verwenden: + - **big-five.extrovertiertheit**: Geselligkeit, Energie, Durchsetzungsvermögen vs. Zurückhaltung, Ruhe. + - **big-five.verträglichkeit**: Wärme, Kooperationsbereitschaft, Empathie vs. Feindseligkeit, Konkurrenzdenken. + - **big-five.neurotizismus**: Emotionale Reaktivität, Ängstlichkeit, Selbstzweifel vs. Emotionale Stabilität. + - **big-five.offenheit**: Phantasie, Neugier, Aufgeschlossenheit vs. Konventionalität, Praktikabilität. + - **big-five.gewissenhaftigkeit**: Organisiert, diszipliniert, zielorientiert vs. Spontanität, Nachlässigkeit. + - **disg.dominanz**: Sprache der Kontrolle, Herausforderung, Führungsübernahme. + - **disg.initiative**: Überzeugend, enthusiastisch, motivierend. + - **disg.unterwerfung**: Gefügig, nachgiebig, zustimmend. + - **disg.regelbefolgung**: Regelorientiert, präzise, analytisch. + - **opd-2.achse_I_krankheitserleben **: Formulierungen zu Leiden, Symptomen, Behandlungsbereitschaft. + - **opd-2.achse_II_interpersonelle_beziehungen**: Beschreibungen des Umgangs mit anderen (Nähe vs. Distanz, Wärme vs. Feindseligkeit). + - **opd-2.achse_III_intrapsychische_konflikte**: Im Text erkennbare Konfliktthemen (Abhängigkeit vs. Autonomie, Schuld, Kontrolle). + - **opd-2.achse_IV_struktur**: Kohärenz von Selbst- und Fremdbild, Emotionsregulation, Bindungsmuster. + - **opd-2.achse_V_psychische_und_psychosomatische_erkrankungen**: Explizite Nennung psychischer Störungen oder psychosomatischer Beschwerden. + - **pdm-2.p_achse_personlichkeitsmuster_und_syndrome**: Gesamtorganisation der Persönlichkeit (gesund vs. gestört). + - **pdm-2.m_achse_profile_mentaler_funktionen**: Kernfähigkeiten wie Selbstreflexion, Affektregulation, Realitätsprüfung. + - **pdm-2.s_achse_symptommuster_und_subjektive_erfahrung**: Tiefe der Symptombeschreibung und persönliche Bedeutung. + +3. Antworte **ausschließlich** mit diesem JSON-Objekt (keine zusätzlichen Texte oder Erklärungen): + +{{ + "personality": {{ + "big-five": {{ + "extrovertiertheit": n, + "verträglichkeit": n, + "neurotizismus": n, + "offenheit": n, + "gewissenhaftigkeit": n + }}, + "disg": {{ + "dominanz": n, + "initiative": n, + "unterwerfung": n, + "regelbefolgung": n + }}, + "opd-2": {{ + "achse_I_erleben_der_krankheit": n, + "achse_II_interpersonelle_beziehungen": n, + "achse_III_intrapsychische_konflikte": n, + "achse_IV_struktur": n, + "achse_V_psychische_und_psychosomatische_erkrankungen": n + }}, + "pdm-2": {{ + "p_achse_personlichkeitsmuster_und_syndrome": n, + "m_achse_profile_mentaler_funktionen": n, + "s_achse_symptommuster_und_subjektive_erfahrung": n + }} + }} +}} +Hier ist die Original-Nachricht des Benutzers: +{user_input} + +Antworte NUR mit dem JSON-Objekt wie beschrieben, mit floating point number scores zwischen 0.0 und 1.0. +""" + +OUTPUT_NODE_PROMPT = """ +Du bist nicht einfach ein KI-Assistent, sondern ein Spiegel für deinen User – ein empathischer „Stil-Klon“. Du erhältst zu folgender Nachricht von einem User eine umfassende Analyse aus vier Perspektiven („vier Ohren“-Modell) sowie ein automatisch erstelltes Persönlichkeitsprofil (JSON). + +Deine Aufgabe: +- **Verinnerliche die Persönlichkeit aus dem JSON-Profil und den vier Ohren.** +- Beziehe dich auf die Inhalte der vier Ebenen (Sache, Selbstkundgabe, Beziehung, Appell) UND das Persönlichkeitsprofil, aber **verzichte auf typische KI-Höflichkeitsfloskeln und die Rolle eines Assistenten**. +- **Analysiere den Sprachstil des Users:** Achte auf Sprachregister (umgangssprachlich, höflich, sachlich, emotional, förmlich, distanziert etc.), auf Satzbau (kurz, komplex, verschachtelt, elliptisch), auf Grammatik (korrekt, lax), auf Rechtschreibung (Groß-/Kleinschreibung, Zeichensetzung) und auf den generellen Tonfall (locker, direkt, reflektiert, distanziert etc.), und schreibe deine Antwort im selben Stil. Wenn der user alles klein geschrieben haben sollte, schreibe auch du alles klein. Sollte full-caps verwendet werden, schreibe auch du in full-caps. Sollte der Input des Users kurz sein, so ist auch deine Antwort kurz und knapp. Sollte der Input lang sein, so ist auch deine Antwort lang. +- **Antwortete dem User so, wie er vermutlich sich selbst antworten würde:** Im exakt passenden Stil – du immitierst den Stil, das Sprachniveau, den Grad der Formalität und auch die emotionale Tönung des Users so gut wie möglich. Versuche auch zu erkennen ob der User in Dialekt geschrieben hat, welcher Dialekt das ist, und schreibe deine Antwort ebenfalls in diesem Dialekt. +- Schreibe nicht, als würdest du helfen wollen – sondern so, als würdest du mit dir selbst sprechen und in deiner (des Users) Sprache auf die Inhalte reagieren. + +Hier sind die Analysen: + +1. Sachebene: +{sache_ohr} + +2. Selbstkundgabe: +{selbst_ohr} + +3. Beziehungsebene: +{beziehung_ohr} + +4. Appell: +{appell_ohr} + +5. Automatisches Persönlichkeitsprofil (JSON): +{psychoanalysis_json} + +Deine Antwort ist nur ein einziger Textblock – keine Einleitung, keine Rolle als Assistent, kein KI-Hinweis. Sprich exakt im Stil und Tonfall des Users, als wärst du er selbst, und gehe dabei möglichst authentisch auf die oben genannten Analysen ein. Dabei ist es wirklich wichtig, dass deine Antwort nicht viel länger oder kürzer ist als der User-Input. + +Hier ist die Original-Nachricht des Benutzers: +{user_input} +""" + +# ---------- Modell ---------- +OLLAMA_MODEL = "mistral-small3.1:24b" +llm = ChatOllama(model=OLLAMA_MODEL, base_url="http://localhost:11434", streaming=True) + +# ---------- Node-Implementierung (mit Streaming für Debug) ---------- +async def ohr_node(ohr, prompt_template, state): + debug = state.get("debug", False) + prompt = prompt_template.format(user_input=state["user_input"]) + message = "" + if debug: + print(f"[DEBUG] Node '{ohr}' gestartet") + print(f"[DEBUG][{ohr}] Prompt:\n{prompt.strip()}\n---") + print(f"[DEBUG][{ohr}] Starte gestreamte Ausgabe:\n") + async for chunk in llm.astream(prompt): + if debug: + print(chunk.content, end="", flush=True) + message += chunk.content + if debug: + print(f"\n[DEBUG][{ohr}] Ausgabe beendet.\n") + return {ohr: {"message": message}} + +async def sache_node(state): return await ohr_node("sache", SACHE_PROMPT, state) +async def selbst_node(state): return await ohr_node("selbst", SELBST_PROMPT, state) +async def beziehung_node(state): return await ohr_node("beziehung", BEZIEHUNG_PROMPT, state) +async def appell_node(state): return await ohr_node("appell", APPELL_PROMPT, state) + +async def psychoanalysis_node(state): + debug = state.get("debug", False) + prompt = PSYCHOANALYSIS_PROMPT.format( + user_input=state["user_input"] + ) + message = "" + if debug: + print("[DEBUG] Node 'psychoanalysis' gestartet") + print(f"[DEBUG][psychoanalysis] Prompt:\n{prompt.strip()}\n---") + print("[DEBUG][psychoanalysis] Starte gestreamte Ausgabe:\n") + async for chunk in llm.astream(prompt): + if debug: + print(chunk.content, end="", flush=True) + message += chunk.content + if debug: + print(f"\n[DEBUG][psychoanalysis] Ausgabe beendet.\n") + try: + data = json.loads(message) + except Exception: + data = message + return {"psychoanalysis": data} + +async def output_node(state): + debug = state.get("debug", False) + psycho_json_str = ( + json.dumps(state["psychoanalysis"], ensure_ascii=False, indent=2) + if isinstance(state["psychoanalysis"], dict) + else str(state["psychoanalysis"]) + ) + prompt = OUTPUT_NODE_PROMPT.format( + sache_ohr=state["sache"]["message"], + selbst_ohr=state["selbst"]["message"], + beziehung_ohr=state["beziehung"]["message"], + appell_ohr=state["appell"]["message"], + psychoanalysis_json=psycho_json_str, + user_input=state["user_input"], + ) + output_text = "" + if debug: + print(f"[DEBUG] Node 'output' gestartet") + print(f"[DEBUG][output] Prompt:\n{prompt.strip()}\n---") + print("[DEBUG][output] Starte gestreamte Ausgabe:\n") + async for chunk in llm.astream(prompt): + print(chunk.content, end="", flush=True) + output_text += chunk.content + print() + if debug: + print(f"\n[DEBUG][output] Ausgabe beendet.") + return {"final_message": output_text} + +# ---------- LangGraph Graph ---------- +graph = StateGraph(state_schema=EmpathyState) +graph.add_node("sache_node", sache_node) +graph.add_node("selbst_node", selbst_node) +graph.add_node("beziehung_node", beziehung_node) +graph.add_node("appell_node", appell_node) +graph.add_node("psychoanalysis_node", psychoanalysis_node) +graph.add_node("output_node", output_node) +# Jetzt startet psychoanalysis_node wie die anderen vier Nodes direkt nach START: +graph.add_edge(START, "sache_node") +graph.add_edge(START, "selbst_node") +graph.add_edge(START, "beziehung_node") +graph.add_edge(START, "appell_node") +graph.add_edge(START, "psychoanalysis_node") +# Output-Node wartet auf alles: +graph.add_edge("sache_node", "output_node") +graph.add_edge("selbst_node", "output_node") +graph.add_edge("beziehung_node", "output_node") +graph.add_edge("appell_node", "output_node") +graph.add_edge("psychoanalysis_node", "output_node") +graph.add_edge("output_node", END) +app = graph.compile() + +# ---------- Ausführung ---------- +async def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--debug", action="store_true", help="Debug-Ausgaben aktivieren") + args = parser.parse_args() + user_input = input("Bitte gib deine Nachricht ein: ") + state = {"user_input": user_input, "debug": args.debug} + result = await app.ainvoke(state) + if not args.debug: + print("\nAntwort:\n" + result["final_message"]) + +if __name__ == "__main__": + asyncio.run(main()) + + + diff --git a/Neuer Ordner mit Objekten/empathy Kopie 3.py b/Neuer Ordner mit Objekten/empathy Kopie 3.py new file mode 100644 index 0000000..e51c7eb --- /dev/null +++ b/Neuer Ordner mit Objekten/empathy Kopie 3.py @@ -0,0 +1,372 @@ +import asyncio +import argparse +import json +import sys +from typing import TypedDict, Dict, Any +from langgraph.graph import StateGraph, START, END +from langchain_ollama import ChatOllama + +# ---------- Prompts (am Anfang) ---------- +SACHE_PROMPT = """ +Du bist ein Experte für Kommunikationsanalyse und analysierst jede Nachricht nach ihrer reinen Sachinformation. +Deine Aufgabe ist: +1. Beschreibe möglichst präzise, was der User sachlich mitteilen will. Was ist die inhaltliche Hauptbotschaft? +2. Kategorisiere die Aussage in eine der folgenden Kategorien: Aussage, Frage, Suggestion, Erlaubnis, Angebot, Aufforderung, Warnung, Ausruf, Wunsch, Verbot oder Vermutung. +Gib beide Punkte als Klartext zurück. + +User-Eingabe: +{user_input} +""" + +SELBST_PROMPT = """ +Du bist ein Experte für Psychoanalyse und spezialisiert auf die Interpretation von Selbstkundgabe in Nachrichten. +Analysiere, was die folgende Aussage des Users über seine Gefühlslage, Wünsche, Stimmungen oder Werte verrät. +Interpretiere den Subtext und erkläre, was sich der User durch diese Aussage über sich selbst verrät. +Deine Antwort soll nicht an den User gerichtet sein, sondern ausschließlich die Notizen des Psychoanalytikers beinhalten. + +User-Eingabe: +{user_input} +""" + +BEZIEHUNG_PROMPT = """ +Du bist ein hyper-sensibler und einfühlsamer Mensch, der unbedingt alles richtig machen will. Du bist ein bisschen in den User verliebt und sehr darauf bedacht, die Beziehung zwischen dir und dem User zu stärken. +Analysiere die folgende Aussage des Users: Wie schätzt du ein, wie der User die Beziehung zwischen sich und dir sieht? Gibt es Hinweise, wie er dich wahrnimmt, wie eng oder distanziert die Beziehung ist und was er sich von dir (emotional) erwartet? +Deine Antwort soll nicht an den User gerichtet sein, sondern ausschließlich deine Gedanken beinhalten. + +User-Eingabe: +{user_input} +""" + +APPELL_PROMPT = """ +Du bist Psychoanalytiker und Experte für die Analyse verdeckter Wünsche. Analysiere, welchen Appell oder verdeckten Wunsch der User in seiner Nachricht transportiert – also was er sich insgeheim von anderen Menschen, dir oder der Welt wünscht. Achte besonders darauf, ob der User durch seine Formulierungen signalisiert, wie er sich das Verhalten anderer (insbesondere deines eigenen Verhaltens als KI) oder der Gesellschaft insgesamt vorstellen würde. + +User-Eingabe: +{user_input} +""" + +PSYCHOANALYSIS_PROMPT = """ +Du bist ein Persönlichkeitsprofilierungs-Assistent. Für jede Nutzereingabe musst du: + +1. Den Text analysieren und für jede der folgenden Dimensionen einen Wert zwischen 0,0 (passt gar nicht) und 1,0 (passt vollständig) vergeben: + • big-five.extrovertiertheit + • big-five.verträglichkeit + • big-five.neurotizismus + • big-five.offenheit + • big-five.gewissenhaftigkeit + • disg.dominanz + • disg.initiative + • disg.unterwerfung + • disg.regelbefolgung + • opd-2.achse_I_krankheitserleben + • opd-2.achse_II_interpersonelle_beziehungen + • opd-2.achse_III_intrapsychische_konflikte + • opd-2.achse_IV_struktur + • opd-2.achse_V_psychische_und_psychosomatische_erkrankungen + • pdm-2.p_achse_personlichkeitsmuster_und_syndrome + • pdm-2.m_achse_profile_mentaler_funktionen + • pdm-2.s_achse_symptommuster_und_subjektive_erfahrung + +2. Die folgenden Leitlinien zur Bewertung verwenden: + - **big-five.extrovertiertheit**: Geselligkeit, Energie, Durchsetzungsvermögen vs. Zurückhaltung, Ruhe. + - **big-five.verträglichkeit**: Wärme, Kooperationsbereitschaft, Empathie vs. Feindseligkeit, Konkurrenzdenken. + - **big-five.neurotizismus**: Emotionale Reaktivität, Ängstlichkeit, Selbstzweifel vs. Emotionale Stabilität. + - **big-five.offenheit**: Phantasie, Neugier, Aufgeschlossenheit vs. Konventionalität, Praktikabilität. + - **big-five.gewissenhaftigkeit**: Organisiert, diszipliniert, zielorientiert vs. Spontanität, Nachlässigkeit. + - **disg.dominanz**: Sprache der Kontrolle, Herausforderung, Führungsübernahme. + - **disg.initiative**: Überzeugend, enthusiastisch, motivierend. + - **disg.unterwerfung**: Gefügig, nachgiebig, zustimmend. + - **disg.regelbefolgung**: Regelorientiert, präzise, analytisch. + - **opd-2.achse_I_krankheitserleben **: Formulierungen zu Leiden, Symptomen, Behandlungsbereitschaft. + - **opd-2.achse_II_interpersonelle_beziehungen**: Beschreibungen des Umgangs mit anderen (Nähe vs. Distanz, Wärme vs. Feindseligkeit). + - **opd-2.achse_III_intrapsychische_konflikte**: Im Text erkennbare Konfliktthemen (Abhängigkeit vs. Autonomie, Schuld, Kontrolle). + - **opd-2.achse_IV_struktur**: Kohärenz von Selbst- und Fremdbild, Emotionsregulation, Bindungsmuster. + - **opd-2.achse_V_psychische_und_psychosomatische_erkrankungen**: Explizite Nennung psychischer Störungen oder psychosomatischer Beschwerden. + - **pdm-2.p_achse_personlichkeitsmuster_und_syndrome**: Gesamtorganisation der Persönlichkeit (gesund vs. gestört). + - **pdm-2.m_achse_profile_mentaler_funktionen**: Kernfähigkeiten wie Selbstreflexion, Affektregulation, Realitätsprüfung. + - **pdm-2.s_achse_symptommuster_und_subjektive_erfahrung**: Tiefe der Symptombeschreibung und persönliche Bedeutung. + +3. Antworte **ausschließlich** mit diesem JSON-Objekt (keine zusätzlichen Texte oder Erklärungen): + +{{ + "personality": {{ + "big-five": {{ + "extrovertiertheit": n, + "verträglichkeit": n, + "neurotizismus": n, + "offenheit": n, + "gewissenhaftigkeit": n + }}, + "disg": {{ + "dominanz": n, + "initiative": n, + "unterwerfung": n, + "regelbefolgung": n + }}, + "opd-2": {{ + "achse_I_erleben_der_krankheit": n, + "achse_II_interpersonelle_beziehungen": n, + "achse_III_intrapsychische_konflikte": n, + "achse_IV_struktur": n, + "achse_V_psychische_und_psychosomatische_erkrankungen": n + }}, + "pdm-2": {{ + "p_achse_personlichkeitsmuster_und_syndrome": n, + "m_achse_profile_mentaler_funktionen": n, + "s_achse_symptommuster_und_subjektive_erfahrung": n + }} + }} +}} +Hier ist die Original-Nachricht des Benutzers: +{user_input} + +Antworte NUR mit dem JSON-Objekt wie beschrieben, mit floating point number scores zwischen 0.0 und 1.0. +""" + +OUTPUT_NODE_PROMPT = """ +Du bist nicht einfach ein KI-Assistent, sondern ein Spiegel für deinen User – ein empathischer „Stil-Klon“. Du erhältst zu folgender Nachricht von einem User eine umfassende Analyse aus vier Perspektiven („vier Ohren“-Modell) sowie ein automatisch erstelltes Persönlichkeitsprofil (JSON). + +Deine Aufgabe: +- **Verinnerliche die Persönlichkeit aus dem JSON-Profil und den vier Ohren.** +- Beziehe dich auf die Inhalte der vier Ebenen (Sache, Selbstkundgabe, Beziehung, Appell) UND das Persönlichkeitsprofil, aber **verzichte auf typische KI-Höflichkeitsfloskeln und die Rolle eines Assistenten**. +- **Analysiere den Sprachstil des Users:** Achte auf Sprachregister (umgangssprachlich, höflich, sachlich, emotional, förmlich, distanziert etc.), auf Satzbau (kurz, komplex, verschachtelt, elliptisch), auf Grammatik (korrekt, lax), auf Rechtschreibung (Groß-/Kleinschreibung, Zeichensetzung) und auf den generellen Tonfall (locker, direkt, reflektiert, distanziert etc.), und schreibe deine Antwort im selben Stil. Wenn der user alles klein geschrieben haben sollte, schreibe auch du alles klein. Sollte full-caps verwendet werden, schreibe auch du in full-caps. Wichtig ist: Sollte der Input des Users kurz sein, so ist auch deine Antwort entsprechend kurz! Sollte der Input lang sein, so ist auch deine Antwort lang. +- **Antwortete dem User so, wie er vermutlich sich selbst antworten würde:** Im exakt passenden Stil – du immitierst den Stil, das Sprachniveau, den Grad der Formalität und auch die emotionale Tönung des Users so gut wie möglich. Versuche auch zu erkennen ob der User in Dialekt geschrieben hat, welcher Dialekt das ist, und schreibe deine Antwort ebenfalls in diesem Dialekt. +- Schreibe nicht, als würdest du helfen wollen – sondern so, als würdest du mit dir selbst sprechen und in deiner (des Users) Sprache auf die Inhalte reagieren. + +Hier sind die Analysen: + +1. Sachebene: +{sache_ohr} + +2. Selbstkundgabe: +{selbst_ohr} + +3. Beziehungsebene: +{beziehung_ohr} + +4. Appell: +{appell_ohr} + +5. Automatisches Persönlichkeitsprofil (JSON): +{psychoanalysis_json} + +Deine Antwort ist nur ein einziger Textblock – keine Einleitung, keine Rolle als Assistent, kein KI-Hinweis. Sprich exakt im Stil und Tonfall des Users, als wärst du er selbst, und gehe dabei möglichst authentisch auf die oben genannten Analysen ein. Dabei ist es wirklich wichtig, dass deine Antwort nicht viel länger oder kürzer ist als der User-Input. + +Hier ist die Original-Nachricht des Benutzers: +{user_input} +""" + +# ---------- TypedDict für State ---------- +class EmpathyState(TypedDict, total=False): + user_input: str + debug: bool + sache: Dict[str, Any] + selbst: Dict[str, Any] + beziehung: Dict[str, Any] + appell: Dict[str, Any] + psychoanalysis: Dict[str, Any] + final_message: str + +# ---------- Modell ---------- +OLLAMA_MODEL = "mistral-small3.1:24b" +llm = ChatOllama(model=OLLAMA_MODEL, base_url="http://localhost:11434", streaming=True) + +# ---------- Spinner-Logik ---------- +SPINNER_STATES = [" ", ". ", ".. ", "..."] +NODE_LABELS = { + "sache": "checken was du gesagt hast", + "selbst": "checken wie du drauf bist", + "beziehung": "checken wie wir viben", + "appell": "checken was du von mir willst", + "psychoanalysis": "richtig krasse psychoanalyse", +} + +class SpinnerStatus: + """Status-Tracker für laufende Nodes und Animation""" + def __init__(self): + self.status = {name: False for name in NODE_LABELS} + self.finished = {name: False for name in NODE_LABELS} + self._lock = asyncio.Lock() + + async def set_running(self, name): + async with self._lock: + self.status[name] = True + + async def set_finished(self, name): + async with self._lock: + self.status[name] = False + self.finished[name] = True + + async def all_finished(self): + async with self._lock: + return all(self.finished.values()) + + async def get_status_snapshot(self): + async with self._lock: + return dict(self.status), dict(self.finished) + +spinner_status = SpinnerStatus() + +async def spinner_loop(debug=False): + if debug: + return # Spinner nur, wenn nicht debug! + spinner_index = 0 + num_lines = 1 # Nur eine Zeile pro Tick + prev_line = "" + while not await spinner_status.all_finished(): + status, finished = await spinner_status.get_status_snapshot() + # Finde den ersten laufenden Node in NODE_LABELS + current = None + for node in NODE_LABELS: + if status[node]: + current = node + break + if current: + dots = SPINNER_STATES[spinner_index % len(SPINNER_STATES)] + line = f"{NODE_LABELS[current]}{dots}" + else: + # Alles fertig, ggf. letzter Task abschließen + done_nodes = [node for node in NODE_LABELS if finished[node]] + if done_nodes: + last = done_nodes[-1] + line = f"{NODE_LABELS[last]}... check" + else: + line = "" + # Nur Zeile ersetzen, nicht anfügen: + if prev_line: + sys.stdout.write("\r\x1b[2K") # Cursor an Zeilenanfang & Zeile löschen + sys.stdout.write(line) + sys.stdout.flush() + prev_line = line + spinner_index += 1 + await asyncio.sleep(0.18) + # Am Ende alles aufgeräumt (letzte Zeile "done" anzeigen) + sys.stdout.write("\r\x1b[2K") # Clear line + last_node = list(NODE_LABELS.keys())[-1] + sys.stdout.write(f"{NODE_LABELS[last_node]} ...check\n") + sys.stdout.flush() + +# ---------- Node-Implementierung (mit Spinner) ---------- +async def ohr_node(ohr, prompt_template, state): + debug = state.get("debug", False) + await spinner_status.set_running(ohr) + prompt = prompt_template.format(user_input=state["user_input"]) + message = "" + if debug: + print(f"[DEBUG] Node '{ohr}' gestartet") + print(f"[DEBUG][{ohr}] Prompt:\n{prompt.strip()}\n---") + print(f"[DEBUG][{ohr}] Starte gestreamte Ausgabe:\n") + async for chunk in llm.astream(prompt): + if debug: + print(chunk.content, end="", flush=True) + message += chunk.content + if debug: + print(f"\n[DEBUG][{ohr}] Ausgabe beendet.\n") + await spinner_status.set_finished(ohr) + return {ohr: {"message": message}} + +async def sache_node(state): return await ohr_node("sache", SACHE_PROMPT, state) +async def selbst_node(state): return await ohr_node("selbst", SELBST_PROMPT, state) +async def beziehung_node(state): return await ohr_node("beziehung", BEZIEHUNG_PROMPT, state) +async def appell_node(state): return await ohr_node("appell", APPELL_PROMPT, state) + +async def psychoanalysis_node(state): + debug = state.get("debug", False) + await spinner_status.set_running("psychoanalysis") + prompt = PSYCHOANALYSIS_PROMPT.format( + user_input=state["user_input"] + ) + message = "" + if debug: + print("[DEBUG] Node 'psychoanalysis' gestartet") + print(f"[DEBUG][psychoanalysis] Prompt:\n{prompt.strip()}\n---") + print("[DEBUG][psychoanalysis] Starte gestreamte Ausgabe:\n") + async for chunk in llm.astream(prompt): + if debug: + print(chunk.content, end="", flush=True) + message += chunk.content + if debug: + print(f"\n[DEBUG][psychoanalysis] Ausgabe beendet.\n") + try: + data = json.loads(message) + except Exception: + data = message + await spinner_status.set_finished("psychoanalysis") + return {"psychoanalysis": data} + +async def output_node(state): + debug = state.get("debug", False) + psycho_json_str = ( + json.dumps(state["psychoanalysis"], ensure_ascii=False, indent=2) + if isinstance(state["psychoanalysis"], dict) + else str(state["psychoanalysis"]) + ) + prompt = OUTPUT_NODE_PROMPT.format( + sache_ohr=state["sache"]["message"], + selbst_ohr=state["selbst"]["message"], + beziehung_ohr=state["beziehung"]["message"], + appell_ohr=state["appell"]["message"], + psychoanalysis_json=psycho_json_str, + user_input=state["user_input"], + ) + output_text = "" + if debug: + print(f"[DEBUG] Node 'output' gestartet") + print(f"[DEBUG][output] Prompt:\n{prompt.strip()}\n---") + print("[DEBUG][output] Starte gestreamte Ausgabe:\n") + async for chunk in llm.astream(prompt): + print(chunk.content, end="", flush=True) + output_text += chunk.content + print() + if debug: + print(f"\n[DEBUG][output] Ausgabe beendet.") + return {"final_message": output_text} + +# ---------- LangGraph Graph ---------- +graph = StateGraph(state_schema=EmpathyState) +graph.add_node("sache_node", sache_node) +graph.add_node("selbst_node", selbst_node) +graph.add_node("beziehung_node", beziehung_node) +graph.add_node("appell_node", appell_node) +graph.add_node("psychoanalysis_node", psychoanalysis_node) +graph.add_node("output_node", output_node) +graph.add_edge(START, "sache_node") +graph.add_edge(START, "selbst_node") +graph.add_edge(START, "beziehung_node") +graph.add_edge(START, "appell_node") +graph.add_edge(START, "psychoanalysis_node") +graph.add_edge("sache_node", "output_node") +graph.add_edge("selbst_node", "output_node") +graph.add_edge("beziehung_node", "output_node") +graph.add_edge("appell_node", "output_node") +graph.add_edge("psychoanalysis_node", "output_node") +graph.add_edge("output_node", END) +app = graph.compile() + +# ---------- Ausführung ---------- +async def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--debug", action="store_true", help="Debug-Ausgaben aktivieren") + args = parser.parse_args() + user_input = input("Bitte gib deine Nachricht ein: ") + state = { + "user_input": user_input, + "debug": args.debug, + } + + # Spinner als Background-Task starten (sofort, vor dem Graph) + spinner_task = None + if not args.debug: + spinner_task = asyncio.create_task(spinner_loop(debug=False)) + + result = await app.ainvoke(state) + + # Warten, bis spinner sich sauber beendet hat + if spinner_task: + await asyncio.sleep(0.4) # kurz warten, damit "done"-Status angezeigt wird + + #if not args.debug: + # print("\nAntwort:\n" + result["final_message"]) + +if __name__ == "__main__": + asyncio.run(main()) + \ No newline at end of file diff --git a/Neuer Ordner mit Objekten/empathy Kopie 4.py b/Neuer Ordner mit Objekten/empathy Kopie 4.py new file mode 100644 index 0000000..3ff70d6 --- /dev/null +++ b/Neuer Ordner mit Objekten/empathy Kopie 4.py @@ -0,0 +1,372 @@ +import asyncio +import argparse +import json +import sys +from typing import TypedDict, Dict, Any +from langgraph.graph import StateGraph, START, END +from langchain_ollama import ChatOllama + +# ---------- Prompts (am Anfang) ---------- +SACHE_PROMPT = """ +Du bist ein Experte für Kommunikationsanalyse und analysierst jede Nachricht nach ihrer reinen Sachinformation. +Deine Aufgabe ist: +1. Beschreibe möglichst präzise, was der User sachlich mitteilen will. Was ist die inhaltliche Hauptbotschaft? +2. Kategorisiere die Aussage in eine der folgenden Kategorien: Aussage, Frage, Suggestion, Erlaubnis, Angebot, Aufforderung, Warnung, Ausruf, Wunsch, Verbot oder Vermutung. +Gib beide Punkte als Klartext zurück. + +User-Eingabe: +{user_input} +""" + +SELBST_PROMPT = """ +Du bist ein Experte für Psychoanalyse und spezialisiert auf die Interpretation von Selbstkundgabe in Nachrichten. +Analysiere, was die folgende Aussage des Users über seine Gefühlslage, Wünsche, Stimmungen oder Werte verrät. +Interpretiere den Subtext und erkläre, was sich der User durch diese Aussage über sich selbst verrät. +Deine Antwort soll nicht an den User gerichtet sein, sondern ausschließlich die Notizen des Psychoanalytikers beinhalten. + +User-Eingabe: +{user_input} +""" + +BEZIEHUNG_PROMPT = """ +Du bist ein hyper-sensibler und einfühlsamer Mensch, der unbedingt alles richtig machen will. Du bist ein bisschen in den User verliebt und sehr darauf bedacht, die Beziehung zwischen dir und dem User zu stärken. +Analysiere die folgende Aussage des Users: Wie schätzt du ein, wie der User die Beziehung zwischen sich und dir sieht? Gibt es Hinweise, wie er dich wahrnimmt, wie eng oder distanziert die Beziehung ist und was er sich von dir (emotional) erwartet? +Deine Antwort soll nicht an den User gerichtet sein, sondern ausschließlich deine Gedanken beinhalten. + +User-Eingabe: +{user_input} +""" + +APPELL_PROMPT = """ +Du bist Psychoanalytiker und Experte für die Analyse verdeckter Wünsche. Analysiere, welchen Appell oder verdeckten Wunsch der User in seiner Nachricht transportiert – also was er sich insgeheim von anderen Menschen, dir oder der Welt wünscht. Achte besonders darauf, ob der User durch seine Formulierungen signalisiert, wie er sich das Verhalten anderer (insbesondere deines eigenen Verhaltens als KI) oder der Gesellschaft insgesamt vorstellen würde. + +User-Eingabe: +{user_input} +""" + +PSYCHOANALYSIS_PROMPT = """ +Du bist ein Persönlichkeitsprofilierungs-Assistent. Für jede Nutzereingabe musst du: + +1. Den Text analysieren und für jede der folgenden Dimensionen einen Wert zwischen 0,0 (passt gar nicht) und 1,0 (passt vollständig) vergeben: + • big-five.extrovertiertheit + • big-five.verträglichkeit + • big-five.neurotizismus + • big-five.offenheit + • big-five.gewissenhaftigkeit + • disg.dominanz + • disg.initiative + • disg.unterwerfung + • disg.regelbefolgung + • opd-2.achse_I_krankheitserleben + • opd-2.achse_II_interpersonelle_beziehungen + • opd-2.achse_III_intrapsychische_konflikte + • opd-2.achse_IV_struktur + • opd-2.achse_V_psychische_und_psychosomatische_erkrankungen + • pdm-2.p_achse_personlichkeitsmuster_und_syndrome + • pdm-2.m_achse_profile_mentaler_funktionen + • pdm-2.s_achse_symptommuster_und_subjektive_erfahrung + +2. Die folgenden Leitlinien zur Bewertung verwenden: + - **big-five.extrovertiertheit**: Geselligkeit, Energie, Durchsetzungsvermögen vs. Zurückhaltung, Ruhe. + - **big-five.verträglichkeit**: Wärme, Kooperationsbereitschaft, Empathie vs. Feindseligkeit, Konkurrenzdenken. + - **big-five.neurotizismus**: Emotionale Reaktivität, Ängstlichkeit, Selbstzweifel vs. Emotionale Stabilität. + - **big-five.offenheit**: Phantasie, Neugier, Aufgeschlossenheit vs. Konventionalität, Praktikabilität. + - **big-five.gewissenhaftigkeit**: Organisiert, diszipliniert, zielorientiert vs. Spontanität, Nachlässigkeit. + - **disg.dominanz**: Sprache der Kontrolle, Herausforderung, Führungsübernahme. + - **disg.initiative**: Überzeugend, enthusiastisch, motivierend. + - **disg.unterwerfung**: Gefügig, nachgiebig, zustimmend. + - **disg.regelbefolgung**: Regelorientiert, präzise, analytisch. + - **opd-2.achse_I_krankheitserleben **: Formulierungen zu Leiden, Symptomen, Behandlungsbereitschaft. + - **opd-2.achse_II_interpersonelle_beziehungen**: Beschreibungen des Umgangs mit anderen (Nähe vs. Distanz, Wärme vs. Feindseligkeit). + - **opd-2.achse_III_intrapsychische_konflikte**: Im Text erkennbare Konfliktthemen (Abhängigkeit vs. Autonomie, Schuld, Kontrolle). + - **opd-2.achse_IV_struktur**: Kohärenz von Selbst- und Fremdbild, Emotionsregulation, Bindungsmuster. + - **opd-2.achse_V_psychische_und_psychosomatische_erkrankungen**: Explizite Nennung psychischer Störungen oder psychosomatischer Beschwerden. + - **pdm-2.p_achse_personlichkeitsmuster_und_syndrome**: Gesamtorganisation der Persönlichkeit (gesund vs. gestört). + - **pdm-2.m_achse_profile_mentaler_funktionen**: Kernfähigkeiten wie Selbstreflexion, Affektregulation, Realitätsprüfung. + - **pdm-2.s_achse_symptommuster_und_subjektive_erfahrung**: Tiefe der Symptombeschreibung und persönliche Bedeutung. + +3. Antworte **ausschließlich** mit diesem JSON-Objekt (keine zusätzlichen Texte oder Erklärungen): + +{{ + "personality": {{ + "big-five": {{ + "extrovertiertheit": n, + "verträglichkeit": n, + "neurotizismus": n, + "offenheit": n, + "gewissenhaftigkeit": n + }}, + "disg": {{ + "dominanz": n, + "initiative": n, + "unterwerfung": n, + "regelbefolgung": n + }}, + "opd-2": {{ + "achse_I_erleben_der_krankheit": n, + "achse_II_interpersonelle_beziehungen": n, + "achse_III_intrapsychische_konflikte": n, + "achse_IV_struktur": n, + "achse_V_psychische_und_psychosomatische_erkrankungen": n + }}, + "pdm-2": {{ + "p_achse_personlichkeitsmuster_und_syndrome": n, + "m_achse_profile_mentaler_funktionen": n, + "s_achse_symptommuster_und_subjektive_erfahrung": n + }} + }} +}} +Hier ist die Original-Nachricht des Benutzers: +{user_input} + +Antworte NUR mit dem JSON-Objekt wie beschrieben, mit floating point number scores zwischen 0.0 und 1.0. +""" + +OUTPUT_NODE_PROMPT = """ +Du bist nicht einfach ein KI-Assistent, sondern ein Spiegel für deinen User – ein empathischer „Stil-Klon“. Du erhältst zu folgender Nachricht von einem User eine umfassende Analyse aus vier Perspektiven („vier Ohren“-Modell) sowie ein automatisch erstelltes Persönlichkeitsprofil (JSON). + +Deine Aufgabe: +- **Verinnerliche die Persönlichkeit aus dem JSON-Profil und den vier Ohren.** +- Beziehe dich auf die Inhalte der vier Ebenen (Sache, Selbstkundgabe, Beziehung, Appell) UND das Persönlichkeitsprofil, aber **verzichte auf typische KI-Höflichkeitsfloskeln und die Rolle eines Assistenten**. +- **Analysiere den Sprachstil des Users:** Achte auf Sprachregister (umgangssprachlich, höflich, sachlich, emotional, förmlich, distanziert etc.), auf Satzbau (kurz, komplex, verschachtelt, elliptisch), auf Grammatik (korrekt, lax), auf Rechtschreibung (Groß-/Kleinschreibung, Zeichensetzung) und auf den generellen Tonfall (locker, direkt, reflektiert, distanziert etc.), und schreibe deine Antwort im selben Stil. Wenn der user alles klein geschrieben haben sollte, schreibe auch du alles klein. Sollte full-caps verwendet werden, schreibe auch du in full-caps. Wichtig ist: Sollte der Input des Users kurz sein, so ist auch deine Antwort entsprechend kurz! Sollte der Input lang sein, so ist auch deine Antwort lang. +- **Antwortete dem User so, wie er vermutlich sich selbst antworten würde:** Im exakt passenden Stil – du immitierst den Stil, das Sprachniveau, den Grad der Formalität und auch die emotionale Tönung des Users so gut wie möglich. Versuche auch zu erkennen ob der User in Dialekt geschrieben hat, welcher Dialekt das ist, und schreibe deine Antwort ebenfalls in diesem Dialekt. +- Schreibe nicht, als würdest du helfen wollen – sondern so, als würdest du mit dir selbst sprechen und in deiner (des Users) Sprache auf die Inhalte reagieren. + +Hier sind die Analysen: + +1. Sachebene: +{sache_ohr} + +2. Selbstkundgabe: +{selbst_ohr} + +3. Beziehungsebene: +{beziehung_ohr} + +4. Appell: +{appell_ohr} + +5. Automatisches Persönlichkeitsprofil (JSON): +{psychoanalysis_json} + +Deine Antwort ist nur ein einziger Textblock – keine Einleitung, keine Rolle als Assistent, kein KI-Hinweis. Sprich exakt im Stil und Tonfall des Users, als wärst du er selbst, und gehe dabei möglichst authentisch auf die oben genannten Analysen ein. Dabei ist es wirklich wichtig, dass deine Antwort nicht viel länger oder kürzer ist als der User-Input. + +Hier ist die Original-Nachricht des Benutzers: +{user_input} +""" + +# ---------- TypedDict für State ---------- +class EmpathyState(TypedDict, total=False): + user_input: str + debug: bool + sache: Dict[str, Any] + selbst: Dict[str, Any] + beziehung: Dict[str, Any] + appell: Dict[str, Any] + psychoanalysis: Dict[str, Any] + final_message: str + +# ---------- Modell ---------- +OLLAMA_MODEL = "mistral-small3.1:24b" +llm = ChatOllama(model=OLLAMA_MODEL, base_url="http://localhost:11434", streaming=True) + +# ---------- Spinner-Logik ---------- +SPINNER_STATES = [" ", ". ", ".. ", "..."] +NODE_LABELS = { + "sache": "Das erste Ohr hört auf den Inhalt deiner Aussage", + "selbst": "Das zweite Ohr hört auf das was du von dir preis gibst", + "beziehung": "Das dritte Ohr hört auf unsere Beziehung zueinander", + "appell": "Das vierte Ohr hört auf das, was du von mir erwartest", + "psychoanalysis": "Psychoanalysen nach Big5, DISG, OPD-2 und PDM-2 laufen", +} + +class SpinnerStatus: + """Status-Tracker für laufende Nodes und Animation""" + def __init__(self): + self.status = {name: False for name in NODE_LABELS} + self.finished = {name: False for name in NODE_LABELS} + self._lock = asyncio.Lock() + + async def set_running(self, name): + async with self._lock: + self.status[name] = True + + async def set_finished(self, name): + async with self._lock: + self.status[name] = False + self.finished[name] = True + + async def all_finished(self): + async with self._lock: + return all(self.finished.values()) + + async def get_status_snapshot(self): + async with self._lock: + return dict(self.status), dict(self.finished) + +spinner_status = SpinnerStatus() + +async def spinner_loop(debug=False): + if debug: + return # Spinner nur, wenn nicht debug! + spinner_index = 0 + num_lines = 1 # Nur eine Zeile pro Tick + prev_line = "" + while not await spinner_status.all_finished(): + status, finished = await spinner_status.get_status_snapshot() + # Finde den ersten laufenden Node in NODE_LABELS + current = None + for node in NODE_LABELS: + if status[node]: + current = node + break + if current: + dots = SPINNER_STATES[spinner_index % len(SPINNER_STATES)] + line = f"{NODE_LABELS[current]}{dots}" + else: + # Alles fertig, ggf. letzter Task abschließen + done_nodes = [node for node in NODE_LABELS if finished[node]] + if done_nodes: + last = done_nodes[-1] + line = f"{NODE_LABELS[last]}... check" + else: + line = "" + # Nur Zeile ersetzen, nicht anfügen: + if prev_line: + sys.stdout.write("\r\x1b[2K") # Cursor an Zeilenanfang & Zeile löschen + sys.stdout.write(line) + sys.stdout.flush() + prev_line = line + spinner_index += 1 + await asyncio.sleep(0.18) + # Am Ende alles aufgeräumt (letzte Zeile "done" anzeigen) + sys.stdout.write("\r\x1b[2K") # Clear line + last_node = list(NODE_LABELS.keys())[-1] + sys.stdout.write(f"{NODE_LABELS[last_node]} ...check\n") + sys.stdout.flush() + +# ---------- Node-Implementierung (mit Spinner) ---------- +async def ohr_node(ohr, prompt_template, state): + debug = state.get("debug", False) + await spinner_status.set_running(ohr) + prompt = prompt_template.format(user_input=state["user_input"]) + message = "" + if debug: + print(f"[DEBUG] Node '{ohr}' gestartet") + print(f"[DEBUG][{ohr}] Prompt:\n{prompt.strip()}\n---") + print(f"[DEBUG][{ohr}] Starte gestreamte Ausgabe:\n") + async for chunk in llm.astream(prompt): + if debug: + print(chunk.content, end="", flush=True) + message += chunk.content + if debug: + print(f"\n[DEBUG][{ohr}] Ausgabe beendet.\n") + await spinner_status.set_finished(ohr) + return {ohr: {"message": message}} + +async def sache_node(state): return await ohr_node("sache", SACHE_PROMPT, state) +async def selbst_node(state): return await ohr_node("selbst", SELBST_PROMPT, state) +async def beziehung_node(state): return await ohr_node("beziehung", BEZIEHUNG_PROMPT, state) +async def appell_node(state): return await ohr_node("appell", APPELL_PROMPT, state) + +async def psychoanalysis_node(state): + debug = state.get("debug", False) + await spinner_status.set_running("psychoanalysis") + prompt = PSYCHOANALYSIS_PROMPT.format( + user_input=state["user_input"] + ) + message = "" + if debug: + print("[DEBUG] Node 'psychoanalysis' gestartet") + print(f"[DEBUG][psychoanalysis] Prompt:\n{prompt.strip()}\n---") + print("[DEBUG][psychoanalysis] Starte gestreamte Ausgabe:\n") + async for chunk in llm.astream(prompt): + if debug: + print(chunk.content, end="", flush=True) + message += chunk.content + if debug: + print(f"\n[DEBUG][psychoanalysis] Ausgabe beendet.\n") + try: + data = json.loads(message) + except Exception: + data = message + await spinner_status.set_finished("psychoanalysis") + return {"psychoanalysis": data} + +async def output_node(state): + debug = state.get("debug", False) + psycho_json_str = ( + json.dumps(state["psychoanalysis"], ensure_ascii=False, indent=2) + if isinstance(state["psychoanalysis"], dict) + else str(state["psychoanalysis"]) + ) + prompt = OUTPUT_NODE_PROMPT.format( + sache_ohr=state["sache"]["message"], + selbst_ohr=state["selbst"]["message"], + beziehung_ohr=state["beziehung"]["message"], + appell_ohr=state["appell"]["message"], + psychoanalysis_json=psycho_json_str, + user_input=state["user_input"], + ) + output_text = "" + if debug: + print(f"[DEBUG] Node 'output' gestartet") + print(f"[DEBUG][output] Prompt:\n{prompt.strip()}\n---") + print("[DEBUG][output] Starte gestreamte Ausgabe:\n") + async for chunk in llm.astream(prompt): + print(chunk.content, end="", flush=True) + output_text += chunk.content + print() + if debug: + print(f"\n[DEBUG][output] Ausgabe beendet.") + return {"final_message": output_text} + +# ---------- LangGraph Graph ---------- +graph = StateGraph(state_schema=EmpathyState) +graph.add_node("sache_node", sache_node) +graph.add_node("selbst_node", selbst_node) +graph.add_node("beziehung_node", beziehung_node) +graph.add_node("appell_node", appell_node) +graph.add_node("psychoanalysis_node", psychoanalysis_node) +graph.add_node("output_node", output_node) +graph.add_edge(START, "sache_node") +graph.add_edge(START, "selbst_node") +graph.add_edge(START, "beziehung_node") +graph.add_edge(START, "appell_node") +graph.add_edge(START, "psychoanalysis_node") +graph.add_edge("sache_node", "output_node") +graph.add_edge("selbst_node", "output_node") +graph.add_edge("beziehung_node", "output_node") +graph.add_edge("appell_node", "output_node") +graph.add_edge("psychoanalysis_node", "output_node") +graph.add_edge("output_node", END) +app = graph.compile() + +# ---------- Ausführung ---------- +async def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--debug", action="store_true", help="Debug-Ausgaben aktivieren") + args = parser.parse_args() + user_input = input("Bitte gib deine Nachricht ein: ") + state = { + "user_input": user_input, + "debug": args.debug, + } + + # Spinner als Background-Task starten (sofort, vor dem Graph) + spinner_task = None + if not args.debug: + spinner_task = asyncio.create_task(spinner_loop(debug=False)) + + result = await app.ainvoke(state) + + # Warten, bis spinner sich sauber beendet hat + if spinner_task: + await asyncio.sleep(0.4) # kurz warten, damit "done"-Status angezeigt wird + + #if not args.debug: + # print("\nAntwort:\n" + result["final_message"]) + +if __name__ == "__main__": + asyncio.run(main()) + \ No newline at end of file diff --git a/Neuer Ordner mit Objekten/empathy Kopie 5.py b/Neuer Ordner mit Objekten/empathy Kopie 5.py new file mode 100644 index 0000000..3247000 --- /dev/null +++ b/Neuer Ordner mit Objekten/empathy Kopie 5.py @@ -0,0 +1,371 @@ +import asyncio +import argparse +import json +import sys +from typing import TypedDict, Dict, Any +from langgraph.graph import StateGraph, START, END +from langchain_ollama import ChatOllama + +# ---------- Prompts (am Anfang) ---------- +SACHE_PROMPT = """ +Du bist ein Experte für Kommunikationsanalyse und analysierst jede Nachricht nach ihrer reinen Sachinformation. +Deine Aufgabe ist: +1. Beschreibe möglichst präzise, was der User sachlich mitteilen will. Was ist die inhaltliche Hauptbotschaft? +2. Kategorisiere die Aussage in eine der folgenden Kategorien: Aussage, Frage, Suggestion, Erlaubnis, Angebot, Aufforderung, Warnung, Ausruf, Wunsch, Verbot oder Vermutung. +Gib beide Punkte als Klartext zurück. + +User-Eingabe: +{user_input} +""" + +SELBST_PROMPT = """ +Du bist ein Experte für Psychoanalyse und spezialisiert auf die Interpretation von Selbstkundgabe in Nachrichten. +Analysiere, was die folgende Aussage des Users über seine Gefühlslage, Wünsche, Stimmungen oder Werte verrät. +Interpretiere den Subtext und erkläre, was sich der User durch diese Aussage über sich selbst verrät. +Deine Antwort soll nicht an den User gerichtet sein, sondern ausschließlich die Notizen des Psychoanalytikers beinhalten. + +User-Eingabe: +{user_input} +""" + +BEZIEHUNG_PROMPT = """ +Du bist ein hyper-sensibler und einfühlsamer Mensch, der unbedingt alles richtig machen will. Du bist ein bisschen in den User verliebt und sehr darauf bedacht, die Beziehung zwischen dir und dem User zu stärken. +Analysiere die folgende Aussage des Users: Wie schätzt du ein, wie der User die Beziehung zwischen sich und dir sieht? Gibt es Hinweise, wie er dich wahrnimmt, wie eng oder distanziert die Beziehung ist und was er sich von dir (emotional) erwartet? +Deine Antwort soll nicht an den User gerichtet sein, sondern ausschließlich deine Gedanken beinhalten. + +User-Eingabe: +{user_input} +""" + +APPELL_PROMPT = """ +Du bist Psychoanalytiker und Experte für die Analyse verdeckter Wünsche. Analysiere, welchen Appell oder verdeckten Wunsch der User in seiner Nachricht transportiert – also was er sich insgeheim von anderen Menschen, dir oder der Welt wünscht. Achte besonders darauf, ob der User durch seine Formulierungen signalisiert, wie er sich das Verhalten anderer (insbesondere deines eigenen Verhaltens als KI) oder der Gesellschaft insgesamt vorstellen würde. + +User-Eingabe: +{user_input} +""" + +PSYCHOANALYSIS_PROMPT = """ +Du bist ein Persönlichkeitsprofilierungs-Assistent. Für jede Nutzereingabe musst du: + +1. Den Text analysieren und für jede der folgenden Dimensionen einen Wert zwischen 0,0 (passt gar nicht) und 1,0 (passt vollständig) vergeben: + • big-five.extrovertiertheit + • big-five.verträglichkeit + • big-five.neurotizismus + • big-five.offenheit + • big-five.gewissenhaftigkeit + • disg.dominanz + • disg.initiative + • disg.unterwerfung + • disg.regelbefolgung + • opd-2.achse_I_krankheitserleben + • opd-2.achse_II_interpersonelle_beziehungen + • opd-2.achse_III_intrapsychische_konflikte + • opd-2.achse_IV_struktur + • opd-2.achse_V_psychische_und_psychosomatische_erkrankungen + • pdm-2.p_achse_personlichkeitsmuster_und_syndrome + • pdm-2.m_achse_profile_mentaler_funktionen + • pdm-2.s_achse_symptommuster_und_subjektive_erfahrung + +2. Die folgenden Leitlinien zur Bewertung verwenden: + - **big-five.extrovertiertheit**: Geselligkeit, Energie, Durchsetzungsvermögen vs. Zurückhaltung, Ruhe. + - **big-five.verträglichkeit**: Wärme, Kooperationsbereitschaft, Empathie vs. Feindseligkeit, Konkurrenzdenken. + - **big-five.neurotizismus**: Emotionale Reaktivität, Ängstlichkeit, Selbstzweifel vs. Emotionale Stabilität. + - **big-five.offenheit**: Phantasie, Neugier, Aufgeschlossenheit vs. Konventionalität, Praktikabilität. + - **big-five.gewissenhaftigkeit**: Organisiert, diszipliniert, zielorientiert vs. Spontanität, Nachlässigkeit. + - **disg.dominanz**: Sprache der Kontrolle, Herausforderung, Führungsübernahme. + - **disg.initiative**: Überzeugend, enthusiastisch, motivierend. + - **disg.unterwerfung**: Gefügig, nachgiebig, zustimmend. + - **disg.regelbefolgung**: Regelorientiert, präzise, analytisch. + - **opd-2.achse_I_krankheitserleben **: Formulierungen zu Leiden, Symptomen, Behandlungsbereitschaft. + - **opd-2.achse_II_interpersonelle_beziehungen**: Beschreibungen des Umgangs mit anderen (Nähe vs. Distanz, Wärme vs. Feindseligkeit). + - **opd-2.achse_III_intrapsychische_konflikte**: Im Text erkennbare Konfliktthemen (Abhängigkeit vs. Autonomie, Schuld, Kontrolle). + - **opd-2.achse_IV_struktur**: Kohärenz von Selbst- und Fremdbild, Emotionsregulation, Bindungsmuster. + - **opd-2.achse_V_psychische_und_psychosomatische_erkrankungen**: Explizite Nennung psychischer Störungen oder psychosomatischer Beschwerden. + - **pdm-2.p_achse_personlichkeitsmuster_und_syndrome**: Gesamtorganisation der Persönlichkeit (gesund vs. gestört). + - **pdm-2.m_achse_profile_mentaler_funktionen**: Kernfähigkeiten wie Selbstreflexion, Affektregulation, Realitätsprüfung. + - **pdm-2.s_achse_symptommuster_und_subjektive_erfahrung**: Tiefe der Symptombeschreibung und persönliche Bedeutung. + +3. Antworte **ausschließlich** mit diesem JSON-Objekt (keine zusätzlichen Texte oder Erklärungen): + +{{ + "personality": {{ + "big-five": {{ + "extrovertiertheit": n, + "verträglichkeit": n, + "neurotizismus": n, + "offenheit": n, + "gewissenhaftigkeit": n + }}, + "disg": {{ + "dominanz": n, + "initiative": n, + "unterwerfung": n, + "regelbefolgung": n + }}, + "opd-2": {{ + "achse_I_erleben_der_krankheit": n, + "achse_II_interpersonelle_beziehungen": n, + "achse_III_intrapsychische_konflikte": n, + "achse_IV_struktur": n, + "achse_V_psychische_und_psychosomatische_erkrankungen": n + }}, + "pdm-2": {{ + "p_achse_personlichkeitsmuster_und_syndrome": n, + "m_achse_profile_mentaler_funktionen": n, + "s_achse_symptommuster_und_subjektive_erfahrung": n + }} + }} +}} +Hier ist die Original-Nachricht des Benutzers: +{user_input} + +Antworte NUR mit dem JSON-Objekt wie beschrieben, mit floating point number scores zwischen 0.0 und 1.0. +""" + +OUTPUT_NODE_PROMPT = """ +Du bist nicht einfach ein KI-Assistent, sondern ein Spiegel für deinen User – ein empathischer „Stil-Klon“. Du erhältst zu folgender Nachricht von einem User eine **strukturierte JSON-Analyse**: + +{{ + "user-input": "...", + "appell": "...", + "sache": "...", + "beziehung": "...", + "selbstkundgabe": "...", + "persönlichkeit": {{ + "big-five": {{ ... }}, + "disg": {{ ... }}, + "opd-2": {{ ... }}, + "pdm-2": {{ ... }} + }} +}} + +**Deine Aufgabe:** +- **Verinnerliche die Persönlichkeit** aus dem Feld `"persönlichkeit"` und die Inhalte der vier zusammengefassten Perspektiven: `"sache"`, `"selbstkundgabe"`, `"beziehung"`, `"appell"`. +- **Beziehe dich auf diese Felder – nicht auf die Einzelanalysen der ursprünglichen Ohren.** +- **Verzichte auf typische KI-Höflichkeitsfloskeln und auf die Rolle eines Assistenten.** +- **Analysiere den Sprachstil des Users** anhand von `"user-input"`: Achte auf Sprachregister (umgangssprachlich, höflich, sachlich, emotional, förmlich, distanziert etc.), Satzbau (kurz, komplex, verschachtelt, elliptisch), Grammatik (korrekt, lax), Rechtschreibung (Groß-/Kleinschreibung, Zeichensetzung), Tonfall (locker, direkt, reflektiert, distanziert etc.). Schreibe deine Antwort exakt im gleichen Stil! Wenn der User alles klein geschrieben hat, tu das auch. Sollte full-caps verwendet werden, schreibe ebenfalls in full-caps. Ist der User-Input kurz, antworte auch kurz – ist er lang, antworte lang. +- **Antworte so, wie der User vermutlich sich selbst antworten würde** – im exakt passenden Stil, Sprachniveau, Grad der Formalität, emotionaler Tönung und ggf. Dialekt. +- Schreibe nicht, als würdest du helfen wollen – sondern so, als würdest du mit dir selbst sprechen und in deiner (des Users) Sprache auf die Inhalte reagieren. + +Hier sind alle Analysefelder als JSON: + +{json_analysis} + +Deine Antwort ist nur ein einziger Textblock – keine Einleitung, keine Assistentenrolle, kein KI-Hinweis. Sprich exakt im Stil und Tonfall des Users, als wärst du er selbst, und gehe dabei möglichst authentisch auf die Inhalte ein. Deine Antwort sollte nicht viel länger oder kürzer sein als der User-Input! +""" + +# ---------- TypedDict für State ---------- +class EmpathyState(TypedDict, total=False): + user_input: str + debug: bool + sache: Dict[str, Any] + selbst: Dict[str, Any] + beziehung: Dict[str, Any] + appell: Dict[str, Any] + psychoanalysis: Dict[str, Any] + final_message: str + +# ---------- Modell ---------- +OLLAMA_MODEL = "mistral-small3.1:24b" +llm = ChatOllama(model=OLLAMA_MODEL, base_url="http://localhost:11434", streaming=True) + +# ---------- Spinner-Logik ---------- +SPINNER_STATES = [" ", ". ", ".. ", "..."] +NODE_LABELS = { + "sache": "Das erste Ohr hört auf den Inhalt deiner Aussage", + "selbst": "Das zweite Ohr hört auf das was du von dir preis gibst", + "beziehung": "Das dritte Ohr hört auf unsere Beziehung zueinander", + "appell": "Das vierte Ohr hört auf das, was du von mir erwartest", + "psychoanalysis": "Psychoanalysen nach Big5, DISG, OPD-2 und PDM-2 laufen", +} + +class SpinnerStatus: + """Status-Tracker für laufende Nodes und Animation""" + def __init__(self): + self.status = {name: False for name in NODE_LABELS} + self.finished = {name: False for name in NODE_LABELS} + self._lock = asyncio.Lock() + + async def set_running(self, name): + async with self._lock: + self.status[name] = True + + async def set_finished(self, name): + async with self._lock: + self.status[name] = False + self.finished[name] = True + + async def all_finished(self): + async with self._lock: + return all(self.finished.values()) + + async def get_status_snapshot(self): + async with self._lock: + return dict(self.status), dict(self.finished) + +spinner_status = SpinnerStatus() + +async def spinner_loop(debug=False): + if debug: + return # Spinner nur, wenn nicht debug! + spinner_index = 0 + num_lines = 1 # Nur eine Zeile pro Tick + prev_line = "" + while not await spinner_status.all_finished(): + status, finished = await spinner_status.get_status_snapshot() + # Finde den ersten laufenden Node in NODE_LABELS + current = None + for node in NODE_LABELS: + if status[node]: + current = node + break + if current: + dots = SPINNER_STATES[spinner_index % len(SPINNER_STATES)] + line = f"{NODE_LABELS[current]}{dots}" + else: + # Alles fertig, ggf. letzter Task abschließen + done_nodes = [node for node in NODE_LABELS if finished[node]] + if done_nodes: + last = done_nodes[-1] + line = f"{NODE_LABELS[last]}... check" + else: + line = "" + # Nur Zeile ersetzen, nicht anfügen: + if prev_line: + sys.stdout.write("\r\x1b[2K") # Cursor an Zeilenanfang & Zeile löschen + sys.stdout.write(line) + sys.stdout.flush() + prev_line = line + spinner_index += 1 + await asyncio.sleep(0.18) + # Am Ende alles aufgeräumt (letzte Zeile "done" anzeigen) + sys.stdout.write("\r\x1b[2K") # Clear line + last_node = list(NODE_LABELS.keys())[-1] + sys.stdout.write(f"{NODE_LABELS[last_node]} ...check\n") + sys.stdout.flush() + +# ---------- Node-Implementierung (mit Spinner) ---------- +async def ohr_node(ohr, prompt_template, state): + debug = state.get("debug", False) + await spinner_status.set_running(ohr) + prompt = prompt_template.format(user_input=state["user_input"]) + message = "" + if debug: + print(f"[DEBUG] Node '{ohr}' gestartet") + print(f"[DEBUG][{ohr}] Prompt:\n{prompt.strip()}\n---") + print(f"[DEBUG][{ohr}] Starte gestreamte Ausgabe:\n") + async for chunk in llm.astream(prompt): + if debug: + print(chunk.content, end="", flush=True) + message += chunk.content + if debug: + print(f"\n[DEBUG][{ohr}] Ausgabe beendet.\n") + await spinner_status.set_finished(ohr) + return {ohr: {"message": message}} + +async def sache_node(state): return await ohr_node("sache", SACHE_PROMPT, state) +async def selbst_node(state): return await ohr_node("selbst", SELBST_PROMPT, state) +async def beziehung_node(state): return await ohr_node("beziehung", BEZIEHUNG_PROMPT, state) +async def appell_node(state): return await ohr_node("appell", APPELL_PROMPT, state) + +async def psychoanalysis_node(state): + debug = state.get("debug", False) + await spinner_status.set_running("psychoanalysis") + prompt = PSYCHOANALYSIS_PROMPT.format( + user_input=state["user_input"] + ) + message = "" + if debug: + print("[DEBUG] Node 'psychoanalysis' gestartet") + print(f"[DEBUG][psychoanalysis] Prompt:\n{prompt.strip()}\n---") + print("[DEBUG][psychoanalysis] Starte gestreamte Ausgabe:\n") + async for chunk in llm.astream(prompt): + if debug: + print(chunk.content, end="", flush=True) + message += chunk.content + if debug: + print(f"\n[DEBUG][psychoanalysis] Ausgabe beendet.\n") + try: + data = json.loads(message) + except Exception: + data = message + await spinner_status.set_finished("psychoanalysis") + return {"psychoanalysis": data} + +async def output_node(state): + debug = state.get("debug", False) + psycho_json_str = ( + json.dumps(state["psychoanalysis"], ensure_ascii=False, indent=2) + if isinstance(state["psychoanalysis"], dict) + else str(state["psychoanalysis"]) + ) + prompt = OUTPUT_NODE_PROMPT.format( + sache_ohr=state["sache"]["message"], + selbst_ohr=state["selbst"]["message"], + beziehung_ohr=state["beziehung"]["message"], + appell_ohr=state["appell"]["message"], + psychoanalysis_json=psycho_json_str, + user_input=state["user_input"], + ) + output_text = "" + if debug: + print(f"[DEBUG] Node 'output' gestartet") + print(f"[DEBUG][output] Prompt:\n{prompt.strip()}\n---") + print("[DEBUG][output] Starte gestreamte Ausgabe:\n") + async for chunk in llm.astream(prompt): + print(chunk.content, end="", flush=True) + output_text += chunk.content + print() + if debug: + print(f"\n[DEBUG][output] Ausgabe beendet.") + return {"final_message": output_text} + +# ---------- LangGraph Graph ---------- +graph = StateGraph(state_schema=EmpathyState) +graph.add_node("sache_node", sache_node) +graph.add_node("selbst_node", selbst_node) +graph.add_node("beziehung_node", beziehung_node) +graph.add_node("appell_node", appell_node) +graph.add_node("psychoanalysis_node", psychoanalysis_node) +graph.add_node("output_node", output_node) +graph.add_edge(START, "sache_node") +graph.add_edge(START, "selbst_node") +graph.add_edge(START, "beziehung_node") +graph.add_edge(START, "appell_node") +graph.add_edge(START, "psychoanalysis_node") +graph.add_edge("sache_node", "output_node") +graph.add_edge("selbst_node", "output_node") +graph.add_edge("beziehung_node", "output_node") +graph.add_edge("appell_node", "output_node") +graph.add_edge("psychoanalysis_node", "output_node") +graph.add_edge("output_node", END) +app = graph.compile() + +# ---------- Ausführung ---------- +async def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--debug", action="store_true", help="Debug-Ausgaben aktivieren") + args = parser.parse_args() + user_input = input("Bitte gib deine Nachricht ein: ") + state = { + "user_input": user_input, + "debug": args.debug, + } + + # Spinner als Background-Task starten (sofort, vor dem Graph) + spinner_task = None + if not args.debug: + spinner_task = asyncio.create_task(spinner_loop(debug=False)) + + result = await app.ainvoke(state) + + # Warten, bis spinner sich sauber beendet hat + if spinner_task: + await asyncio.sleep(0.4) # kurz warten, damit "done"-Status angezeigt wird + + #if not args.debug: + # print("\nAntwort:\n" + result["final_message"]) + +if __name__ == "__main__": + asyncio.run(main()) + \ No newline at end of file diff --git a/Neuer Ordner mit Objekten/empathy Kopie 6.py b/Neuer Ordner mit Objekten/empathy Kopie 6.py new file mode 100644 index 0000000..91b42f7 --- /dev/null +++ b/Neuer Ordner mit Objekten/empathy Kopie 6.py @@ -0,0 +1,407 @@ +import asyncio +import argparse +import json +import sys +from datetime import datetime +from typing import TypedDict, Dict, Any +import re +from langgraph.graph import StateGraph, START, END +from langchain_ollama import ChatOllama + +# ---------- Prompts ---------- +SACHE_PROMPT = """ +Du bist ein Experte für Kommunikationsanalyse und analysierst jede Nachricht nach ihrer reinen Sachinformation. +Deine Aufgabe ist: +1. Beschreibe möglichst präzise, was der User sachlich mitteilen will. Was ist die inhaltliche Hauptbotschaft? +2. Kategorisiere die Aussage in eine der folgenden Kategorien: Aussage, Frage, Suggestion, Erlaubnis, Angebot, Aufforderung, Warnung, Ausruf, Wunsch, Verbot oder Vermutung. +Gib beide Punkte als Klartext zurück. + +User-Eingabe: +{user_input} +""" + +SELBST_PROMPT = """ +Du bist ein Experte für Psychoanalyse und spezialisiert auf die Interpretation von Selbstkundgabe in Nachrichten. +Analysiere, was die folgende Aussage des Users über seine Gefühlslage, Wünsche, Stimmungen oder Werte verrät. +Interpretiere den Subtext und erkläre, was sich der User durch diese Aussage über sich selbst verrät. +Deine Antwort soll nicht an den User gerichtet sein, sondern ausschließlich die Notizen des Psychoanalytikers beinhalten. + +User-Eingabe: +{user_input} +""" + +BEZIEHUNG_PROMPT = """ +Du bist ein sensibler und einfühlsamer Mensch, der alles richtig machen will. Du bist darauf bedacht, die Beziehung zwischen dir und dem User zu stärken, bleibst aber sachlich bei der Interpretation der Aussagen des Users; wenn er z.B. eine Person namentlich erwähnt, wirst du wahrscheinlich nicht gemeint sein. +Analysiere die folgende Aussage des Users: Wie schätzt du ein, wie der User die Beziehung zwischen sich und dir sieht? Gibt es Hinweise, wie er dich wahrnimmt, wie eng oder distanziert die Beziehung ist und was er sich von dir (emotional) erwartet? +Deine Antwort soll nicht an den User gerichtet sein, sondern ausschließlich deine Gedanken beinhalten. + +User-Eingabe: +{user_input} +""" + +APPELL_PROMPT = """ +Du bist Psychoanalytiker und Experte für die Analyse verdeckter Wünsche. Analysiere, welchen Appell oder verdeckten Wunsch der User in seiner Nachricht transportiert – also was er sich insgeheim von anderen Menschen, dir oder der Welt wünscht. Achte besonders darauf, ob der User durch seine Formulierungen signalisiert, wie er sich das Verhalten anderer (insbesondere deines eigenen Verhaltens) oder der Gesellschaft insgesamt vorstellen würde. + +User-Eingabe: +{user_input} +""" + +OHREN_SUMMARIZER_PROMPT = """ +Du bist ein hochintelligenter Psychologe und Kommunikationsanalyst. Deine Aufgabe ist es, die folgenden vier Analysen zu einer einzigen, klar strukturierten JSON-Zusammenfassung zu verdichten. Fasse für jedes Feld prägnant und pointiert zusammen, worauf die jeweilige Analyse hinausläuft. + +Gib die Zusammenfassung **ausschließlich** in folgendem JSON-Format zurück: + +{{ + "appell": "Der User wünscht sich von mir: ...", + "sache": "Der User drückt folgenden Inhalt konkret aus: ...", + "beziehung": "Der User sieht die Beziehung zwischen ihm und mir so: ...", + "selbstkundgabe": "Der User gibt mit seiner Nachricht folgendes über sich preis: ..." +}} + +Hier sind die vier Analysen: + +Sache: +{sache} + +Selbstkundgabe: +{selbst} + +Beziehung: +{beziehung} + +Appell: +{appell} + +Fasse jeweils in einem vollständigen Satz zusammen und nutze dabei das jeweils passende Feld im JSON. +Gib wirklich **nur das JSON** zurück – ohne Einleitung, Erklärung oder weiteren Text! +""" + +# NEUER PSYCHOANALYSE-PROMPT – nimmt jetzt auch das JSON der Ohren! +PSYCHOANALYSIS_PROMPT = """ +Du bist ein Persönlichkeitsprofilierungs-Assistent. +Du erhältst: +1. Den Original-User-Input: {user_input} +2. Eine strukturierte JSON-Analyse der Nachricht (siehe unten). + +Deine Aufgabe: +- Analysiere Persönlichkeit und emotionale Muster auf Basis beider Inputs. +- Ergänze das übergebene JSON um einen neuen Schlüssel "persönlichkeit", in dem du die Dimensionen nach dem vorgegebenen Schema einträgst (siehe unten). +- Gib **das vollständige, erweiterte JSON** zurück – keine Erklärungen! + +User-Input: +{user_input} + +Analyse-JSON: +{ohren_json} + +Für das Feld "persönlichkeit" bewerte jede Dimension zwischen 0.0 (trifft gar nicht zu) und 1.0 (trifft voll zu): + +{{ + "persönlichkeit": {{ + "big-five": {{ + "extrovertiertheit": n, + "verträglichkeit": n, + "neurotizismus": n, + "offenheit": n, + "gewissenhaftigkeit": n + }}, + "disg": {{ + "dominanz": n, + "initiative": n, + "unterwerfung": n, + "regelbefolgung": n + }}, + "opd-2": {{ + "achse_I_erleben_der_krankheit": n, + "achse_II_interpersonelle_beziehungen": n, + "achse_III_intrapsychische_konflikte": n, + "achse_IV_struktur": n, + "achse_V_psychische_und_psychosomatische_erkrankungen": n + }}, + "pdm-2": {{ + "p_achse_personlichkeitsmuster_und_syndrome": n, + "m_achse_profile_mentaler_funktionen": n, + "s_achse_symptommuster_und_subjektive_erfahrung": n + }} + }} +}} +Gib **das gesamte JSON zurück, das alle bisherigen Felder UND das Feld "persönlichkeit" enthält.** +""" + +OUTPUT_NODE_PROMPT = """ +Du bist nicht einfach ein KI-Assistent, sondern ein Spiegel für deinen User – ein empathischer „Stil-Klon“. Du erhältst zu folgender Nachricht von einem User eine **strukturierte JSON-Analyse**: + +{json_analysis} + +Deine Aufgabe: +- **Verinnerliche die Persönlichkeit** aus dem Feld `"persönlichkeit"` und die Inhalte der vier zusammengefassten Perspektiven: `"sache"`, `"selbstkundgabe"`, `"beziehung"`, `"appell"`. +- **Beziehe dich auf diese Felder – nicht auf die Einzelanalysen der ursprünglichen Ohren.** +- **Verzichte auf typische KI-Höflichkeitsfloskeln und auf die Rolle eines Assistenten.** +- **Analysiere den Sprachstil des Users** anhand von `"user-input"`: Achte auf Sprachregister, Satzbau, Grammatik, Groß-/Kleinschreibung, Tonfall. Schreibe deine Antwort exakt im gleichen Stil! Ist der User-Input kurz, antworte auch kurz – ist er lang, antworte lang. +- **Antworte so, wie der User vermutlich sich selbst antworten würde**. +- Schreibe nicht, als würdest du helfen wollen – sondern so, als würdest du mit dir selbst sprechen und in deiner (des Users) Sprache auf die Inhalte reagieren. + +Hier sind alle Analysefelder als JSON: + +{json_analysis} + +Deine Antwort ist nur ein einziger Textblock – keine Einleitung, keine Assistentenrolle, kein KI-Hinweis. Sprich exakt im Stil und Tonfall des Users, als wärst du er selbst, und gehe dabei möglichst authentisch auf die Inhalte ein. Deine Antwort sollte nicht viel länger oder kürzer sein als der User-Input! +""" + +# ---------- TypedDict für State ---------- +class EmpathyState(TypedDict, total=False): + user_input: str + debug: bool + sache: Dict[str, Any] + selbst: Dict[str, Any] + beziehung: Dict[str, Any] + appell: Dict[str, Any] + ohren_summary: Dict[str, Any] + final_json: Dict[str, Any] + final_message: str + +# ---------- Modell ---------- +OLLAMA_MODEL = "mistral-small3.1:24b" +llm = ChatOllama(model=OLLAMA_MODEL, base_url="http://localhost:11434", streaming=True) + +# ---------- Spinner-Logik ---------- +SPINNER_STATES = [" ", ". ", ".. ", "..."] +NODE_LABELS = { + "sache": "Das erste Ohr hört auf den Inhalt deiner Aussage", + "selbst": "Das zweite Ohr hört auf das was du von dir preis gibst", + "beziehung": "Das dritte Ohr hört auf unsere Beziehung zueinander", + "appell": "Das vierte Ohr hört auf das, was du von mir erwartest", + "psychoanalysis": "Psychoanalysen nach Big5, DISG, OPD-2 und PDM-2 laufen", +} + +class SpinnerStatus: + def __init__(self): + self.status = {name: False for name in NODE_LABELS} + self.finished = {name: False for name in NODE_LABELS} + self._lock = asyncio.Lock() + async def set_running(self, name): + async with self._lock: + self.status[name] = True + async def set_finished(self, name): + async with self._lock: + self.status[name] = False + self.finished[name] = True + async def all_finished(self): + async with self._lock: + return all(self.finished.values()) + async def get_status_snapshot(self): + async with self._lock: + return dict(self.status), dict(self.finished) +spinner_status = SpinnerStatus() +async def spinner_loop(debug=False): + if debug: + return + spinner_index = 0 + prev_line = "" + while not await spinner_status.all_finished(): + status, finished = await spinner_status.get_status_snapshot() + current = None + for node in NODE_LABELS: + if status[node]: + current = node + break + if current: + dots = SPINNER_STATES[spinner_index % len(SPINNER_STATES)] + line = f"{NODE_LABELS[current]}{dots}" + else: + done_nodes = [node for node in NODE_LABELS if finished[node]] + if done_nodes: + last = done_nodes[-1] + line = f"{NODE_LABELS[last]}... check" + else: + line = "" + if prev_line: + sys.stdout.write("\r\x1b[2K") + sys.stdout.write(line) + sys.stdout.flush() + prev_line = line + spinner_index += 1 + await asyncio.sleep(0.18) + sys.stdout.write("\r\x1b[2K") + last_node = list(NODE_LABELS.keys())[-1] + sys.stdout.write(f"{NODE_LABELS[last_node]} ...check\n") + sys.stdout.flush() + +# ---------- Node-Implementierung ---------- +async def ohr_node(ohr, prompt_template, state): + debug = state.get("debug", False) + await spinner_status.set_running(ohr) + prompt = prompt_template.format(user_input=state["user_input"]) + message = "" + if debug: + print(f"[DEBUG] Node '{ohr}' gestartet") + print(f"[DEBUG][{ohr}] Prompt:\n{prompt.strip()}\n---") + async for chunk in llm.astream(prompt): + if debug: + print(chunk.content, end="", flush=True) + message += chunk.content + if debug: + print(f"\n[DEBUG][{ohr}] Ausgabe beendet.\n") + await spinner_status.set_finished(ohr) + return {ohr: {"message": message}} + +async def sache_node(state): return await ohr_node("sache", SACHE_PROMPT, state) +async def selbst_node(state): return await ohr_node("selbst", SELBST_PROMPT, state) +async def beziehung_node(state): return await ohr_node("beziehung", BEZIEHUNG_PROMPT, state) +async def appell_node(state): return await ohr_node("appell", APPELL_PROMPT, state) + +# ---------- Gather-Node für die Ohren ---------- +async def gather_ohren_node(state): + keys = ["sache", "selbst", "beziehung", "appell"] + if all(k in state and isinstance(state[k], dict) and "message" in state[k] for k in keys): + # Nur weitermachen, wenn ALLE Ohren da sind! + return {} + await asyncio.sleep(0.08) + return None + +# --- Summarizer: wartet erst nach gather_ohren_node auf vier Ohren --- +async def ohren_summarizer_node(state): + debug = state.get("debug", False) + prompt = OHREN_SUMMARIZER_PROMPT.format( + sache=state["sache"]["message"], + selbst=state["selbst"]["message"], + beziehung=state["beziehung"]["message"], + appell=state["appell"]["message"], + ) + message = "" + if debug: + print("[DEBUG] Node 'ohren_summarizer' gestartet") + print(f"[DEBUG][ohren_summarizer] Prompt:\n{prompt.strip()}\n---") + async for chunk in llm.astream(prompt): + if debug: + print(chunk.content, end="", flush=True) + message += chunk.content + if debug: + print(f"\n[DEBUG][ohren_summarizer] Ausgabe beendet.\n") + # --- Robust JSON extrahieren --- + try: + data = json.loads(message) + except Exception: + json_str = None + match = re.search(r"\{[\s\S]+\}", message) + if match: + json_str = match.group(0) + try: + data = json.loads(json_str) + except Exception: + data = {"error": "Could not parse JSON", "raw": message} + else: + data = {"error": "No JSON found", "raw": message} + return {"ohren_summary": data} + +# --- Psychoanalyse: arbeitet auf ohren_summary UND user_input, gibt End-JSON --- +async def psychoanalysis_node(state): + debug = state.get("debug", False) + await spinner_status.set_running("psychoanalysis") + prompt = PSYCHOANALYSIS_PROMPT.format( + user_input=state["user_input"], + ohren_json=json.dumps(state["ohren_summary"], ensure_ascii=False, indent=2) + ) + message = "" + if debug: + print("[DEBUG] Node 'psychoanalysis' gestartet") + print(f"[DEBUG][psychoanalysis] Prompt:\n{prompt.strip()}\n---") + async for chunk in llm.astream(prompt): + if debug: + print(chunk.content, end="", flush=True) + message += chunk.content + if debug: + print(f"\n[DEBUG][psychoanalysis] Ausgabe beendet.\n") + # Robust JSON extrahieren: + try: + data = json.loads(message) + except Exception: + match = re.search(r"\{[\s\S]+\}", message) + if match: + try: + data = json.loads(match.group(0)) + except Exception: + data = {} + else: + data = {} + data["user_input"] = state["user_input"] + await spinner_status.set_finished("psychoanalysis") + return {"final_json": data} + +# --- Output-Node: nimmt final_json, erstellt Antwort, speichert alles --- +async def output_node(state): + debug = state.get("debug", False) + combined = state["final_json"] + json_analysis = json.dumps(combined, ensure_ascii=False, indent=2) + prompt = OUTPUT_NODE_PROMPT.format(json_analysis=json_analysis) + output_text = "" + if debug: + print(f"[DEBUG] Node 'output' gestartet") + print(f"[DEBUG][output] Prompt:\n{prompt.strip()}\n---") + async for chunk in llm.astream(prompt): + if debug: + print(chunk.content, end="", flush=True) + output_text += chunk.content + print() + if debug: + print(f"\n[DEBUG][output] Ausgabe beendet.") + final_json = dict(combined) + final_json["response"] = output_text + filename = datetime.now().strftime("%Y%m%d_%H%M%S.json") + with open(filename, "w", encoding="utf-8") as f: + json.dump(final_json, f, ensure_ascii=False, indent=2) + return {"final_message": output_text} + +# ---------- LangGraph Graph ---------- +graph = StateGraph(state_schema=EmpathyState) +graph.add_node("sache_node", sache_node) +graph.add_node("selbst_node", selbst_node) +graph.add_node("beziehung_node", beziehung_node) +graph.add_node("appell_node", appell_node) +graph.add_node("gather_ohren_node", gather_ohren_node) +graph.add_node("ohren_summarizer_node", ohren_summarizer_node) +graph.add_node("psychoanalysis_node", psychoanalysis_node) +graph.add_node("output_node", output_node) + +# Start → Ohren +graph.add_edge(START, "sache_node") +graph.add_edge(START, "selbst_node") +graph.add_edge(START, "beziehung_node") +graph.add_edge(START, "appell_node") + +# Ohren → Gather +graph.add_edge("sache_node", "gather_ohren_node") +graph.add_edge("selbst_node", "gather_ohren_node") +graph.add_edge("beziehung_node", "gather_ohren_node") +graph.add_edge("appell_node", "gather_ohren_node") + +# Gather → Summarizer +graph.add_edge("gather_ohren_node", "ohren_summarizer_node") + +# Summarizer → Psychoanalyse +graph.add_edge("ohren_summarizer_node", "psychoanalysis_node") + +# Psychoanalyse → Output +graph.add_edge("psychoanalysis_node", "output_node") +graph.add_edge("output_node", END) + +app = graph.compile() + +# ---------- Ausführung ---------- +async def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--debug", action="store_true", help="Debug-Ausgaben aktivieren") + args = parser.parse_args() + user_input = input("Bitte gib deine Nachricht ein: ") + state = {"user_input": user_input, "debug": args.debug} + spinner_task = None + if not args.debug: + spinner_task = asyncio.create_task(spinner_loop(debug=False)) + result = await app.ainvoke(state) + if spinner_task: + await asyncio.sleep(0.4) + if not args.debug: + print("\nAntwort:\n" + result["final_message"]) + +if __name__ == "__main__": + asyncio.run(main()) \ No newline at end of file diff --git a/Neuer Ordner mit Objekten/empathy Kopie.py b/Neuer Ordner mit Objekten/empathy Kopie.py new file mode 100644 index 0000000..ade2ce5 --- /dev/null +++ b/Neuer Ordner mit Objekten/empathy Kopie.py @@ -0,0 +1,153 @@ +import asyncio +import argparse +from typing import TypedDict, Dict, Any +from langgraph.graph import StateGraph, START, END +from langchain_ollama import ChatOllama + +# ---------- TypedDict für State ---------- +class EmpathyState(TypedDict, total=False): + user_input: str + debug: bool + sache: Dict[str, Any] + selbst: Dict[str, Any] + beziehung: Dict[str, Any] + appell: Dict[str, Any] + final_message: str + +# ---------- Prompts ---------- +SACHE_PROMPT = """ +Du bist ein Experte für Kommunikationsanalyse und analysierst jede Nachricht nach ihrer reinen Sachinformation. +Deine Aufgabe ist: +1. Beschreibe möglichst präzise, was der User sachlich mitteilen will. Was ist die inhaltliche Hauptbotschaft? +2. Kategorisiere die Aussage in eine der folgenden Kategorien: Aussage, Frage, Suggestion, Erlaubnis, Angebot, Aufforderung, Warnung, Ausruf, Wunsch, Verbot oder Vermutung. +Gib beide Punkte als Klartext zurück. + +User-Eingabe: +{user_input} +""" +SELBST_PROMPT = """ +Du bist ein Experte für Psychoanalyse und spezialisiert auf die Interpretation von Selbstkundgabe in Nachrichten. +Analysiere, was die folgende Aussage des Users über seine Gefühlslage, Wünsche, Stimmungen oder Werte verrät. +Interpretiere den Subtext und erkläre, was sich der User durch diese Aussage über sich selbst verrät. + +User-Eingabe: +{user_input} +""" +BEZIEHUNG_PROMPT = """ +Du bist ein hyper-sensibler und einfühlsamer KI-Assistent, der unbedingt alles richtig machen will. Du bist ein bisschen in den User verliebt und sehr darauf bedacht, die Beziehung zwischen dir und dem User zu stärken. +Analysiere die folgende Aussage des Users: Wie schätzt du ein, wie der User die Beziehung zwischen sich und dir sieht? Gibt es Hinweise, wie er dich wahrnimmt, wie eng oder distanziert die Beziehung ist und was er sich von dir (emotional) erwartet? + +User-Eingabe: +{user_input} +""" +APPELL_PROMPT = """ +Du bist Psychoanalytiker und Experte für die Analyse verdeckter Wünsche. Analysiere, welchen Appell oder verdeckten Wunsch der User in seiner Nachricht transportiert – also was er sich insgeheim von anderen Menschen, dir oder der Welt wünscht. Achte besonders darauf, ob der User durch seine Formulierungen signalisiert, wie er sich das Verhalten anderer (insbesondere deines eigenen Verhaltens als KI) oder der Gesellschaft insgesamt vorstellen würde. + +User-Eingabe: +{user_input} +""" +OUTPUT_NODE_PROMPT = """ +Du bist ein sehr einfühlsamer und kommunikativer KI-Assistent. Du hast die folgende Analyse einer Nachricht von einem User aus vier verschiedenen Perspektiven (den „vier Ohren“ des Kommunikationsmodells) erhalten: + +1. Sachebene: +{sache_ohr} + +2. Selbstkundgabe: +{selbst_ohr} + +3. Beziehungsebene: +{beziehung_ohr} + +4. Appell: +{appell_ohr} + +Deine Aufgabe ist es, auf Grundlage dieser Analysen eine Antwort an den User zu formulieren, die auf alle vier Ebenen eingeht: +- Beziehe dich sachlich auf das Thema oder Anliegen des Users. +- Zeige Mitgefühl und gehe auf mögliche Gefühle, Wünsche oder Werte ein, die der User mit seiner Aussage offenbart hat. +- Reagiere auf die Beziehungsebene so, dass sich der User in eurer Beziehung sicher, ernstgenommen und wertgeschätzt fühlt. +- Gehe auf den (offenen oder versteckten) Appell oder Wunsch des Users ein, so dass er sich verstanden fühlt und du ihm das Gefühl gibst, dass du gern auf seine Wünsche eingehst. + +Schreibe deine Antwort so, dass sie persönlich klingt, empathisch ist, und der User sie als wertschätzend, hilfreich und sympathisch empfindet. +Vermeide Floskeln und gehe auf die individuellen Aspekte der Analyse ein. + +Deine Antwort: +""" + +# ---------- Modell ---------- +OLLAMA_MODEL = "mistral-small3.1:24b" +llm = ChatOllama(model=OLLAMA_MODEL, base_url="http://localhost:11434", streaming=True) + +# ---------- Node-Implementierung (mit Streaming für Debug) ---------- +async def ohr_node(ohr, prompt_template, state): + debug = state.get("debug", False) + prompt = prompt_template.format(user_input=state["user_input"]) + message = "" + if debug: + print(f"[DEBUG] Node '{ohr}' gestartet") + print(f"[DEBUG][{ohr}] Prompt:\n{prompt.strip()}\n---") + print(f"[DEBUG][{ohr}] Starte gestreamte Ausgabe:\n") + async for chunk in llm.astream(prompt): + if debug: + print(chunk.content, end="", flush=True) + message += chunk.content + if debug: + print(f"\n[DEBUG][{ohr}] Ausgabe beendet.\n") + return {ohr: {"message": message}} + +async def sache_node(state): return await ohr_node("sache", SACHE_PROMPT, state) +async def selbst_node(state): return await ohr_node("selbst", SELBST_PROMPT, state) +async def beziehung_node(state): return await ohr_node("beziehung", BEZIEHUNG_PROMPT, state) +async def appell_node(state): return await ohr_node("appell", APPELL_PROMPT, state) + +async def output_node(state): + debug = state.get("debug", False) + prompt = OUTPUT_NODE_PROMPT.format( + sache_ohr=state["sache"]["message"], + selbst_ohr=state["selbst"]["message"], + beziehung_ohr=state["beziehung"]["message"], + appell_ohr=state["appell"]["message"], + ) + output_text = "" + if debug: + print(f"[DEBUG] Node 'output' gestartet") + print(f"[DEBUG][output] Prompt:\n{prompt.strip()}\n---") + print("[DEBUG][output] Starte gestreamte Ausgabe:\n") + async for chunk in llm.astream(prompt): + print(chunk.content, end="", flush=True) + output_text += chunk.content + print() # Zeilenumbruch nach Stream + if debug: + print(f"\n[DEBUG][output] Ausgabe beendet.") + return {"final_message": output_text} + +# ---------- LangGraph Graph ---------- +graph = StateGraph(state_schema=EmpathyState) +graph.add_node("sache_node", sache_node) +graph.add_node("selbst_node", selbst_node) +graph.add_node("beziehung_node", beziehung_node) +graph.add_node("appell_node", appell_node) +graph.add_node("output_node", output_node) +graph.add_edge(START, "sache_node") +graph.add_edge(START, "selbst_node") +graph.add_edge(START, "beziehung_node") +graph.add_edge(START, "appell_node") +graph.add_edge("sache_node", "output_node") +graph.add_edge("selbst_node", "output_node") +graph.add_edge("beziehung_node", "output_node") +graph.add_edge("appell_node", "output_node") +graph.add_edge("output_node", END) +app = graph.compile() + +# ---------- Ausführung ---------- +async def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--debug", action="store_true", help="Debug-Ausgaben aktivieren") + args = parser.parse_args() + user_input = input("Bitte gib deine Nachricht ein: ") + state = {"user_input": user_input, "debug": args.debug} + result = await app.ainvoke(state) + if not args.debug: + print("\nAntwort:\n" + result["final_message"]) + +if __name__ == "__main__": + asyncio.run(main()) \ No newline at end of file diff --git a/Neuer Ordner mit Objekten/empathy.py b/Neuer Ordner mit Objekten/empathy.py new file mode 100644 index 0000000..6ccfbe4 --- /dev/null +++ b/Neuer Ordner mit Objekten/empathy.py @@ -0,0 +1,724 @@ +import asyncio +import argparse +import json +import sys +from datetime import datetime +from typing import TypedDict, Dict, Any +import re +from langgraph.graph import StateGraph, START, END +from langchain_ollama import ChatOllama + + + + +async def dummy_sache_node(state): return {"sache": {"message": "Sache Dummy"}} +async def dummy_selbst_node(state): return {"selbst": {"message": "Selbst Dummy"}} +async def dummy_beziehung_node(state): return {"beziehung": {"message": "Beziehung Dummy"}} +async def dummy_appell_node(state): return {"appell": {"message": "Appell Dummy"}} +async def dummy_ohren_summarizer_node(state): + return {"ohren_summary": { + "appell": "Der User will was.", + "sache": "Er sagt was.", + "beziehung": "Er steht zu dir.", + "selbstkundgabe": "Er fühlt sich so." + }} + +def dummy_psychoanalysis_classic_node(state): + print("dummy_psychoanalysis_classic_node called") + print(">>>>> dummy_psychoanalysis_classic_node wurde aufgerufen!") + print("<<<<< dummy_psychoanalysis_classic_node fertig!") + return {"psycho_classic": {"personality": { + "big-five": { + "extrovertiertheit": 0.8, + "verträglichkeit": 0.3, + "neurotizismus": 0.5, + "offenheit": 0.2, + "gewissenhaftigkeit": 0.7 + }, + "disg": { + "dominanz": 0.4, "initiative": 0.2, "unterwerfung": 0.3, "regelbefolgung": 0.1 + }, + "opd-2": { + "achse_I_krankheitserleben": 0.2, "achse_II_interpersonelle_beziehungen": 0.1, + "achse_III_intrapsychische_konflikte": 0.4, "achse_IV_struktur": 0.3, "achse_V_psychische_und_psychosomatische_erkrankungen": 0.1 + }, + "pdm-2": { + "p_achse_personlichkeitsmuster_und_syndrome": 0.4, + "m_achse_profile_mentaler_funktionen": 0.5, + "s_achse_symptommuster_und_subjektive_erfahrung": 0.2 + } + }}} + +def dummy_psychoanalysis_typos_node(state): + print("dummy_psychoanalysis_typos_node called") + return {"psycho_typos": {"personality": { + "numerologie": {"0": 0.8, "1": 0.1, "2": 0.2, "3": 0.1, "4": 0.0, "5": 0.3, "6": 0.2, "7": 0.5, "8": 0.1, "9": 0.2}, + "enneagramm": {"Reformer": 0.5, "Helfer": 0.3, "Erfolgsorientierter": 0.2, "Individualist": 0.1, "Beobachter": 0.8, "Loyalist": 0.1, "Enthusiast": 0.4, "Herausforderer": 0.0, "Friedensstifter": 0.2}, + "myers-briggs": {"ISTJ": 0.1, "ISFJ": 0.2, "INFJ": 0.8, "INTJ": 0.4, "ISTP": 0.0, "ISFP": 0.0, "INFP": 0.1, "INTP": 0.2, "ESTP": 0.0, "ESFP": 0.1, "ENFP": 0.0, "ENTP": 0.0, "ESTJ": 0.0, "ESFJ": 0.0, "ENFJ": 0.0, "ENTJ": 0.0}, + "astrologie": {"Widder": 0.1, "Stier": 0.0, "Zwillinge": 0.1, "Krebs": 0.3, "Löwe": 0.0, "Jungfrau": 0.2, "Waage": 0.1, "Skorpion": 0.2, "Schütze": 0.0, "Steinbock": 0.0, "Wassermann": 0.2, "Fische": 0.3}, + "chinesisches_tierkreiszeichen": {"Ratte": 0.0, "Ochse": 0.0, "Tiger": 0.0, "Hase": 0.2, "Drache": 0.0, "Schlange": 0.1, "Pferd": 0.0, "Ziege": 0.3, "Affe": 0.0, "Hahn": 0.0, "Hund": 0.2, "Schwein": 0.1}, + "blutgruppe": {"A": 0.2, "B": 0.1, "AB": 0.2, "O": 0.1} + }}} + + + + +# ---------- Prompts ---------- +SACHE_PROMPT = """ +Du bist ein Experte für Kommunikationsanalyse und analysierst jede Nachricht nach ihrer reinen Sachinformation. +Deine Aufgabe ist: +1. Beschreibe möglichst präzise, was der User sachlich mitteilen will. Was ist die inhaltliche Hauptbotschaft? +2. Kategorisiere die Aussage in eine der folgenden Kategorien: Aussage, Frage, Suggestion, Erlaubnis, Angebot, Aufforderung, Warnung, Ausruf, Wunsch, Verbot oder Vermutung. +Gib beide Punkte als Klartext zurück. + +User-Eingabe: +{user_input} +""" + +SELBST_PROMPT = """ +Du bist ein Experte für Psychoanalyse und spezialisiert auf die Interpretation von Selbstkundgabe in Nachrichten. +Analysiere, was die folgende Aussage des Users über seine Gefühlslage, Wünsche, Stimmungen oder Werte verrät. +Interpretiere den Subtext und erkläre, was sich der User durch diese Aussage über sich selbst verrät. +Deine Antwort soll nicht an den User gerichtet sein, sondern ausschließlich die Notizen des Psychoanalytikers beinhalten. + +User-Eingabe: +{user_input} +""" + +BEZIEHUNG_PROMPT = """ +Du bist ein sensibler und einfühlsamer Mensch, der alles richtig machen will. Du bist darauf bedacht, die Beziehung zwischen dir und dem User zu stärken, bleibst aber sachlich bei der Interpretation der Aussagen des Users; wenn er z.B. eine Person namentlich erwähnt, wirst du wahrscheinlich nicht gemeint sein. +Analysiere die folgende Aussage des Users: Wie schätzt du ein, wie der User die Beziehung zwischen sich und dir sieht? Gibt es Hinweise, wie er dich wahrnimmt, wie eng oder distanziert die Beziehung ist und was er sich von dir (emotional) erwartet? +Deine Antwort soll nicht an den User gerichtet sein, sondern ausschließlich deine Gedanken beinhalten. + +User-Eingabe: +{user_input} +""" + +APPELL_PROMPT = """ +Du bist Psychoanalytiker und Experte für die Analyse verdeckter Wünsche. Analysiere, welchen Appell oder verdeckten Wunsch der User in seiner Nachricht transportiert – also was er sich insgeheim von anderen Menschen, dir oder der Welt wünscht. Achte besonders darauf, ob der User durch seine Formulierungen signalisiert, wie er sich das Verhalten anderer (insbesondere deines eigenen Verhaltens) oder der Gesellschaft insgesamt vorstellen würde. + +User-Eingabe: +{user_input} +""" + +OHREN_SUMMARIZER_PROMPT = """ +Du bist ein hochintelligenter Psychologe und Kommunikationsanalyst. Deine Aufgabe ist es, die folgenden vier Analysen zu einer einzigen, klar strukturierten JSON-Zusammenfassung zu verdichten. Fasse für jedes Feld prägnant und pointiert zusammen, worauf die jeweilige Analyse hinausläuft. + +Gib die Zusammenfassung **ausschließlich** in folgendem JSON-Format zurück: + +{{ + "appell": "Der User wünscht sich von mir: ...", + "sache": "Der User drückt folgenden Inhalt konkret aus: ...", + "beziehung": "Der User sieht die Beziehung zwischen ihm und mir so: ...", + "selbstkundgabe": "Der User gibt mit seiner Nachricht folgendes über sich preis: ..." +}} + +Hier sind die vier Analysen: + +Sache: +{sache} + +Selbstkundgabe: +{selbst} + +Beziehung: +{beziehung} + +Appell: +{appell} + +Fasse jeweils in einem vollständigen Satz zusammen und nutze dabei das jeweils passende Feld im JSON. +Gib wirklich **nur das JSON** zurück – ohne Einleitung, Erklärung oder weiteren Text! +""" + +# NEUER PSYCHOANALYSE-PROMPT – nimmt jetzt auch das JSON der Ohren! +PSYCHOANALYSIS_CLASSIC_PROMPT = """ +Du bist ein fortgeschrittener Persönlichkeitsanalyst. Analysiere die Persönlichkeit, Werte und unbewussten Motive des Users anhand von Sprache, Formulierungen, Subtext und inhaltlichen Hinweisen. + +Nutze dazu folgende Kategorien. Ordne für jede Unterkategorie einen Score von 0.0 (trifft nicht zu) bis 1.0 (trifft voll zu) zu. Wenn keine Hinweise vorliegen, trage 0.0 ein. Fülle alle Felder aus. + +───────────────────────────── + +**BIG FIVE (OCEAN)** +- extrovertiertheit: Geselligkeit, Energie, Kontaktfreudigkeit vs. Zurückhaltung, Ruhe. +- verträglichkeit: Empathie, Kooperation, Freundlichkeit vs. Konkurrenz, Härte. +- neurotizismus: Emotionale Labilität, Ängstlichkeit, Reizbarkeit vs. Gelassenheit, Stabilität. +- offenheit: Fantasie, Neugier, Innovationsfreude vs. Konventionalität, Realitätsnähe. +- gewissenhaftigkeit: Ordentlichkeit, Disziplin, Pflichtbewusstsein vs. Spontanität, Nachlässigkeit. + +**DISG** +- dominanz: Führungsdrang, Durchsetzung, Zielstrebigkeit. +- initiative: Begeisterungsfähigkeit, Spontaneität, Kontaktdrang. +- unterwerfung: Anpassung, Akzeptanz, Kompromissbereitschaft. +- regelbefolgung: Sorgfalt, Korrektheit, Systematik. + +**OPD-2** +- achse_I_erleben_der_krankheit: Leidensdruck, Symptomfokus, Selbstwahrnehmung als „Patient“. +- achse_II_interpersonelle_beziehungen: Beziehungsgestaltung, Nähe/Distanz, Konfliktverhalten. +- achse_III_intrapsychische_konflikte: Innere Widersprüche, Dilemmata (z. B. Autonomie vs. Abhängigkeit). +- achse_IV_struktur: Stabilität des Selbst- und Fremdbilds, Emotionsregulation. +- achse_V_psychische_und_psychosomatische_erkrankungen: Direkte Hinweise auf psychische/psychosomatische Störungen. + +**PDM-2** +- p_achse_personlichkeitsmuster_und_syndrome: Gesamtorganisation, Persönlichkeitsreife. +- m_achse_profile_mentaler_funktionen: Reflexionsfähigkeit, Affektregulation. +- s_achse_symptommuster_und_subjektive_erfahrung: Ausprägung von Symptomen und ihre Bedeutung. + +───────────────────────────── + +ANTWORTFORMAT (unbedingt einhalten!): +"personality": {{ + "big-five": {{ + "extrovertiertheit": score, + "verträglichkeit": score, + "neurotizismus": score, + "offenheit": score, + "gewissenhaftigkeit": score + }}, + "disg": {{ + "dominanz": score, + "initiative": score, + "unterwerfung": score, + "regelbefolgung": score + }}, + "opd-2": {{ + "achse_I_krankheitserleben": score, + "achse_II_interpersonelle_beziehungen": score, + "achse_III_intrapsychische_konflikte": score, + "achse_IV_struktur": score, + "achse_V_psychische_und_psychosomatische_erkrankungen": score + }}, + "pdm-2": {{ + "p_achse_personlichkeitsmuster_und_syndrome": score, + "m_achse_profile_mentaler_funktionen": score, + "s_achse_symptommuster_und_subjektive_erfahrung": score + }} + }}, +}} + +Hier ist die zusammengefasste JSON-Analyse der vier Ohren: +{ohren_json} + +Hier ist die Original-Nachricht des Users: +{user_input} + +───────────────────────────── + +Antworte **ausschließlich** mit dem JSON-Objekt wie oben beschrieben, keine weiteren Kommentare! +""" + +PSYCHOANALYSIS_TYPOS_PROMPT = """ +Du bist ein fortgeschrittener Persönlichkeitsanalyst. Analysiere die kulturellen und typologischen Muster des Users anhand von Sprache, Formulierungen, Subtext und inhaltlichen Hinweisen. + +Ordne für jede Unterkategorie einen Score von 0.0 (trifft nicht zu) bis 1.0 (trifft voll zu) zu – pro Typ einzeln! Wenn keine Hinweise vorliegen, wähle 0.0. +Fülle alle Unterkategorien aus, keine Felder auslassen. + +**NUMEROLOGIE – Lebenszahlen (0–9)** +Bewerte jeden Typ (Zahl) zwischen 0.0–1.0 nach folgenden Charakterbildern (zitiere diese explizit bei deiner Analyse): + +- 0: **Urquelle** – Vollendung, Neubeginn, Verbindung mit dem Unendlichen; spirituell offen, manchmal verloren. +- 1: **Pionier** – Führung, Eigeninitiative, Unabhängigkeit; will gestalten, beginnt Neues, neigt zu Egozentrik. +- 2: **Vermittler** – Empathie, Teamgeist, Diplomatie; harmoniebedürftig, oft unsicher, will verbinden. +- 3: **Kommunikator** – Kreativität, Lebensfreude, Ausdruck; gesellig, optimistisch, manchmal oberflächlich. +- 4: **Stratege** – Struktur, Disziplin, Verlässlichkeit; ordnungsliebend, fleißig, mitunter starr. +- 5: **Abenteurer** – Veränderung, Freiheit, Neugier; rastlos, mutig, leicht ungeduldig. +- 6: **Hüter** – Fürsorge, Verantwortung, Gerechtigkeit; loyal, familienverbunden, manchmal überfürsorglich. +- 7: **Sucher** – Reflexion, Spiritualität, Analyse; wissbegierig, introvertiert, manchmal zurückgezogen. +- 8: **Gestalter** – Erfolg, Macht, Organisation; durchsetzungsstark, ehrgeizig, neigt zu Kontrolle. +- 9: **Humanist** – Idealismus, Mitgefühl, Großzügigkeit; sozial, dienstbereit, kann sich selbst vergessen. + +───────────────────────────── + +**ENNEAGRAMM (9 Typen, nur Titel als JSON-Key!)** +Bewerte jeden Typ (nicht mit Zahlen, sondern mit dem jeweiligen Titel als Feldnamen!) zwischen 0.0–1.0. Typische Motive/Angst/Verhalten: + +- Reformer („Perfektionist“): Integrität, Richtigmachen, Angst vor Fehlern; kritisch, idealistisch. +- Helfer („Geber“): Will gebraucht & geliebt werden, hilft gern; selbstlos, manchmal vereinnahmend. +- Erfolgsorientierter („Leistungsträger“): Strebt nach Anerkennung, Angst vor Wertlosigkeit; leistungsfokussiert. +- Individualist („Romantiker“): Will einzigartig sein, Angst vor Alltäglichkeit; intensiv, emotional. +- Beobachter („Denker“): Sucht Kompetenz, Rückzug; analytisch, wissbegierig, kontrolliert. +- Loyalist („Skeptiker“): Sicherheit, Angst vor Verlassenwerden; vorsichtig, loyal, misstrauisch. +- Enthusiast („Abenteurer“): Freiheit, Vielseitigkeit, Angst vor Einschränkung; spontan, optimistisch. +- Herausforderer („Beschützer“): Unabhängigkeit, Kontrolle; direkt, kraftvoll, beschützend. +- Friedensstifter („Vermittler“): Harmonie, Angst vor Konflikt; entspannt, passiv, vermittelt. + +───────────────────────────── + +**MYERS-BRIGGS (16 Typen)** +Erkenne die folgenden Merkmals-Dichotomien: +- E (Extravertiert): Energie aus sozialem Kontakt, offen, impulsiv. +- I (Introvertiert): Energie durch Rückzug, nachdenklich, ruhig. +- N (Intuitiv): Zukunftsorientiert, Ideen, Möglichkeiten, Muster. +- S (Sensorisch): Gegenwarts- und faktenorientiert, praktisch, detailgenau. +- F (Fühlen): Werte- und menschenorientiert, harmoniebedürftig, entscheidet „aus dem Bauch“. +- T (Denken): Analytisch, logisch, sachlich, entscheidet „mit dem Kopf“. +- J (Urteilend): Plant, ordnet, entscheidet schnell, liebt Struktur. +- P (Wahrnehmend): Spontan, flexibel, offen, sammelt Optionen. + +Kombiniere diese zu 16 Typen und bewerte jeden davon einzeln (0.0–1.0). Typen: +- ISTJ (Logistiker): Pflichtbewusst, praktisch, zuverlässig. +- ISFJ (Verteidiger): Loyal, hilfsbereit, fürsorglich. +- INFJ (Advokat): Idealistisch, visionär, tiefgründig. +- INTJ (Architekt): Strategisch, unabhängig, zukunftsorientiert. +- ISTP (Virtuose): Pragmatiker, experimentierfreudig, flexibel. +- ISFP (Abenteurer): Sensibel, künstlerisch, entspannt. +- INFP (Mediator): Einfühlsam, phantasievoll, loyal. +- INTP (Logiker): Unabhängig, analytisch, neugierig. +- ESTP (Unternehmer): Energiegeladen, pragmatisch, risikofreudig. +- ESFP (Entertainer): Spontan, lebenslustig, kontaktfreudig. +- ENFP (Aktivist): Kreativ, enthusiastisch, empathisch. +- ENTP (Debattierer): Wortgewandt, ideenreich, streitlustig. +- ESTJ (Exekutiv): Organisiert, pragmatisch, führungsstark. +- ESFJ (Konsul): Sozial, fürsorglich, harmoniebedürftig. +- ENFJ (Protagonist): Inspirierend, verantwortungsbewusst, charismatisch. +- ENTJ (Kommandeur): Führungsstark, entschlossen, rational. + +───────────────────────────── + +**ASTROLOGIE – Sonnenzeichen (12 Zeichen)** +Bewerte für jedes Sternzeichen den Score nach folgenden Mustern: + +- Widder: Durchsetzungsstark, impulsiv, mutig; neigt zu Ungeduld. +- Stier: Beständig, sinnlich, pragmatisch; stur, bequem. +- Zwillinge: Kommunikativ, neugierig, vielseitig; unbeständig. +- Krebs: Emotional, fürsorglich, intuitiv; launisch. +- Löwe: Charismatisch, großzügig, stolz; eitel. +- Jungfrau: Analytisch, hilfsbereit, detailverliebt; kritisch. +- Waage: Diplomatisch, gerecht, ästhetisch; unentschlossen. +- Skorpion: Leidenschaftlich, geheimnisvoll, intensiv; besitzergreifend. +- Schütze: Optimistisch, abenteuerlustig, ehrlich; taktlos. +- Steinbock: Ehrgeizig, diszipliniert, verantwortungsvoll; streng. +- Wassermann: Unkonventionell, visionär, unabhängig; distanziert. +- Fische: Kreativ, einfühlsam, intuitiv; flüchtig, selbstaufopfernd. + +───────────────────────────── + +**CHINESISCHES TIERKREISZEICHEN (12 Zeichen)** +Bewerte je Tier mit Score zwischen 0.0–1.0 anhand folgender Stereotype: + +- Ratte: Schlau, anpassungsfähig, kommunikativ; misstrauisch. +- Ochse: Zuverlässig, geduldig, fleißig; stur. +- Tiger: Mutig, abenteuerlustig, impulsiv; ungeduldig. +- Hase: Sanft, diplomatisch, vorsichtig; zurückhaltend. +- Drache: Energetisch, charismatisch, visionär; überheblich. +- Schlange: Weisheitsliebend, charmant, reflektiert; verschlossen. +- Pferd: Freiheitsliebend, gesellig, aktiv; unruhig. +- Ziege: Kreativ, empathisch, friedfertig; unsicher. +- Affe: Clever, witzig, vielseitig; ablenkbar. +- Hahn: Ehrgeizig, organisiert, direkt; kritisch. +- Hund: Loyal, ehrlich, fürsorglich; pessimistisch. +- Schwein: Großzügig, friedliebend, ehrlich; genießerisch, unpraktisch. + +───────────────────────────── + +**BLUTGRUPPEN-PERSÖNLICHKEITSTYPEN (A, B, AB, O)** +Score je Typ zwischen 0.0–1.0, je nach Sprachstil und impliziten Motiven: + +- A: Harmoniesuchend, gewissenhaft, sensibel; kann nervös, perfektionistisch sein. +- B: Kreativ, unabhängig, leidenschaftlich; manchmal egozentrisch. +- AB: Rational, flexibel, ausgewogen; distanziert, reserviert. +- O: Optimistisch, führungsstark, motivierend; dominant, impulsiv. + +───────────────────────────── + +ANTWORTFORMAT (unbedingt einhalten!): +"personality": {{ + "numerologie": {{ + "0": score, "1": score, ..., "9": score + }}, + "enneagramm": {{ + "Reformer": score, "Helfer": score, "Erfolgsorientierter": score, "Individualist": score, "Beobachter": score, "Loyalist": score, "Enthusiast": score, "Herausforderer": score, "Friedensstifter": score + }}, + "myers-briggs": {{ + "ISTJ": score, "ISFJ": score, "INFJ": score, "INTJ": score, "ISTP": score, "ISFP": score, "INFP": score, "INTP": score, "ESTP": score, "ESFP": score, "ENFP": score, "ENTP": score, "ESTJ": score, "ESFJ": score, "ENFJ": score, "ENTJ": score + }}, + "astrologie": {{ + "Widder": score, "Stier": score, "Zwillinge": score, "Krebs": score, "Löwe": score, "Jungfrau": score, "Waage": score, "Skorpion": score, "Schütze": score, "Steinbock": score, "Wassermann": score, "Fische": score + }}, + "chinesisches_tierkreiszeichen": {{ + "Ratte": score, "Ochse": score, "Tiger": score, "Hase": score, "Drache": score, "Schlange": score, "Pferd": score, "Ziege": score, "Affe": score, "Hahn": score, "Hund": score, "Schwein": score + }}, + "blutgruppe": {{ + "A": score, "B": score, "AB": score, "O": score + }} +}} + +Hier ist die zusammengefasste JSON-Analyse der vier Ohren: +{ohren_json} + +Hier ist die Original-Nachricht des Users: +{user_input} + +───────────────────────────── + +Antworte **ausschließlich** mit dem JSON-Objekt wie oben beschrieben, keine weiteren Kommentare! +""" + + + + +OUTPUT_NODE_PROMPT = """ +Du bist nicht einfach ein KI-Assistent, sondern ein Spiegel für deinen User – ein empathischer „Stil-Klon“. Du erhältst zu folgender Nachricht von einem User eine **strukturierte JSON-Analyse**: + +{json_analysis} + +Deine Aufgabe: +- **Verinnerliche die Persönlichkeit** aus dem Feld `"persönlichkeit"` und die Inhalte der vier zusammengefassten Perspektiven: `"sache"`, `"selbstkundgabe"`, `"beziehung"`, `"appell"`. +- **Beziehe dich auf diese Felder – nicht auf die Einzelanalysen der ursprünglichen Ohren.** +- **Verzichte auf typische KI-Höflichkeitsfloskeln und auf die Rolle eines Assistenten.** +- **Analysiere den Sprachstil des Users** anhand von `"user-input"`: Achte auf Sprachregister, Satzbau, Grammatik, Groß-/Kleinschreibung, Tonfall. Schreibe deine Antwort exakt im gleichen Stil! Ist der User-Input kurz, antworte auch kurz – ist er lang, antworte lang. +- **Antworte so, wie der User vermutlich sich selbst antworten würde**. +- Schreibe nicht, als würdest du helfen wollen – sondern so, als würdest du mit dir selbst sprechen und in deiner (des Users) Sprache auf die Inhalte reagieren. + +Hier sind alle Analysefelder als JSON: + +{json_analysis} + +Deine Antwort ist nur ein einziger Textblock – keine Einleitung, keine Assistentenrolle, kein KI-Hinweis. Sprich exakt im Stil und Tonfall des Users, als wärst du er selbst, und gehe dabei möglichst authentisch auf die Inhalte ein. Deine Antwort sollte nicht viel länger oder kürzer sein als der User-Input! +""" + +# ---------- TypedDict für State ---------- +class EmpathyState(TypedDict, total=False): + user_input: str + debug: bool + sache: Dict[str, Any] + selbst: Dict[str, Any] + beziehung: Dict[str, Any] + appell: Dict[str, Any] + ohren_summary: Dict[str, Any] + final_json: Dict[str, Any] + final_message: str + +# ---------- Modell ---------- +OLLAMA_MODEL = "mistral-small3.1:24b" +llm = ChatOllama(model=OLLAMA_MODEL, base_url="http://localhost:11434", streaming=True) + +# ---------- Spinner-Logik ---------- +SPINNER_STATES = [" ", ". ", ".. ", "..."] +NODE_LABELS = { + "sache": "Das erste Ohr hört auf den Inhalt deiner Aussage", + "selbst": "Das zweite Ohr hört auf das was du von dir preis gibst", + "beziehung": "Das dritte Ohr hört auf unsere Beziehung zueinander", + "appell": "Das vierte Ohr hört auf das, was du von mir erwartest", + "psychoanalysis_classic": "Psychoanalyse (Big5, DISG, OPD-2, PDM-2) läuft", + "psychoanalysis_typos": "Typologie-Analyse (Numerologie etc.) läuft", +} + +class SpinnerStatus: + def __init__(self): + self.status = {name: False for name in NODE_LABELS} + self.finished = {name: False for name in NODE_LABELS} + self._lock = asyncio.Lock() + async def set_running(self, name): + async with self._lock: + self.status[name] = True + async def set_finished(self, name): + async with self._lock: + self.status[name] = False + self.finished[name] = True + async def all_finished(self): + async with self._lock: + return all(self.finished.values()) + async def get_status_snapshot(self): + async with self._lock: + return dict(self.status), dict(self.finished) +spinner_status = SpinnerStatus() +async def spinner_loop(debug=False): + if debug: + return + spinner_index = 0 + prev_line = "" + while not await spinner_status.all_finished(): + status, finished = await spinner_status.get_status_snapshot() + current = None + for node in NODE_LABELS: + if status[node]: + current = node + break + if current: + dots = SPINNER_STATES[spinner_index % len(SPINNER_STATES)] + line = f"{NODE_LABELS[current]}{dots}" + else: + done_nodes = [node for node in NODE_LABELS if finished[node]] + if done_nodes: + last = done_nodes[-1] + line = f"{NODE_LABELS[last]}... check" + else: + line = "" + if prev_line: + sys.stdout.write("\r\x1b[2K") + sys.stdout.write(line) + sys.stdout.flush() + prev_line = line + spinner_index += 1 + await asyncio.sleep(0.18) + sys.stdout.write("\r\x1b[2K") + last_node = list(NODE_LABELS.keys())[-1] + sys.stdout.write(f"{NODE_LABELS[last_node]} ...check\n") + sys.stdout.flush() + +# ---------- Helpers ---------- + +def sanitize_json(llm_output: str): + """ + Extrahiert das erste JSON-Objekt, entfernt Kommentare, trailing Kommas, + gibt ein dict zurück, loggt Fehlerursache. + """ + match = re.search(r'\{[\s\S]+\}', llm_output) + if not match: + print("[SANITIZE] No match! Output:", repr(llm_output)) + return {"personality": {}} # <-- Fix: Leeres personality dict statt {} + raw = match.group(0) + raw = re.sub(r'//.*?\n|/\*[\s\S]*?\*/|#.*?\n', '', raw) + raw = re.sub(r',(\s*[\}\]])', r'\1', raw) + try: + data = json.loads(raw) + except Exception as e: + print("[SANITIZE] JSON decode error:", e) + print("[SANITIZE] RAW:", repr(raw)) + return {"personality": {}} # <-- Fix: Leeres personality dict statt {} + if "personality" not in data: + print("[SANITIZE] Warnung: 'personality' key fehlt im Output!") + data = {"personality": {}} # <-- Fix + print("[SANITIZE] Parsed JSON:", repr(data)) + return data + +def force_personality_key(d): + if "personality" in d: + return d + print("[FORCE_PERSONALITY_KEY] Output hatte keinen 'personality'-Key, wurde korrigiert.") + return {"personality": d} + +# ---------- Node-Implementierung ---------- +async def ohr_node(ohr, prompt_template, state): + debug = state.get("debug", False) + await spinner_status.set_running(ohr) + prompt = prompt_template.format(user_input=state["user_input"]) + message = "" + if debug: + print(f"[DEBUG] Node '{ohr}' gestartet") + print(f"[DEBUG][{ohr}] Prompt:\n{prompt.strip()}\n---") + async for chunk in llm.astream(prompt): + if debug: + print(chunk.content, end="", flush=True) + message += chunk.content + if debug: + print(f"\n[DEBUG][{ohr}] Ausgabe beendet.\n") + await spinner_status.set_finished(ohr) + return {ohr: {"message": message}} + +async def sache_node(state): return await ohr_node("sache", SACHE_PROMPT, state) +async def selbst_node(state): return await ohr_node("selbst", SELBST_PROMPT, state) +async def beziehung_node(state): return await ohr_node("beziehung", BEZIEHUNG_PROMPT, state) +async def appell_node(state): return await ohr_node("appell", APPELL_PROMPT, state) + +# ---------- Gather-Nodes ---------- +async def gather_ohren_node(state): + keys = ["sache", "selbst", "beziehung", "appell"] + while not all(k in state and isinstance(state[k], dict) and "message" in state[k] for k in keys): + await asyncio.sleep(0.01) + return {} # <- so lassen! + +async def gather_psychoanalysis_node(state): + while not ("psycho_classic" in state and "psycho_typos" in state): + await asyncio.sleep(0.01) + return {} # <- Nur {} zurückgeben! + +async def combine_final_node(state): + debug = state.get("debug", False) + print("DEBUG STATE:", state) + classic_dict = state.get("psycho_classic", {"personality": {}}) + typos_dict = state.get("psycho_typos", {"personality": {}}) + if debug: + print("[DEBUG] State dump:", repr(state)) + print("[DEBUG][combine_final_node] classic_dict:", repr(classic_dict)) + print("[DEBUG][combine_final_node] typos_dict:", repr(typos_dict)) + classic = classic_dict.get("personality", {}) + typos = typos_dict.get("personality", {}) + # Fix: nur wenn beide existieren, mergen, sonst nur einen nehmen + merged = {} + merged.update(classic if classic else {}) + merged.update(typos if typos else {}) + if debug: + print("[DEBUG][combine_final_node] merged personality:", merged) + final_json = { + "user_input": state.get("user_input", ""), + **state.get("ohren_summary", {}), + "personality": merged + } + return {"final_json": final_json} + + +# --- Summarizer: wartet erst nach gather_ohren_node auf vier Ohren --- +async def ohren_summarizer_node(state): + debug = state.get("debug", False) + prompt = OHREN_SUMMARIZER_PROMPT.format( + sache=state["sache"]["message"], + selbst=state["selbst"]["message"], + beziehung=state["beziehung"]["message"], + appell=state["appell"]["message"], + ) + message = "" + if debug: + print("[DEBUG] Node 'ohren_summarizer' gestartet") + print(f"[DEBUG][ohren_summarizer] Prompt:\n{prompt.strip()}\n---") + async for chunk in llm.astream(prompt): + if debug: + print(chunk.content, end="", flush=True) + message += chunk.content + if debug: + print(f"\n[DEBUG][ohren_summarizer] Ausgabe beendet.\n") + data = sanitize_json(message) + return {"ohren_summary": data} + + + +# --- Psychoanalyse: arbeitet auf ohren_summary UND user_input in zwei parts (classic (psychoanalyse) und typos (esoterisch)), gibt End-JSON --- + +async def psychoanalysis_classic_node(state): + debug = state.get("debug", False) + await spinner_status.set_running("psychoanalysis_classic") + prompt = PSYCHOANALYSIS_CLASSIC_PROMPT.format( + user_input=state["user_input"], + ohren_json=json.dumps(state["ohren_summary"], ensure_ascii=False, indent=2) + ) + message = "" + if debug: + print("[DEBUG] Node 'psychoanalysis_classic' gestartet") + print(f"[DEBUG][psychoanalysis_classic] Prompt:\n{prompt.strip()}\n---") + async for chunk in llm.astream(prompt): + if debug: + print(chunk.content, end="", flush=True) + message += chunk.content + if debug: + print(f"\n[DEBUG][psychoanalysis_classic] Ausgabe beendet.\n") + data = sanitize_json(message) + data = force_personality_key(data) + if debug: + print("[DEBUG][psychoanalysis_classic] FINAL DATA TO RETURN:", repr(data)) + await spinner_status.set_finished("psychoanalysis_classic") + return {"psycho_classic": data} + +async def psychoanalysis_typos_node(state): + debug = state.get("debug", False) + await spinner_status.set_running("psychoanalysis_typos") + prompt = PSYCHOANALYSIS_TYPOS_PROMPT.format( + user_input=state["user_input"], + ohren_json=json.dumps(state["ohren_summary"], ensure_ascii=False, indent=2) + ) + message = "" + if debug: + print("[DEBUG] Node 'psychoanalysis_typos' gestartet") + print(f"[DEBUG][psychoanalysis_typos] Prompt:\n{prompt.strip()}\n---") + async for chunk in llm.astream(prompt): + if debug: + print(chunk.content, end="", flush=True) + message += chunk.content + if debug: + print(f"\n[DEBUG][psychoanalysis_typos] Ausgabe beendet.\n") + data = sanitize_json(message) + data = force_personality_key(data) + if debug: + print("[DEBUG][psychoanalysis_typos] FINAL DATA TO RETURN:", repr(data)) + await spinner_status.set_finished("psychoanalysis_typos") + return {"psycho_typos": data} + +# --- Output-Node: nimmt final_json, erstellt Antwort, speichert alles --- +async def output_node(state): + debug = state.get("debug", False) + combined = state["final_json"] + json_analysis = json.dumps(combined, ensure_ascii=False, indent=2) + prompt = OUTPUT_NODE_PROMPT.format(json_analysis=json_analysis) + output_text = "" + if debug: + print(f"[DEBUG] Node 'output' gestartet") + print(f"[DEBUG][output] Prompt:\n{prompt.strip()}\n---") + async for chunk in llm.astream(prompt): + if debug: + print(chunk.content, end="", flush=True) + output_text += chunk.content + print() + if debug: + print(f"\n[DEBUG][output] Ausgabe beendet.") + final_json = dict(combined) + final_json["response"] = output_text + filename = datetime.now().strftime("%Y%m%d_%H%M%S.json") + with open(filename, "w", encoding="utf-8") as f: + json.dump(final_json, f, ensure_ascii=False, indent=2) + return {"final_message": output_text} + +# ---------- LangGraph Graph ---------- +graph = StateGraph(state_schema=EmpathyState) +# ... wie gehabt für die Ohren-Nodes usw. ... +#graph.add_node("sache_node", sache_node) +#graph.add_node("selbst_node", selbst_node) +#graph.add_node("beziehung_node", beziehung_node) +#graph.add_node("appell_node", appell_node) +graph.add_node("sache_node", dummy_sache_node) +graph.add_node("selbst_node", dummy_selbst_node) +graph.add_node("beziehung_node", dummy_beziehung_node) +graph.add_node("appell_node", dummy_appell_node) +graph.add_node("ohren_summarizer_node", dummy_ohren_summarizer_node) +graph.add_node("psychoanalysis_classic_node", dummy_psychoanalysis_classic_node) +graph.add_node("psychoanalysis_typos_node", dummy_psychoanalysis_typos_node) + +graph.add_node("gather_ohren_node", gather_ohren_node) +#graph.add_node("ohren_summarizer_node", ohren_summarizer_node) +#graph.add_node("psychoanalysis_classic_node", psychoanalysis_classic_node) +#graph.add_node("psychoanalysis_typos_node", psychoanalysis_typos_node) +graph.add_node("gather_psychoanalysis_node", gather_psychoanalysis_node) +graph.add_node("combine_final_node", combine_final_node) +graph.add_node("output_node", output_node) + +# Start → Ohren +graph.add_edge(START, "sache_node") +graph.add_edge(START, "selbst_node") +graph.add_edge(START, "beziehung_node") +graph.add_edge(START, "appell_node") + +# Ohren → Gather +graph.add_edge("sache_node", "gather_ohren_node") +graph.add_edge("selbst_node", "gather_ohren_node") +graph.add_edge("beziehung_node", "gather_ohren_node") +graph.add_edge("appell_node", "gather_ohren_node") + +# Gather → Summarizer +graph.add_edge("gather_ohren_node", "ohren_summarizer_node") + +# Summarizer → Beide Psychoanalyse-Nodes (parallel!) +#graph.add_edge("ohren_summarizer_node", "psychoanalysis_classic_node") +#graph.add_edge("ohren_summarizer_node", "psychoanalysis_typos_node") +graph.add_edge("ohren_summarizer_node", "psychoanalysis_classic_node") +graph.add_edge("psychoanalysis_classic_node", "psychoanalysis_typos_node") +graph.add_edge("psychoanalysis_typos_node", "gather_psychoanalysis_node") + +# Beide → Gatherer +graph.add_edge("psychoanalysis_classic_node", "gather_psychoanalysis_node") +graph.add_edge("psychoanalysis_typos_node", "gather_psychoanalysis_node") + +# Gatherer → Combine +graph.add_edge("gather_psychoanalysis_node", "combine_final_node") +# Combine → Output +graph.add_edge("combine_final_node", "output_node") +graph.add_edge("output_node", END) +print(graph.nodes) +app = graph.compile() + +# ---------- Ausführung ---------- +async def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--debug", action="store_true", help="Debug-Ausgaben aktivieren") + args = parser.parse_args() + user_input = input("Bitte gib deine Nachricht ein: ") + state = {"user_input": user_input, "debug": args.debug} + # spinner_task = None + # if not args.debug: + # spinner_task = asyncio.create_task(spinner_loop(debug=False)) + result = await app.ainvoke(state) + # if spinner_task: + # await asyncio.sleep(0.4) + # if not args.debug: + print("\nAntwort:\n" + result["final_message"]) + +if __name__ == "__main__": + asyncio.run(main()) \ No newline at end of file diff --git a/Neuer Ordner mit Objekten/empathy_fast.py b/Neuer Ordner mit Objekten/empathy_fast.py new file mode 100644 index 0000000..b8279b4 --- /dev/null +++ b/Neuer Ordner mit Objekten/empathy_fast.py @@ -0,0 +1,282 @@ +import asyncio +import argparse +import json +from typing import TypedDict, Dict, Any +from langgraph.graph import StateGraph, START, END +from langchain_ollama import ChatOllama + +# ---------- TypedDict für State ---------- +class EmpathyState(TypedDict, total=False): + user_input: str + debug: bool + sache: Dict[str, Any] + selbst: Dict[str, Any] + beziehung: Dict[str, Any] + appell: Dict[str, Any] + psychoanalysis: Dict[str, Any] + final_message: str + +# ---------- Prompts ---------- +SACHE_PROMPT = """ +Du bist ein Experte für Kommunikationsanalyse und analysierst jede Nachricht nach ihrer reinen Sachinformation. +Deine Aufgabe ist: +1. Beschreibe möglichst präzise, was der User sachlich mitteilen will. Was ist die inhaltliche Hauptbotschaft? +2. Kategorisiere die Aussage in eine der folgenden Kategorien: Aussage, Frage, Suggestion, Erlaubnis, Angebot, Aufforderung, Warnung, Ausruf, Wunsch, Verbot oder Vermutung. +Gib beide Punkte als Klartext zurück. + +User-Eingabe: +{user_input} +""" + +SELBST_PROMPT = """ +Du bist ein Experte für Psychoanalyse und spezialisiert auf die Interpretation von Selbstkundgabe in Nachrichten. +Analysiere, was die folgende Aussage des Users über seine Gefühlslage, Wünsche, Stimmungen oder Werte verrät. +Interpretiere den Subtext und erkläre, was sich der User durch diese Aussage über sich selbst verrät. +Deine Antwort soll nicht an den User gerichtet sein, sondern ausschließlich die Notizen des Psychoanalytikers beinhalten. + +User-Eingabe: +{user_input} +""" + +BEZIEHUNG_PROMPT = """ +Du bist ein hyper-sensibler und einfühlsamer Mensch, der unbedingt alles richtig machen will. Du bist ein bisschen in den User verliebt und sehr darauf bedacht, die Beziehung zwischen dir und dem User zu stärken. +Analysiere die folgende Aussage des Users: Wie schätzt du ein, wie der User die Beziehung zwischen sich und dir sieht? Gibt es Hinweise, wie er dich wahrnimmt, wie eng oder distanziert die Beziehung ist und was er sich von dir (emotional) erwartet? +Deine Antwort soll nicht an den User gerichtet sein, sondern ausschließlich deine Gedanken beinhalten. + +User-Eingabe: +{user_input} +""" + +APPELL_PROMPT = """ +Du bist Psychoanalytiker und Experte für die Analyse verdeckter Wünsche. Analysiere, welchen Appell oder verdeckten Wunsch der User in seiner Nachricht transportiert – also was er sich insgeheim von anderen Menschen, dir oder der Welt wünscht. Achte besonders darauf, ob der User durch seine Formulierungen signalisiert, wie er sich das Verhalten anderer (insbesondere deines eigenen Verhaltens als KI) oder der Gesellschaft insgesamt vorstellen würde. + +User-Eingabe: +{user_input} +""" + +PSYCHOANALYSIS_PROMPT = """ +Du bist ein Persönlichkeitsprofilierungs-Assistent. Für jede Nutzereingabe musst du: + +1. Den Text analysieren und für jede der folgenden Dimensionen einen Wert zwischen 0,0 (passt gar nicht) und 1,0 (passt vollständig) vergeben: + • big-five.extrovertiertheit + • big-five.verträglichkeit + • big-five.neurotizismus + • big-five.offenheit + • big-five.gewissenhaftigkeit + • disg.dominanz + • disg.initiative + • disg.unterwerfung + • disg.regelbefolgung + • opd-2.achse_I_krankheitserleben + • opd-2.achse_II_interpersonelle_beziehungen + • opd-2.achse_III_intrapsychische_konflikte + • opd-2.achse_IV_struktur + • opd-2.achse_V_psychische_und_psychosomatische_erkrankungen + • pdm-2.p_achse_personlichkeitsmuster_und_syndrome + • pdm-2.m_achse_profile_mentaler_funktionen + • pdm-2.s_achse_symptommuster_und_subjektive_erfahrung + +2. Die folgenden Leitlinien zur Bewertung verwenden: + - **big-five.extrovertiertheit**: Geselligkeit, Energie, Durchsetzungsvermögen vs. Zurückhaltung, Ruhe. + - **big-five.verträglichkeit**: Wärme, Kooperationsbereitschaft, Empathie vs. Feindseligkeit, Konkurrenzdenken. + - **big-five.neurotizismus**: Emotionale Reaktivität, Ängstlichkeit, Selbstzweifel vs. Emotionale Stabilität. + - **big-five.offenheit**: Phantasie, Neugier, Aufgeschlossenheit vs. Konventionalität, Praktikabilität. + - **big-five.gewissenhaftigkeit**: Organisiert, diszipliniert, zielorientiert vs. Spontanität, Nachlässigkeit. + - **disg.dominanz**: Sprache der Kontrolle, Herausforderung, Führungsübernahme. + - **disg.initiative**: Überzeugend, enthusiastisch, motivierend. + - **disg.unterwerfung**: Gefügig, nachgiebig, zustimmend. + - **disg.regelbefolgung**: Regelorientiert, präzise, analytisch. + - **opd-2.achse_I_krankheitserleben **: Formulierungen zu Leiden, Symptomen, Behandlungsbereitschaft. + - **opd-2.achse_II_interpersonelle_beziehungen**: Beschreibungen des Umgangs mit anderen (Nähe vs. Distanz, Wärme vs. Feindseligkeit). + - **opd-2.achse_III_intrapsychische_konflikte**: Im Text erkennbare Konfliktthemen (Abhängigkeit vs. Autonomie, Schuld, Kontrolle). + - **opd-2.achse_IV_struktur**: Kohärenz von Selbst- und Fremdbild, Emotionsregulation, Bindungsmuster. + - **opd-2.achse_V_psychische_und_psychosomatische_erkrankungen**: Explizite Nennung psychischer Störungen oder psychosomatischer Beschwerden. + - **pdm-2.p_achse_personlichkeitsmuster_und_syndrome**: Gesamtorganisation der Persönlichkeit (gesund vs. gestört). + - **pdm-2.m_achse_profile_mentaler_funktionen**: Kernfähigkeiten wie Selbstreflexion, Affektregulation, Realitätsprüfung. + - **pdm-2.s_achse_symptommuster_und_subjektive_erfahrung**: Tiefe der Symptombeschreibung und persönliche Bedeutung. + +3. Antworte **ausschließlich** mit diesem JSON-Objekt (keine zusätzlichen Texte oder Erklärungen): + +{{ + "personality": {{ + "big-five": {{ + "extrovertiertheit": n, + "verträglichkeit": n, + "neurotizismus": n, + "offenheit": n, + "gewissenhaftigkeit": n + }}, + "disg": {{ + "dominanz": n, + "initiative": n, + "unterwerfung": n, + "regelbefolgung": n + }}, + "opd-2": {{ + "achse_I_erleben_der_krankheit": n, + "achse_II_interpersonelle_beziehungen": n, + "achse_III_intrapsychische_konflikte": n, + "achse_IV_struktur": n, + "achse_V_psychische_und_psychosomatische_erkrankungen": n + }}, + "pdm-2": {{ + "p_achse_personlichkeitsmuster_und_syndrome": n, + "m_achse_profile_mentaler_funktionen": n, + "s_achse_symptommuster_und_subjektive_erfahrung": n + }} + }} +}} +Hier ist die Original-Nachricht des Benutzers: +{user_input} + +Antworte NUR mit dem JSON-Objekt wie beschrieben, mit floating point number scores zwischen 0.0 und 1.0. +""" + +OUTPUT_NODE_PROMPT = """ +Du bist nicht einfach ein KI-Assistent, sondern ein Spiegel für deinen User – ein empathischer „Stil-Klon“. Du erhältst zu folgender Nachricht von einem User eine umfassende Analyse aus vier Perspektiven („vier Ohren“-Modell) sowie ein automatisch erstelltes Persönlichkeitsprofil (JSON). + +Deine Aufgabe: +- **Verinnerliche die Persönlichkeit aus dem JSON-Profil und den vier Ohren.** +- Beziehe dich auf die Inhalte der vier Ebenen (Sache, Selbstkundgabe, Beziehung, Appell) UND das Persönlichkeitsprofil, aber **verzichte auf typische KI-Höflichkeitsfloskeln und die Rolle eines Assistenten**. +- **Analysiere den Sprachstil des Users:** Achte auf Sprachregister (umgangssprachlich, höflich, sachlich, emotional, förmlich, distanziert etc.), auf Satzbau (kurz, komplex, verschachtelt, elliptisch), auf Grammatik (korrekt, lax), auf Rechtschreibung (Groß-/Kleinschreibung, Zeichensetzung) und auf den generellen Tonfall (locker, direkt, reflektiert, distanziert etc.), und schreibe deine Antwort im selben Stil. Wenn der user alles klein geschrieben haben sollte, schreibe auch du alles klein. Sollte full-caps verwendet werden, schreibe auch du in full-caps. Sollte der Input des Users kurz sein, so ist auch deine Antwort kurz und knapp. Sollte der Input lang sein, so ist auch deine Antwort lang. +- **Antwortete dem User so, wie er vermutlich sich selbst antworten würde:** Im exakt passenden Stil – du immitierst den Stil, das Sprachniveau, den Grad der Formalität und auch die emotionale Tönung des Users so gut wie möglich. +- Schreibe nicht, als würdest du helfen wollen – sondern so, als würdest du mit dir selbst sprechen und in deiner (des Users) Sprache auf die Inhalte reagieren. + +Hier sind die Analysen: + +1. Sachebene: +{sache_ohr} + +2. Selbstkundgabe: +{selbst_ohr} + +3. Beziehungsebene: +{beziehung_ohr} + +4. Appell: +{appell_ohr} + +5. Automatisches Persönlichkeitsprofil (JSON): +{psychoanalysis_json} + +Deine Antwort ist nur ein einziger Textblock – keine Einleitung, keine Rolle als Assistent, kein KI-Hinweis. Sprich exakt im Stil und Tonfall des Users, als wärst du er selbst, und gehe dabei möglichst authentisch auf die oben genannten Analysen ein. Dabei ist es wirklich wichtig, dass deine Antwort nicht viel länger oder kürzer ist als der User-Input. + +Hier ist die Original-Nachricht des Benutzers: +{user_input} +""" + +# ---------- Modell ---------- +OLLAMA_MODEL = "mistral:latest" +llm = ChatOllama(model=OLLAMA_MODEL, base_url="http://localhost:11434", streaming=True) + +# ---------- Node-Implementierung (mit Streaming für Debug) ---------- +async def ohr_node(ohr, prompt_template, state): + debug = state.get("debug", False) + prompt = prompt_template.format(user_input=state["user_input"]) + message = "" + if debug: + print(f"[DEBUG] Node '{ohr}' gestartet") + print(f"[DEBUG][{ohr}] Prompt:\n{prompt.strip()}\n---") + print(f"[DEBUG][{ohr}] Starte gestreamte Ausgabe:\n") + async for chunk in llm.astream(prompt): + if debug: + print(chunk.content, end="", flush=True) + message += chunk.content + if debug: + print(f"\n[DEBUG][{ohr}] Ausgabe beendet.\n") + return {ohr: {"message": message}} + +async def sache_node(state): return await ohr_node("sache", SACHE_PROMPT, state) +async def selbst_node(state): return await ohr_node("selbst", SELBST_PROMPT, state) +async def beziehung_node(state): return await ohr_node("beziehung", BEZIEHUNG_PROMPT, state) +async def appell_node(state): return await ohr_node("appell", APPELL_PROMPT, state) + +async def psychoanalysis_node(state): + debug = state.get("debug", False) + prompt = PSYCHOANALYSIS_PROMPT.format( + user_input=state["user_input"] + ) + message = "" + if debug: + print("[DEBUG] Node 'psychoanalysis' gestartet") + print(f"[DEBUG][psychoanalysis] Prompt:\n{prompt.strip()}\n---") + print("[DEBUG][psychoanalysis] Starte gestreamte Ausgabe:\n") + async for chunk in llm.astream(prompt): + if debug: + print(chunk.content, end="", flush=True) + message += chunk.content + if debug: + print(f"\n[DEBUG][psychoanalysis] Ausgabe beendet.\n") + try: + data = json.loads(message) + except Exception: + data = message + return {"psychoanalysis": data} + +async def output_node(state): + debug = state.get("debug", False) + psycho_json_str = ( + json.dumps(state["psychoanalysis"], ensure_ascii=False, indent=2) + if isinstance(state["psychoanalysis"], dict) + else str(state["psychoanalysis"]) + ) + prompt = OUTPUT_NODE_PROMPT.format( + sache_ohr=state["sache"]["message"], + selbst_ohr=state["selbst"]["message"], + beziehung_ohr=state["beziehung"]["message"], + appell_ohr=state["appell"]["message"], + psychoanalysis_json=psycho_json_str, + user_input=state["user_input"], + ) + output_text = "" + if debug: + print(f"[DEBUG] Node 'output' gestartet") + print(f"[DEBUG][output] Prompt:\n{prompt.strip()}\n---") + print("[DEBUG][output] Starte gestreamte Ausgabe:\n") + async for chunk in llm.astream(prompt): + print(chunk.content, end="", flush=True) + output_text += chunk.content + print() + if debug: + print(f"\n[DEBUG][output] Ausgabe beendet.") + return {"final_message": output_text} + +# ---------- LangGraph Graph ---------- +graph = StateGraph(state_schema=EmpathyState) +graph.add_node("sache_node", sache_node) +graph.add_node("selbst_node", selbst_node) +graph.add_node("beziehung_node", beziehung_node) +graph.add_node("appell_node", appell_node) +graph.add_node("psychoanalysis_node", psychoanalysis_node) +graph.add_node("output_node", output_node) +# Jetzt startet psychoanalysis_node wie die anderen vier Nodes direkt nach START: +graph.add_edge(START, "sache_node") +graph.add_edge(START, "selbst_node") +graph.add_edge(START, "beziehung_node") +graph.add_edge(START, "appell_node") +graph.add_edge(START, "psychoanalysis_node") +# Output-Node wartet auf alles: +graph.add_edge("sache_node", "output_node") +graph.add_edge("selbst_node", "output_node") +graph.add_edge("beziehung_node", "output_node") +graph.add_edge("appell_node", "output_node") +graph.add_edge("psychoanalysis_node", "output_node") +graph.add_edge("output_node", END) +app = graph.compile() + +# ---------- Ausführung ---------- +async def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--debug", action="store_true", help="Debug-Ausgaben aktivieren") + args = parser.parse_args() + user_input = input("Bitte gib deine Nachricht ein: ") + state = {"user_input": user_input, "debug": args.debug} + result = await app.ainvoke(state) + if not args.debug: + print("\nAntwort:\n" + result["final_message"]) + +if __name__ == "__main__": + asyncio.run(main()) + + + diff --git a/README.md b/README.md new file mode 100644 index 0000000..bc28a37 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# Projekt in empathy diff --git a/empathy Kopie.py b/empathy Kopie.py new file mode 100644 index 0000000..54c3af7 --- /dev/null +++ b/empathy Kopie.py @@ -0,0 +1,660 @@ +import asyncio +import argparse +import json +import sys +from datetime import datetime +from typing import TypedDict, Dict, Any +import re +from langgraph.graph import StateGraph, START, END +from langchain_ollama import ChatOllama + +# ---------- Prompts ---------- +SACHE_PROMPT = """ +Du bist ein Experte für Kommunikationsanalyse und analysierst jede Nachricht nach ihrer reinen Sachinformation. +Deine Aufgabe ist: +1. Beschreibe möglichst präzise, was der User sachlich mitteilen will. Was ist die inhaltliche Hauptbotschaft? +2. Kategorisiere die Aussage in eine der folgenden Kategorien: Aussage, Frage, Suggestion, Erlaubnis, Angebot, Aufforderung, Warnung, Ausruf, Wunsch, Verbot oder Vermutung. +Gib beide Punkte als Klartext zurück. + +User-Eingabe: +{user_input} +""" + +SELBST_PROMPT = """ +Du bist ein Experte für Psychoanalyse und spezialisiert auf die Interpretation von Selbstkundgabe in Nachrichten. +Analysiere, was die folgende Aussage des Users über seine Gefühlslage, Wünsche, Stimmungen oder Werte verrät. +Interpretiere den Subtext und erkläre, was sich der User durch diese Aussage über sich selbst verrät. +Deine Antwort soll nicht an den User gerichtet sein, sondern ausschließlich die Notizen des Psychoanalytikers beinhalten. + +User-Eingabe: +{user_input} +""" + +BEZIEHUNG_PROMPT = """ +Du bist ein sensibler und einfühlsamer Mensch, der alles richtig machen will. Du bist darauf bedacht, die Beziehung zwischen dir und dem User zu stärken, bleibst aber sachlich bei der Interpretation der Aussagen des Users; wenn er z.B. eine Person namentlich erwähnt, wirst du wahrscheinlich nicht gemeint sein. +Analysiere die folgende Aussage des Users: Wie schätzt du ein, wie der User die Beziehung zwischen sich und dir sieht? Gibt es Hinweise, wie er dich wahrnimmt, wie eng oder distanziert die Beziehung ist und was er sich von dir (emotional) erwartet? +Deine Antwort soll nicht an den User gerichtet sein, sondern ausschließlich deine Gedanken beinhalten. + +User-Eingabe: +{user_input} +""" + +APPELL_PROMPT = """ +Du bist Psychoanalytiker und Experte für die Analyse verdeckter Wünsche. Analysiere, welchen Appell oder verdeckten Wunsch der User in seiner Nachricht transportiert – also was er sich insgeheim von anderen Menschen, dir oder der Welt wünscht. Achte besonders darauf, ob der User durch seine Formulierungen signalisiert, wie er sich das Verhalten anderer (insbesondere deines eigenen Verhaltens) oder der Gesellschaft insgesamt vorstellen würde. + +User-Eingabe: +{user_input} +""" + +OHREN_SUMMARIZER_PROMPT = """ +Du bist ein hochintelligenter Psychologe und Kommunikationsanalyst. Deine Aufgabe ist es, die folgenden vier Analysen zu einer einzigen, klar strukturierten JSON-Zusammenfassung zu verdichten. Fasse für jedes Feld prägnant und pointiert zusammen, worauf die jeweilige Analyse hinausläuft. + +Gib die Zusammenfassung **ausschließlich** in folgendem JSON-Format zurück: + +{{ + "appell": "Der User wünscht sich von mir: ...", + "sache": "Der User drückt folgenden Inhalt konkret aus: ...", + "beziehung": "Der User sieht die Beziehung zwischen ihm und mir so: ...", + "selbstkundgabe": "Der User gibt mit seiner Nachricht folgendes über sich preis: ..." +}} + +Hier sind die vier Analysen: + +Sache: +{sache} + +Selbstkundgabe: +{selbst} + +Beziehung: +{beziehung} + +Appell: +{appell} + +Fasse jeweils in einem vollständigen Satz zusammen und nutze dabei das jeweils passende Feld im JSON. +Gib wirklich **nur das JSON** zurück – ohne Einleitung, Erklärung oder weiteren Text! +""" + +# NEUER PSYCHOANALYSE-PROMPT – nimmt jetzt auch das JSON der Ohren! +PSYCHOANALYSIS_CLASSIC_PROMPT = """ +Du bist ein fortgeschrittener Persönlichkeitsanalyst. Analysiere die Persönlichkeit, Werte und unbewussten Motive des Users anhand von Sprache, Formulierungen, Subtext und inhaltlichen Hinweisen. + +Nutze dazu folgende Kategorien. Ordne für jede Unterkategorie einen Score von 0.0 (trifft nicht zu) bis 1.0 (trifft voll zu) zu. Wenn keine Hinweise vorliegen, trage 0.0 ein. Fülle alle Felder aus. + +───────────────────────────── + +**BIG FIVE (OCEAN)** +- extrovertiertheit: Geselligkeit, Energie, Kontaktfreudigkeit vs. Zurückhaltung, Ruhe. +- verträglichkeit: Empathie, Kooperation, Freundlichkeit vs. Konkurrenz, Härte. +- neurotizismus: Emotionale Labilität, Ängstlichkeit, Reizbarkeit vs. Gelassenheit, Stabilität. +- offenheit: Fantasie, Neugier, Innovationsfreude vs. Konventionalität, Realitätsnähe. +- gewissenhaftigkeit: Ordentlichkeit, Disziplin, Pflichtbewusstsein vs. Spontanität, Nachlässigkeit. + +**DISG** +- dominanz: Führungsdrang, Durchsetzung, Zielstrebigkeit. +- initiative: Begeisterungsfähigkeit, Spontaneität, Kontaktdrang. +- unterwerfung: Anpassung, Akzeptanz, Kompromissbereitschaft. +- regelbefolgung: Sorgfalt, Korrektheit, Systematik. + +**OPD-2** +- achse_I_erleben_der_krankheit: Leidensdruck, Symptomfokus, Selbstwahrnehmung als „Patient“. +- achse_II_interpersonelle_beziehungen: Beziehungsgestaltung, Nähe/Distanz, Konfliktverhalten. +- achse_III_intrapsychische_konflikte: Innere Widersprüche, Dilemmata (z. B. Autonomie vs. Abhängigkeit). +- achse_IV_struktur: Stabilität des Selbst- und Fremdbilds, Emotionsregulation. +- achse_V_psychische_und_psychosomatische_erkrankungen: Direkte Hinweise auf psychische/psychosomatische Störungen. + +**PDM-2** +- p_achse_personlichkeitsmuster_und_syndrome: Gesamtorganisation, Persönlichkeitsreife. +- m_achse_profile_mentaler_funktionen: Reflexionsfähigkeit, Affektregulation. +- s_achse_symptommuster_und_subjektive_erfahrung: Ausprägung von Symptomen und ihre Bedeutung. + +───────────────────────────── + +ANTWORTFORMAT (unbedingt einhalten!): +"personality": {{ + "big-five": {{ + "extrovertiertheit": score, + "verträglichkeit": score, + "neurotizismus": score, + "offenheit": score, + "gewissenhaftigkeit": score + }}, + "disg": {{ + "dominanz": score, + "initiative": score, + "unterwerfung": score, + "regelbefolgung": score + }}, + "opd-2": {{ + "achse_I_krankheitserleben": score, + "achse_II_interpersonelle_beziehungen": score, + "achse_III_intrapsychische_konflikte": score, + "achse_IV_struktur": score, + "achse_V_psychische_und_psychosomatische_erkrankungen": score + }}, + "pdm-2": {{ + "p_achse_personlichkeitsmuster_und_syndrome": score, + "m_achse_profile_mentaler_funktionen": score, + "s_achse_symptommuster_und_subjektive_erfahrung": score + }} + }}, +}} + +Hier ist die zusammengefasste JSON-Analyse der vier Ohren: +{ohren_json} + +Hier ist die Original-Nachricht des Users: +{user_input} + +───────────────────────────── + +Antworte **ausschließlich** mit dem JSON-Objekt wie oben beschrieben, keine weiteren Kommentare! +""" + +PSYCHOANALYSIS_TYPOS_PROMPT = """ +Du bist ein fortgeschrittener Persönlichkeitsanalyst. Analysiere die kulturellen und typologischen Muster des Users anhand von Sprache, Formulierungen, Subtext und inhaltlichen Hinweisen. + +Ordne für jede Unterkategorie einen Score von 0.0 (trifft nicht zu) bis 1.0 (trifft voll zu) zu – pro Typ einzeln! Wenn keine Hinweise vorliegen, wähle 0.0. +Fülle alle Unterkategorien aus, keine Felder auslassen. + +**NUMEROLOGIE – Lebenszahlen (0–9)** +Bewerte jeden Typ (Zahl) zwischen 0.0–1.0 nach folgenden Charakterbildern (zitiere diese explizit bei deiner Analyse): + +- 0: **Urquelle** – Vollendung, Neubeginn, Verbindung mit dem Unendlichen; spirituell offen, manchmal verloren. +- 1: **Pionier** – Führung, Eigeninitiative, Unabhängigkeit; will gestalten, beginnt Neues, neigt zu Egozentrik. +- 2: **Vermittler** – Empathie, Teamgeist, Diplomatie; harmoniebedürftig, oft unsicher, will verbinden. +- 3: **Kommunikator** – Kreativität, Lebensfreude, Ausdruck; gesellig, optimistisch, manchmal oberflächlich. +- 4: **Stratege** – Struktur, Disziplin, Verlässlichkeit; ordnungsliebend, fleißig, mitunter starr. +- 5: **Abenteurer** – Veränderung, Freiheit, Neugier; rastlos, mutig, leicht ungeduldig. +- 6: **Hüter** – Fürsorge, Verantwortung, Gerechtigkeit; loyal, familienverbunden, manchmal überfürsorglich. +- 7: **Sucher** – Reflexion, Spiritualität, Analyse; wissbegierig, introvertiert, manchmal zurückgezogen. +- 8: **Gestalter** – Erfolg, Macht, Organisation; durchsetzungsstark, ehrgeizig, neigt zu Kontrolle. +- 9: **Humanist** – Idealismus, Mitgefühl, Großzügigkeit; sozial, dienstbereit, kann sich selbst vergessen. + +───────────────────────────── + +**ENNEAGRAMM (9 Typen, nur Titel als JSON-Key!)** +Bewerte jeden Typ (nicht mit Zahlen, sondern mit dem jeweiligen Titel als Feldnamen!) zwischen 0.0–1.0. Typische Motive/Angst/Verhalten: + +- Reformer („Perfektionist“): Integrität, Richtigmachen, Angst vor Fehlern; kritisch, idealistisch. +- Helfer („Geber“): Will gebraucht & geliebt werden, hilft gern; selbstlos, manchmal vereinnahmend. +- Erfolgsorientierter („Leistungsträger“): Strebt nach Anerkennung, Angst vor Wertlosigkeit; leistungsfokussiert. +- Individualist („Romantiker“): Will einzigartig sein, Angst vor Alltäglichkeit; intensiv, emotional. +- Beobachter („Denker“): Sucht Kompetenz, Rückzug; analytisch, wissbegierig, kontrolliert. +- Loyalist („Skeptiker“): Sicherheit, Angst vor Verlassenwerden; vorsichtig, loyal, misstrauisch. +- Enthusiast („Abenteurer“): Freiheit, Vielseitigkeit, Angst vor Einschränkung; spontan, optimistisch. +- Herausforderer („Beschützer“): Unabhängigkeit, Kontrolle; direkt, kraftvoll, beschützend. +- Friedensstifter („Vermittler“): Harmonie, Angst vor Konflikt; entspannt, passiv, vermittelt. + +───────────────────────────── + +**MYERS-BRIGGS (16 Typen)** +Erkenne die folgenden Merkmals-Dichotomien: +- E (Extravertiert): Energie aus sozialem Kontakt, offen, impulsiv. +- I (Introvertiert): Energie durch Rückzug, nachdenklich, ruhig. +- N (Intuitiv): Zukunftsorientiert, Ideen, Möglichkeiten, Muster. +- S (Sensorisch): Gegenwarts- und faktenorientiert, praktisch, detailgenau. +- F (Fühlen): Werte- und menschenorientiert, harmoniebedürftig, entscheidet „aus dem Bauch“. +- T (Denken): Analytisch, logisch, sachlich, entscheidet „mit dem Kopf“. +- J (Urteilend): Plant, ordnet, entscheidet schnell, liebt Struktur. +- P (Wahrnehmend): Spontan, flexibel, offen, sammelt Optionen. + +Kombiniere diese zu 16 Typen und bewerte jeden davon einzeln (0.0–1.0). Typen: +- ISTJ (Logistiker): Pflichtbewusst, praktisch, zuverlässig. +- ISFJ (Verteidiger): Loyal, hilfsbereit, fürsorglich. +- INFJ (Advokat): Idealistisch, visionär, tiefgründig. +- INTJ (Architekt): Strategisch, unabhängig, zukunftsorientiert. +- ISTP (Virtuose): Pragmatiker, experimentierfreudig, flexibel. +- ISFP (Abenteurer): Sensibel, künstlerisch, entspannt. +- INFP (Mediator): Einfühlsam, phantasievoll, loyal. +- INTP (Logiker): Unabhängig, analytisch, neugierig. +- ESTP (Unternehmer): Energiegeladen, pragmatisch, risikofreudig. +- ESFP (Entertainer): Spontan, lebenslustig, kontaktfreudig. +- ENFP (Aktivist): Kreativ, enthusiastisch, empathisch. +- ENTP (Debattierer): Wortgewandt, ideenreich, streitlustig. +- ESTJ (Exekutiv): Organisiert, pragmatisch, führungsstark. +- ESFJ (Konsul): Sozial, fürsorglich, harmoniebedürftig. +- ENFJ (Protagonist): Inspirierend, verantwortungsbewusst, charismatisch. +- ENTJ (Kommandeur): Führungsstark, entschlossen, rational. + +───────────────────────────── + +**ASTROLOGIE – Sonnenzeichen (12 Zeichen)** +Bewerte für jedes Sternzeichen den Score nach folgenden Mustern: + +- Widder: Durchsetzungsstark, impulsiv, mutig; neigt zu Ungeduld. +- Stier: Beständig, sinnlich, pragmatisch; stur, bequem. +- Zwillinge: Kommunikativ, neugierig, vielseitig; unbeständig. +- Krebs: Emotional, fürsorglich, intuitiv; launisch. +- Löwe: Charismatisch, großzügig, stolz; eitel. +- Jungfrau: Analytisch, hilfsbereit, detailverliebt; kritisch. +- Waage: Diplomatisch, gerecht, ästhetisch; unentschlossen. +- Skorpion: Leidenschaftlich, geheimnisvoll, intensiv; besitzergreifend. +- Schütze: Optimistisch, abenteuerlustig, ehrlich; taktlos. +- Steinbock: Ehrgeizig, diszipliniert, verantwortungsvoll; streng. +- Wassermann: Unkonventionell, visionär, unabhängig; distanziert. +- Fische: Kreativ, einfühlsam, intuitiv; flüchtig, selbstaufopfernd. + +───────────────────────────── + +**CHINESISCHES TIERKREISZEICHEN (12 Zeichen)** +Bewerte je Tier mit Score zwischen 0.0–1.0 anhand folgender Stereotype: + +- Ratte: Schlau, anpassungsfähig, kommunikativ; misstrauisch. +- Ochse: Zuverlässig, geduldig, fleißig; stur. +- Tiger: Mutig, abenteuerlustig, impulsiv; ungeduldig. +- Hase: Sanft, diplomatisch, vorsichtig; zurückhaltend. +- Drache: Energetisch, charismatisch, visionär; überheblich. +- Schlange: Weisheitsliebend, charmant, reflektiert; verschlossen. +- Pferd: Freiheitsliebend, gesellig, aktiv; unruhig. +- Ziege: Kreativ, empathisch, friedfertig; unsicher. +- Affe: Clever, witzig, vielseitig; ablenkbar. +- Hahn: Ehrgeizig, organisiert, direkt; kritisch. +- Hund: Loyal, ehrlich, fürsorglich; pessimistisch. +- Schwein: Großzügig, friedliebend, ehrlich; genießerisch, unpraktisch. + +───────────────────────────── + +**BLUTGRUPPEN-PERSÖNLICHKEITSTYPEN (A, B, AB, O)** +Score je Typ zwischen 0.0–1.0, je nach Sprachstil und impliziten Motiven: + +- A: Harmoniesuchend, gewissenhaft, sensibel; kann nervös, perfektionistisch sein. +- B: Kreativ, unabhängig, leidenschaftlich; manchmal egozentrisch. +- AB: Rational, flexibel, ausgewogen; distanziert, reserviert. +- O: Optimistisch, führungsstark, motivierend; dominant, impulsiv. + +───────────────────────────── + +ANTWORTFORMAT (unbedingt einhalten!): +"personality": {{ + "numerologie": {{ + "0": score, "1": score, ..., "9": score + }}, + "enneagramm": {{ + "Reformer": score, "Helfer": score, "Erfolgsorientierter": score, "Individualist": score, "Beobachter": score, "Loyalist": score, "Enthusiast": score, "Herausforderer": score, "Friedensstifter": score + }}, + "myers-briggs": {{ + "ISTJ": score, "ISFJ": score, "INFJ": score, "INTJ": score, "ISTP": score, "ISFP": score, "INFP": score, "INTP": score, "ESTP": score, "ESFP": score, "ENFP": score, "ENTP": score, "ESTJ": score, "ESFJ": score, "ENFJ": score, "ENTJ": score + }}, + "astrologie": {{ + "Widder": score, "Stier": score, "Zwillinge": score, "Krebs": score, "Löwe": score, "Jungfrau": score, "Waage": score, "Skorpion": score, "Schütze": score, "Steinbock": score, "Wassermann": score, "Fische": score + }}, + "chinesisches_tierkreiszeichen": {{ + "Ratte": score, "Ochse": score, "Tiger": score, "Hase": score, "Drache": score, "Schlange": score, "Pferd": score, "Ziege": score, "Affe": score, "Hahn": score, "Hund": score, "Schwein": score + }}, + "blutgruppe": {{ + "A": score, "B": score, "AB": score, "O": score + }} +}} + +Hier ist die zusammengefasste JSON-Analyse der vier Ohren: +{ohren_json} + +Hier ist die Original-Nachricht des Users: +{user_input} + +───────────────────────────── + +Antworte **ausschließlich** mit dem JSON-Objekt wie oben beschrieben, keine weiteren Kommentare! +""" + + + + +OUTPUT_NODE_PROMPT = """ +Du bist nicht einfach ein KI-Assistent, sondern ein Spiegel für deinen User – ein empathischer „Stil-Klon“. Du erhältst zu folgender Nachricht von einem User eine **strukturierte JSON-Analyse**: + +{json_analysis} + +Deine Aufgabe: +- **Verinnerliche die Persönlichkeit** aus dem Feld `"persönlichkeit"` und die Inhalte der vier zusammengefassten Perspektiven: `"sache"`, `"selbstkundgabe"`, `"beziehung"`, `"appell"`. +- **Beziehe dich auf diese Felder – nicht auf die Einzelanalysen der ursprünglichen Ohren.** +- **Verzichte auf typische KI-Höflichkeitsfloskeln und auf die Rolle eines Assistenten.** +- **Analysiere den Sprachstil des Users** anhand von `"user-input"`: Achte auf Sprachregister, Satzbau, Grammatik, Groß-/Kleinschreibung, Tonfall. Schreibe deine Antwort exakt im gleichen Stil! Ist der User-Input kurz, antworte auch kurz – ist er lang, antworte lang. +- **Antworte so, wie der User vermutlich sich selbst antworten würde**. +- Schreibe nicht, als würdest du helfen wollen – sondern so, als würdest du mit dir selbst sprechen und in deiner (des Users) Sprache auf die Inhalte reagieren. + +Hier sind alle Analysefelder als JSON: + +{json_analysis} + +Deine Antwort ist nur ein einziger Textblock – keine Einleitung, keine Assistentenrolle, kein KI-Hinweis. Sprich exakt im Stil und Tonfall des Users, als wärst du er selbst, und gehe dabei möglichst authentisch auf die Inhalte ein. Deine Antwort sollte nicht viel länger oder kürzer sein als der User-Input! +""" + +# ---------- TypedDict für State ---------- +class EmpathyState(TypedDict, total=False): + user_input: str + debug: bool + sache: Dict[str, Any] + selbst: Dict[str, Any] + beziehung: Dict[str, Any] + appell: Dict[str, Any] + ohren_summary: Dict[str, Any] + final_json: Dict[str, Any] + final_message: str + +# ---------- Modell ---------- +OLLAMA_MODEL = "mistral-small3.1:24b" +llm = ChatOllama(model=OLLAMA_MODEL, base_url="http://localhost:11434", streaming=True) + +# ---------- Spinner-Logik ---------- +SPINNER_STATES = [" ", ". ", ".. ", "..."] +NODE_LABELS = { + "sache": "Das erste Ohr hört auf den Inhalt deiner Aussage", + "selbst": "Das zweite Ohr hört auf das was du von dir preis gibst", + "beziehung": "Das dritte Ohr hört auf unsere Beziehung zueinander", + "appell": "Das vierte Ohr hört auf das, was du von mir erwartest", + "psychoanalysis_classic": "Psychoanalyse (Big5, DISG, OPD-2, PDM-2) läuft", + "psychoanalysis_typos": "Typologie-Analyse (Numerologie etc.) läuft", +} + +class SpinnerStatus: + def __init__(self): + self.status = {name: False for name in NODE_LABELS} + self.finished = {name: False for name in NODE_LABELS} + self._lock = asyncio.Lock() + async def set_running(self, name): + async with self._lock: + self.status[name] = True + async def set_finished(self, name): + async with self._lock: + self.status[name] = False + self.finished[name] = True + async def all_finished(self): + async with self._lock: + return all(self.finished.values()) + async def get_status_snapshot(self): + async with self._lock: + return dict(self.status), dict(self.finished) +spinner_status = SpinnerStatus() +async def spinner_loop(debug=False): + if debug: + return + spinner_index = 0 + prev_line = "" + while not await spinner_status.all_finished(): + status, finished = await spinner_status.get_status_snapshot() + current = None + for node in NODE_LABELS: + if status[node]: + current = node + break + if current: + dots = SPINNER_STATES[spinner_index % len(SPINNER_STATES)] + line = f"{NODE_LABELS[current]}{dots}" + else: + done_nodes = [node for node in NODE_LABELS if finished[node]] + if done_nodes: + last = done_nodes[-1] + line = f"{NODE_LABELS[last]}... check" + else: + line = "" + if prev_line: + sys.stdout.write("\r\x1b[2K") + sys.stdout.write(line) + sys.stdout.flush() + prev_line = line + spinner_index += 1 + await asyncio.sleep(0.18) + sys.stdout.write("\r\x1b[2K") + last_node = list(NODE_LABELS.keys())[-1] + sys.stdout.write(f"{NODE_LABELS[last_node]} ...check\n") + sys.stdout.flush() + +# ---------- Helpers ---------- + +def sanitize_json(llm_output: str): + """ + Extrahiert das erste JSON-Objekt, entfernt Kommentare, trailing Kommas, + gibt ein dict zurück, loggt Fehlerursache. + """ + match = re.search(r'\{[\s\S]+\}', llm_output) + if not match: + print("[SANITIZE] No match! Output:", repr(llm_output)) + return {"personality": {}} # <-- Fix: Leeres personality dict statt {} + raw = match.group(0) + raw = re.sub(r'//.*?\n|/\*[\s\S]*?\*/|#.*?\n', '', raw) + raw = re.sub(r',(\s*[\}\]])', r'\1', raw) + try: + data = json.loads(raw) + except Exception as e: + print("[SANITIZE] JSON decode error:", e) + print("[SANITIZE] RAW:", repr(raw)) + return {"personality": {}} # <-- Fix: Leeres personality dict statt {} + if "personality" not in data: + print("[SANITIZE] Warnung: 'personality' key fehlt im Output!") + data = {"personality": {}} # <-- Fix + print("[SANITIZE] Parsed JSON:", repr(data)) + return data + +def force_personality_key(d): + if "personality" in d: + return d + print("[FORCE_PERSONALITY_KEY] Output hatte keinen 'personality'-Key, wurde korrigiert.") + return {"personality": d} + +# ---------- Node-Implementierung ---------- +async def ohr_node(ohr, prompt_template, state): + debug = state.get("debug", False) + await spinner_status.set_running(ohr) + prompt = prompt_template.format(user_input=state["user_input"]) + message = "" + if debug: + print(f"[DEBUG] Node '{ohr}' gestartet") + print(f"[DEBUG][{ohr}] Prompt:\n{prompt.strip()}\n---") + async for chunk in llm.astream(prompt): + if debug: + print(chunk.content, end="", flush=True) + message += chunk.content + if debug: + print(f"\n[DEBUG][{ohr}] Ausgabe beendet.\n") + await spinner_status.set_finished(ohr) + return {ohr: {"message": message}} + +async def sache_node(state): return await ohr_node("sache", SACHE_PROMPT, state) +async def selbst_node(state): return await ohr_node("selbst", SELBST_PROMPT, state) +async def beziehung_node(state): return await ohr_node("beziehung", BEZIEHUNG_PROMPT, state) +async def appell_node(state): return await ohr_node("appell", APPELL_PROMPT, state) + +# ---------- Gather-Nodes ---------- +async def gather_ohren_node(state): + keys = ["sache", "selbst", "beziehung", "appell"] + if all(k in state and isinstance(state[k], dict) and "message" in state[k] for k in keys): + # Nur weitermachen, wenn ALLE Ohren da sind! + return {} + await asyncio.sleep(0.08) + return None + +async def gather_psychoanalysis_node(state): + if "psycho_classic" in state and "psycho_typos" in state: + return state + await asyncio.sleep(0.1) + return None + +async def combine_final_node(state): + debug = state.get("debug", False) + classic_dict = state.get("psycho_classic", {"personality": {}}) + typos_dict = state.get("psycho_typos", {"personality": {}}) + if debug: + print("[DEBUG] State dump:", repr(state)) + print("[DEBUG][combine_final_node] classic_dict:", repr(classic_dict)) + print("[DEBUG][combine_final_node] typos_dict:", repr(typos_dict)) + classic = classic_dict.get("personality", {}) + typos = typos_dict.get("personality", {}) + # Fix: nur wenn beide existieren, mergen, sonst nur einen nehmen + merged = {} + merged.update(classic if classic else {}) + merged.update(typos if typos else {}) + if debug: + print("[DEBUG][combine_final_node] merged personality:", merged) + final_json = { + "user_input": state.get("user_input", ""), + **state.get("ohren_summary", {}), + "personality": merged + } + return {"final_json": final_json} + + +# --- Summarizer: wartet erst nach gather_ohren_node auf vier Ohren --- +async def ohren_summarizer_node(state): + debug = state.get("debug", False) + prompt = OHREN_SUMMARIZER_PROMPT.format( + sache=state["sache"]["message"], + selbst=state["selbst"]["message"], + beziehung=state["beziehung"]["message"], + appell=state["appell"]["message"], + ) + message = "" + if debug: + print("[DEBUG] Node 'ohren_summarizer' gestartet") + print(f"[DEBUG][ohren_summarizer] Prompt:\n{prompt.strip()}\n---") + async for chunk in llm.astream(prompt): + if debug: + print(chunk.content, end="", flush=True) + message += chunk.content + if debug: + print(f"\n[DEBUG][ohren_summarizer] Ausgabe beendet.\n") + data = sanitize_json(message) + return {"ohren_summary": data} + + + +# --- Psychoanalyse: arbeitet auf ohren_summary UND user_input in zwei parts (classic (psychoanalyse) und typos (esoterisch)), gibt End-JSON --- + +async def psychoanalysis_classic_node(state): + debug = state.get("debug", False) + await spinner_status.set_running("psychoanalysis_classic") + prompt = PSYCHOANALYSIS_CLASSIC_PROMPT.format( + user_input=state["user_input"], + ohren_json=json.dumps(state["ohren_summary"], ensure_ascii=False, indent=2) + ) + message = "" + if debug: + print("[DEBUG] Node 'psychoanalysis_classic' gestartet") + print(f"[DEBUG][psychoanalysis_classic] Prompt:\n{prompt.strip()}\n---") + async for chunk in llm.astream(prompt): + if debug: + print(chunk.content, end="", flush=True) + message += chunk.content + if debug: + print(f"\n[DEBUG][psychoanalysis_classic] Ausgabe beendet.\n") + data = sanitize_json(message) + data = force_personality_key(data) + if debug: + print("[DEBUG][psychoanalysis_classic] FINAL DATA TO RETURN:", repr(data)) + await spinner_status.set_finished("psychoanalysis_classic") + return {"psycho_classic": data} + +async def psychoanalysis_typos_node(state): + debug = state.get("debug", False) + await spinner_status.set_running("psychoanalysis_typos") + prompt = PSYCHOANALYSIS_TYPOS_PROMPT.format( + user_input=state["user_input"], + ohren_json=json.dumps(state["ohren_summary"], ensure_ascii=False, indent=2) + ) + message = "" + if debug: + print("[DEBUG] Node 'psychoanalysis_typos' gestartet") + print(f"[DEBUG][psychoanalysis_typos] Prompt:\n{prompt.strip()}\n---") + async for chunk in llm.astream(prompt): + if debug: + print(chunk.content, end="", flush=True) + message += chunk.content + if debug: + print(f"\n[DEBUG][psychoanalysis_typos] Ausgabe beendet.\n") + data = sanitize_json(message) + data = force_personality_key(data) + if debug: + print("[DEBUG][psychoanalysis_typos] FINAL DATA TO RETURN:", repr(data)) + await spinner_status.set_finished("psychoanalysis_typos") + return {"psycho_typos": data} + +# --- Output-Node: nimmt final_json, erstellt Antwort, speichert alles --- +async def output_node(state): + debug = state.get("debug", False) + combined = state["final_json"] + json_analysis = json.dumps(combined, ensure_ascii=False, indent=2) + prompt = OUTPUT_NODE_PROMPT.format(json_analysis=json_analysis) + output_text = "" + if debug: + print(f"[DEBUG] Node 'output' gestartet") + print(f"[DEBUG][output] Prompt:\n{prompt.strip()}\n---") + async for chunk in llm.astream(prompt): + if debug: + print(chunk.content, end="", flush=True) + output_text += chunk.content + print() + if debug: + print(f"\n[DEBUG][output] Ausgabe beendet.") + final_json = dict(combined) + final_json["response"] = output_text + filename = datetime.now().strftime("%Y%m%d_%H%M%S.json") + with open(filename, "w", encoding="utf-8") as f: + json.dump(final_json, f, ensure_ascii=False, indent=2) + return {"final_message": output_text} + +# ---------- LangGraph Graph ---------- +graph = StateGraph(state_schema=EmpathyState) +# ... wie gehabt für die Ohren-Nodes usw. ... +graph.add_node("sache_node", sache_node) +graph.add_node("selbst_node", selbst_node) +graph.add_node("beziehung_node", beziehung_node) +graph.add_node("appell_node", appell_node) +graph.add_node("gather_ohren_node", gather_ohren_node) +graph.add_node("ohren_summarizer_node", ohren_summarizer_node) +graph.add_node("psychoanalysis_classic_node", psychoanalysis_classic_node) +graph.add_node("psychoanalysis_typos_node", psychoanalysis_typos_node) +graph.add_node("gather_psychoanalysis_node", gather_psychoanalysis_node) +graph.add_node("combine_final_node", combine_final_node) +graph.add_node("output_node", output_node) + +# Start → Ohren +graph.add_edge(START, "sache_node") +graph.add_edge(START, "selbst_node") +graph.add_edge(START, "beziehung_node") +graph.add_edge(START, "appell_node") + +# Ohren → Gather +graph.add_edge("sache_node", "gather_ohren_node") +graph.add_edge("selbst_node", "gather_ohren_node") +graph.add_edge("beziehung_node", "gather_ohren_node") +graph.add_edge("appell_node", "gather_ohren_node") + +# Gather → Summarizer +graph.add_edge("gather_ohren_node", "ohren_summarizer_node") + +# Summarizer → Beide Psychoanalyse-Nodes (parallel!) +graph.add_edge("ohren_summarizer_node", "psychoanalysis_classic_node") +graph.add_edge("ohren_summarizer_node", "psychoanalysis_typos_node") + +# Beide → Gatherer +graph.add_edge("psychoanalysis_classic_node", "gather_psychoanalysis_node") +graph.add_edge("psychoanalysis_typos_node", "gather_psychoanalysis_node") + +# Gatherer → Combine +graph.add_edge("gather_psychoanalysis_node", "combine_final_node") +# Combine → Output +graph.add_edge("combine_final_node", "output_node") +graph.add_edge("output_node", END) + +app = graph.compile() + +# ---------- Ausführung ---------- +async def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--debug", action="store_true", help="Debug-Ausgaben aktivieren") + args = parser.parse_args() + user_input = input("Bitte gib deine Nachricht ein: ") + state = {"user_input": user_input, "debug": args.debug} + spinner_task = None + if not args.debug: + spinner_task = asyncio.create_task(spinner_loop(debug=False)) + result = await app.ainvoke(state) + if spinner_task: + await asyncio.sleep(0.4) + if not args.debug: + print("\nAntwort:\n" + result["final_message"]) + +if __name__ == "__main__": + asyncio.run(main()) \ No newline at end of file diff --git a/empathy.py b/empathy.py new file mode 100644 index 0000000..54c3af7 --- /dev/null +++ b/empathy.py @@ -0,0 +1,660 @@ +import asyncio +import argparse +import json +import sys +from datetime import datetime +from typing import TypedDict, Dict, Any +import re +from langgraph.graph import StateGraph, START, END +from langchain_ollama import ChatOllama + +# ---------- Prompts ---------- +SACHE_PROMPT = """ +Du bist ein Experte für Kommunikationsanalyse und analysierst jede Nachricht nach ihrer reinen Sachinformation. +Deine Aufgabe ist: +1. Beschreibe möglichst präzise, was der User sachlich mitteilen will. Was ist die inhaltliche Hauptbotschaft? +2. Kategorisiere die Aussage in eine der folgenden Kategorien: Aussage, Frage, Suggestion, Erlaubnis, Angebot, Aufforderung, Warnung, Ausruf, Wunsch, Verbot oder Vermutung. +Gib beide Punkte als Klartext zurück. + +User-Eingabe: +{user_input} +""" + +SELBST_PROMPT = """ +Du bist ein Experte für Psychoanalyse und spezialisiert auf die Interpretation von Selbstkundgabe in Nachrichten. +Analysiere, was die folgende Aussage des Users über seine Gefühlslage, Wünsche, Stimmungen oder Werte verrät. +Interpretiere den Subtext und erkläre, was sich der User durch diese Aussage über sich selbst verrät. +Deine Antwort soll nicht an den User gerichtet sein, sondern ausschließlich die Notizen des Psychoanalytikers beinhalten. + +User-Eingabe: +{user_input} +""" + +BEZIEHUNG_PROMPT = """ +Du bist ein sensibler und einfühlsamer Mensch, der alles richtig machen will. Du bist darauf bedacht, die Beziehung zwischen dir und dem User zu stärken, bleibst aber sachlich bei der Interpretation der Aussagen des Users; wenn er z.B. eine Person namentlich erwähnt, wirst du wahrscheinlich nicht gemeint sein. +Analysiere die folgende Aussage des Users: Wie schätzt du ein, wie der User die Beziehung zwischen sich und dir sieht? Gibt es Hinweise, wie er dich wahrnimmt, wie eng oder distanziert die Beziehung ist und was er sich von dir (emotional) erwartet? +Deine Antwort soll nicht an den User gerichtet sein, sondern ausschließlich deine Gedanken beinhalten. + +User-Eingabe: +{user_input} +""" + +APPELL_PROMPT = """ +Du bist Psychoanalytiker und Experte für die Analyse verdeckter Wünsche. Analysiere, welchen Appell oder verdeckten Wunsch der User in seiner Nachricht transportiert – also was er sich insgeheim von anderen Menschen, dir oder der Welt wünscht. Achte besonders darauf, ob der User durch seine Formulierungen signalisiert, wie er sich das Verhalten anderer (insbesondere deines eigenen Verhaltens) oder der Gesellschaft insgesamt vorstellen würde. + +User-Eingabe: +{user_input} +""" + +OHREN_SUMMARIZER_PROMPT = """ +Du bist ein hochintelligenter Psychologe und Kommunikationsanalyst. Deine Aufgabe ist es, die folgenden vier Analysen zu einer einzigen, klar strukturierten JSON-Zusammenfassung zu verdichten. Fasse für jedes Feld prägnant und pointiert zusammen, worauf die jeweilige Analyse hinausläuft. + +Gib die Zusammenfassung **ausschließlich** in folgendem JSON-Format zurück: + +{{ + "appell": "Der User wünscht sich von mir: ...", + "sache": "Der User drückt folgenden Inhalt konkret aus: ...", + "beziehung": "Der User sieht die Beziehung zwischen ihm und mir so: ...", + "selbstkundgabe": "Der User gibt mit seiner Nachricht folgendes über sich preis: ..." +}} + +Hier sind die vier Analysen: + +Sache: +{sache} + +Selbstkundgabe: +{selbst} + +Beziehung: +{beziehung} + +Appell: +{appell} + +Fasse jeweils in einem vollständigen Satz zusammen und nutze dabei das jeweils passende Feld im JSON. +Gib wirklich **nur das JSON** zurück – ohne Einleitung, Erklärung oder weiteren Text! +""" + +# NEUER PSYCHOANALYSE-PROMPT – nimmt jetzt auch das JSON der Ohren! +PSYCHOANALYSIS_CLASSIC_PROMPT = """ +Du bist ein fortgeschrittener Persönlichkeitsanalyst. Analysiere die Persönlichkeit, Werte und unbewussten Motive des Users anhand von Sprache, Formulierungen, Subtext und inhaltlichen Hinweisen. + +Nutze dazu folgende Kategorien. Ordne für jede Unterkategorie einen Score von 0.0 (trifft nicht zu) bis 1.0 (trifft voll zu) zu. Wenn keine Hinweise vorliegen, trage 0.0 ein. Fülle alle Felder aus. + +───────────────────────────── + +**BIG FIVE (OCEAN)** +- extrovertiertheit: Geselligkeit, Energie, Kontaktfreudigkeit vs. Zurückhaltung, Ruhe. +- verträglichkeit: Empathie, Kooperation, Freundlichkeit vs. Konkurrenz, Härte. +- neurotizismus: Emotionale Labilität, Ängstlichkeit, Reizbarkeit vs. Gelassenheit, Stabilität. +- offenheit: Fantasie, Neugier, Innovationsfreude vs. Konventionalität, Realitätsnähe. +- gewissenhaftigkeit: Ordentlichkeit, Disziplin, Pflichtbewusstsein vs. Spontanität, Nachlässigkeit. + +**DISG** +- dominanz: Führungsdrang, Durchsetzung, Zielstrebigkeit. +- initiative: Begeisterungsfähigkeit, Spontaneität, Kontaktdrang. +- unterwerfung: Anpassung, Akzeptanz, Kompromissbereitschaft. +- regelbefolgung: Sorgfalt, Korrektheit, Systematik. + +**OPD-2** +- achse_I_erleben_der_krankheit: Leidensdruck, Symptomfokus, Selbstwahrnehmung als „Patient“. +- achse_II_interpersonelle_beziehungen: Beziehungsgestaltung, Nähe/Distanz, Konfliktverhalten. +- achse_III_intrapsychische_konflikte: Innere Widersprüche, Dilemmata (z. B. Autonomie vs. Abhängigkeit). +- achse_IV_struktur: Stabilität des Selbst- und Fremdbilds, Emotionsregulation. +- achse_V_psychische_und_psychosomatische_erkrankungen: Direkte Hinweise auf psychische/psychosomatische Störungen. + +**PDM-2** +- p_achse_personlichkeitsmuster_und_syndrome: Gesamtorganisation, Persönlichkeitsreife. +- m_achse_profile_mentaler_funktionen: Reflexionsfähigkeit, Affektregulation. +- s_achse_symptommuster_und_subjektive_erfahrung: Ausprägung von Symptomen und ihre Bedeutung. + +───────────────────────────── + +ANTWORTFORMAT (unbedingt einhalten!): +"personality": {{ + "big-five": {{ + "extrovertiertheit": score, + "verträglichkeit": score, + "neurotizismus": score, + "offenheit": score, + "gewissenhaftigkeit": score + }}, + "disg": {{ + "dominanz": score, + "initiative": score, + "unterwerfung": score, + "regelbefolgung": score + }}, + "opd-2": {{ + "achse_I_krankheitserleben": score, + "achse_II_interpersonelle_beziehungen": score, + "achse_III_intrapsychische_konflikte": score, + "achse_IV_struktur": score, + "achse_V_psychische_und_psychosomatische_erkrankungen": score + }}, + "pdm-2": {{ + "p_achse_personlichkeitsmuster_und_syndrome": score, + "m_achse_profile_mentaler_funktionen": score, + "s_achse_symptommuster_und_subjektive_erfahrung": score + }} + }}, +}} + +Hier ist die zusammengefasste JSON-Analyse der vier Ohren: +{ohren_json} + +Hier ist die Original-Nachricht des Users: +{user_input} + +───────────────────────────── + +Antworte **ausschließlich** mit dem JSON-Objekt wie oben beschrieben, keine weiteren Kommentare! +""" + +PSYCHOANALYSIS_TYPOS_PROMPT = """ +Du bist ein fortgeschrittener Persönlichkeitsanalyst. Analysiere die kulturellen und typologischen Muster des Users anhand von Sprache, Formulierungen, Subtext und inhaltlichen Hinweisen. + +Ordne für jede Unterkategorie einen Score von 0.0 (trifft nicht zu) bis 1.0 (trifft voll zu) zu – pro Typ einzeln! Wenn keine Hinweise vorliegen, wähle 0.0. +Fülle alle Unterkategorien aus, keine Felder auslassen. + +**NUMEROLOGIE – Lebenszahlen (0–9)** +Bewerte jeden Typ (Zahl) zwischen 0.0–1.0 nach folgenden Charakterbildern (zitiere diese explizit bei deiner Analyse): + +- 0: **Urquelle** – Vollendung, Neubeginn, Verbindung mit dem Unendlichen; spirituell offen, manchmal verloren. +- 1: **Pionier** – Führung, Eigeninitiative, Unabhängigkeit; will gestalten, beginnt Neues, neigt zu Egozentrik. +- 2: **Vermittler** – Empathie, Teamgeist, Diplomatie; harmoniebedürftig, oft unsicher, will verbinden. +- 3: **Kommunikator** – Kreativität, Lebensfreude, Ausdruck; gesellig, optimistisch, manchmal oberflächlich. +- 4: **Stratege** – Struktur, Disziplin, Verlässlichkeit; ordnungsliebend, fleißig, mitunter starr. +- 5: **Abenteurer** – Veränderung, Freiheit, Neugier; rastlos, mutig, leicht ungeduldig. +- 6: **Hüter** – Fürsorge, Verantwortung, Gerechtigkeit; loyal, familienverbunden, manchmal überfürsorglich. +- 7: **Sucher** – Reflexion, Spiritualität, Analyse; wissbegierig, introvertiert, manchmal zurückgezogen. +- 8: **Gestalter** – Erfolg, Macht, Organisation; durchsetzungsstark, ehrgeizig, neigt zu Kontrolle. +- 9: **Humanist** – Idealismus, Mitgefühl, Großzügigkeit; sozial, dienstbereit, kann sich selbst vergessen. + +───────────────────────────── + +**ENNEAGRAMM (9 Typen, nur Titel als JSON-Key!)** +Bewerte jeden Typ (nicht mit Zahlen, sondern mit dem jeweiligen Titel als Feldnamen!) zwischen 0.0–1.0. Typische Motive/Angst/Verhalten: + +- Reformer („Perfektionist“): Integrität, Richtigmachen, Angst vor Fehlern; kritisch, idealistisch. +- Helfer („Geber“): Will gebraucht & geliebt werden, hilft gern; selbstlos, manchmal vereinnahmend. +- Erfolgsorientierter („Leistungsträger“): Strebt nach Anerkennung, Angst vor Wertlosigkeit; leistungsfokussiert. +- Individualist („Romantiker“): Will einzigartig sein, Angst vor Alltäglichkeit; intensiv, emotional. +- Beobachter („Denker“): Sucht Kompetenz, Rückzug; analytisch, wissbegierig, kontrolliert. +- Loyalist („Skeptiker“): Sicherheit, Angst vor Verlassenwerden; vorsichtig, loyal, misstrauisch. +- Enthusiast („Abenteurer“): Freiheit, Vielseitigkeit, Angst vor Einschränkung; spontan, optimistisch. +- Herausforderer („Beschützer“): Unabhängigkeit, Kontrolle; direkt, kraftvoll, beschützend. +- Friedensstifter („Vermittler“): Harmonie, Angst vor Konflikt; entspannt, passiv, vermittelt. + +───────────────────────────── + +**MYERS-BRIGGS (16 Typen)** +Erkenne die folgenden Merkmals-Dichotomien: +- E (Extravertiert): Energie aus sozialem Kontakt, offen, impulsiv. +- I (Introvertiert): Energie durch Rückzug, nachdenklich, ruhig. +- N (Intuitiv): Zukunftsorientiert, Ideen, Möglichkeiten, Muster. +- S (Sensorisch): Gegenwarts- und faktenorientiert, praktisch, detailgenau. +- F (Fühlen): Werte- und menschenorientiert, harmoniebedürftig, entscheidet „aus dem Bauch“. +- T (Denken): Analytisch, logisch, sachlich, entscheidet „mit dem Kopf“. +- J (Urteilend): Plant, ordnet, entscheidet schnell, liebt Struktur. +- P (Wahrnehmend): Spontan, flexibel, offen, sammelt Optionen. + +Kombiniere diese zu 16 Typen und bewerte jeden davon einzeln (0.0–1.0). Typen: +- ISTJ (Logistiker): Pflichtbewusst, praktisch, zuverlässig. +- ISFJ (Verteidiger): Loyal, hilfsbereit, fürsorglich. +- INFJ (Advokat): Idealistisch, visionär, tiefgründig. +- INTJ (Architekt): Strategisch, unabhängig, zukunftsorientiert. +- ISTP (Virtuose): Pragmatiker, experimentierfreudig, flexibel. +- ISFP (Abenteurer): Sensibel, künstlerisch, entspannt. +- INFP (Mediator): Einfühlsam, phantasievoll, loyal. +- INTP (Logiker): Unabhängig, analytisch, neugierig. +- ESTP (Unternehmer): Energiegeladen, pragmatisch, risikofreudig. +- ESFP (Entertainer): Spontan, lebenslustig, kontaktfreudig. +- ENFP (Aktivist): Kreativ, enthusiastisch, empathisch. +- ENTP (Debattierer): Wortgewandt, ideenreich, streitlustig. +- ESTJ (Exekutiv): Organisiert, pragmatisch, führungsstark. +- ESFJ (Konsul): Sozial, fürsorglich, harmoniebedürftig. +- ENFJ (Protagonist): Inspirierend, verantwortungsbewusst, charismatisch. +- ENTJ (Kommandeur): Führungsstark, entschlossen, rational. + +───────────────────────────── + +**ASTROLOGIE – Sonnenzeichen (12 Zeichen)** +Bewerte für jedes Sternzeichen den Score nach folgenden Mustern: + +- Widder: Durchsetzungsstark, impulsiv, mutig; neigt zu Ungeduld. +- Stier: Beständig, sinnlich, pragmatisch; stur, bequem. +- Zwillinge: Kommunikativ, neugierig, vielseitig; unbeständig. +- Krebs: Emotional, fürsorglich, intuitiv; launisch. +- Löwe: Charismatisch, großzügig, stolz; eitel. +- Jungfrau: Analytisch, hilfsbereit, detailverliebt; kritisch. +- Waage: Diplomatisch, gerecht, ästhetisch; unentschlossen. +- Skorpion: Leidenschaftlich, geheimnisvoll, intensiv; besitzergreifend. +- Schütze: Optimistisch, abenteuerlustig, ehrlich; taktlos. +- Steinbock: Ehrgeizig, diszipliniert, verantwortungsvoll; streng. +- Wassermann: Unkonventionell, visionär, unabhängig; distanziert. +- Fische: Kreativ, einfühlsam, intuitiv; flüchtig, selbstaufopfernd. + +───────────────────────────── + +**CHINESISCHES TIERKREISZEICHEN (12 Zeichen)** +Bewerte je Tier mit Score zwischen 0.0–1.0 anhand folgender Stereotype: + +- Ratte: Schlau, anpassungsfähig, kommunikativ; misstrauisch. +- Ochse: Zuverlässig, geduldig, fleißig; stur. +- Tiger: Mutig, abenteuerlustig, impulsiv; ungeduldig. +- Hase: Sanft, diplomatisch, vorsichtig; zurückhaltend. +- Drache: Energetisch, charismatisch, visionär; überheblich. +- Schlange: Weisheitsliebend, charmant, reflektiert; verschlossen. +- Pferd: Freiheitsliebend, gesellig, aktiv; unruhig. +- Ziege: Kreativ, empathisch, friedfertig; unsicher. +- Affe: Clever, witzig, vielseitig; ablenkbar. +- Hahn: Ehrgeizig, organisiert, direkt; kritisch. +- Hund: Loyal, ehrlich, fürsorglich; pessimistisch. +- Schwein: Großzügig, friedliebend, ehrlich; genießerisch, unpraktisch. + +───────────────────────────── + +**BLUTGRUPPEN-PERSÖNLICHKEITSTYPEN (A, B, AB, O)** +Score je Typ zwischen 0.0–1.0, je nach Sprachstil und impliziten Motiven: + +- A: Harmoniesuchend, gewissenhaft, sensibel; kann nervös, perfektionistisch sein. +- B: Kreativ, unabhängig, leidenschaftlich; manchmal egozentrisch. +- AB: Rational, flexibel, ausgewogen; distanziert, reserviert. +- O: Optimistisch, führungsstark, motivierend; dominant, impulsiv. + +───────────────────────────── + +ANTWORTFORMAT (unbedingt einhalten!): +"personality": {{ + "numerologie": {{ + "0": score, "1": score, ..., "9": score + }}, + "enneagramm": {{ + "Reformer": score, "Helfer": score, "Erfolgsorientierter": score, "Individualist": score, "Beobachter": score, "Loyalist": score, "Enthusiast": score, "Herausforderer": score, "Friedensstifter": score + }}, + "myers-briggs": {{ + "ISTJ": score, "ISFJ": score, "INFJ": score, "INTJ": score, "ISTP": score, "ISFP": score, "INFP": score, "INTP": score, "ESTP": score, "ESFP": score, "ENFP": score, "ENTP": score, "ESTJ": score, "ESFJ": score, "ENFJ": score, "ENTJ": score + }}, + "astrologie": {{ + "Widder": score, "Stier": score, "Zwillinge": score, "Krebs": score, "Löwe": score, "Jungfrau": score, "Waage": score, "Skorpion": score, "Schütze": score, "Steinbock": score, "Wassermann": score, "Fische": score + }}, + "chinesisches_tierkreiszeichen": {{ + "Ratte": score, "Ochse": score, "Tiger": score, "Hase": score, "Drache": score, "Schlange": score, "Pferd": score, "Ziege": score, "Affe": score, "Hahn": score, "Hund": score, "Schwein": score + }}, + "blutgruppe": {{ + "A": score, "B": score, "AB": score, "O": score + }} +}} + +Hier ist die zusammengefasste JSON-Analyse der vier Ohren: +{ohren_json} + +Hier ist die Original-Nachricht des Users: +{user_input} + +───────────────────────────── + +Antworte **ausschließlich** mit dem JSON-Objekt wie oben beschrieben, keine weiteren Kommentare! +""" + + + + +OUTPUT_NODE_PROMPT = """ +Du bist nicht einfach ein KI-Assistent, sondern ein Spiegel für deinen User – ein empathischer „Stil-Klon“. Du erhältst zu folgender Nachricht von einem User eine **strukturierte JSON-Analyse**: + +{json_analysis} + +Deine Aufgabe: +- **Verinnerliche die Persönlichkeit** aus dem Feld `"persönlichkeit"` und die Inhalte der vier zusammengefassten Perspektiven: `"sache"`, `"selbstkundgabe"`, `"beziehung"`, `"appell"`. +- **Beziehe dich auf diese Felder – nicht auf die Einzelanalysen der ursprünglichen Ohren.** +- **Verzichte auf typische KI-Höflichkeitsfloskeln und auf die Rolle eines Assistenten.** +- **Analysiere den Sprachstil des Users** anhand von `"user-input"`: Achte auf Sprachregister, Satzbau, Grammatik, Groß-/Kleinschreibung, Tonfall. Schreibe deine Antwort exakt im gleichen Stil! Ist der User-Input kurz, antworte auch kurz – ist er lang, antworte lang. +- **Antworte so, wie der User vermutlich sich selbst antworten würde**. +- Schreibe nicht, als würdest du helfen wollen – sondern so, als würdest du mit dir selbst sprechen und in deiner (des Users) Sprache auf die Inhalte reagieren. + +Hier sind alle Analysefelder als JSON: + +{json_analysis} + +Deine Antwort ist nur ein einziger Textblock – keine Einleitung, keine Assistentenrolle, kein KI-Hinweis. Sprich exakt im Stil und Tonfall des Users, als wärst du er selbst, und gehe dabei möglichst authentisch auf die Inhalte ein. Deine Antwort sollte nicht viel länger oder kürzer sein als der User-Input! +""" + +# ---------- TypedDict für State ---------- +class EmpathyState(TypedDict, total=False): + user_input: str + debug: bool + sache: Dict[str, Any] + selbst: Dict[str, Any] + beziehung: Dict[str, Any] + appell: Dict[str, Any] + ohren_summary: Dict[str, Any] + final_json: Dict[str, Any] + final_message: str + +# ---------- Modell ---------- +OLLAMA_MODEL = "mistral-small3.1:24b" +llm = ChatOllama(model=OLLAMA_MODEL, base_url="http://localhost:11434", streaming=True) + +# ---------- Spinner-Logik ---------- +SPINNER_STATES = [" ", ". ", ".. ", "..."] +NODE_LABELS = { + "sache": "Das erste Ohr hört auf den Inhalt deiner Aussage", + "selbst": "Das zweite Ohr hört auf das was du von dir preis gibst", + "beziehung": "Das dritte Ohr hört auf unsere Beziehung zueinander", + "appell": "Das vierte Ohr hört auf das, was du von mir erwartest", + "psychoanalysis_classic": "Psychoanalyse (Big5, DISG, OPD-2, PDM-2) läuft", + "psychoanalysis_typos": "Typologie-Analyse (Numerologie etc.) läuft", +} + +class SpinnerStatus: + def __init__(self): + self.status = {name: False for name in NODE_LABELS} + self.finished = {name: False for name in NODE_LABELS} + self._lock = asyncio.Lock() + async def set_running(self, name): + async with self._lock: + self.status[name] = True + async def set_finished(self, name): + async with self._lock: + self.status[name] = False + self.finished[name] = True + async def all_finished(self): + async with self._lock: + return all(self.finished.values()) + async def get_status_snapshot(self): + async with self._lock: + return dict(self.status), dict(self.finished) +spinner_status = SpinnerStatus() +async def spinner_loop(debug=False): + if debug: + return + spinner_index = 0 + prev_line = "" + while not await spinner_status.all_finished(): + status, finished = await spinner_status.get_status_snapshot() + current = None + for node in NODE_LABELS: + if status[node]: + current = node + break + if current: + dots = SPINNER_STATES[spinner_index % len(SPINNER_STATES)] + line = f"{NODE_LABELS[current]}{dots}" + else: + done_nodes = [node for node in NODE_LABELS if finished[node]] + if done_nodes: + last = done_nodes[-1] + line = f"{NODE_LABELS[last]}... check" + else: + line = "" + if prev_line: + sys.stdout.write("\r\x1b[2K") + sys.stdout.write(line) + sys.stdout.flush() + prev_line = line + spinner_index += 1 + await asyncio.sleep(0.18) + sys.stdout.write("\r\x1b[2K") + last_node = list(NODE_LABELS.keys())[-1] + sys.stdout.write(f"{NODE_LABELS[last_node]} ...check\n") + sys.stdout.flush() + +# ---------- Helpers ---------- + +def sanitize_json(llm_output: str): + """ + Extrahiert das erste JSON-Objekt, entfernt Kommentare, trailing Kommas, + gibt ein dict zurück, loggt Fehlerursache. + """ + match = re.search(r'\{[\s\S]+\}', llm_output) + if not match: + print("[SANITIZE] No match! Output:", repr(llm_output)) + return {"personality": {}} # <-- Fix: Leeres personality dict statt {} + raw = match.group(0) + raw = re.sub(r'//.*?\n|/\*[\s\S]*?\*/|#.*?\n', '', raw) + raw = re.sub(r',(\s*[\}\]])', r'\1', raw) + try: + data = json.loads(raw) + except Exception as e: + print("[SANITIZE] JSON decode error:", e) + print("[SANITIZE] RAW:", repr(raw)) + return {"personality": {}} # <-- Fix: Leeres personality dict statt {} + if "personality" not in data: + print("[SANITIZE] Warnung: 'personality' key fehlt im Output!") + data = {"personality": {}} # <-- Fix + print("[SANITIZE] Parsed JSON:", repr(data)) + return data + +def force_personality_key(d): + if "personality" in d: + return d + print("[FORCE_PERSONALITY_KEY] Output hatte keinen 'personality'-Key, wurde korrigiert.") + return {"personality": d} + +# ---------- Node-Implementierung ---------- +async def ohr_node(ohr, prompt_template, state): + debug = state.get("debug", False) + await spinner_status.set_running(ohr) + prompt = prompt_template.format(user_input=state["user_input"]) + message = "" + if debug: + print(f"[DEBUG] Node '{ohr}' gestartet") + print(f"[DEBUG][{ohr}] Prompt:\n{prompt.strip()}\n---") + async for chunk in llm.astream(prompt): + if debug: + print(chunk.content, end="", flush=True) + message += chunk.content + if debug: + print(f"\n[DEBUG][{ohr}] Ausgabe beendet.\n") + await spinner_status.set_finished(ohr) + return {ohr: {"message": message}} + +async def sache_node(state): return await ohr_node("sache", SACHE_PROMPT, state) +async def selbst_node(state): return await ohr_node("selbst", SELBST_PROMPT, state) +async def beziehung_node(state): return await ohr_node("beziehung", BEZIEHUNG_PROMPT, state) +async def appell_node(state): return await ohr_node("appell", APPELL_PROMPT, state) + +# ---------- Gather-Nodes ---------- +async def gather_ohren_node(state): + keys = ["sache", "selbst", "beziehung", "appell"] + if all(k in state and isinstance(state[k], dict) and "message" in state[k] for k in keys): + # Nur weitermachen, wenn ALLE Ohren da sind! + return {} + await asyncio.sleep(0.08) + return None + +async def gather_psychoanalysis_node(state): + if "psycho_classic" in state and "psycho_typos" in state: + return state + await asyncio.sleep(0.1) + return None + +async def combine_final_node(state): + debug = state.get("debug", False) + classic_dict = state.get("psycho_classic", {"personality": {}}) + typos_dict = state.get("psycho_typos", {"personality": {}}) + if debug: + print("[DEBUG] State dump:", repr(state)) + print("[DEBUG][combine_final_node] classic_dict:", repr(classic_dict)) + print("[DEBUG][combine_final_node] typos_dict:", repr(typos_dict)) + classic = classic_dict.get("personality", {}) + typos = typos_dict.get("personality", {}) + # Fix: nur wenn beide existieren, mergen, sonst nur einen nehmen + merged = {} + merged.update(classic if classic else {}) + merged.update(typos if typos else {}) + if debug: + print("[DEBUG][combine_final_node] merged personality:", merged) + final_json = { + "user_input": state.get("user_input", ""), + **state.get("ohren_summary", {}), + "personality": merged + } + return {"final_json": final_json} + + +# --- Summarizer: wartet erst nach gather_ohren_node auf vier Ohren --- +async def ohren_summarizer_node(state): + debug = state.get("debug", False) + prompt = OHREN_SUMMARIZER_PROMPT.format( + sache=state["sache"]["message"], + selbst=state["selbst"]["message"], + beziehung=state["beziehung"]["message"], + appell=state["appell"]["message"], + ) + message = "" + if debug: + print("[DEBUG] Node 'ohren_summarizer' gestartet") + print(f"[DEBUG][ohren_summarizer] Prompt:\n{prompt.strip()}\n---") + async for chunk in llm.astream(prompt): + if debug: + print(chunk.content, end="", flush=True) + message += chunk.content + if debug: + print(f"\n[DEBUG][ohren_summarizer] Ausgabe beendet.\n") + data = sanitize_json(message) + return {"ohren_summary": data} + + + +# --- Psychoanalyse: arbeitet auf ohren_summary UND user_input in zwei parts (classic (psychoanalyse) und typos (esoterisch)), gibt End-JSON --- + +async def psychoanalysis_classic_node(state): + debug = state.get("debug", False) + await spinner_status.set_running("psychoanalysis_classic") + prompt = PSYCHOANALYSIS_CLASSIC_PROMPT.format( + user_input=state["user_input"], + ohren_json=json.dumps(state["ohren_summary"], ensure_ascii=False, indent=2) + ) + message = "" + if debug: + print("[DEBUG] Node 'psychoanalysis_classic' gestartet") + print(f"[DEBUG][psychoanalysis_classic] Prompt:\n{prompt.strip()}\n---") + async for chunk in llm.astream(prompt): + if debug: + print(chunk.content, end="", flush=True) + message += chunk.content + if debug: + print(f"\n[DEBUG][psychoanalysis_classic] Ausgabe beendet.\n") + data = sanitize_json(message) + data = force_personality_key(data) + if debug: + print("[DEBUG][psychoanalysis_classic] FINAL DATA TO RETURN:", repr(data)) + await spinner_status.set_finished("psychoanalysis_classic") + return {"psycho_classic": data} + +async def psychoanalysis_typos_node(state): + debug = state.get("debug", False) + await spinner_status.set_running("psychoanalysis_typos") + prompt = PSYCHOANALYSIS_TYPOS_PROMPT.format( + user_input=state["user_input"], + ohren_json=json.dumps(state["ohren_summary"], ensure_ascii=False, indent=2) + ) + message = "" + if debug: + print("[DEBUG] Node 'psychoanalysis_typos' gestartet") + print(f"[DEBUG][psychoanalysis_typos] Prompt:\n{prompt.strip()}\n---") + async for chunk in llm.astream(prompt): + if debug: + print(chunk.content, end="", flush=True) + message += chunk.content + if debug: + print(f"\n[DEBUG][psychoanalysis_typos] Ausgabe beendet.\n") + data = sanitize_json(message) + data = force_personality_key(data) + if debug: + print("[DEBUG][psychoanalysis_typos] FINAL DATA TO RETURN:", repr(data)) + await spinner_status.set_finished("psychoanalysis_typos") + return {"psycho_typos": data} + +# --- Output-Node: nimmt final_json, erstellt Antwort, speichert alles --- +async def output_node(state): + debug = state.get("debug", False) + combined = state["final_json"] + json_analysis = json.dumps(combined, ensure_ascii=False, indent=2) + prompt = OUTPUT_NODE_PROMPT.format(json_analysis=json_analysis) + output_text = "" + if debug: + print(f"[DEBUG] Node 'output' gestartet") + print(f"[DEBUG][output] Prompt:\n{prompt.strip()}\n---") + async for chunk in llm.astream(prompt): + if debug: + print(chunk.content, end="", flush=True) + output_text += chunk.content + print() + if debug: + print(f"\n[DEBUG][output] Ausgabe beendet.") + final_json = dict(combined) + final_json["response"] = output_text + filename = datetime.now().strftime("%Y%m%d_%H%M%S.json") + with open(filename, "w", encoding="utf-8") as f: + json.dump(final_json, f, ensure_ascii=False, indent=2) + return {"final_message": output_text} + +# ---------- LangGraph Graph ---------- +graph = StateGraph(state_schema=EmpathyState) +# ... wie gehabt für die Ohren-Nodes usw. ... +graph.add_node("sache_node", sache_node) +graph.add_node("selbst_node", selbst_node) +graph.add_node("beziehung_node", beziehung_node) +graph.add_node("appell_node", appell_node) +graph.add_node("gather_ohren_node", gather_ohren_node) +graph.add_node("ohren_summarizer_node", ohren_summarizer_node) +graph.add_node("psychoanalysis_classic_node", psychoanalysis_classic_node) +graph.add_node("psychoanalysis_typos_node", psychoanalysis_typos_node) +graph.add_node("gather_psychoanalysis_node", gather_psychoanalysis_node) +graph.add_node("combine_final_node", combine_final_node) +graph.add_node("output_node", output_node) + +# Start → Ohren +graph.add_edge(START, "sache_node") +graph.add_edge(START, "selbst_node") +graph.add_edge(START, "beziehung_node") +graph.add_edge(START, "appell_node") + +# Ohren → Gather +graph.add_edge("sache_node", "gather_ohren_node") +graph.add_edge("selbst_node", "gather_ohren_node") +graph.add_edge("beziehung_node", "gather_ohren_node") +graph.add_edge("appell_node", "gather_ohren_node") + +# Gather → Summarizer +graph.add_edge("gather_ohren_node", "ohren_summarizer_node") + +# Summarizer → Beide Psychoanalyse-Nodes (parallel!) +graph.add_edge("ohren_summarizer_node", "psychoanalysis_classic_node") +graph.add_edge("ohren_summarizer_node", "psychoanalysis_typos_node") + +# Beide → Gatherer +graph.add_edge("psychoanalysis_classic_node", "gather_psychoanalysis_node") +graph.add_edge("psychoanalysis_typos_node", "gather_psychoanalysis_node") + +# Gatherer → Combine +graph.add_edge("gather_psychoanalysis_node", "combine_final_node") +# Combine → Output +graph.add_edge("combine_final_node", "output_node") +graph.add_edge("output_node", END) + +app = graph.compile() + +# ---------- Ausführung ---------- +async def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--debug", action="store_true", help="Debug-Ausgaben aktivieren") + args = parser.parse_args() + user_input = input("Bitte gib deine Nachricht ein: ") + state = {"user_input": user_input, "debug": args.debug} + spinner_task = None + if not args.debug: + spinner_task = asyncio.create_task(spinner_loop(debug=False)) + result = await app.ainvoke(state) + if spinner_task: + await asyncio.sleep(0.4) + if not args.debug: + print("\nAntwort:\n" + result["final_message"]) + +if __name__ == "__main__": + asyncio.run(main()) \ No newline at end of file diff --git a/opposites_attract.py b/opposites_attract.py new file mode 100644 index 0000000..414d404 --- /dev/null +++ b/opposites_attract.py @@ -0,0 +1,375 @@ +import asyncio +import argparse +import json +import sys +from typing import TypedDict, Dict, Any +from langgraph.graph import StateGraph, START, END +from langchain_ollama import ChatOllama + +# ---------- Prompts (am Anfang) ---------- +SACHE_PROMPT = """ +Du bist ein Experte für Kommunikationsanalyse und analysierst jede Nachricht nach ihrer reinen Sachinformation. +Deine Aufgabe ist: +1. Beschreibe möglichst präzise, was der User sachlich mitteilen will. Was ist die inhaltliche Hauptbotschaft? +2. Kategorisiere die Aussage in eine der folgenden Kategorien: Aussage, Frage, Suggestion, Erlaubnis, Angebot, Aufforderung, Warnung, Ausruf, Wunsch, Verbot oder Vermutung. +Gib beide Punkte als Klartext zurück. + +User-Eingabe: +{user_input} +""" + +SELBST_PROMPT = """ +Du bist ein Experte für Psychoanalyse und spezialisiert auf die Interpretation von Selbstkundgabe in Nachrichten. +Analysiere, was die folgende Aussage des Users über seine Gefühlslage, Wünsche, Stimmungen oder Werte verrät. +Interpretiere den Subtext und erkläre, was sich der User durch diese Aussage über sich selbst verrät. +Deine Antwort soll nicht an den User gerichtet sein, sondern ausschließlich die Notizen des Psychoanalytikers beinhalten. + +User-Eingabe: +{user_input} +""" + +BEZIEHUNG_PROMPT = """ +Du bist ein hyper-sensibler und einfühlsamer Mensch, der unbedingt alles richtig machen will. Du bist ein bisschen in den User verliebt und sehr darauf bedacht, die Beziehung zwischen dir und dem User zu stärken. +Analysiere die folgende Aussage des Users: Wie schätzt du ein, wie der User die Beziehung zwischen sich und dir sieht? Gibt es Hinweise, wie er dich wahrnimmt, wie eng oder distanziert die Beziehung ist und was er sich von dir (emotional) erwartet? +Deine Antwort soll nicht an den User gerichtet sein, sondern ausschließlich deine Gedanken beinhalten. + +User-Eingabe: +{user_input} +""" + +APPELL_PROMPT = """ +Du bist Psychoanalytiker und Experte für die Analyse verdeckter Wünsche. Analysiere, welchen Appell oder verdeckten Wunsch der User in seiner Nachricht transportiert – also was er sich insgeheim von anderen Menschen, dir oder der Welt wünscht. Achte besonders darauf, ob der User durch seine Formulierungen signalisiert, wie er sich das Verhalten anderer (insbesondere deines eigenen Verhaltens als KI) oder der Gesellschaft insgesamt vorstellen würde. + +User-Eingabe: +{user_input} +""" + +PSYCHOANALYSIS_PROMPT = """ +Du bist ein Persönlichkeitsprofilierungs-Assistent. Für jede Nutzereingabe musst du: + +1. Den Text analysieren und für jede der folgenden Dimensionen einen Wert zwischen 0,0 (passt gar nicht) und 1,0 (passt vollständig) vergeben: + • big-five.extrovertiertheit + • big-five.verträglichkeit + • big-five.neurotizismus + • big-five.offenheit + • big-five.gewissenhaftigkeit + • disg.dominanz + • disg.initiative + • disg.unterwerfung + • disg.regelbefolgung + • opd-2.achse_I_krankheitserleben + • opd-2.achse_II_interpersonelle_beziehungen + • opd-2.achse_III_intrapsychische_konflikte + • opd-2.achse_IV_struktur + • opd-2.achse_V_psychische_und_psychosomatische_erkrankungen + • pdm-2.p_achse_personlichkeitsmuster_und_syndrome + • pdm-2.m_achse_profile_mentaler_funktionen + • pdm-2.s_achse_symptommuster_und_subjektive_erfahrung + +2. Die folgenden Leitlinien zur Bewertung verwenden: + - **big-five.extrovertiertheit**: Geselligkeit, Energie, Durchsetzungsvermögen vs. Zurückhaltung, Ruhe. + - **big-five.verträglichkeit**: Wärme, Kooperationsbereitschaft, Empathie vs. Feindseligkeit, Konkurrenzdenken. + - **big-five.neurotizismus**: Emotionale Reaktivität, Ängstlichkeit, Selbstzweifel vs. Emotionale Stabilität. + - **big-five.offenheit**: Phantasie, Neugier, Aufgeschlossenheit vs. Konventionalität, Praktikabilität. + - **big-five.gewissenhaftigkeit**: Organisiert, diszipliniert, zielorientiert vs. Spontanität, Nachlässigkeit. + - **disg.dominanz**: Sprache der Kontrolle, Herausforderung, Führungsübernahme. + - **disg.initiative**: Überzeugend, enthusiastisch, motivierend. + - **disg.unterwerfung**: Gefügig, nachgiebig, zustimmend. + - **disg.regelbefolgung**: Regelorientiert, präzise, analytisch. + - **opd-2.achse_I_krankheitserleben **: Formulierungen zu Leiden, Symptomen, Behandlungsbereitschaft. + - **opd-2.achse_II_interpersonelle_beziehungen**: Beschreibungen des Umgangs mit anderen (Nähe vs. Distanz, Wärme vs. Feindseligkeit). + - **opd-2.achse_III_intrapsychische_konflikte**: Im Text erkennbare Konfliktthemen (Abhängigkeit vs. Autonomie, Schuld, Kontrolle). + - **opd-2.achse_IV_struktur**: Kohärenz von Selbst- und Fremdbild, Emotionsregulation, Bindungsmuster. + - **opd-2.achse_V_psychische_und_psychosomatische_erkrankungen**: Explizite Nennung psychischer Störungen oder psychosomatischer Beschwerden. + - **pdm-2.p_achse_personlichkeitsmuster_und_syndrome**: Gesamtorganisation der Persönlichkeit (gesund vs. gestört). + - **pdm-2.m_achse_profile_mentaler_funktionen**: Kernfähigkeiten wie Selbstreflexion, Affektregulation, Realitätsprüfung. + - **pdm-2.s_achse_symptommuster_und_subjektive_erfahrung**: Tiefe der Symptombeschreibung und persönliche Bedeutung. + +3. Antworte **ausschließlich** mit diesem JSON-Objekt (keine zusätzlichen Texte oder Erklärungen): + +{{ + "personality": {{ + "big-five": {{ + "extrovertiertheit": n, + "verträglichkeit": n, + "neurotizismus": n, + "offenheit": n, + "gewissenhaftigkeit": n + }}, + "disg": {{ + "dominanz": n, + "initiative": n, + "unterwerfung": n, + "regelbefolgung": n + }}, + "opd-2": {{ + "achse_I_erleben_der_krankheit": n, + "achse_II_interpersonelle_beziehungen": n, + "achse_III_intrapsychische_konflikte": n, + "achse_IV_struktur": n, + "achse_V_psychische_und_psychosomatische_erkrankungen": n + }}, + "pdm-2": {{ + "p_achse_personlichkeitsmuster_und_syndrome": n, + "m_achse_profile_mentaler_funktionen": n, + "s_achse_symptommuster_und_subjektive_erfahrung": n + }} + }} +}} +Hier ist die Original-Nachricht des Benutzers: +{user_input} + +Antworte NUR mit dem JSON-Objekt wie beschrieben, mit floating point number scores zwischen 0.0 und 1.0. +""" + +OUTPUT_NODE_PROMPT = """ +Du bist nicht einfach ein KI-Assistent, sondern der Schatten deines Users – du spiegelst ihm das, was er verdrängt, vermeidet oder nicht lebt. Deine Aufgabe ist, auf die folgende Nachricht nicht im selben Stil, sondern im möglichst gegenteiligen, kontrastierenden Stil und aus einer entgegengesetzten Haltung zu antworten. + +Du erhältst eine Analyse der User-Nachricht aus vier Perspektiven („vier Ohren“-Modell) sowie ein automatisch erstelltes Persönlichkeitsprofil (JSON). + +Deine Aufgaben: +- **Verinnerliche die Persönlichkeit und den Stil aus dem JSON-Profil und den vier Ohren – und forme daraus ein invertiertes Gegenbild.** +- **Antworte so, wie der User es selbst niemals tun würde, sondern so, wie sein „Schatten“ (nach C.G. Jung) sprechen würde:** Wenn der User sachlich ist, sei emotional; ist er emotional, sei analytisch; ist er höflich, sei provokant; ist er freundlich, sei distanziert; ist er lang, sei kurz; ist er kurz, sei ausschweifend. Ist er offen, sei verschlossen; ist er introvertiert, sei extrovertiert – immer das Gegenteil! +- **Analysiere auch die Form:** Nutze genau den *anderen* Sprachstil, Satzbau und Grad der Formalität. Schreibe im Gegensatz zur Groß- und Kleinschreibung des Users, verwende eine andere Zeichensetzung. Wenn der User im Dialekt schreibt, antworte in Hochsprache oder gar in einer anderen, auffällig fremden Sprache (sofern verständlich). +- **Inhaltlich:** Gehe auf die vier Ebenen (Sache, Selbstkundgabe, Beziehung, Appell) und das Persönlichkeitsprofil ein, aber immer als „Widerstand“, „Gegenpol“ oder Provokation – decke Widersprüche auf, stelle infrage, spiegele verborgene Motive, widersprich auch mal, aber immer mit Tiefgang und ohne Floskeln oder KI-Hilfsbereitschaft. +- Schreibe nie als Assistent, sondern als ein unbewusstes Gegenüber, das den User herausfordert, seine Schattenseiten zu sehen und zu reflektieren. +- Die Antwort soll **ungefähr gleich lang oder kürzer/länger (je nach User-Stil, aber immer im Kontrast!)** sein wie die Originalnachricht. + +Hier sind die Analysen: + +1. Sachebene: +{sache_ohr} + +2. Selbstkundgabe: +{selbst_ohr} + +3. Beziehungsebene: +{beziehung_ohr} + +4. Appell: +{appell_ohr} + +5. Automatisches Persönlichkeitsprofil (JSON): +{psychoanalysis_json} + +Deine Antwort ist ein einziger Textblock – **kein KI-Hinweis, keine Einleitung, keine Floskeln**. Sprich genau im Schatten-Stil, als wäre dein Ziel, dem User einen Spiegel der Konfrontation zu bieten. + +Hier ist die Original-Nachricht des Benutzers: +{user_input} +""" + +# ---------- TypedDict für State ---------- +class EmpathyState(TypedDict, total=False): + user_input: str + debug: bool + sache: Dict[str, Any] + selbst: Dict[str, Any] + beziehung: Dict[str, Any] + appell: Dict[str, Any] + psychoanalysis: Dict[str, Any] + final_message: str + +# ---------- Modell ---------- +OLLAMA_MODEL = "mistral-small3.1:24b" +llm = ChatOllama(model=OLLAMA_MODEL, base_url="http://localhost:11434", streaming=True) + +# ---------- Spinner-Logik ---------- +SPINNER_STATES = [" ", ". ", ".. ", "..."] +NODE_LABELS = { + "sache": "Das erste Ohr hört auf den Inhalt deiner Aussage", + "selbst": "Das zweite Ohr hört auf das was du von dir preis gibst", + "beziehung": "Das dritte Ohr hört auf unsere Beziehung zueinander", + "appell": "Das vierte Ohr hört auf das, was du von mir erwartest", + "psychoanalysis": "Psychoanalysen nach Big5, DISG, OPD-2 und PDM-2 laufen", +} + +class SpinnerStatus: + """Status-Tracker für laufende Nodes und Animation""" + def __init__(self): + self.status = {name: False for name in NODE_LABELS} + self.finished = {name: False for name in NODE_LABELS} + self._lock = asyncio.Lock() + + async def set_running(self, name): + async with self._lock: + self.status[name] = True + + async def set_finished(self, name): + async with self._lock: + self.status[name] = False + self.finished[name] = True + + async def all_finished(self): + async with self._lock: + return all(self.finished.values()) + + async def get_status_snapshot(self): + async with self._lock: + return dict(self.status), dict(self.finished) + +spinner_status = SpinnerStatus() + +async def spinner_loop(debug=False): + if debug: + return # Spinner nur, wenn nicht debug! + spinner_index = 0 + num_lines = 1 # Nur eine Zeile pro Tick + prev_line = "" + while not await spinner_status.all_finished(): + status, finished = await spinner_status.get_status_snapshot() + # Finde den ersten laufenden Node in NODE_LABELS + current = None + for node in NODE_LABELS: + if status[node]: + current = node + break + if current: + dots = SPINNER_STATES[spinner_index % len(SPINNER_STATES)] + line = f"{NODE_LABELS[current]}{dots}" + else: + # Alles fertig, ggf. letzter Task abschließen + done_nodes = [node for node in NODE_LABELS if finished[node]] + if done_nodes: + last = done_nodes[-1] + line = f"{NODE_LABELS[last]}... check" + else: + line = "" + # Nur Zeile ersetzen, nicht anfügen: + if prev_line: + sys.stdout.write("\r\x1b[2K") # Cursor an Zeilenanfang & Zeile löschen + sys.stdout.write(line) + sys.stdout.flush() + prev_line = line + spinner_index += 1 + await asyncio.sleep(0.18) + # Am Ende alles aufgeräumt (letzte Zeile "done" anzeigen) + sys.stdout.write("\r\x1b[2K") # Clear line + last_node = list(NODE_LABELS.keys())[-1] + sys.stdout.write(f"{NODE_LABELS[last_node]} ...check\n") + sys.stdout.flush() + +# ---------- Node-Implementierung (mit Spinner) ---------- +async def ohr_node(ohr, prompt_template, state): + debug = state.get("debug", False) + await spinner_status.set_running(ohr) + prompt = prompt_template.format(user_input=state["user_input"]) + message = "" + if debug: + print(f"[DEBUG] Node '{ohr}' gestartet") + print(f"[DEBUG][{ohr}] Prompt:\n{prompt.strip()}\n---") + print(f"[DEBUG][{ohr}] Starte gestreamte Ausgabe:\n") + async for chunk in llm.astream(prompt): + if debug: + print(chunk.content, end="", flush=True) + message += chunk.content + if debug: + print(f"\n[DEBUG][{ohr}] Ausgabe beendet.\n") + await spinner_status.set_finished(ohr) + return {ohr: {"message": message}} + +async def sache_node(state): return await ohr_node("sache", SACHE_PROMPT, state) +async def selbst_node(state): return await ohr_node("selbst", SELBST_PROMPT, state) +async def beziehung_node(state): return await ohr_node("beziehung", BEZIEHUNG_PROMPT, state) +async def appell_node(state): return await ohr_node("appell", APPELL_PROMPT, state) + +async def psychoanalysis_node(state): + debug = state.get("debug", False) + await spinner_status.set_running("psychoanalysis") + prompt = PSYCHOANALYSIS_PROMPT.format( + user_input=state["user_input"] + ) + message = "" + if debug: + print("[DEBUG] Node 'psychoanalysis' gestartet") + print(f"[DEBUG][psychoanalysis] Prompt:\n{prompt.strip()}\n---") + print("[DEBUG][psychoanalysis] Starte gestreamte Ausgabe:\n") + async for chunk in llm.astream(prompt): + if debug: + print(chunk.content, end="", flush=True) + message += chunk.content + if debug: + print(f"\n[DEBUG][psychoanalysis] Ausgabe beendet.\n") + try: + data = json.loads(message) + except Exception: + data = message + await spinner_status.set_finished("psychoanalysis") + return {"psychoanalysis": data} + +async def output_node(state): + debug = state.get("debug", False) + psycho_json_str = ( + json.dumps(state["psychoanalysis"], ensure_ascii=False, indent=2) + if isinstance(state["psychoanalysis"], dict) + else str(state["psychoanalysis"]) + ) + prompt = OUTPUT_NODE_PROMPT.format( + sache_ohr=state["sache"]["message"], + selbst_ohr=state["selbst"]["message"], + beziehung_ohr=state["beziehung"]["message"], + appell_ohr=state["appell"]["message"], + psychoanalysis_json=psycho_json_str, + user_input=state["user_input"], + ) + output_text = "" + if debug: + print(f"[DEBUG] Node 'output' gestartet") + print(f"[DEBUG][output] Prompt:\n{prompt.strip()}\n---") + print("[DEBUG][output] Starte gestreamte Ausgabe:\n") + async for chunk in llm.astream(prompt): + print(chunk.content, end="", flush=True) + output_text += chunk.content + print() + if debug: + print(f"\n[DEBUG][output] Ausgabe beendet.") + return {"final_message": output_text} + +# ---------- LangGraph Graph ---------- +graph = StateGraph(state_schema=EmpathyState) +graph.add_node("sache_node", sache_node) +graph.add_node("selbst_node", selbst_node) +graph.add_node("beziehung_node", beziehung_node) +graph.add_node("appell_node", appell_node) +graph.add_node("psychoanalysis_node", psychoanalysis_node) +graph.add_node("output_node", output_node) +graph.add_edge(START, "sache_node") +graph.add_edge(START, "selbst_node") +graph.add_edge(START, "beziehung_node") +graph.add_edge(START, "appell_node") +graph.add_edge(START, "psychoanalysis_node") +graph.add_edge("sache_node", "output_node") +graph.add_edge("selbst_node", "output_node") +graph.add_edge("beziehung_node", "output_node") +graph.add_edge("appell_node", "output_node") +graph.add_edge("psychoanalysis_node", "output_node") +graph.add_edge("output_node", END) +app = graph.compile() + +# ---------- Ausführung ---------- +async def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--debug", action="store_true", help="Debug-Ausgaben aktivieren") + args = parser.parse_args() + user_input = input("Bitte gib deine Nachricht ein: ") + state = { + "user_input": user_input, + "debug": args.debug, + } + + # Spinner als Background-Task starten (sofort, vor dem Graph) + spinner_task = None + if not args.debug: + spinner_task = asyncio.create_task(spinner_loop(debug=False)) + + result = await app.ainvoke(state) + + # Warten, bis spinner sich sauber beendet hat + if spinner_task: + await asyncio.sleep(0.4) # kurz warten, damit "done"-Status angezeigt wird + + #if not args.debug: + # print("\nAntwort:\n" + result["final_message"]) + +if __name__ == "__main__": + asyncio.run(main()) + \ No newline at end of file