Vom Portfolio zur KI-gesteuerten Engineering-Plattform
Wie dieselben Datenbankzeilen zu Evidenz für Anschreiben, einem Korpus für Q&A und einer Oberfläche für MCP-Tools wurden
KI-Systeme·Experte·7 Min. Lesezeit · 6. Juli 2026
Der Anfang
Ich habe dieses Portfolio im Oktober 2025 gebaut, um genau eine Sache zu tun: meine Arbeit zu zeigen. Eine Seite, ein paar Abschnitte, Berufserfahrung und Skills gerendert aus dem, was auch immer ich in der jeweiligen Woche hartkodiert hatte. Es war in jeder relevanten Hinsicht statisch — Inhalte lagen im Markup, und eine Aktualisierung bedeutete: Datei bearbeiten, neu deployen.
Das war in Ordnung. Es war in Ordnung, bis es nicht mehr editierbar genug war, um es anfassen zu wollen, und ich anfing, kleine Updates zu vermeiden, weil "die ganze Seite neu deployen, um einen Tippfehler im Jobtitel zu korrigieren" kein Workflow ist, den irgendjemand lange genießt.
Die erste Lösung
Also gab ich der Seite im Januar 2026 eine Datenbank. SQLite hinter einer kleinen Admin-CRUD-Oberfläche, ein paar Wochen später mit Authentifizierung abgesichert. Berufserfahrung, Ausbildung, Skills — jetzt alles über ein Formular editierbar, kein Redeploy nötig. Das fühlte sich wie die naheliegende Lösung an, und das war es auch: Sie löste genau das Problem, das ich hatte, nämlich "ich will meine eigenen Inhalte bearbeiten können, ohne Code anzufassen."
Etwa vier Monate lang war das die ganze Geschichte. Ein Portfolio mit einer Content-Management-Schicht. Nichts daran musste klüger sein als ein Formular und eine Tabelle.
Was kaputtging
Der Bruch kam im Mai, als ich beschloss, dass die Seite etwas tun sollte, was eine statische/CRUD-Seite grundsätzlich nicht kann: aus einer Stellenbeschreibung ein auf meine tatsächliche Berufserfahrung zugeschnittenes Anschreiben generieren. Wochen später tauchte dieselbe Art von Problem noch zweimal auf — ich wollte, dass Besucher Fragen zu meinen Engineering-Entscheidungen stellen und echte Antworten bekommen, und ich wollte eine Fotosammlung, die nach Bedeutung durchsuchbar ist, nicht nur nach Dateiname.
Jedes Mal lief ich gegen dieselbe Wand. Die Datenbank, die ich gebaut hatte, war darauf ausgelegt, eine Frage zu beantworten — "was soll auf dieser Seite gerendert werden" — und keine dieser neuen Funktionen ist diese Frage. Ein Anschreiben-Generator muss bewerten, welche Erfahrungsstücke für eine konkrete Stellenbeschreibung tatsächlich relevant sind. Eine Q&A-Oberfläche muss Evidenz für eine Aussage abrufen, nicht nur eine statische Biografie anzeigen. Eine Fotosuche braucht Embeddings, keine WHERE-Klausel. Nichts davon existierte, und schlimmer: Nichts davon ließ sich einfach an die bestehenden Tabellen anflanschen, ohne ein Risiko einzugehen, das mir sehr wichtig war: dass ein LLM Text produziert, der sich unbemerkt von dem entfernt, was die Datenbank tatsächlich sagt. Ein Anschreiben, das ein Projekt erfindet. Ein CV, das meine Daten für eine schönere Geschichte umsortiert. Das sind keine Formatierungsfehler — das System lügt über mich gegenüber einem Arbeitgeber.
Die Entscheidung
Die naheliegende, aber verlockende Bewegung wäre gewesen, jedes davon als "KI-Feature hinzufügen" zu behandeln — einen Claude-Aufruf an die jeweilige Seite anflanschen und schauen, was passiert. Das habe ich fast sofort verworfen, weil ich schon gesehen hatte, was passiert, wenn man einem Modell einen Haufen lose relevanten Kontext gibt und es bittet, überzeugend zu schreiben: Es schweigt nicht über die Lücken, es füllt sie mit etwas Plausiblem. Ich wollte keine drei unabhängigen Anbauten, von denen jeder Claude ein bisschen mehr vertraut als der vorherige, aus angesammelter Bequemlichkeit.
Stattdessen machte ich die Datenbank zum Fundament, aus dem jedes Teilsystem liest, und zog dieselbe Linie durch alle drei: Eine deterministische Hälfte berechnet und bewertet, was wahr und relevant ist, und nur dieses vorab ausgewählte, begrenzte Ergebnis geht überhaupt an Claude, dessen Aufgabe auf Text reduziert ist. Anschreiben und CV, Engineering-Q&A sowie Fotosuche und -anreicherung landeten am Ende alle bei derselben Form, obwohl sie Monate auseinander und aus unterschiedlichen Gründen gebaut wurden — weil sich die Einschränkung (ein Modell darf nicht entscheiden, was wahr ist) nicht von Feature zu Feature ändert.
Das ist ein echter Kompromiss, und ich will nicht so tun, als wäre er das nicht. Dieselben Tabellen sowohl fürs Rendern als auch für den Abruf zu nutzen bedeutet, dass Schemaänderungen jetzt weiter wirken, als sie es auf einer rein präsentativen Seite würden — genau deshalb läuft db:init immer nur mit CREATE TABLE IF NOT EXISTS, nie mit einer destruktiven Migration; ich wollte nicht, dass eine Schemaänderung für ein Teilsystem eine Chance hat, unbemerkt ein anderes zu zerstören.
Die neue Architektur
Statischer Inhalt
↓
SQLite + Admin-CRUD (editierbar, noch nicht durchdacht)
↓
Drei deterministische Pipelines, ein gemeinsames Prinzip
↓
Anschreiben / CV Ask-Vikram-Q&A Fotosuche
↓ ↓ ↓
bewertete Evidenz abgerufene Entscheidungen eingebettete Fotos
↓ ↓ ↓
Claude — nur Text, begrenzter Input
↓
MCP-Tools (dieselben Pipelines, außerhalb des Browsers aufrufbar)
Sobald die Anschreiben- und CV-Pipelines als isolierte, benannte Funktionen existierten statt als Inline-Logik im Umfang einer Seite, war die Bereitstellung als MCP-Tools fast beiläufig — eine dünne Adapterschicht über Arbeit, die ohnehin existieren musste. Das war nichts, was ich von Anfang an geplant hatte; es war etwas, das die frühere Entscheidung, Pipeline-Logik aus Routen und Komponenten herauszuhalten, später fast kostenlos gemacht hat.
Was ich gelernt habe
Am meisten überrascht hat mich, wie früh der MCP-Schritt kam, sobald die Pipeline funktionsförmig war — ich habe ihn innerhalb eines Tages hinzugefügt, nachdem die Pipeline überhaupt erst funktionierte, nicht als ausgereiftes Feature, sondern weil kaum noch etwas zu bauen war. Ich hatte angenommen, Tools für einen externen Client bereitzustellen wäre ein eigenes Projekt. War es nicht, und das hat mir gezeigt, dass die frühere Entscheidung — Pipeline-Logik isolieren, aus Route-Handlern heraushalten — schon den schwierigen Teil erledigt hatte, ohne dass mir das damals bewusst war.
Ich habe auch etwas falsch gemacht: Ich habe die Dokumentation hinter der Architektur zurückfallen lassen. mcp/README.md beschreibt Bearer-Token-Auth noch immer als den primären Weg, Monate nachdem das tatsächliche System auf OAuth 2.1 mit GitHub-Login umgestellt wurde. Das ist eine kleine Abweichung, aber genau der Fehlermodus, den diese ganze Plattform an anderer Stelle verhindern soll — eine Stelle, an der das Geschriebene und das Wahre unbemerkt auseinanderlaufen — und er ist in meiner eigenen Dokumentation passiert, bevor ich ihn bemerkt habe.
Der Blick nach vorn
Die Tabelle engineering_decision — der Korpus, aus dem Ask Vikram abruft — hat noch nicht das Autoring-Tooling, das die Blog-/Journal-Inhalte gerade bekommen haben. Es ist eine kleinere, seltener genutzte Admin-Oberfläche als die Inhaltstypen, die ich ständig anfasse, und dieses Ungleichgewicht sollte ich beheben, bevor der Korpus so groß wird, dass es mehr zählt.
Fazit
Ein Portfolio wird nicht dadurch zur Plattform, dass man ihm ein KI-Feature hinzufügt. Es wird zu einer, wenn dieselben Zeilen derselben Tabellen zu etwas werden, das mehrere deterministische Pipelines lesen und durchdenken können, jede hinter ihrer eigenen Grenze — eine öffentliche Seite, ein Admin-Dashboard, eine Retrieval-Pipeline und jetzt ein MCP-Client, alle gerichtet auf Daten, die nie mit einem von ihnen im Speziellen im Kopf geschrieben wurden.