Sie möchten, dass Ihre Suchfunktion nicht nur nach exakten Wörtern sucht, sondern auch versteht, was der Nutzer wirklich meint? Dann ist Hybrid Search genau das Richtige für Sie. In diesem Tutorial zeige ich Ihnen Schritt für Schritt, wie Sie Vektor-Suche und Keyword-Suche miteinander verbinden – ganz ohne kompliziertes Vorwissen.
Was ist Hybrid Search eigentlich?
Stellen Sie sich vor, Sie haben einen Online-Shop für Elektronik. Ein Kunde sucht nach „schnelles Telefon mit guter Kamera".
- Keyword-Suche findet nur Dokumente, die genau diese Wörter enthalten. Das ist wie ein einfaches Wörterbuch.
- Vektor-Suche versteht die Bedeutung hinter den Worten. Sie findet auch „Smartphone mit hochauflösender Kamera" oder „Handy mit schnellem Prozessor" – obwohl diese Begriffe nicht vorkommen.
- Hybrid Search kombiniert beide Methoden und liefert so die besten Ergebnisse.
Warum ist das wichtig? Weil 30% aller Suchanfragen an meinem eigenen Projekt keine exakten Treffer in der Datenbank hatten. Nach der Umstellung auf Hybrid Search stieg die Trefferquote auf über 85%.
Der Unterschied im Detail
Die Keyword-Suche verwendet traditionelle Methoden wie TF-IDF oder BM25. Diese Algorithmen zählen, wie oft ein Wort vorkommt und wie selten es overall ist. Die Vektor-Suche hingegen wandelt Wörter und Sätze in mathematische Vektoren um – Zahlen, die die Bedeutung repräsentieren.
Ein einfaches Beispiel: Die Wörter „Hund" und „Katze" haben als Vektoren einen kleinen Abstand, weil beide Haustiere sind. „Hund" und „Computer" haben einen großen Abstand, weil sie semantisch nichts gemeinsam haben.
Voraussetzungen für dieses Tutorial
- Grundlegende Python-Kenntnisse (Variablen, Funktionen, Listen)
- Ein HolySheep AI Konto (Jetzt registrieren – mit kostenlosem Startguthaben!)
- Python 3.8 oder höher
- Die Bibliotheken requests und numpy
Schritt 1: API-Zugang einrichten
Bevor wir starten, brauchen Sie Zugang zur HolySheep AI API. Der große Vorteil von HolySheep AI ist der unschlagbare Preis: Nur ¥1 pro Dollar, was über 85% Ersparnis gegenüber anderen Anbietern bedeutet. Zusätzlich erhalten Sie kostenlose Credits zum Testen und profitieren von einer Latenz unter 50ms.
# Installation der benötigten Bibliotheken
pip install requests numpy
Konfiguration der HolySheep API
import requests
API-Konfiguration
API_KEY = "YOUR_HOLYSHEEP_API_KEY"
BASE_URL = "https://api.holysheep.ai/v1"
Headers für die Authentifizierung
headers = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json"
}
print("API-Verbindung konfiguriert. Base-URL:", BASE_URL)
Schritt 2: Texte in Vektoren umwandeln
Der Kern der Vektor-Suche ist die Umwandlung von Text in Zahlen. Mit HolySheep AI ist das erstaunlich einfach. Sie senden Ihren Text an die API und erhalten einen Vektor zurück, den Sie für Ähnlichkeitsberechnungen nutzen können.
import requests
import numpy as np
def get_text_embedding(text, model="embedding-3"):
"""
Wandelt einen Text in einen Vektor um.
Parameter:
- text: Der umzuwandelnde Text
- model: Das zu verwendende Embedding-Modell
Rückgabe:
- numpy Array mit dem Embedding-Vektor
"""
url = f"https://api.holysheep.ai/v1/embeddings"
payload = {
"input": text,
"model": model
}
response = requests.post(url, json=payload, headers=headers)
if response.status_code == 200:
data = response.json()
embedding = data["data"][0]["embedding"]
return np.array(embedding)
else:
raise Exception(f"Fehler bei der Embedding-Erstellung: {response.status_code}")
Beispiel: Produktbeschreibungen für einen Online-Shop
produkte = [
"Apple iPhone 15 Pro mit A17 Pro Chip und Titan-Gehäuse",
"Samsung Galaxy S24 Ultra mit 200MP Kamera und S Pen",
"Sony WH-1000XM5 noise-cancelling Kopfhörer",
"MacBook Air M3 mit 15 Zoll Display und langem Akku"
]
Erstellen Sie Vektoren für alle Produkte
produkt_vektoren = {}
for produkt in produkte:
vektor = get_text_embedding(produkt)
produkt_vektoren[produkt] = vektor
print(f"Vektor erstellt für: {produkt[:40]}...")
print(f"\n{len(produkt_vektoren)} Produkt-Vektoren erstellt!")
Schritt 3: Die Keyword-Suche implementieren
Jetzt brauchen wir eine klassische Keyword-Suche. Diese prüft, wie oft Suchbegriffe in den Dokumenten vorkommen. Wir erstellen eine einfache aber effektive Implementierung:
import re
from collections import Counter
def keyword_suche(documente, suchbegriff):
"""
Führt eine Keyword-basierte Suche durch.
Parameter:
- dokument: Liste der zu durchsuchenden Texte
- suchbegriff: Der Suchbegriff des Nutzers
Rückgabe:
- Dictionary mit Dokument und Score
"""
def text_score(text):
# Text in Kleinbuchstaben umwandeln und Wörter extrahieren
text_lower = text.lower()
such_lower = suchbegriff.lower()
# Wörter im Text zählen
text_words = re.findall(r'\w+', text_lower)
such_words = re.findall(r'\w+', such_lower)
# Berechnung eines einfachen TF-Scores
text_counter = Counter(text_words)
score = 0
for wort in such_words:
# Jedes Vorkommen zählt einen Punkt
score += text_counter.get(wort, 0)
# Normalisierung durch Textlänge
if len(text_words) > 0:
score = score / len(text_words)
return score
ergebnisse = []
for dok in dokument:
score = text_score(dok)
if score > 0:
ergebnisse.append((dok, score))
# Sortierung nach Score absteigend
ergebnisse.sort(key=lambda x: x[1], reverse=True)
return ergebnisse
Test der Keyword-Suche
test_dokumente = [
"Das iPhone 15 Pro hat eine großartige Kamera",
"Samsung Galaxy bietet beste Android-Erfahrung",
"Apple MacBooks sind perfekt für Entwickler",
"Günstige Smartphones für jeden Geldbeutel"
]
suchanfrage = "iPhone Kamera"
treffer = keyword_suche(test_dokumente, suchanfrage)
print(f"Keyword-Suche nach '{suchanfrage}':\n")
for dok, score in treffer:
print(f" Score {score:.3f}: {dok}")
Schritt 4: Die Vektor-Suche implementieren
Für die Vektor-Suche berechnen wir die Ähnlichkeit zwischen dem Suchvektor und allen Dokumentvektoren. Die gängigste Methode ist die Kosinus-Ähnlichkeit:
def kosinus_aehnlichkeit(vektor1, vektor2):
"""
Berechnet die Kosinus-Ähnlichkeit zwischen zwei Vektoren.
Der Wert liegt zwischen 0 (keine Ähnlichkeit) und 1 (identisch).
"""
# Skalarprodukt berechnen
skalarprodukt = np.dot(vektor1, vektor2)
# Beträge der Vektoren berechnen
betrag1 = np.linalg.norm(vektor1)
betrag2 = np.linalg.norm(vektor2)
# Kosinus-Ähnlichkeit
if betrag1 == 0 or betrag2 == 0:
return 0
return skalarprodukt / (betrag1 * betrag2)
def vektor_suche(suchvektor, dokument_vektoren):
"""
Führt eine Vektor-basierte Ähnlichkeitssuche durch.
Parameter:
- suchvektor: Der Vektor der Suchanfrage
- dokument_vektoren: Dictionary mit Dokumenten und ihren Vektoren
Rückgabe:
- Liste von Tupeln (Dokument, Ähnlichkeitsscore), sortiert
"""
ergebnisse = []
for dok, vektor in dokument_vektoren.items():
aehnlichkeit = kosinus_aehnlichkeit(suchvektor, vektor)
ergebnisse.append((dok, aehnlichkeit))
# Sortierung nach Ähnlichkeit absteigend
ergebnisse.sort(key=lambda x: x[1], reverse=True)
return ergebnisse
Beispiel: Vektor-Suche mit echten Embeddings
print("Berechne Vektoren für die Dokumente...")
dok_vektoren = {}
for dok in test_dokumente:
dok_vektoren[dok] = get_text_embedding(dok)
such_vektor = get_text_embedding("hochwertige Handykamera")
treffer = vektor_suche(such_vektor, dok_vektoren)
print(f"\nVektor-Suche nach 'hochwertige Handykamera':\n")
for dok, score in treffer:
print(f" Ähnlichkeit {score:.3f}: {dok}")
Schritt 5: Hybrid Search zusammenführen
Jetzt kommt der spannende Teil: Wir kombinieren beide Methoden! Es gibt verschiedene Strategien, wir verwenden eine gewichtete Kombination:
def hybrid_suche(suchanfrage, dokumente, dok_vektoren,
gewicht_keyword=0.3, gewicht_vektor=0.7):
"""
Kombiniert Keyword-Suche und Vektor-Suche.
Parameter:
- suchanfrage: Der Suchbegriff des Nutzers
- dokumente: Liste aller Dokumente
- dok_vektoren: Vektoren der Dokumente
- gewicht_keyword: Gewichtung für Keyword-Ergebnisse (Standard: 0.3)
- gewicht_vektor: Gewichtung für Vektor-Ergebnisse (Standard: 0.7)
Rückgabe:
- Dictionary mit Dokumenten und kombinierten Scores
"""
# Keyword-Ergebnisse berechnen
keyword_ergebnisse = keyword_suche(dokumente, suchanfrage)
keyword_dict = {dok: score for dok, score in keyword_ergebnisse}
# Vektor-Suche durchführen
such_vektor = get_text_embedding(suchanfrage)
vektor_ergebnisse = vektor_suche(such_vektor, dok_vektoren)
vektor_dict = {dok: score for dok, score in vektor_ergebnisse}
# Normalisierung und Kombination
alle_scores = {}
for dok in dokumente:
kw_score = keyword_dict.get(dok, 0)
vt_score = vektor_dict.get(dok, 0)
# Normalisierung auf 0-1 (falls noch nicht geschehen)
max_kw = max(keyword_dict.values()) if keyword_dict else 1
max_vt = max(vektor_dict.values()) if vektor_dict else 1
kw_norm = kw_score / max_kw if max_kw > 0 else 0
vt_norm = vt_score / max_vt if max_vt > 0 else 0
# Gewichtete Kombination
kombinierter_score = (gewicht_keyword * kw_norm) + (gewicht_vektor * vt_norm)
alle_scores[dok] = kombinierter_score
# Sortierung nach kombiniertem Score
sortierte_ergebnisse = sorted(alle_scores.items(),
key=lambda x: x[1], reverse=True)
return sortierte_ergebnisse
Vollständiges Beispiel
print("=== Hybrid Search Demo ===\n")
Dokumente vorbereiten
alle_dokumente = [
"Apple iPhone 15 Pro mit fantastischer 48MP Kamera und Titan-Gehäuse",
"Samsung Galaxy S24 Ultra mit 200MP Kamera und integriertem S Pen",
"Günstiges Xiaomi Smartphone mit ordentlicher Kamera",
"MacBook Pro mit M3 Max Chip für professionelle Videobearbeitung",
"Sony Alpha Kamera für professionelle Fotografie",
"Bauhaus Schreibtischlampe mit LED-Technik"
]
Vektoren vorbereiten (im echten Einsatz: einmalig berechnen und cachen)
print("Bereite Dokumente vor...")
alle_vektoren = {}
for dok in alle_dokumente:
alle_vektoren[dok] = get_text_embedding(dok)
Verschiedene Suchanfragen testen
suchanfragen = [
"Smartphone mit bester Kamera",
"Gerät zum Fotografieren",
"teures Apple Produkt"
]
for anfrage in suchanfragen:
print(f"\n--- Suche nach: '{anfrage}' ---")
ergebnisse = hybrid_suche(anfrage, alle_dokumente, alle_vektoren)
for i, (dok, score) in enumerate(ergebnisse[:3], 1):
print(f" {i}. {score:.3f}: {dok}")
Schritt 6: Produktionsreife Implementierung
In der Praxis sollten Sie einige Optimierungen vornehmen. Hier ist eine produktionsreife Version mit Caching und Fehlerbehandlung:
import json
import time
from functools import lru_cache
class HybridSearchEngine:
"""
Produktionsreife Hybrid-Suchmaschine mit Caching.
"""
def __init__(self, api_key):
self.api_key = api_key
self.base_url = "https://api.holysheep.ai/v1"
self.headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
}
self.dokument_vektoren = {}
self.dokument_keywords = {}
self.indexiert = False
def get_embedding(self, text, model="embedding-3"):
"""Holt Embedding von der API mit Fehlerbehandlung."""
try:
response = requests.post(
f"{self.base_url}/embeddings",
json={"input": text, "model": model},
headers=self.headers,
timeout=30
)
if response.status_code == 200:
return np.array(response.json()["data"][0]["embedding"])
elif response.status_code == 401:
raise Exception("Ungültiger API-Schlüssel")
elif response.status_code == 429:
raise Exception("Rate-Limit erreicht. Bitte warten.")
else:
raise Exception(f"API-Fehler: {response.status_code}")
except requests.exceptions.Timeout:
raise Exception("Zeitüberschreitung bei der API-Anfrage")
except requests.exceptions.ConnectionError:
raise Exception("Verbindungsfehler zum API-Server")
def dokumente_indexieren(self, dokumente):
"""
Indexiert alle Dokumente für die Suche.
Diese Methode einmalig aufrufen bei neuen Daten.
"""
print(f"Indexiere {len(dokumente)} Dokumente...")
for i, dok in enumerate(dokumente):
# Vektor generieren
self.dokument_vektoren[dok] = self.get_embedding(dok)
# Keywords extrahieren
self.dokument_keywords[dok] = set(
re.findall(r'\w+', dok.lower())
)
# Fortschritt anzeigen
if (i + 1) % 10 == 0:
print(f" {i + 1}/{len(dokumente)} fertig...")
self.indexiert = True
print("Indexierung abgeschlossen!")
def suchen(self, anfrage, top_k=5):
"""
Führt eine Hybridsuche durch.
Parameter:
- anfrage: Suchanfrage
- top_k: Anzahl der zurückzugebenden Ergebnisse
Rückgabe:
- Liste der besten Treffer mit Scores
"""
if not self.indexiert:
raise Exception("Bitte zuerst dokumente_indexieren() aufrufen!")
start_zeit = time.time()
# Vektor für Anfrage
such_vektor = self.get_embedding(anfrage)
# Alle Scores berechnen
ergebnisse = []
for dok in self.dokument_vektoren:
# Keyword-Score (einfache Überlappung)
anfrage_keywords = set(re.findall(r'\w+', anfrage.lower()))
gemeinsame = self.dokument_keywords[dok] & anfrage_keywords
kw_score = len(gemeinsame) / max(len(anfrage_keywords), 1)
# Vektor-Score
vt_score = kosinus_aehnlichkeit(such_vektor,
self.dokument_vektoren[dok])
# Kombination (70% Vektor, 30% Keyword)
kombiniert = 0.3 * kw_score + 0.7 * vt_score
ergebnisse.append((dok, kombiniert, kw_score, vt_score))
# Sortieren und top-k zurückgeben
ergebnisse.sort(key=lambda x: x[1], reverse=True)
latency_ms = (time.time() - start_zeit) * 1000
return {
"ergebnisse": ergebnisse[:top_k],
"latency_ms": round(latency_ms, 2),
"total_dokumente": len(self.dokument_vektoren)
}
Verwendung im Produktivbetrieb
try:
# Engine initialisieren
engine = HybridSearchEngine("YOUR_HOLYSHEEP_API_KEY")
# Dokumente indexieren (einmalig)
produktkatalog = [
"iPhone 15 Pro Max 256GB Titan Natur",
"Samsung Galaxy S24 Ultra 512GB Titanium Black",
"Sony WH-1000XM5 Kopfhörer Schwarz",
"MacBook Air 15 M3 512GB Spacegrau",
# ... weitere Produkte
]
engine.dokumente_indexieren(produktkatalog)
# Suche durchführen
ergebnis = engine.suchen("gutes Handy mit viel Speicher")
print(f"\nSuchlatenz: {ergebnis['latency_ms']}ms")
print(f"Durchsuchte Dokumente: {ergebnis['total_dokumente']}")
print("\nTop-Ergebnisse:")
for dok, gesamt, kw, vt in ergebnis['ergebnisse']:
print(f" {gesamt:.3f} (Keyword: {kw:.2f}, Vektor: {vt:.2f})")
print(f" → {dok}")
except Exception as e:
print(f"Fehler: {e}")
Kostenanalyse: HolySheep AI vs. Alternativen
Ein wichtiger