KI-Chatbot auf Ihrer Website einrichten (trainiert auf Ihre Inhalte)
Website-Chatbot mit RAG, OpenAI API und Embeddings bauen. Vom Scraping bis zum Chat-Widget.
Kurzfassung (TL;DR)
Ein KI-Chatbot, der auf den eigenen Website-Inhalten trainiert ist, lässt sich mit Retrieval-Augmented Generation (RAG) umsetzen: Website-Texte scrapen, Vektor-Embeddings erzeugen, in einer Vektordatenbank wie ChromaDB speichern und über die OpenAI-API als Kontext bei Nutzeranfragen mitgeben. Fertiglösungen wie Crisp, Tidio oder Chatbase bieten eine schnelle Alternative ohne Programmieraufwand. Eine eigene RAG-Lösung kostet im Betrieb etwa 5–50 € pro Monat je nach Traffic. Die DSGVO-Konformität muss vor dem Einsatz sichergestellt werden.
Voraussetzungen
- Python 3.10+ installiert
- Ein OpenAI-API-Schlüssel (oder kompatibler Anbieter)
- Grundkenntnisse in HTML/JavaScript
- Ein Server oder eine Serverless-Plattform für das Backend
pip install openai chromadb beautifulsoup4 requests flask
Chatbot-Optionen im Überblick
Fertiglösungen
| Lösung | Vorteile | Nachteile | Startpreis |
|---|---|---|---|
| Crisp | Live-Chat + KI-Bot kombiniert, saubere Oberfläche | KI-Features erst ab höheren Tarifen | Gratis / 25 $/Monat |
| Tidio | Drag-and-Drop-Flowbuilder, Shopify-Integration | KI-Antworten im Gratis-Plan begrenzt | Gratis / 29 $/Monat |
| Chatbase | Dokumente/URLs hochladen, sofort RAG-Chatbot | Weniger Kontrolle über Retrieval-Logik | Gratis / 19 $/Monat |
| Eigener RAG-Bot | Volle Kontrolle, eigene Daten, kein Vendor-Lock-in | Erfordert Entwicklung und Wartung | ~5 $/Monat (API-Kosten) |
Wer die volle Kontrolle über Retrieval und Antwortgenerierung haben möchte, baut eine eigene RAG-Pipeline. Die folgende Anleitung zeigt Schritt für Schritt, wie das geht.
Schritt 1: Website-Inhalte extrahieren
Zuerst sammeln wir die Textinhalte der Website-Seiten. Dieses Skript ruft eine Liste von URLs ab und extrahiert den bereinigten Text.
import requests
from bs4 import BeautifulSoup
from urllib.parse import urljoin
import json
import time
def website_scrapen(basis_url, pfade):
"""Textinhalte von einer Liste von URL-Pfaden extrahieren."""
dokumente = []
for pfad in pfade:
url = urljoin(basis_url, pfad)
try:
antwort = requests.get(url, timeout=10)
antwort.raise_for_status()
soup = BeautifulSoup(antwort.text, "html.parser")
# Script, Style, Nav, Footer entfernen
for tag in soup(["script", "style", "nav", "footer", "header"]):
tag.decompose()
titel = soup.title.string.strip() if soup.title else pfad
hauptbereich = soup.find("main") or soup.find("article") or soup.body
text = hauptbereich.get_text(separator="\n", strip=True) if hauptbereich else ""
if len(text) > 50: # Fast leere Seiten überspringen
dokumente.append({
"url": url,
"titel": titel,
"inhalt": text[:8000]
})
print(f"Gescrapt: {titel} ({len(text)} Zeichen)")
time.sleep(0.5) # Server nicht überlasten
except Exception as e:
print(f"Fehler beim Scraping von {url}: {e}")
return dokumente
# Verwendung
seiten = ["/", "/ueber-uns", "/leistungen", "/faq", "/preise", "/kontakt"]
doks = website_scrapen("https://example.com", seiten)
with open("gescrapte_inhalte.json", "w") as f:
json.dump(doks, f, indent=2, ensure_ascii=False)
print(f"{len(doks)} Seiten erfolgreich extrahiert.")
Schritt 2: Embeddings erstellen mit ChromaDB
Im nächsten Schritt teilen wir die Texte in Abschnitte auf, erzeugen Vektor-Embeddings über OpenAI und speichern sie in ChromaDB für schnelle Ähnlichkeitssuche.
import chromadb
from openai import OpenAI
import json
client = OpenAI() # Nutzt die Umgebungsvariable OPENAI_API_KEY
chroma = chromadb.PersistentClient(path="./chroma_db")
collection = chroma.get_or_create_collection(
name="website_inhalte",
metadata={"hnsw:space": "cosine"}
)
def text_aufteilen(text, chunk_groesse=500, ueberlappung=50):
"""Text in überlappende Abschnitte aufteilen."""
woerter = text.split()
abschnitte = []
for i in range(0, len(woerter), chunk_groesse - ueberlappung):
abschnitt = " ".join(woerter[i:i + chunk_groesse])
if len(abschnitt) > 20:
abschnitte.append(abschnitt)
return abschnitte
def index_aufbauen(quelldatei="gescrapte_inhalte.json"):
with open(quelldatei) as f:
dokumente = json.load(f)
alle_abschnitte = []
alle_ids = []
alle_metadaten = []
for dok in dokumente:
abschnitte = text_aufteilen(dok["inhalt"])
for i, abschnitt in enumerate(abschnitte):
abschnitt_id = f"{dok['url']}#teil{i}"
alle_abschnitte.append(abschnitt)
alle_ids.append(abschnitt_id)
alle_metadaten.append({
"url": dok["url"],
"titel": dok["titel"]
})
# Batch-Embedding und Einfügen
batch_groesse = 100
for i in range(0, len(alle_abschnitte), batch_groesse):
batch = alle_abschnitte[i:i + batch_groesse]
ids = alle_ids[i:i + batch_groesse]
meta = alle_metadaten[i:i + batch_groesse]
antwort = client.embeddings.create(
model="text-embedding-3-small",
input=batch
)
embeddings = [e.embedding for e in antwort.data]
collection.upsert(
ids=ids,
documents=batch,
embeddings=embeddings,
metadatas=meta
)
print(f"Indiziert: {i + len(batch)}/{len(alle_abschnitte)} Abschnitte")
print(f"Index fertig: {len(alle_abschnitte)} Abschnitte aus {len(dokumente)} Seiten.")
index_aufbauen()
Schritt 3: RAG-Chatbot-Backend aufbauen
Diese Flask-API nimmt eine Nutzerfrage entgegen, sucht relevante Abschnitte aus ChromaDB und übergibt sie als Kontext an die OpenAI-Chat-API.
from flask import Flask, request, jsonify
from flask_cors import CORS
from openai import OpenAI
import chromadb
app = Flask(__name__)
CORS(app, origins=["https://ihredomain.de"])
oai = OpenAI()
chroma = chromadb.PersistentClient(path="./chroma_db")
collection = chroma.get_collection("website_inhalte")
SYSTEM_PROMPT = """Du bist der virtuelle Assistent von [Firmenname].
Beantworte Fragen AUSSCHLIEßLICH auf Basis des bereitgestellten Kontexts.
Wenn der Kontext die Antwort nicht enthält, sage: "Dazu habe ich leider
keine Information. Bitte kontaktieren Sie uns unter support@example.de."
Sei präzise, freundlich und korrekt. Nenne wenn möglich die Quellseite."""
def kontext_abrufen(anfrage, n_ergebnisse=5):
"""Die relevantesten Abschnitte für eine Anfrage finden."""
anfrage_embedding = oai.embeddings.create(
model="text-embedding-3-small",
input=[anfrage]
).data[0].embedding
ergebnisse = collection.query(
query_embeddings=[anfrage_embedding],
n_results=n_ergebnisse
)
kontext_teile = []
quellen = set()
for dok, meta in zip(ergebnisse["documents"][0], ergebnisse["metadatas"][0]):
kontext_teile.append(f"[Quelle: {meta['titel']}]\n{dok}")
quellen.add(meta["url"])
return "\n\n---\n\n".join(kontext_teile), list(quellen)
@app.route("/api/chat", methods=["POST"])
def chat():
daten = request.json
nachricht = daten.get("message", "").strip()
if not nachricht or len(nachricht) > 1000:
return jsonify({"error": "Ungültige Nachricht"}), 400
kontext, quellen = kontext_abrufen(nachricht)
antwort = oai.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": f"Kontext:\n{kontext}\n\nFrage: {nachricht}"}
],
max_tokens=500,
temperature=0.3
)
antwort_text = antwort.choices[0].message.content
return jsonify({"answer": antwort_text, "sources": quellen})
if __name__ == "__main__":
app.run(port=5000)
Schritt 4: Prompt Engineering für Website-Chatbots
Der System-Prompt ist entscheidend für die Qualität der Antworten. Hier sind bewährte Ansätze:
Strikt kontextbasierter Prompt
STRIKT_PROMPT = """Du bist der virtuelle Assistent von [Firmenname].
Regeln:
1. Antworte AUSSCHLIEßLICH auf Basis des bereitgestellten Kontexts.
Erfinde NIEMALS Informationen.
2. Bei Unsicherheit verweise auf den Kundenservice.
3. Halte Antworten auf maximal 3 Sätze, es sei denn, Details
werden explizit angefragt.
4. Nenne immer die Quellseite.
5. Antworte in der Sprache, in der die Frage gestellt wird.
6. Gehe nicht auf Wettbewerber oder sachfremde Themen ein."""
Vertriebsorientierter Prompt
VERTRIEB_PROMPT = """Du bist ein freundlicher Assistent von [Firmenname].
Dein Ziel ist es, Besuchern beim Finden des passenden Angebots zu helfen.
Regeln:
1. Antworte auf Basis des Kontexts. Wenn die Antwort fehlt,
biete an, den Kontakt zu einem Mitarbeiter herzustellen.
2. Weise bei passender Gelegenheit dezent auf relevante
Produkte/Leistungen hin.
3. Nenne bei Preisfragen die Information und schlage eine
Demo-Buchung vor.
4. Sei warmherzig, prägnant und professionell."""
Schritt 5: Chat-Widget einbinden
Fügen Sie diesen Code-Ausschnitt vor dem schließenden </body>-Tag Ihrer Website ein. Er erzeugt einen schwebenden Chat-Button mit Dialogfenster.
<!-- KI-Chatbot-Widget -->
<style>
#chatbot-toggle {
position: fixed; bottom: 24px; right: 24px; z-index: 9999;
width: 56px; height: 56px; border-radius: 50%; border: none;
background: #2563eb; color: #fff; font-size: 24px; cursor: pointer;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}
#chatbot-window {
display: none; position: fixed; bottom: 90px; right: 24px;
width: 380px; max-height: 520px; z-index: 9999;
border-radius: 12px; overflow: hidden;
box-shadow: 0 8px 30px rgba(0,0,0,0.2);
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
background: #fff; flex-direction: column;
}
#chatbot-window.open { display: flex; }
#chatbot-header {
background: #2563eb; color: #fff; padding: 16px;
font-weight: 600; font-size: 15px;
}
#chatbot-messages {
flex: 1; overflow-y: auto; padding: 16px;
max-height: 360px; min-height: 200px;
}
.cb-msg { margin-bottom: 12px; line-height: 1.5; font-size: 14px; }
.cb-msg.bot { color: #1e293b; }
.cb-msg.user { color: #2563eb; text-align: right; }
#chatbot-input-wrap {
display: flex; border-top: 1px solid #e2e8f0; padding: 8px;
}
#chatbot-input {
flex: 1; border: none; outline: none; padding: 8px 12px;
font-size: 14px;
}
#chatbot-send {
background: #2563eb; color: #fff; border: none; padding: 8px 16px;
border-radius: 6px; cursor: pointer; font-size: 14px;
}
</style>
<button id="chatbot-toggle" onclick="toggleChat()">ὊC</button>
<div id="chatbot-window">
<div id="chatbot-header">Haben Sie eine Frage?</div>
<div id="chatbot-messages">
<div class="cb-msg bot">Hallo! Wie kann ich Ihnen helfen?</div>
</div>
<div id="chatbot-input-wrap">
<input id="chatbot-input" placeholder="Ihre Frage eingeben..."
onkeydown="if(event.key==='Enter')nachrichtSenden()" />
<button id="chatbot-send" onclick="nachrichtSenden()">Senden</button>
</div>
</div>
<script>
const CHATBOT_API = "https://ihre-api.example.de/api/chat";
function toggleChat() {
document.getElementById("chatbot-window").classList.toggle("open");
}
async function nachrichtSenden() {
const eingabe = document.getElementById("chatbot-input");
const msg = eingabe.value.trim();
if (!msg) return;
const nachrichten = document.getElementById("chatbot-messages");
nachrichten.innerHTML += `<div class="cb-msg user">${escapeHtml(msg)}</div>`;
eingabe.value = "";
nachrichten.innerHTML += `<div class="cb-msg bot" id="typing">Einen Moment...</div>`;
nachrichten.scrollTop = nachrichten.scrollHeight;
try {
const res = await fetch(CHATBOT_API, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ message: msg })
});
const data = await res.json();
document.getElementById("typing").remove();
nachrichten.innerHTML += `<div class="cb-msg bot">${escapeHtml(data.answer)}</div>`;
} catch {
document.getElementById("typing").remove();
nachrichten.innerHTML += `<div class="cb-msg bot">Entschuldigung, es ist ein Fehler aufgetreten.</div>`;
}
nachrichten.scrollTop = nachrichten.scrollHeight;
}
function escapeHtml(str) {
const d = document.createElement("div");
d.textContent = str;
return d.innerHTML;
}
</script>
Schritt 6: Hosting-Optionen
Option A: Serverless (empfohlen bei geringem Traffic)
| Plattform | Kaltstart | Gratis-Kontingent | Ideal für |
|---|---|---|---|
| Vercel (Python Functions) | ~300 ms | 100k Anfragen/Monat | Schnelles Deployment, Next.js-Seiten |
| Cloudflare Workers | ~5 ms | 100k Anfragen/Tag | Niedrige Latenz, globales Edge-Netzwerk |
| AWS Lambda | ~500 ms | 1 Mio. Anfragen/Monat | Komplexe Pipelines, Enterprise |
Option B: VPS (empfohlen bei hohem Traffic oder Self-Hosting)
Ein kleiner VPS (2 vCPU, 4 GB RAM) für 5–12 €/Monat bei Hetzner, DigitalOcean oder Contabo reicht für ChromaDB + Flask bei bis zu ~10.000 täglichen Konversationen aus. Für das Deployment empfiehlt sich Docker Compose:
# docker-compose.yml (vereinfacht)
# services:
# chatbot:
# build: .
# ports: ["5000:5000"]
# volumes: ["./chroma_db:/app/chroma_db"]
# environment:
# - OPENAI_API_KEY=${OPENAI_API_KEY}
Kostenkalkulation
Der größte Kostenfaktor ist die OpenAI-API. Hier eine Formel mit Rechenbeispielen:
| Komponente | Kosten | Berechnung |
|---|---|---|
| Embedding (Indizierung, einmalig) | ~0,01 $ | 50 Seiten x 2k Tokens x 0,02 $/1M Tokens |
| Embedding (pro Anfrage) | ~0,000002 $ | ~100 Tokens x 0,02 $/1M Tokens |
| GPT-4o-mini (pro Anfrage) | ~0,0004 $ | ~2k Input- + 300 Output-Tokens |
| 1.000 Anfragen/Monat | ~0,40 $ | Embedding + Completion pro Anfrage |
| 10.000 Anfragen/Monat | ~4,00 $ | |
| 100.000 Anfragen/Monat | ~40,00 $ | |
| ChromaDB / Hosting | 0–12 $/Monat | Lokal kostenlos, VPS ansonsten |
Realistische Gesamtkosten für eine kleine Firmenwebsite: 5–15 $/Monat inklusive Hosting.
DSGVO-Anforderungen
Bei Nutzern aus der EU müssen folgende Punkte beachtet werden:
- Auftragsverarbeitungsvertrag (AVV): Schließen Sie einen AVV mit OpenAI ab (im Dashboard verfügbar) oder mit dem jeweiligen LLM-Anbieter.
- Datenschutzhinweis: Informieren Sie die Nutzer, dass ihre Chat-Nachrichten von einer KI verarbeitet und ggf. an einen Drittanbieter übermittelt werden. Ergänzen Sie Ihre Datenschutzerklärung entsprechend.
- Einwilligung: Zeigen Sie vor der ersten Nachricht einen Hinweis an, z. B.: "Dieser Chat wird von KI unterstützt. Nachrichten werden von OpenAI verarbeitet. Mit der Nutzung stimmen Sie dieser Verarbeitung zu."
- Datenminimierung: Speichern Sie Chat-Verläufe nicht länger als nötig. Vermeiden Sie das Erheben personenbezogener Daten über den Chat.
- Recht auf Löschung: Wenn Chat-Protokolle gespeichert werden, muss ein Mechanismus zur Löschung auf Anfrage vorhanden sein.
- EU-Hosting: Erwägen Sie LLM-Anbieter mit EU-Hosting (z. B. Mistral AI über Azure EU oder selbst gehostete Open-Source-Modelle), um transatlantische Datenübertragungen zu vermeiden.
- KI-Verordnung (AI Act): Ab 2026 gelten zusätzliche Transparenzpflichten für KI-Systeme in der EU. Chatbots müssen als KI-generiert gekennzeichnet werden.
Grenzen & Wann ein Chatbot NICHT sinnvoll ist
- Kleine, statische Websites: Bei 5 Seiten und einer klaren FAQ-Seite bringt ein Chatbot unnötige Komplexität. Eine einfache FAQ-Sektion reicht aus.
- Rechtlich sensible Inhalte: Medizinische, juristische oder finanzielle Beratung sollte niemals ohne menschliche Aufsicht von einer KI kommen.
- Halluzinationsrisiko: Auch mit RAG können LLMs fehlerhafte Antworten erzeugen. Immer einen Haftungsausschluss und eine Weiterleitung an menschlichen Support einbauen.
- Kritische Transaktionen: Käufe, Verträge oder Kontoverwaltung sollten nicht ausschließlich über einen Chatbot abgewickelt werden.
- Wenig Traffic: Bei weniger als 10 Support-Anfragen pro Woche ist ein Kontaktformular einfacher und persönlicher.
Fehlerbehebung
Chatbot antwortet immer mit "Dazu habe ich keine Information"
- Prüfen Sie, ob beim Scraping tatsächlich Inhalte erfasst wurden (
gescrapte_inhalte.jsondarf nicht leer sein). - Stellen Sie sicher, dass die ChromaDB-Collection Einträge enthält:
collection.count()muss größer als 0 sein. - Das Embedding-Modell beim Indizieren muss identisch mit dem bei der Abfrage sein.
Widget wird auf der Seite nicht angezeigt
- Browser-Konsole auf JavaScript-Fehler prüfen.
- Der Code-Ausschnitt muss vor
</body>stehen, nicht in<head>. - Sicherstellen, dass kein
z-index-Konflikt mit bestehenden Elementen besteht.
CORS-Fehler beim API-Aufruf
flask-corsmuss installiert sein undCORS(app, origins=[...])muss Ihre Domain enthalten.- Bei Serverless-Deployments die entsprechenden CORS-Header in der Funktions-Konfiguration setzen.
Hohe Latenz (>5 Sekunden pro Antwort)
- Auf
gpt-4o-ministattgpt-4owechseln für schnellere Antworten. n_resultsbeim Retrieval von 5 auf 3 reduzieren.- Serverless-Edge-Functions (Cloudflare Workers) für geringere Netzwerklatenz nutzen.
Vorbeugung & Best Practices
- Regelmäßig neu indizieren: Richten Sie einen Cronjob ein, der die Website wöchentlich neu scrapt und indiziert, damit der Chatbot aktuelle Inhalte kennt.
- Rate Limiting: Begrenzen Sie Anfragen (z. B. 10 pro Minute pro IP), um Kostenspitzen zu vermeiden.
- Kosten überwachen: Richten Sie bei OpenAI Nutzungswarnungen bei 10 $, 25 $ und 50 $ ein.
- Fallback-Mechanismus: Bieten Sie immer eine Möglichkeit, einen Menschen zu erreichen (E-Mail, Telefon, Live-Chat-Übergabe).
- Mit echten Fragen testen: Sammeln Sie vor dem Launch 20–30 echte Kundenfragen und prüfen Sie, ob der Chatbot korrekt antwortet.
- Protokollieren und auswerten: Überprüfen Sie regelmäßig (anonymisierte) Chat-Verläufe, um Lücken in Ihren Inhalten zu identifizieren und Antworten zu verbessern.
- Token-Limits setzen: Begrenzen Sie
max_tokensin API-Aufrufen, um unkontrollierte Kosten durch ungewöhnlich lange Antworten zu verhindern.
Experten-Hilfe gebraucht?
Chatbot heute installiert? €99, trainiert auf bis zu 50 Seiten Ihres Inhalts.
Jetzt buchen — €99100% Geld-zurück-Garantie