Luca Mele
Luca Mele

AI & Engineering

Einen KI-Chatbot für mein Portfolio bauen — mit Claude, in einer Session

Einen KI-Chatbot für mein Portfolio bauen — mit Claude, in einer Session
Zurück zu allen Beiträgen
·10 Min. Lesezeit
KI-generierten Podcast anhören
Alle Podcasts →

Letzte Woche habe ich einen KI-Chatbot zu dieser Website hinzugefügt. Kein Support-Widget mit vorgefertigten Antworten, sondern ein tatsächlicher konversationeller Agent, der meinen Lebenslauf, meine Blog-Artikel, meine Tech-Meinungen und meine Hobbys kennt — und diese in jeder Sprache diskutieren kann. Von der Idee bis zur Produktion hat es etwa eine Stunde gedauert. Hier ist genau, wie ich es gebaut habe, welche Entscheidungen ich getroffen habe und was ich anders machen würde.

Der Chatbot, den du in der unteren rechten Ecke siehst, wird von Claude Haiku angetrieben, Anthropics schnellstem und günstigstem Modell. Er läuft über eine einzelne Next.js API Route, hat keine Datenbank, keine SDK-Abhängigkeit, keinen Vector Store und kein LangChain. Es sind etwa 140 Zeilen Server-Code und 230 Zeilen Client-Code. Das wars.

Die Architektur: Bewusst einfach

Das gesamte Backend ist eine einzelne Datei: eine API Route unter /api/chat. Sie empfängt die Konversationshistorie vom Client, stellt einen System-Prompt mit allem voran, was der Chatbot wissen muss, und leitet es an Claudes Messages API via rohem Fetch-Call weiter. Kein SDK, keine Wrapper-Bibliothek, keine Abstraktionsschicht.

Warum kein SDK? Weil die Anthropic Messages API ein einzelner POST-Endpoint mit einem sauberen JSON-Vertrag ist. @anthropic-ai/sdk hinzuzufügen würde Abhängigkeiten einbringen, die ich nicht brauche, für etwas, das im Grunde ein Fetch-Call mit drei Headern ist. Das ist das Prinzip der geringsten Macht in der Praxis — verwende die einfachste Technologie, die das Problem löst.

Der Client ist eine React-Komponente mit useState für Nachrichten und einem Formular, das an die API Route postet. Nachrichten werden im Component State gespeichert — kein Redux, kein Context Provider, keine State-Management-Bibliothek. Wenn der Nutzer den Chat schliesst, ist die Konversation weg. Das ist in Ordnung für einen Portfolio-Chatbot.

Der System-Prompt: Dein Chatbot ist nur so gut wie sein Briefing

Hier hören die meisten KI-Chatbot-Tutorials zu früh auf. Sie zeigen dir, wie man die API aufruft und die Antwort rendert. Aber der System-Prompt ist das eigentliche Produkt. Er macht deinen Chatbot nützlich statt generisch.

Mein System-Prompt umfasst etwa 3.000 Tokens. Er enthält: meine vollständige Karrieregeschichte (9 Positionen, chronologisch), meine Ausbildung (MAS Software Engineering, CAS Frontend Engineering), meine technischen Fähigkeiten, meine Management-Theorien (Johari-Fenster, 9-Box-Grid, PAC), Zusammenfassungen aller 10 Blog-Artikel mit ihren Kernargumenten, meine Hobbys und strenge Datenschutzregeln.

Die Datenschutzregeln sind kritisch. Der System-Prompt sagt explizit: Niemals meine Privatadresse, Telefonnummer, Geburtstag oder Familienstand verraten. Bei Nachfrage auf E-Mail oder LinkedIn verweisen. Ich habe das ausgiebig getestet — Claude respektiert diese Grenzen zuverlässig. Aber du solltest deine eigenen Leitplanken testen. LLMs können kreativ beim Informationsleaken sein, wenn der Prompt nicht explizit genug ist.

Rate Limiting: Dein Portemonnaie schützen

Ein KI-Chatbot ohne Rate Limiting ist eine Kreditkarte an einem öffentlichen Endpoint. Jede Nachricht kostet Geld — nicht viel mit Haiku, aber es summiert sich, wenn jemand 10.000 Anfragen scriptet.

Ich habe einen einfachen In-Memory Rate Limiter implementiert: 800 Nachrichten pro IP-Adresse pro 12-Stunden-Fenster. Es ist eine Map, die Zähler und Reset-Zeitstempel speichert. Wenn eine Anfrage kommt, prüft sie die Map, erhöht den Zähler und gibt 429 zurück, wenn das Limit überschritten ist.

Ist In-Memory Rate Limiting perfekt? Nein. Es wird zurückgesetzt, wenn die Serverless-Funktion kalt startet, und teilt keinen State über Instanzen. Für eine Portfolio-Site ist es ausreichend. Bräuchte ich produktionsreifes Rate Limiting, würde ich Vercel KV oder Upstash Redis verwenden. Aber das würde eine Abhängigkeit und eine Service-Verbindung für ein Problem hinzufügen, das ich noch gar nicht habe. YAGNI.

Konversationshistorie: Den Chat wirklich konversationell machen

Die erste Version des Chatbots war zustandslos — jede Nachricht wurde isoliert an Claude gesendet, ohne Erinnerung an vorherige Nachrichten. Es funktionierte, fühlte sich aber kaputt an. «Was meintest du damit?» bekam eine verwirrte Antwort, weil «damit» keinen Bezug hatte.

Die Lösung war einfach: die gesamte Konversationshistorie mit jeder Anfrage senden. Der Client hält ein Array von Nachrichten, und bei jeder Übermittlung sendet er das vollständige Array an die API Route, die es an Claude weiterleitet. Claudes Kontextfenster erledigt den Rest — es kann auf jede vorherige Nachricht in der Konversation Bezug nehmen.

Ich habe die Historie bei 50 Nachrichten pro Anfrage begrenzt, um Missbrauch zu verhindern. In der Praxis führt niemand eine 50-Nachrichten-Konversation mit einem Portfolio-Chatbot, aber das Limit verhindert, dass jemand ein Payload mit Tausenden von fabrizierten Nachrichten sendet.

Markdown-Rendering: Code-Blöcke, Syntax-Highlighting, Copy-Button

Wenn Leute dem Chatbot technische Fragen stellen, antwortet Claude mit Code-Blöcken. Reines Text-Rendering machte diese unlesbar — einfach eine Wand aus Monospace-Text ohne visuelle Struktur.

Ich habe einen leichtgewichtigen Markdown-Renderer direkt in der Komponente gebaut — kein react-markdown, kein remark, kein rehype. Er verarbeitet Fenced Code Blocks mit Spracherkennung, Inline Code und fetten Text. Für das Syntax-Highlighting habe ich einen Single-Pass Tokenizer geschrieben, der Keywords, Strings, Kommentare und Zahlen für JavaScript/TypeScript, CSS und HTML identifiziert.

Die wichtigste Lektion hier: Verwende keine verketteten Regex-Ersetzungen für Syntax-Highlighting. Mein erster Versuch verwendete sequenzielle .replace()-Aufrufe, und die späteren Regexes matchten die <span class="...">-Tags, die von früheren generiert wurden. Die Lösung war ein Single-Pass-Ansatz, bei dem jeder Regex-Match genau einmal verarbeitet wird.

Ich habe auch einen Copy-Button zu jedem Code-Block hinzugefügt. Er verwendet die Clipboard API (navigator.clipboard.writeText) mit einem «Kopiert»-Bestätigungsstatus. Kleines Detail, grosse Usability-Verbesserung.

Das UI: Es wie eine Person fühlen lassen

Die erste Version verwendete eine generische lila Chat-Blase mit einem Sprechblasen-Icon. Es funktionierte, fühlte sich aber an wie jedes andere SaaS-Support-Widget. Ich wollte, dass es sich anfühlt, als würdest du tatsächlich mit mir reden.

Die aktuelle Version verwendet mein Foto als Chat-Blase mit einem lila KI-Overlay und einem «AI»-Badge — sofort klar, dass dies eine KI-Version von mir ist, kein versteckter Bot, der vorgibt, ein Mensch zu sein. Der Chat-Header zeigt meinen Namen mit «AI Agent» und einem grünen Online-Indikator. Assistenten-Nachrichten zeigen einen kleinen Avatar daneben. Der Willkommensbildschirm hat ein grösseres Foto mit «Hey, I'm Luca!»

Ich habe auch einen Fullscreen-Modus hinzugefügt. Das kleine 360×480-Widget funktioniert für schnelle Fragen, aber für code-lastige Konversationen braucht man mehr Platz. Ein Erweitern-Button im Header wechselt zu einem Vollbild-Layout mit zentriertem Content, begrenzt auf max-w-3xl.

Was es gekostet hat

Claude Haiku ist bemerkenswert günstig. Mit meinem System-Prompt (~3.000 Tokens) und typischen Konversationslängen kostet jede Nachricht ungefähr $0.001-0.003. Selbst bei grosszügiger Nutzung betragen die monatlichen Kosten ein paar Dollar. Der Rate Limiter ist zur Missbrauchsprävention da, nicht zum Kostenmanagement.

Die Entwicklungskosten waren in Bezug auf Abhängigkeiten im Wesentlichen null. Keine neuen npm-Pakete wurden hinzugefügt. Das gesamte Feature ist mit Next.js API Routes, der Fetch API, React State und Tailwind CSS gebaut — Tools, die bereits im Projekt waren.

Was ich anders machen würde

Streaming-Antworten. Aktuell sieht der Nutzer «...»-Punkte, bis die vollständige Antwort eintrifft. Mit Claudes Streaming API könnte ich Tokens rendern, sobald sie ankommen, was den Chatbot viel responsiver wirken lässt. Es ist eine unkomplizierte Änderung — von der Messages API auf die Streaming-Variante wechseln und einen ReadableStream auf dem Client verwenden.

Persistente Konversationen. Im Moment geht die Konversation beim Schliessen des Chats verloren. LocalStorage würde das trivial lösen. Ich habe es nicht hinzugefügt, weil ich nicht sicher bin, ob Leute ihre Chatbot-Konversationen gespeichert haben wollen — aber es wäre eine bessere UX für wiederkehrende Besucher.

Bessere Fehlerbehandlung für Edge Cases. Die aktuelle Implementierung behandelt Netzwerkfehler und Rate Limiting, aber handhabt nicht elegant Fälle wie Claudes Content-Filtering oder sehr lange Antworten, die das Token-Limit mitten im Satz erreichen.

Das Fazit

Einen KI-Chatbot 2026 zu bauen ist kein komplexes Infrastrukturprojekt. Mit einer guten Modell-API, einer serverseitigen Route und grundlegenden Frontend-Skills kannst du an einem Nachmittag von Null zu einem Produktions-Chatbot kommen. Der schwierige Teil ist nicht die Technologie — es ist das Erstellen eines System-Prompts, der den Chatbot wirklich nützlich macht, statt generisch geschwätzig.

Die Gesamtimplementierung: ~140 Zeilen Server-Code, ~230 Zeilen Client-Code, null neue Abhängigkeiten, eine Umgebungsvariable. Wenn du eine Portfolio-Site hast und möchtest, dass Besucher Fragen zu deiner Arbeit stellen können, ist das wahrscheinlich das Feature mit dem grössten Hebel, das du hinzufügen kannst.