jarvis-assist/docs/superpowers/specs/2026-09-13-cross-chat-memor...

6.7 KiB

JARVIS Chatuebergreifendes Gedaechtnis - Design

Datum: 2026-09-13 Status: Approved, bereit fuer Implementierungsplan

Kontext

Der Chat kennt bisher nur den Verlauf der eigenen conversation_id (main.py:754, get_messages(conversation_id)). Startet man einen neuen Chat (z.B. ueber den neuen "Neuer Chat"-Button), gehen alle Infos aus frueheren Unterhaltungen verloren - JARVIS "vergisst" Namen, Vorlieben und Kontext aus vorherigen Gespraechen. Ziel dieser Phase: ein Gedaechtnis, das ueber einzelne Conversations hinweg besteht, kombiniert aus zwei Quellen:

  • Explizite Fakten - Dinge, die der Nutzer ausdruecklich festhalten laesst ("merke dir...", "vergiss, dass...")
  • Automatische Zusammenfassungen - jede Conversation wird laufend zusammengefasst und per Aehnlichkeitssuche wiedergefunden, wenn sie zum aktuellen Thema passt

Die vorhandene Knowledge-Base-Infrastruktur (Phase 3a: pgvector, Ollama nomic-embed-text Embeddings) wird fuer die Zusammenfassungen wiederverwendet statt eine zweite Vector-Search-Loesung einzufuehren.

Datenmodell (migrations/006_memory.sql)

CREATE TABLE memory_facts (
    id SERIAL PRIMARY KEY,
    user_id INTEGER NOT NULL REFERENCES users(id),
    content TEXT NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE conversation_summaries (
    conversation_id INTEGER PRIMARY KEY REFERENCES conversations(id),
    summary TEXT NOT NULL,
    embedding vector(768) NOT NULL,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Kein ivfflat/hnsw-Index vorerst - gleiche Begruendung wie bei document_chunks (Phase 3a): bei wenig Zeilen liefert der Index 0 Treffer, Sequential Scan ist bei der zu erwartenden Groessenordnung (Dutzende bis niedrige Hunderte Conversations) schnell genug. Nachtrag folgt, sobald genug Daten vorhanden sind (gleicher offene Punkt wie bei den Dokumenten).

Fakten erfassen: zwei neue Claude-Tools

Analog zu Kalender/E-Mail in run_chat_completion():

  • remember_fact(fact: str) - legt sofort einen Eintrag in memory_facts an, keine Rueckfrage noetig (wie create_calendar_event) - ein gespeicherter Fakt ist folgenlos und leicht wieder zu entfernen.
  • forget_fact(query: str) - sucht per ILIKE '%query%' nach passenden Fakten. Bei einem Treffer: loescht ihn erst nach Bestaetigung im Chat (wie delete_calendar_event/send_email). Bei mehreren Treffern: gibt sie Claude als Liste zurueck, damit es im Chat nachfragt, welcher gemeint ist, statt blind zu loeschen. Bei keinem Treffer: klare Fehlermeldung an Claude ("kein passender Fakt gefunden").

Der System-Prompt bekommt eine kurze Ergaenzung, die Claude anweist, diese Tools zu nutzen, wenn der Nutzer explizit um Merken/Vergessen bittet - analog zu den bestehenden Anweisungen fuer Kalender-Loeschen.

Automatische Zusammenfassung

Nach dem Speichern der Assistant-Antwort in POST /api/v1/chat (nach save_message(..., "assistant", ...)) wird per asyncio.create_task ein Hintergrund-Job gestartet, der die Chat-Antwort nicht blockiert:

  1. Vollstaendigen Verlauf der Conversation laden (get_messages)
  2. Kurzer zusaetzlicher Claude-Call (kleines max_tokens-Limit): "Fasse dieses Gespraech in 2-3 Saetzen zusammen"
  3. Zusammenfassung mit der vorhandenen Ollama-Embedding-Funktion (aus Phase 3a, bereits fuer document_chunks genutzt) embedden
  4. INSERT INTO conversation_summaries (...) VALUES (...) ON CONFLICT (conversation_id) DO UPDATE SET summary = ..., embedding = ..., updated_at = CURRENT_TIMESTAMP

Laeuft nach jeder Turn-Antwort (kein Batching/Intervall-Logik noetig - bei diesem Nutzungsvolumen vernachlaessigbare Zusatzkosten, ein Client wartet nicht darauf). Ergebnis ist immer die Zusammenfassung des gesamten bisherigen Gespraechs, nicht nur der letzten Nachricht.

Einbindung in den Chat (run_chat_completion)

Vor dem eigentlichen Claude-Call wird der System-Prompt um zwei Abschnitte ergaenzt:

  1. "Bekannte Fakten ueber den Nutzer" - alle Zeilen aus memory_facts werden vollstaendig geladen und als Liste angehaengt (kleine Tabelle, kein Retrieval noetig - Vollstaendigkeit hat hier Vorrang vor Relevanz-Filterung, damit kein gemerkter Fakt durch eine unguenstige Formulierung der aktuellen Frage durchrutscht).
  2. "Relevante fruehere Gespraeche" - die aktuelle Nutzernachricht wird embedded (gleiche Ollama-Funktion), die Top-3 aehnlichsten Zeilen aus conversation_summaries per Cosinus-Distanz geholt (<=>-Operator wie bei der Dokumentensuche), exklusive der aktuellen Conversation (die steht ja bereits vollstaendig in der History).

Beide Abschnitte werden nur angehaengt, wenn sie nicht leer sind.

Fehlerbehandlung

  • Ollama nicht erreichbar waehrend der Retrieval-Suche (Embedding der aktuellen Nachricht schlaegt fehl): Abschnitt "Relevante fruehere Gespraeche" wird einfach weggelassen, der Chat laeuft normal weiter (gleiches Muster wie bestehende check_ollama()-Fehlerbehandlung).
  • Hintergrund-Summary-Task wirft eine Exception (Claude-Call oder Embedding schlaegt fehl): wird geloggt, hat keinen Einfluss auf die bereits an den Nutzer gesendete Chat-Antwort (Task laeuft komplett nach dem Response Return).
  • forget_fact ohne Treffer: kein Fehler, sondern eine normale Tool-Antwort an Claude, damit es im Chat mitteilt, dass nichts gefunden wurde.

Testing

  • Unit-Tests (tests/, gemockte DB wie bei bestehenden Tool-Tests) fuer:
    • remember_fact/forget_fact (Insert, ILIKE-Suche, Mehrfachtreffer, kein Treffer)
    • Prompt-Zusammensetzung: Facts-Abschnitt korrekt formatiert, leer wenn keine Fakten vorhanden; Retrieval-Abschnitt korrekt formatiert, leer wenn Embedding fehlschlaegt
  • Manueller End-to-End-Test nach Deployment: in Chat A "merke dir, dass mein Hund Bruno heisst" sagen, ueber den "Neuer Chat"-Button einen neuen Chat B starten, dort nach dem Hundenamen fragen (Fakt-Pfad) und ueber ein thematisch verwandtes, aber nicht identisches Thema aus Chat A fragen (Zusammenfassungs-Retrieval-Pfad); anschliessend "vergiss, dass mein Hund Bruno heisst" in einem dritten Chat testen.

Out of Scope (bewusst nicht Teil dieser Phase)

  • Automatisches Bereinigen alter/widerspruechlicher Fakten (z.B. wenn sich eine Info spaeter aendert, muss der Nutzer explizit forget_fact + remember_fact nutzen).
  • ivfflat/hnsw-Index fuer conversation_summaries (Nachtrag, sobald Datenmenge es rechtfertigt - siehe Phase 3a Follow-up).
  • Mehrere Nutzer/Rollen (weiterhin ein einzelner DEFAULT_USER_ID, siehe bestehende "Security"-Einschraenkung in der Handoff-Doku).
  • UI zum Anzeigen/Verwalten der gespeicherten Fakten im Dashboard (aktuell nur ueber Chat-Kommandos verwaltbar).