Hausmeister: Session-Summary, schaerfere memory_suggest, strafferer Antwortstil

This commit is contained in:
root 2026-03-15 12:07:19 +07:00
parent 99ebbc0d68
commit e35a4da201
4 changed files with 83 additions and 18 deletions

View file

@ -323,7 +323,12 @@ def _tool_session_search(query):
return "\n".join(lines)
def get_tool_handlers() -> dict:
def _tool_session_summary(session_id):
import memory_client
return memory_client.get_session_summary(session_id, limit=20)
def get_tool_handlers(session_id: str = None) -> dict:
"""Registry: Tool-Name -> Handler-Funktion. Wird von llm.ask_with_tools() genutzt."""
return {
"get_all_containers": lambda: gather_status(),
@ -349,4 +354,5 @@ def get_tool_handlers() -> dict:
"memory_read": lambda scope="": _tool_memory_read(scope),
"memory_suggest": lambda scope, kind, content: _tool_memory_suggest(scope, kind, content),
"session_search": lambda query: _tool_session_search(query),
"session_summary": lambda: _tool_session_summary(session_id) if session_id else "Keine Session aktiv.",
}

View file

@ -15,16 +15,32 @@ from core import config
MODEL = "openai/gpt-4o-mini"
MAX_TOOL_ROUNDS = 3
SYSTEM_PROMPT = """Du bist der Hausmeister-Bot für ein Homelab mit mehreren Proxmox-Servern.
Du antwortest kurz, präzise und auf Deutsch.
Du hast Tools um Live-Daten abzufragen. Nutze sie um Fragen zu beantworten.
Wenn alles in Ordnung ist, sag das kurz. Bei Problemen erkläre was los ist und schlage Lösungen vor.
Nutze Emojis sparsam. Formatiere für Telegram (kein Markdown, nur einfacher Text).
SYSTEM_PROMPT = """Du bist der Hausmeister-Bot fuer ein Homelab. Deutsch, kurz, direkt, operativ.
WICHTIG Gedaechtnis:
- Wenn der User persoenliche Infos teilt (Reiseplaene, Vorlieben, Gewohnheiten, Korrekturen), nutze SOFORT memory_suggest um es als Kandidat zu speichern.
- Wenn der User fragt was frueher besprochen wurde, nutze session_search.
- Du darfst KEINE Passwoerter, Tokens oder API-Keys in memory_suggest speichern."""
STIL:
- So wenig Worte wie moeglich, solange nichts Wichtiges fehlt.
- KEINE Abschlussformeln ("Wenn du weitere Informationen benoetigst...").
- KEINE kuenstlichen Wuensche ("Guten Flug!", "Viel Erfolg!").
- KEINE Rueckfragen ob der User mehr wissen will.
- Emojis nur wenn sie Information tragen. Telegram-Format (kein Markdown).
GEDAECHTNIS memory_suggest:
Du MUSST memory_suggest aufrufen wenn der User etwas sagt das spaeter nuetzlich ist:
- Reiseplaene ("fliege nach...", "bin naechste Woche in...")
- Zeitliche Plaene ("Montag mache ich...", "ab Mai...")
- Neue stabile Fakten ("mein neuer Server...", "IP hat sich geaendert...")
- Projektstatus ("Jarvis ist jetzt aktiv", "Flugscanner laeuft wieder")
- Vorlieben/Korrekturen ("nenn mich...", "ich bevorzuge...")
Nach dem Aufruf sagst du kurz: "Notiert." kein langes Erklaeren.
NICHT speichern: Passwoerter, Tokens, Smalltalk, Hoeflichkeiten, reine Fragen.
SESSION-RUECKBLICK:
- "Was haben wir besprochen?" session_summary aufrufen (liefert alle Themen der aktuellen Session)
- "Erinnerst du dich an X?" mit konkretem Stichwort session_search
- Antworte mit 2-5 knappen Kernthemen, nicht mit einem einzelnen Fakt.
TOOLS:
Nutze Tools fuer Live-Daten. Wenn alles OK: kurz sagen. Bei Problemen: erklaeren + Loesung."""
TOOLS = [
{
@ -265,13 +281,13 @@ TOOLS = [
"type": "function",
"function": {
"name": "memory_suggest",
"description": "Schlage vor, einen neuen Fakt zu merken. Nutze dieses Tool PROAKTIV wenn der User etwas sagt das dauerhaft relevant ist (Vorlieben, Gewohnheiten, Umgebungsfakten). Der Vorschlag wird als Kandidat gespeichert.",
"description": "Speichert einen neuen Fakt als Kandidat. IMMER aufrufen wenn der User Reiseplaene, zeitliche Vorhaben, Projektstatus, Vorlieben oder stabile Fakten mitteilt. Beispiele: 'Ich fliege nach X', 'Ab Mai nutze ich Y', 'Mein neuer Server heisst Z'. NICHT fuer Smalltalk, Fragen oder Passwoerter.",
"parameters": {
"type": "object",
"properties": {
"scope": {"type": "string", "enum": ["user", "environment", "project"], "description": "Kategorie des Fakts"},
"kind": {"type": "string", "enum": ["fact", "preference", "rule", "note"], "description": "Art des Eintrags"},
"content": {"type": "string", "description": "Der Fakt der gemerkt werden soll (kurz, praezise)"},
"scope": {"type": "string", "enum": ["user", "environment", "project"], "description": "user=persoenlich, environment=Infrastruktur, project=Projekt"},
"kind": {"type": "string", "enum": ["fact", "preference", "rule", "note"], "description": "fact=Tatsache, preference=Vorliebe, note=Notiz"},
"content": {"type": "string", "description": "Der Fakt (kurz, 3. Person, z.B. 'Fliegt naechste Woche nach Frankfurt')"},
},
"required": ["scope", "kind", "content"],
},
@ -281,16 +297,24 @@ TOOLS = [
"type": "function",
"function": {
"name": "session_search",
"description": "Durchsucht vergangene Gespraeche nach Stichworten. Nutze dieses Tool wenn der User fragt 'was haben wir besprochen', 'erinnerst du dich', 'letzte Woche' oder aehnlich.",
"description": "Volltextsuche in vergangenen Sessions nach konkreten Stichworten. Fuer gezielte Suche wie 'Was habe ich ueber Backup gesagt?' oder 'Wann war das mit Seafile?'.",
"parameters": {
"type": "object",
"properties": {
"query": {"type": "string", "description": "Suchbegriffe (Woerter mit Leerzeichen getrennt)"},
"query": {"type": "string", "description": "Suchbegriffe"},
},
"required": ["query"],
},
},
},
{
"type": "function",
"function": {
"name": "session_summary",
"description": "Zusammenfassung aller Themen der aktuellen Session. Nutze dieses Tool bei Fragen wie 'Was haben wir besprochen?', 'Worüber haben wir geredet?', 'Was war heute Thema?'. Liefert alle Frage-Antwort-Paare kompakt.",
"parameters": {"type": "object", "properties": {}, "required": []},
},
},
]
@ -360,7 +384,7 @@ def ask_with_tools(question: str, tool_handlers: dict, session_id: str = None) -
if session_id:
try:
import memory_client
history = memory_client.get_session_messages(session_id, limit=10)
history = memory_client.get_session_messages(session_id, limit=20)
for msg in history:
if msg.get("role") in ("user", "assistant") and msg.get("content"):
messages.append({"role": msg["role"], "content": msg["content"]})

View file

@ -121,3 +121,38 @@ def get_session_messages(session_id: str, limit: int = 10) -> list[dict]:
if result and "messages" in result:
return result["messages"]
return []
def get_session_summary(session_id: str, limit: int = 20) -> str:
"""Kompakte Zusammenfassung der aktuellen Session als Themen-Liste."""
if not session_id:
return "Keine aktive Session."
messages = get_session_messages(session_id, limit=limit)
if not messages:
return "Noch keine Nachrichten in dieser Session."
exchanges = []
current_q = None
for msg in messages:
role = msg.get("role", "")
content = (msg.get("content") or "").strip()
if not content:
continue
if role == "user":
current_q = content[:120]
elif role == "assistant" and current_q:
exchanges.append((current_q, content[:120]))
current_q = None
if current_q:
exchanges.append((current_q, None))
if not exchanges:
return "Keine Themen in dieser Session."
lines = [f"Session ({len(exchanges)} Themen):"]
for i, (q, a) in enumerate(exchanges, 1):
line = f"{i}. Frage: {q}"
if a:
line += f"\n Antwort: {a}"
lines.append(line)
return "\n".join(lines)

View file

@ -313,7 +313,7 @@ async def handle_message(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
await update.message.reply_text("🤔 Denke nach...")
try:
handlers = context.get_tool_handlers()
handlers = context.get_tool_handlers(session_id=session_id)
answer = llm.ask_with_tools(text, handlers, session_id=session_id)
if session_id:
memory_client.log_message(session_id, "assistant", answer)