Warum Chat-Gedächtnis nur 25% Recall schafft und wie man die Suche repariert
Ein echter Freelance-Auftrag zeigt ein Gedächtnissystem mit perfekter Speicherung, aber 25% Recall. Query-Umschreibung, HyDE, Reranking und mehrstufige Suche, plus Rat: reparieren oder ersetzen.
Ein Chat-Gedächtnis, das innerhalb einer Sitzung funktioniert und dann bei allem, was älter als ein paar Monate ist, leer bleibt, ist ein Muster, das man in KI-Companion-Apps immer wieder sieht. Ein auf Upwork veröffentlichter Freelance-Auftrag macht aus der vagen Klage Messwerte und eignet sich daher besser als Fallstudie als jedes ausgedachte Beispiel.
Die Zahlen des Kunden
Die eigene Bilanz des Kunden: rund 5.000 gespeicherte Nachrichten, 300 episodische Ereignisse und etwa 3.500 semantische Erkenntnisse. Das Team wählte acht konkrete Fakten, die der Nutzer im Juni genannt hatte, und fragte danach so, wie ein echter Nutzer es heute tun würde. Nur zwei kamen zurück, eine Trefferquote von 25%, obwohl jede Originalnachricht noch Wort für Wort gespeichert war. Beim Schreiben der Erinnerungen geht nichts verloren.
Diese letzte Aussage sollte man ernst nehmen. Was auch immer schiefläuft, am Speicher liegt es nicht.
Und das war kein Wochenend-Prototyp. Der Stack lief auf Postgres mit pgvector, die Suche erfolgte in zwei Stufen mit einem Reranker, dazu kamen eigene Orchestrierung und eigener Extraktionscode. Er hielt mehrere Arten von Gedächtnis vor (episodische Ereignisse, semantische Erkenntnisse, ein Verzeichnis der Menschen im Leben des Nutzers und prozeduralen Stil), jede mit eigenem Suchweg. Jede Runde wurde protokolliert: was abgerufen, was verworfen wurde und was am Ende beim Modell ankam. Das Team hatte außerdem mem0 an 120 eigenen Nachrichten getestet und betrieb eine Hindsight-Instanz im Schattenmodus. Vernünftige Experimente, und der Recall blieb bei 25%.
Wo es wirklich bricht
Eine einzige Testanfrage macht das Problem offensichtlich. Tippen Sie "welche Wörter benutzt mein Vater", und das System liefert frühere Nachrichten, in denen sich der Nutzer über seinen Vater beklagt. Die eine Nachricht, die ihn zitiert, taucht nicht auf.
Das ist keine Eigenheit dieser Codebasis. Embedding-Ähnlichkeit über einem Chatverlauf verhält sich genau so, denn der Text, der einer Frage am nächsten liegt, ist fast immer eine andere Frage. Eine Anfrage und ihre Antwort liegen in verschiedenen Gegenden desselben Vektorraums, und die Kosinus-Ähnlichkeit kann nicht erkennen, dass sie zusammengehören. Der Gedächtnisspeicher ist also in Ordnung. Die Schwachstelle liegt in der Orchestrierung, also darin, was zur Generierungszeit ausgewählt, gerankt und in den Kontext gepackt wird.
Vier Lösungen, nach Aufwand geordnet
Die Anfrage umschreiben. Betten Sie nicht die rohe Frage ein. Lassen Sie ein LLM mehrere Formulierungen erzeugen, die so aussehen wie die Antwort: genaue Sätze, die der Vater des Nutzers sagt, Wörter, mit denen er den Nutzer anspricht, direkte Zitate, die ihm zugeschrieben werden. Die Suche zielt dann darauf, wie eine Antwort aussieht, statt darauf, wie eine Frage aussieht.
HyDE. Gao und Kollegen schlugen 2022 vor, noch einen Schritt weiterzugehen. Machen Sie aus der Frage keine Suchbegriffe, sondern lassen Sie das LLM eine hypothetische Antwort schreiben und betten Sie diese ein. Eine erfundene Antwort landet viel näher an einer echten als die Frage. Für genau diesen Fehler ist das wohl das direkteste Mittel.
Nicht nur nach Ähnlichkeit reranken. Hat man einen Pool von Kandidaten, wiederholt eine Sortierung allein nach semantischer Nähe den ursprünglichen Fehler im Kleinen. Bewerten Sie Übereinstimmung von Entitäten, wie wahrscheinlich ein Ausschnitt eine Antwort ist, ob er neben einer Runde steht, die plausibel eine enthält, und den zeitlichen Kontext. Denken Sie aber an die Obergrenze: Ein Reranker ordnet nur um, was die Suche ihm geliefert hat, und kann nichts herbeizaubern, das nie aufgetaucht ist.
In mehreren Schritten suchen. Ein Durchgang reicht für Fragen nach einem einzelnen Fakt. Bei zusammenfassenden Fragen versagt er. Die Antwort auf eine Frage wie "welche Spitznamen hat mir mein Vater gegeben" verteilt sich auf fünf Nachrichten über drei Monate. Dafür braucht es eine breite erste Suche, eine Gruppierung der Kandidaten nach Typ, eine Prüfung, ob das Gesammelte genügt, und eine weitere Suche, wenn nicht. Es ist dieselbe Form aus Abrufen, Nachdenken und Entscheiden über eine erneute Suche wie bei Self-RAG und Corrective RAG (CRAG). Geben Sie dem Ganzen einen festen Abbruch, entweder eine Obergrenze für die Schritte oder einen Konfidenztest, sonst läuft es endlos oder macht jede Antwort teuer.
Eine minimale Schleife sieht so aus:
def retrieve_memory(user_query, vector_db, llm, max_hops=2):
rewrites = llm.rewrite(
query=user_query,
instruction=(
"Turn this question into 3-5 retrieval-oriented phrasings "
"that describe what the ANSWER would say, not what the "
"question says."
),
)
candidates = []
for q in rewrites:
candidates += vector_db.search(q, top_k=20)
ranked = []
for hop in range(max_hops):
ranked = rerank(
user_query,
candidates,
factors=[
"semantic_relevance",
"entity_match",
"answer_likelihood",
"conversational_relationship",
"temporal_context",
],
)
if llm.is_sufficient(user_query, ranked[:10]):
break
follow_up = llm.next_query(user_query, ranked[:10])
candidates += vector_db.search(follow_up, top_k=20)
return ranked[:10]
Nichts davon ist exotisch. Entscheidend ist die Reihenfolge: zuerst Recall und Aufbau der Anfrage reparieren, dann das Reranking, und erst danach über zusätzliche Infrastruktur nachdenken.
Rat für den Kunden
Die Audit-Anfrage hatte drei Teile: wo das Gedächtnis zwischen Speicherung und Generierung leckt, ob das Design die zehnfache Datenmenge übersteht, und eine klare Empfehlung zwischen Reparieren des Bestehenden und Einführen von etwas Neuem.
Wo es leckt. Zwischen Suche und Generierung, und zwar genau beim Aufbau der Anfrage, bevor Reranking überhaupt helfen kann.
Bei zehnfacher Menge. Das Design mit mehreren Gedächtnistypen und das Logging sind solide und skalieren. Die einstufige Embedding-Suche nicht, denn mehr Daten bedeuten mehr ähnliche Fragen, die um dieselben Kandidatenplätze konkurrieren. Die Schichten für Umschreibung und mehrstufige Suche werden lange vor dieser Marke von optional zu notwendig.
Reparieren oder ersetzen. Das Eigene reparieren. mem0 und Hindsight sind beide Speicherschichten für Gedächtnis. Keine von beiden berührt die Orchestrierung, und genau dort verschwinden die fehlenden 75%. Eine andere Datenbank hilft keiner Anfrage, die weiterhin nach dem Falschen sucht.
Manchmal vergisst ein Gedächtnissystem gar nichts. Es beantwortet eine Frage, die niemand gestellt hat, weil Anfrage und Beleg nie dasselbe gesucht haben.