# 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`) ```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).