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

145 lines
6.7 KiB
Markdown

# 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 (`Claude outputs/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).