Was entstand
Entstanden ist eine Portfolio-Seite mit eingebautem Chat, der Fragen zu Projekten, Skills und Berufserfahrung beantwortet. Die Antworten kommen nicht aus statischen FAQ-Blöcken, sondern aus einer kleinen RAG-Pipeline auf Basis meiner eigenen Profildaten.
Der technische Teil wurde von Grund auf gebaut: Backend, Indexierung, Azure-Setup, Deployment und das Frontend für den Chat. Kein Template, kein Baukasten, kein kopiertes Starter-Projekt.
Wie dieses Projekt gebaut wurde – das eigentliche USP
Design und Layout der Website waren bereits da. Neu gebaut wurden Backend, KI-Anbindung, Azure-Infrastruktur und Deployment-Strecke. Von der ersten Spezifikation bis zur Live-URL hat das rund 7 Stunden gedauert.
Der entscheidende Punkt war nicht "KI macht den Job", sondern ein sauberer Arbeitsmodus mit GitHub Copilot Agent Mode nach dem Muster Spec → Feature → Build → Review:
Spec first
Bevor Code entsteht, steht die Aufgabe sauber da: Ziel, Inputs, Outputs, Abhängigkeiten und Grenzen. Das verhindert, dass sich die Umsetzung in generische Vorschläge verläuft.
One feature at a time
Embedding-Pipeline, Suchindex, Prompting und Deployment wurden nicht parallel improvisiert, sondern nacheinander gebaut, getestet und abgenommen.
Der Entwickler als Architekt
Die KI hilft beim Schreiben und Recherchieren. Architektur, Datenmodell, Sicherheitsgrenzen und technische Entscheidungen habe ich selbst festgelegt.
Fehler als Teil des Prozesses
Probleme bei Deployment, Index-Aufbau und Azure-Authentifizierung wurden nicht kaschiert, sondern direkt gelöst. Gerade diese Fehlerfälle zeigen, ob man das System wirklich verstanden hat.
Der eigentliche Nachweis ist nicht die Live-App allein, sondern der Weg dorthin: in kurzer Zeit ein funktionierendes System bauen, Entscheidungen begründen und die typischen Integrationsprobleme sauber ausräumen.
Zielsetzung und Anforderungen
Funktional
- Zwei Portfolio-Ansichten (ausführlich / minimal) aus einer einzigen Datenquelle
- KI-Assistent, der Fragen zum Profil beantwortet – ohne zu halluzinieren
- Gesprächsgedächtnis innerhalb einer Session
- Sicheres Verhalten bei Fragen außerhalb des Profil-Kontexts
Technisch
- Kein Vendor-Lock-in durch proprietäre Frameworks
- Vollständig automatisiertes Deployment bei jedem Code-Push
- Secrets dürfen niemals im Repository landen
- Der Wissensstand des Assistenten soll unabhängig vom Code aktualisierbar sein
Tech Stack
Backend
Das Backend läuft auf Python 3.12 und FastAPI. Die Requests an Azure OpenAI und AI Search sind asynchron, damit Embedding, Suche und Antwortgenerierung nicht unnötig blockieren. Eingehende Chat-Anfragen werden vorab validiert, der Server selbst läuft über Uvicorn.
KI-Komponenten (Azure OpenAI)
Zwei separate Modell-Deployments kommen zum Einsatz:
- text-embedding-3-large: erzeugt die Vektoren für Index und Nutzeranfragen. Verwendet wurde das größere Modell, weil der Datenbestand klein ist, die Trefferqualität aber hoch sein sollte.
- GPT-4o: formuliert die Antwort aus System-Prompt, Gesprächsverlauf und gefundenen Kontextstellen. Die Temperatur ist niedrig, damit der Chat eher verlässlich als kreativ antwortet.
Wissensindex (Azure AI Search)
Azure AI Search speichert die zerlegten Profiltexte als Dokumente mit Text, Quelle und Embedding. Bei einer Frage wird derselbe Embedding-Schritt für die Anfrage ausgeführt und dann gegen den Index gesucht. Die relevantesten Treffer landen als Kontext im Prompt.
Rohdaten-Verwaltung (Azure Blob Storage)
Die Markdown-Dateien liegen separat im Blob Storage. Das ist absichtlich vom Suchindex getrennt: Erst wenn ein Rebuild läuft, werden Änderungen aus den Quelldateien in den Index übernommen.
Hosting (Azure App Service)
Gehostet wird die App auf Azure App Service im B1-Tier. Deployments laufen bei jedem Push auf main. Secrets liegen ausschließlich in den App Settings und nicht im Repository.
CI/CD (GitHub Actions)
Die Pipeline läuft in GitHub Actions. Authentifiziert wird nicht über ein Publish Profile, sondern über einen Azure Service Principal mit RBAC-Rechten auf die Resource Group. Dadurch bleibt das Deployment skriptbar und nachvollziehbar.
Frontend
Das Frontend besteht aus zwei statischen HTML-Ansichten. Beide laden ihre Inhalte aus derselben JSON-Quelle und werden über ein gemeinsames JavaScript-Modul befüllt. So bleibt die Pflege an einer Stelle.
Architektur-Entscheidungen
Warum RAG statt Fine-Tuning?
Für diesen Anwendungsfall wäre Fine-Tuning unnötig gewesen. Die Daten ändern sich, sind überschaubar und sollen kontrollierbar bleiben. RAG passt besser, weil der Chat bei jeder Anfrage auf den aktuellen Datenbestand zugreift, statt auf ein einmal trainiertes Modellwissen.
Warum Python + FastAPI?
Python lag nahe, weil die Azure- und KI-Bausteine dort schnell nutzbar waren. FastAPI war für das Projekt groß genug, um sauber zu strukturieren, und klein genug, um nicht schon durch das Framework selbst Komplexität aufzubauen.
Warum Azure?
Azure war hier nicht nur Infrastruktur, sondern Teil des Nachweises. OpenAI, Search, Storage und Hosting lassen sich in einem Stack betreiben, und genau dieser Stack taucht auch in realen Unternehmensprojekten auf.
Warum statisches Frontend?
Das Frontend musste in diesem Projekt nicht das Komplizierteste sein. HTML, CSS und etwas JavaScript reichen für diesen Use Case aus und halten den Fokus dort, wo die eigentliche Arbeit lag: im Backend und in der Systemintegration.
Der Prozess
Spec und Datenstrategie
Der erste Schritt war nicht Code, sondern die Trennung der Datenpfade. Die sichtbare Profilansicht kommt aus einer kontrollierten JSON-Datei. Der Chat greift separat auf geprüfte Markdown-Quellen im Suchindex zu. So bleibt steuerbar, was öffentlich angezeigt wird und was im Chat überhaupt auffindbar ist.
Backend
Der Chat-Endpunkt arbeitet in vier Schritten: Embed, Retrieve, Augment, Generate. Die Nutzerfrage wird vektorisiert, passende Textstellen werden gesucht, als Kontext ergänzt und danach von GPT-4o beantwortet.
Der System-Prompt begrenzt die Rolle klar: Der Assistent spricht in Ich-Form, bleibt bei meinen Profildaten und soll nichts dazuerfinden. Für wiederholte Off-Topic-Fragen gibt es ein bewusst eingebautes Fallback-Verhalten.
Frontend und Datenschicht
Karriere-Ansicht und Minimal-CV hängen an derselben Datenquelle. Das spart doppelte Pflege und sorgt dafür, dass Profiländerungen nicht an zwei Stellen auseinanderlaufen.
Azure-Infrastruktur
Azure App Service liefert API und statische Seiten aus. Azure AI Search hält den Vektorindex, Blob Storage die Quelldateien. Wichtig war dabei weniger die reine Bereitstellung als die saubere Trennung von Code, Daten und Secrets.
Wissensindex befüllen
Die Wissensbasis besteht aus vier Markdown-Dateien. Beim Rebuild werden sie in überlappende Abschnitte zerlegt, eingebettet und in Batches in den Index geschrieben. Das Ergebnis waren 107 Chunks. Der gesamte Vorgang ist reproduzierbar, weil der Index bei Bedarf komplett neu aufgebaut werden kann.
CI/CD-Pipeline
Jeder Push auf main stößt ein Deployment an. Statt eines Publish Profiles kommt ein Service Principal zum Einsatz, weil Azure Basic Auth auf App Services standardmäßig deaktiviert. Das war nicht nur sauberer, sondern praktisch notwendig.
Stolpersteine (real, nicht vereinfacht)
Azure erzeugt beim ersten Portal-Setup einen eigenen Deployment-Workflow mit falschen Secrets. Dieser konkurriert beim Merge mit dem eigenen Workflow. Lösung: den automatisch generierten Workflow sofort identifizieren und entfernen.
Neu gesetzte Secrets greifen erst nach einem expliziten App-Neustart. Ohne diesen Schritt startet die App mit leeren Konfigurationswerten und wirft irreführende Fehler.
Das Löschen einer Datei aus dem Blob Storage hat keinen Effekt auf den Vektorindex. Beide Schichten müssen getrennt bereinigt werden, sonst bleiben veraltete Chunks im Index aktiv.
Deutsche Anführungszeichen in Markdown-Quelltexten brechen JSON-Parser. Zeichenkodierung muss explizit berücksichtigt werden.
Technologie-Entscheidungen auf einen Blick
| Entscheidung | Warum |
|---|---|
| RAG statt Fine-Tuning | Aktuelle Daten bleiben direkt änderbar; kein neuer Trainingslauf nötig |
| Python + FastAPI | Schnell für API, Azure-Anbindung und asynchrone Requests |
| Azure AI Search | Vektorindex im selben Stack wie Hosting und OpenAI |
| text-embedding-3-large | Mehr Trefferqualität war hier wichtiger als maximale Sparsamkeit |
| GPT-4o | Gute Instruktionsfolge für den klar begrenzten Chat-Use-Case |
| GitHub Actions | Pipeline liegt direkt im Projekt und ist leicht nachvollziehbar |
| Service Principal statt Publish Profile | Passt zu App Service ohne Basic Auth und ist sauberer für CI/CD |
| Statisches Frontend | Wenig Overhead und genug für diesen Anwendungsfall |
Was dieses Projekt zeigt
- Eigenständiger Aufbau eines lauffähigen Systems aus Hosting, Suchindex, Prompting und UI
- Sicherheitsbewusstsein bei Datenquellen, sichtbaren Inhalten und Secrets
- RAG-Verständnis als pragmatische Lösung für kleine, änderbare Wissensbestände
- Automatisiertes Deployment mit RBAC-konformer Authentifizierung
- Fehleranalyse unter Realbedingungen statt nur lokaler Demo-Setups
Kostenanalyse (Azure)
Dienste und Tiers
| Dienst | Region | Tier |
|---|---|---|
| Azure App Service | Germany West Central | B1 (Linux) |
| Azure OpenAI – GPT-4o | East US 2 | Pay-as-you-go |
| Azure OpenAI – text-embedding-3-large | East US | Pay-as-you-go |
| Azure AI Search | West Europe | Free Tier |
| Azure Blob Storage | West Europe | LRS, Hot |
Gesamtkosten pro Monat
| Szenario | App Service | OpenAI | Gesamt |
|---|---|---|---|
| Demo / Portfolio (~30 Anfragen) | $13.10 | $0.23 | ~$13.33 |
| Bewerbungsphase (~200 Anfragen) | $13.10 | $1.55 | ~$14.65 |
| Starker Traffic (~1.000 Anfragen) | $13.10 | $7.75 | ~$20.85 |