Warum jedes KI-Feature mit strukturierten Daten beginnt
Warum das Schema hinter drei voneinander unabhängigen KI-Features immer wieder gleich aussieht — und was mich eine einzelne JSON-Blob-Spalte darüber gelehrt hat, wie viel Struktur eigentlich genug ist
Architektur·Experte·8 Min. Lesezeit · 6. Juli 2026
Der Anfang
Sobald die Maschinerie der Aufrufstellen vereinheitlicht war, war die verbleibende Frage nicht mehr "wie rufe ich Claude auf" — sie war "was gebe ich ihm mit". Und die Antwort auf diese Frage lag bereits im Schema, in Entscheidungen, die ich aus Gründen getroffen hatte, die überhaupt nichts mit KI zu tun hatten.
Die Tabelle experience ist das klarste Beispiel. Sie speichert einen Job nicht als einen Absatz. Es ist eine Zeile (company, duration, title_en/title_de) mit vier Kindtabellen daran — experience_summary, experience_tech, experience_tech_icon, experience_work_sample —, jede indiziert auf experience_id. Ich habe diese Form ursprünglich gebaut, damit der öffentliche Work-Bereich eine saubere Liste von Aufzählungspunkten und eine Tech-Stack-Zeile pro Job rendern konnte. Ich habe beim Schreiben nicht an Retrieval gedacht.
Die erste Lösung
Der naheliegende Weg, "erwähnt dieser Job Kubernetes" gegen eine solche Form zu beantworten, ist derselbe Regex-über-Text-Ansatz, den ich gegen jeden Textblock verwenden würde — die Zusammenfassungsabsätze nach einem Schlüsselwort durchsuchen und hoffen, dass die Formulierung passt. Ungefähr das passiert heute in scoreCandidateEvidence(), außer dass es keinen Blob pro Job durchsucht. Es durchsucht exp.tech_stack — die wörtliche Liste aus experience_tech — nach einem exakten, groß-/kleinschreibungsunabhängigen Wortgrenzen-Treffer gegen die Schlüsselwörter einer Stellenbeschreibung, und vergibt fünf Punkte bei einem Treffer. Es ist dieselbe Operation, die ich oben beschrieben habe, aber Struktur hat sie von einer unscharfen Absatzsuche in eine exakte Abfrage gegen ein Feld verwandelt, das genau für diesen Wert existiert.
Was kaputtging
Nichts ging im Sinne eines Vorfalls kaputt — das ist der eine Artikel dieser Staffel, in dem der Fehler ein nicht eingeschlagener Weg ist statt ein Weg, den ich gegangen bin und wieder zurücknehmen musste. Das Gegenfaktische ist in der eigenen dokumentierten Invariante der Codebasis sichtbar: Evidenzauswahl muss deterministisch bleiben, und sie wird nie mit KI-Ranking umgangen, Punkt. Diese Regel müsste nicht so explizit aufgeschrieben werden, wenn die Alternative — Claude direkt gegen lose geformten Kontext über Relevanz urteilen zu lassen — keine echte, bestehende Versuchung wäre. Der Grund, warum es nicht so läuft, ist, dass ein Modell, das gefragt wird "ist das relevant", nicht mit einem sauberen Ja oder Nein gegen mehrdeutigen Input antwortet; es antwortet mit etwas Überzeugendem, und überzeugend ist genau die Eigenschaft, die eine falsche Antwort hier gefährlich statt nur nutzlos macht.
Die Stelle, an der ich diesen Unterschied tatsächlich in der Praxis relevant werden sah, war die Form von engineering_decision, die einen sichtbar anderen strukturellen Weg nahm als experience. Es sind nicht vier normalisierte Kindtabellen — es ist eine title-Spalte neben einem einzelnen decision_json-Blob. Das ist eine bewusste, engere Art von Struktur: genug, um den Titel zu indizieren und abzufragen, und darunter ein in sich geschlossenes JSON-Dokument, das Ask Vikrams Retrieval liest, indem es es einbettet, nicht indem es dagegen joint. engineering_decision so zu strukturieren, wie ich experience strukturiert habe — jedes Feld, das das JSON aktuell hält, in eine eigene Spalte zu normalisieren —, wäre die falsche Menge Struktur dafür gewesen, wie es tatsächlich konsumiert wird. Niemand joint über Engineering-Entscheidungen hinweg, so wie der Work-Bereich Erfahrung mit ihrem Tech-Stack fürs Rendern verbindet; der Korpus wird von Retrieval gelesen, nicht von einer Seitenvorlage, also brauchte er nicht dieselbe Form.
Die Entscheidung
Die Entscheidung war jedes Mal, wie viel Struktur ein Datenstück tatsächlich brauchte, nicht ob überhaupt welche. experience brauchte echte relationale Struktur, weil zwei verschiedene Konsumenten es auf zwei verschiedene Weisen lesen — eine Seite rendert es, scoreCandidateEvidence() bewertet es —, und beide mussten bestimmte Felder direkt ansprechen können. engineering_decision brauchte eine abfragbare Identität (den Titel) und eine intern strukturierte, aber relational undurchsichtige Nutzlast, weil sein einziger Konsument eine Retrieval-Pipeline ist, die bereits weiß, wie man strukturiertes JSON liest, und kein SQL braucht, um hineinzujoinen.
Die Fotografie ging einen dritten Weg, und aus einem Grund, der überhaupt nichts mit Retrieval-Bequemlichkeit zu tun hat — es geht darum, was Struktur durchsetzbar macht. photo, photo_exif und photo_tag sind drei getrennte Tabellen, wobei GPS-Koordinaten speziell innerhalb von photo_exif isoliert sind. Diese Trennung ist es, die eine Regel wie "GPS verlässt diesen Server niemals" auf Typebene durchsetzbar macht: PublicPhotoExifSummary hat schlicht keine GPS-Felder, also gibt es keine Laufzeitprüfung, die man vergessen könnte, kein Feld, das versehentlich serialisiert werden könnte. Diese Garantie kann man gegen einen unstrukturierten Blob nicht bauen — es gibt keinen Typsystem-Prüfpunkt, an den man sie setzen könnte. Struktur macht hier Retrieval nicht schneller; sie verwandelt eine Datenschutzregel von "etwas, das ich mich erinnern muss zu schwärzen" in "etwas, das nicht kompilieren kann".
Die neue Architektur
Dieselben Tabellen, zwei Konsumenten, keine duplizierten Fakten:
experience / experience_tech → Work-Bereich (Rendern)
→ scoreCandidateEvidence() (Bewertung, +5 exakter Tech-Treffer)
engineering_decision.decision_json → Ask-Vikram-Embeddings (Retrieval, kein Join)
photo / photo_exif (GPS isoliert) → Öffentliche Galerie (GPS-freier Typ)
→ Nur-Admin-Anreicherung (volles EXIF)
Drei verschiedene Formen, drei verschiedene Gründe — normalisieren, wo zwei Konsumenten dasselbe Feld unabhängig ansprechen müssen, ein JSON-Dokument intakt lassen, wo nur eine Embedding-Pipeline es liest, und eine Tabelle genau dort teilen, wo eine Datenschutzgrenze beweisbar statt erinnerbar sein muss.
Was ich gelernt habe
Ich habe die Vierertabellen-Aufteilung von experience nicht mit Anschreiben-Bewertung im Kopf entworfen — sie existierte für den Work-Bereich lange bevor es irgendeine der KI-Pipelines gab, und sie war zufällig schon die richtige Form, als scoreCandidateEvidence() sie brauchte. Das ist ein glücklicher Zufall, kein Prinzip, das ich mir im Voraus zugutehalten kann. Wo ich dieses Glück nicht habe, ist engineering_decision: ein einzelner JSON-Blob lässt Ask Vikram bei allem auf Embedding-Ähnlichkeit setzen, ohne die exakte Treffer-Bewertung, die der Anschreiben-Evidenz ihre Klarheit "+5 exakter Tech-Treffer" gibt. Das ist ein echter Kompromiss, den ich für eine Tabelle akzeptiert habe, die, wie ich bereits festgestellt habe, noch nicht das Autoring-Tooling hat, das die Blog-Inhalte gerade bekommen haben — eine strukturell so dünne Tabelle ist leichter zu akzeptieren, solange der Korpus noch klein ist, und es lohnt sich, das zu überdenken, sobald er es nicht mehr ist.
Der Blick nach vorn
Die richtige Datenform zu haben, beantwortet "habe ich die richtigen Fakten, mit denen ich arbeiten kann". Sie sagt nichts darüber, wie viele dieser Fakten man einem Modell auf einmal mitgibt, oder in welcher Reihenfolge. Dass experience_tech abfragbar ist, hindert niemanden daran, jedes passende Tech-Stack-Element, jeden Zusammenfassungspunkt und jede Skill-Gruppe auf einen Schlag an Claude zu geben, "nur um sicherzugehen". Strukturierte Daten sind notwendig. Sie sind nicht hinreichend — die nächste Frage ist, wie eine begrenzte, gerankte Auswahl dieser Daten tatsächlich aussieht.
Fazit
Jedes KI-Feature auf dieser Plattform beginnt mit strukturierten Daten, nicht weil Struktur modisch ist, sondern weil jede Domäne unabhängig gegen dieselbe Wand gelaufen ist: Ein Feld, das man nicht direkt ansprechen kann, ist ein Feld, das man nicht bewerten, nicht schwärzen und über das man nichts beweisen kann. Die Form der Lösung war nie "alles in eine Tabelle packen" — sie war "genau so viel Struktur in jede Tabelle packen, wie ihre tatsächlichen Konsumenten ansprechen müssen", und keine Domäne hier brauchte dieselbe Menge.