JARVIS Docs

Memory Service

Cel

Memory Service ogranicza koszt tokenów i poprawia ciągłość rozmowy przez zastąpienie pełnej historii krótkim kontekstem pamięciowym:

  • rolling summary aktualnej rozmowy,
  • wybrane trwałe fakty i preferencje użytkownika,
  • kolejka background jobs w Redis,
  • lokalny extractor LLM z fallbackiem heurystycznym,
  • docelowo vector search w Postgres + pgvector.

Status MVP

  • [x] Dodać ConversationSummary dla krótkiego streszczenia rozmowy per użytkownik.
  • [x] Dodać MemoryItem dla zapamiętanych faktów i preferencji.
  • [x] Dodać Redis queue dla jobów po każdej zapisanej odpowiedzi chatu.
  • [x] Dodać memory-worker jako osobny proces/kontener z tego samego obrazu aplikacji.
  • [x] Worker aktualizuje summary rozmowy i zapisuje kandydatów pamięci.
  • [x] Dodać lokalny LLM extractor z modelem qwen2.5:3b-instruct.
  • [x] Prompt builder dodaje krótkie memory context przed ostatnimi wiadomościami.
  • [x] Docker Compose uruchamia Postgres z obsługą pgvector jako docelowy storage embeddings.

Przepływ

  1. POST /chat/ask generuje odpowiedź przez OpenAI.
  2. openai_service.py zapisuje ConversationHistory.
  3. Po zapisie historii aplikacja wrzuca payload do jarvis:memory:jobs w Redis.
  4. backend.workers.memory_worker pobiera job przez BRPOP.
  5. Worker ładuje historię i aktualny ConversationSummary.
  6. memory_extractors.py próbuje użyć lokalnego LLM albo fallbacku heurystycznego.
  7. Wynik jest walidowany, filtrowany i zapisywany do conversation_summary oraz memory_items.
  8. Kolejny prompt może dostać Context memory z summary i najtrafniejszych memory items.

Chat nie czeka na worker. Jeżeli Redis albo lokalny LLM są chwilowo niedostępne, odpowiedź użytkownika nadal może zostać zwrócona, a worker loguje problem w tle.

Lokalny LLM Extractor

memory-worker może używać lokalnego modelu przez OpenAI-compatible endpoint Ollamy:

MEMORY_EXTRACTOR_PROVIDER=local_llm
MEMORY_LLM_BASE_URL=http://local-llm:11434/v1
MEMORY_LLM_API_KEY=local
MEMORY_LLM_MODEL=qwen2.5:3b-instruct
MEMORY_LLM_TIMEOUT_SECONDS=30
MEMORY_MIN_CONFIDENCE=0.7

Kontrakt odpowiedzi extractora jest walidowany po stronie aplikacji. Worker zapisuje tylko elementy pamięci z dozwolonym kind, confidence >= MEMORY_MIN_CONFIDENCE, krótkim content i bez sekretów.

Ostatnie filtry jakości:

  • odrzucanie restricted=true,
  • odrzucanie wpisów poniżej MEMORY_MIN_CONFIDENCE,
  • odrzucanie pustych albo zbyt długich treści,
  • odrzucanie placeholderów typu favorite color, favorite hobby, preferred music genre,
  • odrzucanie ustawień samego asystenta jako pamięci użytkownika,
  • deduplikacja z ignorowaniem wielkości liter, nadmiarowych spacji i końcowej kropki.

Debugowanie

Sprawdzenie kolejki:

docker compose exec redis redis-cli llen jarvis:memory:jobs

Ostatnie memory items:

docker compose exec db psql -U jarvis -d jarvis_db -c "select kind, content, salience, updated_at from memory_items order by updated_at desc limit 10;"

Ostatnie summary:

docker compose exec db psql -U jarvis -d jarvis_db -c "select user_id, conversation_id, summary, message_count, updated_at from conversation_summary order by updated_at desc limit 5;"

Logi workera:

docker compose logs -f memory-worker

Jeżeli w logach pojawia się Timeout reading from socket przy pustej kolejce Redis, aktualna implementacja traktuje to jako brak joba, a nie błąd procesu. BRPOP ma krótki timeout i jest wykonywany w pętli.

Następne Kroki

  • [ ] Dodać prawdziwe embeddings dla MemoryItem.
  • [ ] Przenieść embedding_json na natywną kolumnę vector po ustaleniu migracji.
  • [ ] Dodać vector similarity search per user_id.
  • [ ] Dodać panel do przeglądania i usuwania pamięci użytkownika.
  • [ ] Dodać politykę retencji i sanityzację pamięci.