Gedächtnis, das weiß, wer es gesagt hat.
Menschen erinnern sich nach Person: was Bob versprochen hat, was du zugesagt hast. Gib jeder Erinnerung einen Sprecher und dein Agent tut dasselbe, auf jedem Tablet- und Scroll-Modell.
Ein Team, drei Erinnerungen
Ein Store hält viele Stimmen auseinander. Registriere eine Person einmal, speichere jede Äußerung unter ihrem Sprecher und frage später nach Person.
Der Store kennt Bob jetzt. Das 50er-Limit wird hier bei der Registrierung gezählt; Speicheraufrufe liefern nie einen Limit-Fehler.
Die Erinnerung gehört jetzt Bob: jede Suche, die sie zurückgibt, sagt das auch.
Auch die eigenen Worte werden erinnert, und "me" zählt nie gegen das Limit.
Nur Bobs Worte kommen zurück. Die Worte einer Person kommen nie als die einer anderen zurück.
Drei Regeln zum Merken
- "me" ist der Assistent selbst. Nie registriert, nie gezählt. Reserviert und kleingeschrieben:
speaker: "Me"oder"ME"liefert400 invalid_request_errorstatt stiller Umdeutung. - Das Limit zählt bei der Registrierung: zunächst 50 pro Store. Darüber hinaus liefert die Registrierung
400 invalid_request_errormitspeaker_limit: 50im Fehlerbody. Speichern mit unregistriertem Namen liefert ebenfalls400und speichert nichts. Suche mit unregistriertem Namen im Filter liefert404 not_found_error. Verzweige auf Status und Felder, nicht auf den Meldungstext; wir planen, das Limit anzuheben. - Labels leben in jeder Leseoperation. Suchergebnisse, der Langzeit-Kontext von recall und Engram-Ergebnisse tragen ihren Sprecher — das Modell weiß immer, wessen Worte es hält. Mit speaker in der Suche kommen nur die Worte einer Person zurück. Ein supersede behält den Sprecher; forget entfernt ihn.
- Namen sind Unicode: jede Sprache funktioniert. さくら, Иван und 하늘 sind gültige Sprecher, und die Zuordnung verhält sich in jeder Sprache identisch. Der Abgleich ist exakt nach Trim und Unicode-Normalisierung, also sind
Bobundbobzwei Personen. Namen sind bis 80 Zeichen lang.
# POST /speakers past the limit { "type": "error", "error": { "type": "invalid_request_error", "message": "This store already has 50 registered speakers, ...", "speaker_limit": 50 } } # store with an unregistered name → 400, nothing stored { "type": "error", "error": { "type": "invalid_request_error", "message": "speaker 'Bob' is not registered in this store. Register it first: ...", "speaker": "Bob" } } # search filtered by an unregistered name → 404 { "type": "error", "error": { "type": "not_found_error", "message": "speaker 'Bob' is not registered in this store.", "speaker": "Bob" } }
Zwei Randnotizen. speaker gehört zu add / store: add_turn merkt sich den ganzen Austausch, Personen-Labels und der Filter kommen aus Erinnerungen mit explizitem speaker. Und Sitzungspassagen (expand) sind Komposite mehrerer Erinnerungen, tragen also kein Label; ein speaker-Filter liefert immer atomare, gelabelte Erinnerungen. Und ein Schreibvorgang, dessen Bedeutung einer gespeicherten Erinnerung nahe genug kommt, wird verworfen - entscheidend ist die Bedeutung, nicht der Wortlaut: ein solcher store liefert status "duplicate" mit expliziter note, speichert nichts und hängt keinen Sprecher an. Auch eine wirklich neue Tatsache, die sich nur in einem Detail von einer vorhandenen unterscheidet ("Schalentierallergie" nach "Erdnussallergie"), fällt unter dieselbe Regel - lesen Sie also status, statt anzunehmen, der Schreibvorgang sei angekommen.
Wir testen das auf die harte Tour: Erinnerungen ohne Namen im Text, abgerufen pro Person. Die Zuordnung kommt aus dem Sprecher-Register, nicht aus Wortabgleich, und verhält sich daher in jeder Sprache gleich.
So nutzt du es
mem.add_speaker("Bob", user_id="alice") # once per person; "me" needs no registration
mem.add("Bob said the deadline moved to Tuesday", user_id="alice", speaker="Bob")
mem.add("I promised the summary by Friday", user_id="alice", speaker="me")
hits = mem.search("what did Bob say about the deadline?", user_id="alice", speaker="Bob")
mem.list_speakers(user_id="alice")
mem.remove_speaker("Bob", user_id="alice") # memories stay, the tag goes{ "memories": [
{ "content": "Bob said the deadline moved to Tuesday",
"speaker": "Bob", ... } ] }{ "user_id": "alice",
"speakers": [ { "speaker": "Bob", "memories": 2, "created_at": "2026-07-10T04:20:39Z" } ],
"count": 1, "limit": 50 }Die Liste zeigt, wen der Store kennt, mit Erinnerungszahl pro Person gegen das Limit. Entfernen löscht nur die Registrierung: die Erinnerungen bleiben, nur das Namenslabel geht.
Die Erinnerungen einer einzelnen Person lesen
by_speaker gibt zurück, was eine Person gesagt hat, neueste zuerst, ohne Suchanfrage. "me" liefert die eigenen Äußerungen des Assistenten. Gleiche Cursor-Paginierung wie bei Bilder: Geben Sie next_before und next_skip_ids zurück.
page = mem.by_speaker("Bob", limit=50)
page["memories"], page["chunks"]| Feld | Was er tut |
|---|---|
| memories | Die Erinnerungen, neueste zuerst. Gleiche Struktur, wie eine Suche sie zurückgibt. |
| chunks | Fragmente auf Satzebene hinter diesen Erinnerungen - was ein Löschvorgang tatsächlich entfernen würde. Üblicherweise größer als die Anzahl der Erinnerungen und vor einer Bestätigung anzuzeigen. Wird auch als points_to_delete gemeldet. |
| next_before | Cursor für die nächste Seite, zusammen mit next_skip_ids. Beides ist nötig, weil Erinnerungen denselben Zeitstempel haben können. |
speaker ist hier die beim Speichern geschriebene Markierung, keine Suche über den Text. Eine ohne Sprecher gespeicherte Erinnerung ist über die Suche erreichbar, aber nie über by_speaker, auch nicht unter "me".Sprecher auflisten, durchsehen und entfernen
mem.list_speakers() # who is registered
mem.by_speaker("Bob") # what Bob said, newest first
mem.remove_speaker("Bob") # unregister; the memories stay