Eine Erinnerungs-Engine bauen
on gerankten Ergebnissen zu Beziehungen, Chronologie und einem eigenen Foto auf der Startseite
Fotografie-KI·Experte·7 Min. Lesezeit · 10. Juli 2026
Das Foto
Frag den Assistenten Anfang Juli "was habe ich an diesem Tag fotografiert", und er konnte jedes einzelne Foto perfekt beantworten — korrekte Bildunterschrift, korrekte Tags, ein Zitat, das man überprüfen konnte. Frag ihn, ob zwei dieser Fotos zu demselben Nachmittag gehörten, und er hatte nichts. Keine falsche Antwort — überhaupt keinen Mechanismus, um zu antworten. Jedes Foto im Evidenzpaket war immer noch, still, eine Insel.
Das Problem
Phase 1 des Assistenten konnte mehrere Fotos in einer Antwort zitieren, aber nicht darüber nachdenken, wie sie zusammenhingen — jede Mehrfoto-Antwort war strukturell Einzelfoto-Prosa, pro Zitat wiederholt. Das ist nicht, was "Erinnerung" bedeutet. Eine Erinnerung ist keine Liste unabhängig korrekter Fakten; sie sind Fakten, verbunden durch Zeit, Ort und Wiederholung — dieses Foto und jenes waren derselbe Tag, jenes andere teilt denselben Ort, diese drei tauchen immer wieder zusammen auf. Nichts davon existierte irgendwo in der Pipeline. Die Daten, um es zu berechnen — taken_at, Ortsangaben, Kamera-/Objektiv-Felder, Dominantfarben — lagen seit Phase 1 in photo_exif, ungenutzt für genau diesen Zweck.
Die Untersuchung
Die Umfangsfrage kam zuerst: Sollten Beziehungen mit Embeddings inferiert werden — ein Ähnlichkeitswert zwischen Fotos, dasselbe Werkzeug, das bereits zweimal zu Fehlern in dieser Serie geführt hatte — oder aus Feldern berechnet werden, die die Datenbank bereits speichert? Ich habe die Idee eines ähnlichkeitsbasierten "diese fühlen sich verwandt an"-Scores getestet und verworfen, bevor ich sie baute, aus einem Grund, den diese ganze Serie mir jedes Mal auf dieselbe Weise beibrachte: Eine unscharfe Beziehung ist eine Beziehung, die Claude später über ihre tatsächliche Sicherheit hinaus erzählen könnte, und ich hatte gerade ein ganzes Kapitel damit verbracht, einen Claim Validator speziell dagegen zu bauen. Ein neues unscharfes Signal ein Kapitel nach dem Schließen dieser Lücke hinzuzufügen, wäre der Bau der nächsten Version desselben Fehlers gewesen.
Die eigentliche Untersuchung ging also um Exaktheit: welche Feldpaare, exakt verglichen, eine Beziehung ergeben, die es wert ist, sichtbar zu werden. Gleicher Ortsstring. Gleiche Stimmung. Ein geteilter Tag. Gleiche Kamera. Gleiches Objektiv. Gleicher Kalendertag, oder ein angrenzender. Eine geteilte Dominantfarbe — exakte Hex-Schnittmenge, keine RGB-Distanz, weil nirgendwo sonst im Codebase ein Farbdistanz-Werkzeug existierte und eines zu bauen eine neue Heuristik gewesen wäre, die diese Phase bewusst ausschloss. Gleicher Ausrichtungs-Bucket. Acht Signale, jedes entweder wahr oder nicht, nichts dazwischen.
Die Entscheidung
buildEvidenceRelationships() vergleicht jedes Paar von Fotos, die bereits im Evidenzpaket sind — nie ein Foto außerhalb davon — über diese acht exakten Signale, und erzeugt eine chronologicalOrder für die Paket-Fotos mit bekanntem taken_at. Beide Seiten jedes Vergleichs kommen aus pack.photos selbst, was "eine Beziehung, die auf ein Foto außerhalb des Pakets verweist" zu keinem Fall macht, gegen den sich der Code verteidigen muss — es ist ein Fall, der nicht auftreten kann, weil es keinen Datenpfad gibt, um ihn zu konstruieren. Ich habe die Funktion bewusst rein gehalten: (EvidencePack, PhotoRelationshipMetadata[]) -> EvidencePack, keine Persistenz, keine Akkumulation über Anfragen hinweg. Beziehungen werden jedes Mal frisch neu berechnet, was einen wachsenden Wissensgraphen ausschließt — das war ein echter Kompromiss, kein Versehen; ein persistierter Graph bräuchte seine eigene Invalidierungsgeschichte in dem Moment, in dem sich die Metadaten eines Fotos ändern, und diese Phase war nicht der Ort, das anzugehen.
Die abschließende Entscheidung dieser ganzen Saison war jedoch überhaupt keine Retrieval-Entscheidung. Am 08.07.2026 setzten eine is_featured-Spalte und eine PassionProject-Komponente ein einzelnes Foto — bewusst gewählt, nicht algorithmisch — auf die About-Seite und die Startseite, neben dieselbe persönliche Erzählung, die der Rest dieser Seite erzählt. Alles in dieser Serie existiert, um Suche und Erklärung vertrauenswürdig zu machen. Dieses letzte Stück ist die eine Stelle, an der das System aufhört, Fragen zu beantworten, und einfach etwas zeigt, das mir wichtig ist, ungerankt und unerklärt, weil manche Dinge keinen Match-Prozentsatz brauchen.
Die Architektur
Evidenzpaket (≤8 Fotos, bereits gebaut)
↓
getPhotoRelationshipMetadata() begrenzter Lesevorgang, genau auf die photoIds
des Pakets beschränkt
↓
buildEvidenceRelationships() 8 Exakt-Treffer-Signale pro Paar:
Ort, Stimmung, Tag, Kamera, Objektiv,
gleicher/angrenzender Tag, Dominantfarbe, Ausrichtung
↓
EvidencePack.photos[].relatedPhotos pro Foto, gedeckelt auf 8, sortiert für Determinismus
EvidencePack.chronologicalOrder Paket-photoIds mit bekanntem taken_at, aufsteigend
↓
Claude erzählt den Graphen — berechnet ihn nie, erweitert ihn nie
↓ (verifiziert durch validateClaims(), voriges Kapitel)
Separat, ungerankt:
photo.is_featured = 1 → PassionProject-Komponente → About-Seite + Startseite
Was ich gelernt habe
Ich hatte erwartet, dass der Bau von Beziehungen der "KI-hafteste" Teil dieses gesamten Features sein würde — die Stelle, an der ein Modell endlich frei über Verbindungen nachdenken dürfte. Es ist das Gegenteil: Es ist das am striktesten deterministische Stück in der gesamten Pipeline, absichtlich, gerade weil "diese Fotos hängen zusammen" genau die Art von Behauptung ist, die harmlos klingt und sich als diejenige erweist, der Nutzer am meisten vertrauen würden, unverifiziert.
Die Featured-Photo-Entscheidung hat mich überrascht, indem sie überhaupt keine technische Entscheidung war — is_featured ist ein Boolean, ein Admin-Toggle, eine Komponente. Nach sechs Kapiteln voller Scoring, Schwellenwerte und Validatoren ist das kleinste Stück Code der ganzen Serie dasjenige, das tatsächlich etwas Wahres darüber sagt, warum all das existierte.
Der Blick nach vorn
Was ausdrücklich noch nicht gebaut ist, und das sage ich lieber, als etwas anderes anzudeuten: gesprächsbasierte Erinnerung über mehrere Fragen einer Sitzung hinweg, erzählerische Album-Zusammenfassungen, und jede Beziehungserkennung, die kein exakter Feldabgleich ist. Der Beziehungsgraph, den dieses Kapitel gebaut hat, wird bei jeder Frage von Grund auf neu berechnet — eine echte Erinnerung, im Sinne, wie ein Mensch es meint, akkumuliert über die Zeit. Diese hier fängt jedes Mal von vorne an. Diese Lücke ist die ehrliche Antwort auf die Frage, mit der diese ganze Saison begann: Das System erinnert sich an das, was gerade vor ihm liegt, ein Evidenzpaket nach dem anderen. An dich erinnert es sich noch nicht.