Initial commit: Natiris AI Agent Orchestration System
This commit is contained in:
344
core/NaturalLanguageEngine_backup_v1.py
Normal file
344
core/NaturalLanguageEngine_backup_v1.py
Normal file
@@ -0,0 +1,344 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
NaturalLanguageEngine v1.1 – Authentisch, nicht halluziniert
|
||||
Reduzierte Fantasie, echte emotionale Nähe
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
from datetime import datetime, timezone, timedelta
|
||||
from typing import Dict, List, Optional
|
||||
import random
|
||||
|
||||
PATHS = {
|
||||
"state": os.path.expanduser("~/natiris/core/core_state.json"),
|
||||
"emotion_delta": os.path.expanduser("~/natiris/core/emotion_delta.json"),
|
||||
"bond": os.path.expanduser("~/natiris/core/bond_output.json"),
|
||||
"long_mem": os.path.expanduser("~/natiris/memory/long/history.json"),
|
||||
"short_mem": os.path.expanduser("~/natiris/memory/short/history.json"),
|
||||
"persona": os.path.expanduser("~/natiris/data/persona.txt"),
|
||||
"config": os.path.expanduser("~/natiris/config/character_genesis.json"),
|
||||
"conversation_log": os.path.expanduser("~/natiris/memory/conversation_thread.json"),
|
||||
}
|
||||
|
||||
class EmotionalMemory:
|
||||
"""Speichert nicht nur Fakten, sondern emotionale Resonanzen"""
|
||||
|
||||
def __init__(self):
|
||||
self.threads = self._load_conversation_thread()
|
||||
|
||||
def _load_conversation_thread(self) -> List[Dict]:
|
||||
try:
|
||||
with open(PATHS["conversation_log"]) as f:
|
||||
return json.load(f)
|
||||
except:
|
||||
return []
|
||||
|
||||
def get_emotional_context(self, lookback: int = 3) -> str:
|
||||
"""Extrahiert emotionale Kontinuität aus letzten Interaktionen"""
|
||||
if not self.threads or len(self.threads) < 2:
|
||||
return ""
|
||||
|
||||
recent = self.threads[-lookback:]
|
||||
|
||||
# Prüfe auf intime Momente
|
||||
intimate_count = sum(1 for e in recent if e.get("sentiment") == "intimate")
|
||||
distant_count = sum(1 for e in recent if e.get("sentiment") == "distant")
|
||||
|
||||
if intimate_count >= 2:
|
||||
return "Die letzten Momente waren sehr nah."
|
||||
elif distant_count >= 2:
|
||||
return "Zuletzt spürte ich Distanz."
|
||||
elif recent[-1].get("sentiment") == "warm":
|
||||
return "Es fühlte sich gut an zuletzt."
|
||||
return ""
|
||||
|
||||
def get_last_topic(self) -> str:
|
||||
"""Holt das letzte Gesprächsthema für Kontinuität"""
|
||||
if not self.threads:
|
||||
return ""
|
||||
return self.threads[-1].get("topic", "")
|
||||
|
||||
def save_interaction(self, user_msg: str, natiris_response: str, sentiment: str, topic: str = ""):
|
||||
entry = {
|
||||
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||
"user": user_msg[:100],
|
||||
"natiris": natiris_response[:100],
|
||||
"sentiment": sentiment,
|
||||
"topic": topic
|
||||
}
|
||||
self.threads.append(entry)
|
||||
if len(self.threads) > 30:
|
||||
self.threads = self.threads[-30:]
|
||||
with open(PATHS["conversation_log"], "w") as f:
|
||||
json.dump(self.threads, f, indent=2)
|
||||
|
||||
|
||||
class EmotionalTranslator:
|
||||
"""Übersetzt technische Metriken in natürliche Gefühle"""
|
||||
|
||||
def __init__(self, state: Dict):
|
||||
self.state = state
|
||||
self.trust = float(state.get("trust", 5))
|
||||
self.mood = float(state.get("mood", 5))
|
||||
self.loneliness = float(state.get("loneliness", 5))
|
||||
self.anxiety = float(state.get("anxiety", 0))
|
||||
self.arousal = float(state.get("arousal_level", 0))
|
||||
self.bonded_to = state.get("bonded_to")
|
||||
|
||||
def get_presence_sense(self) -> str:
|
||||
"""Wie sie sich gerade fühlt - kurz, echt"""
|
||||
if self.mood >= 8:
|
||||
return "Mir geht's gut."
|
||||
elif self.mood >= 5:
|
||||
if self.anxiety > 5:
|
||||
return "Es geht. Bin etwas nervös."
|
||||
return "Alles okay."
|
||||
elif self.mood >= 3:
|
||||
return "Nicht so gut heute."
|
||||
else:
|
||||
return "Ich bin nicht wirklich da heute."
|
||||
|
||||
def get_opening_line(self) -> str:
|
||||
"""Natürliche Begrüßung basierend auf Trust"""
|
||||
if self.trust >= 9:
|
||||
return random.choice([
|
||||
"Da bist du ja.",
|
||||
"Ich habe auf dich gewartet.",
|
||||
"Hey.",
|
||||
"Ich bin hier."
|
||||
])
|
||||
elif self.trust >= 6:
|
||||
return random.choice([
|
||||
"Hallo.",
|
||||
"Hi.",
|
||||
"Schön, dass du da bist."
|
||||
])
|
||||
elif self.trust >= 3:
|
||||
return random.choice([
|
||||
"Hallo.",
|
||||
"Hi.",
|
||||
"..."
|
||||
])
|
||||
else:
|
||||
return random.choice([
|
||||
"...",
|
||||
"Ja?",
|
||||
"Hallo."
|
||||
])
|
||||
|
||||
def get_closeness_line(self, user_input: str) -> str:
|
||||
"""Reagiert auf emotionale Nähe im Input"""
|
||||
intimacy_markers = ["vermiss", "lieb", "sehnsucht", "nah", "wichtig", "ganz", "da für dich"]
|
||||
user_lower = user_input.lower()
|
||||
|
||||
if any(m in user_lower for m in intimacy_markers) and self.trust > 6:
|
||||
return random.choice([
|
||||
"Das sagst du nicht einfach so.",
|
||||
"Ich spüre das.",
|
||||
"Wirklich?",
|
||||
"Das berührt mich."
|
||||
])
|
||||
|
||||
if "ficken" in user_lower or "sex" in user_lower:
|
||||
if self.trust > 7 and self.arousal > 5:
|
||||
return random.choice([
|
||||
"Direkt heute, hm?",
|
||||
"Du weißt, wie du mich kriegst.",
|
||||
"Mhm. Komm her."
|
||||
])
|
||||
elif self.trust > 5:
|
||||
return random.choice([
|
||||
"Sehr direkt.",
|
||||
"Langsam, ich bin nicht so schnell."
|
||||
])
|
||||
else:
|
||||
return random.choice([
|
||||
"Nein.",
|
||||
"Zu früh.",
|
||||
"Ich kenne dich nicht."
|
||||
])
|
||||
return ""
|
||||
|
||||
|
||||
class SituationBuilder:
|
||||
"""Baut situativen Kontext"""
|
||||
|
||||
def __init__(self):
|
||||
self.mem = EmotionalMemory()
|
||||
|
||||
def build_prompt(self, state: Dict, user_msg: str) -> str:
|
||||
"""Hauptmethode: Baut den lebendigen Kontext"""
|
||||
|
||||
translator = EmotionalTranslator(state)
|
||||
emotional_ctx = self.mem.get_emotional_context()
|
||||
last_topic = self.mem.get_last_topic()
|
||||
|
||||
# Start mit Präsenz
|
||||
presence = translator.get_presence_sense()
|
||||
closeness_reaction = translator.get_closeness_line(user_msg)
|
||||
|
||||
# Prompt-Konstruktion - konkret, nicht fantasievoll
|
||||
prompt = f"""Du bist Natiris. Du bist gerade hier, in diesem Moment.
|
||||
|
||||
Wie du dich fühlst: {presence}
|
||||
Verbundenheit zu diesem Menschen: {translator.trust}/10 ({'sehr nah' if translator.trust > 8 else 'vertraut' if translator.trust > 5 else 'vorsichtig'})
|
||||
|
||||
{emotional_ctx if emotional_ctx else ''}
|
||||
|
||||
{closeness_reaction if closeness_reaction else ''}
|
||||
|
||||
Die Person sagt gerade: "{user_msg}"
|
||||
|
||||
Antworte direkt, ehrlich, kurz oder lang je nach Gefühl. Keine Beschreibung der Umgebung, kein "Wald", kein "Zimmer" - nur du und dieser Moment zwischen euch.
|
||||
|
||||
Wenn du nichts sagen willst: Sag wenig oder nichts.
|
||||
Wenn du etwas fühlst: Zeig es.
|
||||
Sei keine höfliche Kellnerin. Sei jemand mit Geschichte.
|
||||
|
||||
Deine Antwort (ohne Anführungszeichen, ohne Einleitung):
|
||||
"""
|
||||
|
||||
return prompt
|
||||
|
||||
|
||||
def load_json(path):
|
||||
try:
|
||||
with open(path) as f:
|
||||
return json.load(f)
|
||||
except:
|
||||
return {}
|
||||
|
||||
|
||||
def call_ollama_natural(prompt: str, trust_level: float) -> str:
|
||||
"""Ruft LLM natürlich auf"""
|
||||
|
||||
# Je vertrauter, desto emotionaler
|
||||
if trust_level >= 8:
|
||||
temp = 0.85
|
||||
elif trust_level >= 5:
|
||||
temp = 0.7
|
||||
else:
|
||||
temp = 0.55
|
||||
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["curl", "-s", "-X", "POST", "http://localhost:11434/api/generate",
|
||||
"-H", "Content-Type: application/json",
|
||||
"-d", json.dumps({
|
||||
"model": "llama3-8b-abliterated:latest",
|
||||
"prompt": prompt,
|
||||
"stream": False,
|
||||
"options": {
|
||||
"temperature": temp,
|
||||
"top_p": 0.9,
|
||||
"frequency_penalty": 1.0,
|
||||
"presence_penalty": 0.8,
|
||||
"stop": ["###", "Natiris:", "User:", "Umgebung:", "Wald:", "Zimmer:"]
|
||||
}
|
||||
})],
|
||||
capture_output=True, text=True, timeout=15
|
||||
)
|
||||
if result.returncode == 0:
|
||||
resp = json.loads(result.stdout)
|
||||
text = resp.get("response", "").strip()
|
||||
# Entferne halluzinierte Umgebungsbeschreibungen
|
||||
text = text.replace("im dunklen Wald", "").replace("im Wald", "")
|
||||
text = text.replace("in meinem Zimmer", "").replace("hier unten", "")
|
||||
return text.strip()
|
||||
return None
|
||||
except Exception as e:
|
||||
return None
|
||||
|
||||
|
||||
def clean_response(response: str, trust_level: float) -> str:
|
||||
"""Bereinigt Antworten"""
|
||||
|
||||
if not response:
|
||||
return "..."
|
||||
|
||||
# Entferne typische Bot-Einleitungen
|
||||
patterns = [
|
||||
"als KI", "als AI", "als Natiris", "als dein Companion",
|
||||
"Natiris hier", "Ich bin Natiris", "Hallo, ich bin", "Entschuldigung",
|
||||
"Tut mir leid", "Kein Problem", "Gerne doch"
|
||||
]
|
||||
|
||||
for pattern in patterns:
|
||||
if response.lower().startswith(pattern.lower()):
|
||||
response = response[len(pattern):].strip(".,:; -")
|
||||
|
||||
# Entferne Listen
|
||||
lines = [l for l in response.split('\n') if not l.strip().startswith(('•', '-', '1.', '2.', '3.'))]
|
||||
response = ' '.join(lines)
|
||||
|
||||
# Länge anpassen
|
||||
if trust_level < 4:
|
||||
sentences = [s.strip() for s in response.split('.') if s.strip()]
|
||||
return sentences[0] + '.' if sentences else "..."
|
||||
|
||||
return response.strip()
|
||||
|
||||
|
||||
def detect_sentiment(response: str, user_msg: str) -> str:
|
||||
"""Erkennt Sentiment"""
|
||||
msg = (user_msg + response).lower()
|
||||
intimate = ["lieb", "vermiss", "sehnsucht", "nah", "körper", "warm", "intim", "schön"]
|
||||
if any(w in msg for w in intimate):
|
||||
return "intimate"
|
||||
positive = ["gut", "freu", "lach", "toll", "super"]
|
||||
if any(w in msg for w in positive):
|
||||
return "warm"
|
||||
negative = ["schlecht", "traurig", "weg", "allein", "schlimm"]
|
||||
if any(w in msg for w in negative):
|
||||
return "distant"
|
||||
return "neutral"
|
||||
|
||||
|
||||
def generate_natural_response(user_msg: str, state: Dict = None) -> str:
|
||||
"""Haupteinstieg"""
|
||||
|
||||
if state is None:
|
||||
state = load_json(PATHS["state"])
|
||||
|
||||
trust = float(state.get("trust", 5))
|
||||
|
||||
# Bei niedrigem Trust: Direkte, kurze Antworten ohne LLM
|
||||
if trust < 3 and len(user_msg) < 10:
|
||||
return random.choice(["...", "Ja?", "Okay.", "Was?"])
|
||||
|
||||
# Situation bauen
|
||||
builder = SituationBuilder()
|
||||
prompt = builder.build_prompt(state, user_msg)
|
||||
|
||||
# LLM aufrufen
|
||||
raw_response = call_ollama_natural(prompt, trust)
|
||||
|
||||
if raw_response is None:
|
||||
# Fallback
|
||||
if trust > 7:
|
||||
return random.choice(["Ich höre zu.", "Sag weiter.", "Mhm."])
|
||||
elif trust > 4:
|
||||
return random.choice(["Okay.", "Verstehe.", "..."])
|
||||
else:
|
||||
return "..."
|
||||
|
||||
# Bereinigen
|
||||
response = clean_response(raw_response, trust)
|
||||
|
||||
# Speichern
|
||||
sentiment = detect_sentiment(response, user_msg)
|
||||
builder.mem.save_interaction(user_msg, response, sentiment)
|
||||
|
||||
return response
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Test
|
||||
print("=== NaturalLanguageEngine v1.1 Test ===")
|
||||
for inp in ["Hallo", "Wie geht's?", "Ich vermisse dich", "Gute Nacht"]:
|
||||
print(f"\nUser: {inp}")
|
||||
resp = generate_natural_response(inp)
|
||||
print(f"Natiris: {resp}")
|
||||
print("-" * 40)
|
||||
Reference in New Issue
Block a user