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 inmemory_factsan, keine Rueckfrage noetig (wiecreate_calendar_event) - ein gespeicherter Fakt ist folgenlos und leicht wieder zu entfernen.forget_fact(query: str)- sucht perILIKE '%query%'nach passenden Fakten. Bei einem Treffer: loescht ihn erst nach Bestaetigung im Chat (wiedelete_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:
- Vollstaendigen Verlauf der Conversation laden (
get_messages) - Kurzer zusaetzlicher Claude-Call (kleines
max_tokens-Limit): "Fasse dieses Gespraech in 2-3 Saetzen zusammen" - Zusammenfassung mit der vorhandenen Ollama-Embedding-Funktion (aus Phase
3a, bereits fuer
document_chunksgenutzt) embedden 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:
- "Bekannte Fakten ueber den Nutzer" - alle Zeilen aus
memory_factswerden 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). - "Relevante fruehere Gespraeche" - die aktuelle Nutzernachricht wird
embedded (gleiche Ollama-Funktion), die Top-3 aehnlichsten Zeilen aus
conversation_summariesper 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_factohne 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_factnutzen). ivfflat/hnsw-Index fuerconversation_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).