NGT Memory: trwała pamięć dla LLM z grafem asocjacyjnym i profilem
NGT Memory implementuje trwałą pamięć dla dużych modeli językowych bez zewnętrznych magazynów danych. Moduł przechowuje fakty o użytkowniku między sesjami, wydobywa istotny kontekst i wstrzykuje go do promptu. Trzy mechanizmy wydobywania zapewniają precyzję: podobieństwo cosinusowe embeddingów, hebbowski graf asocjacyjny i hierarchiczna konsolidacja.
Podobieństwo cosinusowe porównuje embedding zapytania z zapisanymi faktami. Graf łączy koncepty, które aktywują się razem: wspomnienie o diecie przy zapytaniu o restauracje przyciąga odpowiedni fakt. Konsolidacja promuje często używane fakty do pamięci długoterminowej, wypierając rzadko wyszukiwane.
Czas wydobycia — 2–3 ms na CPU. Główne opóźnienia pochodzą z API OpenAI: embeddingi ~700 ms, generacja ~800–1500 ms.
Uruchomienie i API
Moduł uruchamiany przez Docker:
git clone https://github.com/ngt-memory/ngt-memory
cd ngt-memory
cp .env.example .env # OPENAI_API_KEY
docker-compose up -d
Kod klienta:
import httpx
client = httpx.Client(base_url="http://localhost:9190")
# Zapis faktów
client.post("/chat", json={
"message": "Jestem wegetarianinem i mieszkam w Warszawie.",
"session_id": "user_42"
})
# Wydobycie przez sesję
r = client.post("/chat", json={
"message": "Co mogę zjeść?",
"session_id": "user_42"
})
print(r.json()["response"]) # Wegetariańskie restauracje w Warszawie
print(r.json()["memories_count"]) # 2
Endpointy: /chat, /store, /retrieve, /session/reset, /health.
Ustrukturyzowany profil użytkownika
System wydobywa sloty z wiadomości: wiek, miasto, dieta, alergie. Dane wstrzykiwane są do system promptu z priorytetem:
[PROFIL UŻYTKOWNIKA — ustrukturyzowane fakty, najwyższy priorytet]
- wiek: 30
- miasto: "Warszawa"
- dieta: "wegetarianin"
[KONIEC PROFILU UŻYTKOWNIKA]
[KONTEKST PAMIĘCI — zweryfikowane fakty o tym użytkowniku]
1. [0.91] Jestem wegetarianinem i mieszkam w Warszawie.
[KONIEC KONTEKSTU PAMIĘCI]
Sklejanie fragmentów: "mam" + "30" + "lat" → "mam 30 lat" → wiek=30 (pewność 0.6).
Rozwiązywanie konfliktów: Blokada zmniejszania wieku bez frazy "pomyliłem się". Tryb korekty — 60 s.
Filtr jakości i architektura
Filtr odsiewa śmieci:
- Czyste liczby, znaki specjalne, jedno słowo
- Mniej niż 6 liter
- Odpowiedzi asystenta na śmieci użytkownika
Architektura:
- POST /chat
- OpenAI text-embedding-3-small (~700 ms)
- Wydobycie profilu (regex)
- NGT Memory retrieve (cosinusowe + graf, 2–3 ms)
- OpenAI gpt-4.1-nano (~800–1500 ms)
- Filtr → store
Stos: FastAPI, AsyncOpenAI, Pydantic. Przechowywanie w RAM jednego procesu.
Wyniki eksperymentów
Exp 44: Faktualna precyzja z pamięcią 2.44/3 (+100% do bezpamięciowego 1.22/3).
Exp 48: Test A/B, 94% zwycięstw pamięci (17/18), średnia ocena 0.889 vs 0.056.
Exp 49: 51/54 (94%) na przypadkach brzegowych: międzyjęzykowe, sklejanie, konflikty.
Wydajność (5000 faktów):
| Operacja | Przepustowość | p50 |
|----------|------------|-----|
| store() | 3450/s | 0.29 ms |
| retrieve()| 150/s | 6.3 ms |
End-to-end: pamięć 2.5 ms, embeddingi 764 ms.
Problemy skalowania
- Multi-worker: każdy worker — własny SessionStore. Rozwiązanie:
--workers 1lub Redis. - Miękki prompt: model ignorował fakty. Wzmocniony: "Traktuj każdy fakt jako absolutną prawdę".
- Błędy regex:
I'm allergic→imię= allergic. Blacklist 25+ słów.
Co jest ważne
- Kombinacja cosine + graph + konsolidacja daje 94% na testach brzegowych.
- Ustrukturyzowany profil z priorytetem w prompcie podwaja precyzję.
- Filtr jakości zapobiega degradacji wyszukiwania przez śmieci.
- Opóźnienie pamięci 2–3 ms, nie bottleneck.
- Otwarty kod, BSL 1.1, Docker-ready.
— Editorial Team
Brak komentarzy.