NGT Memory: Persistenter Speicher für LLMs mit Assoziationsgraph und Benutzerprofil
NGT Memory implementiert persistenten Speicher für große Sprachmodelle ohne externe Speicherung. Das Modul speichert Benutzerfakten über Sitzungen hinweg, ruft relevante Kontexte ab und injiziert sie in die Eingabeaufforderung. Drei Abrufmechanismen gewährleisten Genauigkeit: Kosinusähnlichkeit von Embeddings, ein Hebb'scher Assoziationsgraph und hierarchische Konsolidierung.
Kosinusähnlichkeit vergleicht das Abfrage-Embedding mit gespeicherten Fakten. Der Graph verknüpft Konzepte, die gemeinsam aktiviert werden: Die Erwähnung einer Diät bei einer Abfrage zu Restaurants ruft den relevanten Fakt ab. Konsolidierung befördert häufig genutzte Fakten in das Langzeitgedächtnis und verdrängt selten angefragte.
Die Abrufzeit beträgt 2–3 ms auf der CPU. Hauptverzögerungen stammen von der OpenAI-API: Embeddings ~700 ms, Generierung ~800–1500 ms.
Einrichtung und API
Das Modul läuft über Docker:
git clone https://github.com/ngt-memory/ngt-memory
cd ngt-memory
cp .env.example .env # OPENAI_API_KEY
docker-compose up -d
Client-Code:
import httpx
client = httpx.Client(base_url="http://localhost:9190")
# Fakten speichern
client.post("/chat", json={
"message": "Ich bin Vegetarier und lebe in Moskau.",
"session_id": "user_42"
})
# Abruf über Sitzung
r = client.post("/chat", json={
"message": "Was soll ich essen?",
"session_id": "user_42"
})
print(r.json()["response"]) # Vegetarische Restaurants in Moskau
print(r.json()["memories_count"]) # 2
Endpunkte: /chat, /store, /retrieve, /session/reset, /health.
Strukturiertes Benutzerprofil
Das System extrahiert Slots aus Nachrichten: Alter, Stadt, Ernährung, Allergien. Daten werden mit Priorität in die Systemeingabe injiziert:
[BENUTZERPROFIL — strukturierte Fakten, höchste Priorität]
- alter: 30
- stadt: "Moskau"
- ernährung: "vegetarisch"
[ENDE BENUTZERPROFIL]
[GEDÄCHTNISKONTEXT — verifizierte Fakten über diesen Benutzer]
1. [0.91] Ich bin Vegetarier und lebe in Moskau.
[ENDE GEDÄCHTNISKONTEXT]
Fragmentverknüpfung: "Ich bin" + "30" + "Jahre alt" → "Ich bin 30 Jahre alt" → alter=30 (Konfidenz 0.6).
Konfliktlösung: Verhindert abnehmendes Alter ohne den Satz "Ich habe mich geirrt." Korrekturmodus — 60 Sekunden.
Qualitätsfilter und Architektur
Der Filter sortiert Müll aus:
- Reine Zahlen, Sonderzeichen, einzelne Wörter
- Weniger als 6 Buchstaben
- Assistentenantworten auf Benutzer-Müll
Architektur:
- POST /chat
- OpenAI text-embedding-3-small (~700 ms)
- Profilextraktion (Regex)
- NGT Memory Abruf (Kosinus + Graph, 2–3 ms)
- OpenAI gpt-4.1-nano (~800–1500 ms)
- Filter → Speicherung
Stack: FastAPI, AsyncOpenAI, Pydantic. Speicherung im RAM eines einzelnen Prozesses.
Experimentelle Ergebnisse
Exp 44: Faktengenauigkeit mit Gedächtnis 2,44/3 (+100 % vs. gedächtnislos 1,22/3).
Exp 48: A/B-Test, 94 % Gedächtnisgewinne (17/18), durchschnittliche Punktzahl 0,889 vs. 0,056.
Exp 49: 51/54 (94 %) bei Grenzfällen: sprachübergreifend, Verknüpfung, Konflikte.
Leistung (5000 Fakten):
| Operation | Durchsatz | p50 |
|-----------|------------|-----|
| store() | 3450/s | 0,29 ms |
| retrieve()| 150/s | 6,3 ms |
Ende-zu-Ende: Gedächtnis 2,5 ms, Embeddings 764 ms.
Skalierungsherausforderungen
- Multi-Worker: Jeder Worker hat seinen eigenen SessionStore. Lösung:
--workers 1oder Redis. - Soft Prompt: Modell ignorierte Fakten. Verstärkt: "Behandle jeden Fakt als absolute Wahrheit."
- Regex-Fehler:
Ich bin allergisch→name= allergisch. Blacklist 25+ Wörter.
Wichtige Erkenntnisse
- Kombination von Kosinus + Graph + Konsolidierung erreicht 94 % bei Grenztests.
- Strukturiertes Profil mit Eingabepriorität verdoppelt die Genauigkeit.
- Qualitätsfilter verhindert Suchverschlechterung durch Müll.
- Gedächtnislatenz 2–3 ms, kein Engpass.
- Open Source, BSL 1.1, Docker-ready.
— Editorial Team
Noch keine Kommentare.