Retrieval-Augmented Generation (RAG) verbessert LLM-Antworten durch Abrufen relevanten Kontexts aus Wissensbasen. Vektordatenbanken ermöglichen effiziente Ähnlichkeitssuche, die für RAG-Systeme entscheidend ist.
Überblick über die RAG-Architektur
Komponenten
- 1. Dokumenten-Ingestion: Quelldokumente verarbeiten und in Chunks aufteilen
- 2. Embedding-Generierung: Chunks in Vektor-Embeddings umwandeln
- 3. Vektorspeicherung: Embeddings in Vektordatenbank speichern
- 4. Abfrageverarbeitung: Benutzerabfragen in Embeddings umwandeln
- 5. Ähnlichkeitssuche: Relevante Chunks finden
- 6. Kontext-Assemblierung: Abgerufene Chunks kombinieren
- 7. LLM-Generierung: Antwort mit Kontext generieren
Vorteile
- Aktuelle Informationen ohne Retraining
- Quellenangabe für Antworten
- Reduzierte Halluzinationen
- Domänenspezifisches Wissen
- Kosteneffektiv im Vergleich zu Fine-Tuning
- Einfache Aktualisierung der Wissensbasis
Vektordatenbank-Vergleich
Pinecone
Vollständig verwaltete Vektordatenbank als Service.
Vorteile:
- Keine Infrastrukturverwaltung
- Hohe Leistung für großflächige Bereitstellungen
- Automatische Skalierung
- Echtzeit-Updates
- 99,9% SLA-Verfügbarkeit
Überlegungen:
- Nur SaaS (kein Self-Hosting)
- Pro-Vektor-Preise können im großen Maßstab teuer sein
- Daten extern gespeichert (DSGVO-Überlegungen)
Weaviate
Open-Source-Vektordatenbank mit Cloud- und Self-Hosting-Optionen.
Vorteile:
- Flexible Bereitstellungsoptionen
- Integrierte ML-Modelle für Vektorisierung
- GraphQL-API
- Hybride Suche (Vektor + Schlüsselwort)
- Starke Community-Unterstützung
Überlegungen:
- Erfordert Infrastrukturverwaltung bei Self-Hosting
- Lernkurve für GraphQL
- Performance-Tuning für Skalierung erforderlich
Milvus
Open-Source-Vektordatenbank optimiert für Milliarden-Maßstab-Bereitstellungen.
Vorteile:
- Exzellente Leistung in großem Maßstab
- Mehrere Indextypen für Optimierung
- Cloud-native Architektur (Kubernetes)
- Aktive Entwicklung und Community
- GPU-Beschleunigungsunterstützung
Überlegungen:
- Komplexe Einrichtung und Konfiguration
- Erfordert erhebliche Ops-Expertise
- Ressourcenintensiv
pgvector (PostgreSQL-Erweiterung)
Vektor-Ähnlichkeitssuche als PostgreSQL-Erweiterung.
Vorteile:
- Bestehende PostgreSQL-Infrastruktur nutzen
- Vektorsuche mit relationalen Daten kombinieren
- Vertraute PostgreSQL-Tools und -Expertise
- ACID-Transaktionen
- Geringere operative Komplexität
Überlegungen:
- Leistung im Vergleich zu spezialisierten Datenbanken begrenzt
- Am besten für kleine bis mittlere Datensätze (<1M Vektoren)
- Weniger optimiert für reine Vektoroperationen
Embedding-Modelle
OpenAI text-embedding-3-large
- 3072 Dimensionen
- Exzellente Allzweckleistung
- Kosten: $0,13 pro 1M Token (Oktober 2025)
- Einfache API-Integration
Cohere Embed v3
- 1024 Dimensionen
- Mehrsprachige Unterstützung
- Wettbewerbsfähige Preise
- Gut für semantische Suche
Open-Source: BGE-large
- 1024 Dimensionen
- Self-Hosting möglich (keine API-Kosten)
- Starke Leistung in Benchmarks
- Erfordert Rechenressourcen
Auswahlkriterien
- Aufgabenspezifische Leistung: Auf Ihren Daten testen
- Kosten: API vs. Self-Hosting
- Dimensionalität: Speicher vs. Leistungs-Trade-off
- Sprachunterstützung: Mehrsprachige Anforderungen
- Latenz: Embedding-Generierungsgeschwindigkeit
Dokumentenverarbeitung
Chunking-Strategien
Chunking mit fester Größe:
- Dokumente in feste Token-Anzahlen aufteilen (z.B. 512 Token)
- Einfach zu implementieren
- Kann mitten im Satz oder Konzept aufteilen
Semantisches Chunking:
- An natürlichen Grenzen aufteilen (Absätze, Abschnitte)
- Bewahrt semantische Kohärenz
- Variable Chunk-Größen
Überlappende Chunks:
- Überlappung zwischen Chunks einschließen (z.B. 50 Token)
- Verhindert Informationsverlust an Grenzen
- Erhöht Speicheranforderungen
Metadaten
Metadaten mit Vektoren speichern:
- Quelldokument-ID und -Titel
- Chunk-Position im Dokument
- Zeitstempel der letzten Aktualisierung
- Autor, Kategorie, Tags
- Zugriffskontroll-Metadaten
Ähnlichkeitssuche
Distanzmetriken
Kosinus-Ähnlichkeit:
- Am häufigsten für Text-Embeddings
- Misst Winkel zwischen Vektoren
- Bereich: -1 bis 1 (1 = identisch)
Euklidische Distanz:
- Geometrische Distanz zwischen Punkten
- Empfindlich für Vektormagnitude
- Verwenden, wenn Magnitude wichtig ist
Punktprodukt:
- Schnelle Berechnung
- Erfordert normalisierte Vektoren für Ähnlichkeit
- Üblich in Produktionssystemen
Abrufparameter
- Top-k: Anzahl der abzurufenden Ergebnisse (typisch: 3-10)
- Ähnlichkeitsschwellenwert: Minimaler Ähnlichkeitswert
- Filter: Metadaten-basierte Filterung vor oder nach Suche
- Re-Ranking: Nachbearbeitung der Ergebnisse für Relevanz
Code-Beispiel: Vollständige RAG-Pipeline mit Pinecone
import pinecone
from openai import OpenAI
from langchain.text_splitter import RecursiveCharacterTextSplitter
from typing import List, Dict
import numpy as np
class RAGPipeline:
"""Produktions-RAG-System mit Pinecone und OpenAI"""
def __init__(self, pinecone_api_key: str, openai_api_key: str, index_name: str = "knowledge-base"):
# Pinecone initialisieren
pinecone.init(api_key=pinecone_api_key)
self.index = pinecone.Index(index_name)
# OpenAI initialisieren
self.openai_client = OpenAI(api_key=openai_api_key)
# Text-Splitter initialisieren
self.text_splitter = RecursiveCharacterTextSplitter(
chunk_size=512,
chunk_overlap=50,
length_function=len
)
def embed_text(self, text: str) -> List[float]:
"""Embeddings mit OpenAI generieren"""
response = self.openai_client.embeddings.create(
model="text-embedding-3-large",
input=text
)
return response.data[0].embedding
def ingest_documents(self, documents: List[Dict[str, str]]):
"""Dokumente in Chunks aufteilen und in Pinecone indizieren"""
vectors_to_upsert = []
for doc_id, doc in enumerate(documents):
# Dokument in Chunks aufteilen
chunks = self.text_splitter.split_text(doc["content"])
for chunk_id, chunk in enumerate(chunks):
# Embedding generieren
embedding = self.embed_text(chunk)
# Metadaten vorbereiten
metadata = {
"text": chunk,
"source": doc.get("source", "unknown"),
"doc_id": doc_id,
"chunk_id": chunk_id,
"title": doc.get("title", "")
}
# Vektor-ID erstellen
vector_id = f"doc{doc_id}_chunk{chunk_id}"
vectors_to_upsert.append((
vector_id,
embedding,
metadata
))
# Batch-Upsert alle 100 Vektoren
if len(vectors_to_upsert) >= 100:
self.index.upsert(vectors=vectors_to_upsert)
vectors_to_upsert = []
# Verbleibende Vektoren hochladen
if vectors_to_upsert:
self.index.upsert(vectors=vectors_to_upsert)
def retrieve_context(self, query: str, top_k: int = 5, min_score: float = 0.7) -> List[Dict]:
"""Relevanten Kontext für Abfrage abrufen"""
# Abfrage-Embedding generieren
query_embedding = self.embed_text(query)
# Pinecone durchsuchen
results = self.index.query(
vector=query_embedding,
top_k=top_k,
include_metadata=True
)
# Nach Mindestwert filtern und Kontext extrahieren
context_chunks = []
for match in results.matches:
if match.score >= min_score:
context_chunks.append({
"text": match.metadata["text"],
"source": match.metadata["source"],
"score": match.score
})
return context_chunks
def generate_response(self, query: str, context_chunks: List[Dict]) -> str:
"""Antwort mit abgerufenem Kontext generieren"""
# Kontext assemblieren
context_text = "\n\n".join([
f"[Quelle: {chunk['source']}]\n{chunk['text']}"
for chunk in context_chunks
])
# Prompt erstellen
prompt = f"""Beantworte die Frage basierend auf dem bereitgestellten Kontext. Falls der Kontext nicht genügend Informationen enthält, sage dies.
Kontext:
{context_text}
Frage: {query}
Antwort:"""
# Antwort generieren
response = self.openai_client.chat.completions.create(
model="gpt-5",
messages=[{"role": "user", "content": prompt}],
temperature=0.3,
max_tokens=500
)
return response.choices[0].message.content
def query(self, question: str, top_k: int = 5) -> Dict[str, any]:
"""Vollständige RAG-Pipeline: Abrufen und Generieren"""
# Relevanten Kontext abrufen
context_chunks = self.retrieve_context(question, top_k=top_k)
if not context_chunks:
return {
"answer": "Ich konnte keine relevanten Informationen finden, um Ihre Frage zu beantworten.",
"sources": [],
"confidence": "low"
}
# Antwort generieren
answer = self.generate_response(question, context_chunks)
# Eindeutige Quellen extrahieren
sources = list(set([chunk["source"] for chunk in context_chunks]))
return {
"answer": answer,
"sources": sources,
"context_chunks": context_chunks,
"confidence": "high" if context_chunks[0]["score"] > 0.85 else "medium"
}
# Verwendungsbeispiel
rag = RAGPipeline(
pinecone_api_key="ihr-pinecone-schlüssel",
openai_api_key="ihr-openai-schlüssel"
)
# Dokumente einpflegen
documents = [
{
"title": "KI-Sicherheitsrichtlinien",
"content": "KI-Systeme sollten mit Sicherheit als Priorität entwickelt werden...",
"source": "sicherheitsdokument.pdf"
},
{
"title": "Produktions-Deployment",
"content": "Beim Bereitstellen von KI-Systemen in der Produktion...",
"source": "deployment_leitfaden.pdf"
}
]
rag.ingest_documents(documents)
# System abfragen
result = rag.query("Wie sollte ich KI-Systeme sicher bereitstellen?")
print(f"Antwort: {result['answer']}")
print(f"Quellen: {result['sources']}")
print(f"Vertrauen: {result['confidence']}")
Code-Beispiel: RAG mit pgvector (PostgreSQL)
import psycopg2
from psycopg2.extras import execute_values
from openai import OpenAI
from typing import List, Dict
import numpy as np
class PgVectorRAG:
"""RAG-System mit PostgreSQL und pgvector-Erweiterung"""
def __init__(self, db_config: Dict[str, str], openai_api_key: str):
# Mit PostgreSQL verbinden
self.conn = psycopg2.connect(**db_config)
self.cursor = self.conn.cursor()
# OpenAI initialisieren
self.openai_client = OpenAI(api_key=openai_api_key)
# Tabelle mit Vektor-Erweiterung erstellen
self._init_database()
def _init_database(self):
"""Datenbankschema mit pgvector initialisieren"""
self.cursor.execute("CREATE EXTENSION IF NOT EXISTS vector;")
self.cursor.execute("""
CREATE TABLE IF NOT EXISTS documents (
id SERIAL PRIMARY KEY,
content TEXT NOT NULL,
embedding vector(3072), -- text-embedding-3-large Dimensionen
metadata JSONB,
created_at TIMESTAMP DEFAULT NOW()
);
""")
# Vektorindex für schnelle Ähnlichkeitssuche erstellen
self.cursor.execute("""
CREATE INDEX IF NOT EXISTS documents_embedding_idx
ON documents USING ivfflat (embedding vector_cosine_ops)
WITH (lists = 100);
""")
self.conn.commit()
def embed_text(self, text: str) -> List[float]:
"""Embeddings generieren"""
response = self.openai_client.embeddings.create(
model="text-embedding-3-large",
input=text
)
return response.data[0].embedding
def add_document(self, content: str, metadata: Dict = None):
"""Dokument zur Vektordatenbank hinzufügen"""
embedding = self.embed_text(content)
self.cursor.execute(
"""
INSERT INTO documents (content, embedding, metadata)
VALUES (%s, %s, %s)
""",
(content, embedding, metadata or {})
)
self.conn.commit()
def semantic_search(self, query: str, top_k: int = 5, threshold: float = 0.7) -> List[Dict]:
"""Semantische Suche mit Kosinus-Ähnlichkeit durchführen"""
query_embedding = self.embed_text(query)
self.cursor.execute(
"""
SELECT
id,
content,
metadata,
1 - (embedding <=> %s::vector) as similarity
FROM documents
WHERE 1 - (embedding <=> %s::vector) > %s
ORDER BY embedding <=> %s::vector
LIMIT %s;
""",
(query_embedding, query_embedding, threshold, query_embedding, top_k)
)
results = []
for row in self.cursor.fetchall():
results.append({
"id": row[0],
"content": row[1],
"metadata": row[2],
"similarity": float(row[3])
})
return results
def generate_answer(self, query: str, top_k: int = 5) -> Dict:
"""Vollständiges RAG: Suchen und Generieren"""
# Relevante Dokumente abrufen
results = self.semantic_search(query, top_k=top_k)
if not results:
return {"answer": "Keine relevanten Informationen gefunden.", "sources": []}
# Kontext aufbauen
context = "\n\n".join([doc["content"] for doc in results])
# Antwort generieren
prompt = f"""Beantworte die Frage basierend auf folgendem Kontext.
Kontext:
{context}
Frage: {query}
Antwort:"""
response = self.openai_client.chat.completions.create(
model="gpt-5",
messages=[{"role": "user", "content": prompt}],
temperature=0.3
)
return {
"answer": response.choices[0].message.content,
"sources": results,
"num_sources": len(results)
}
def close(self):
"""Datenbankverbindung schließen"""
self.cursor.close()
self.conn.close()
# Verwendung
db_config = {
"host": "localhost",
"database": "rag_db",
"user": "postgres",
"password": "ihr-passwort"
}
rag = PgVectorRAG(db_config, openai_api_key="ihr-openai-schlüssel")
# Dokumente hinzufügen
rag.add_document(
"Vektordatenbanken ermöglichen effiziente Ähnlichkeitssuche für RAG-Systeme.",
metadata={"source": "rag_leitfaden.pdf", "page": 1}
)
# Abfrage
result = rag.generate_answer("Wofür werden Vektordatenbanken verwendet?")
print(result["answer"])
rag.close()
Produktionsarchitektur
Ingestion-Pipeline
- 1. Dokumenten-Warteschlange (z.B. SQS, RabbitMQ)
- 2. Verarbeitungs-Worker: Parsen, in Chunks aufteilen, bereinigen
- 3. Embedding-Service: Vektoren generieren (Batch für Effizienz)
- 4. Vektordatenbank-Schreibvorgang: Mit Metadaten speichern
- 5. Überwachung: Ingestion-Metriken verfolgen
Abfrage-Pipeline
- 1. Benutzerabfrage empfangen
- 2. Abfragevorverarbeitung (Normalisierung, Erweiterung)
- 3. Abfrage-Embedding generieren
- 4. Vektor-Ähnlichkeitssuche mit Filtern
- 5. Ergebnisse re-ranken (optional)
- 6. Kontext für LLM assemblieren
- 7. Antwort generieren
- 8. Nachbearbeiten und zurückgeben
Caching-Schichten
- Abfrage-Cache: Beliebte Abfrageergebnisse speichern
- Embedding-Cache: Embeddings für häufige Abfragen wiederverwenden
- LLM-Antwort-Cache: Vollständige Antworten cachen
- TTL basierend auf Aktualisierungsfrequenz des Inhalts
Optimierungsstrategien
Indizierung
- HNSW (Hierarchical Navigable Small World): Schnelle approximative Suche
- IVF (Inverted File Index): Raum für Effizienz partitionieren
- Trade-off: Geschwindigkeit vs. Genauigkeit
- Indexparameter basierend auf Datensatzgröße anpassen
Hybride Suche
Vektor- und Schlüsselwortsuche kombinieren:
- Vektorsuche für semantische Ähnlichkeit
- Schlüsselwortsuche für exakte Übereinstimmungen
- Ergebnisse mit gewichteter Bewertung kombinieren
- Verbessert Recall und Precision
Abfrageerweiterung
- Mehrere Abfragevariationen generieren
- Für jede Variation abrufen
- Ergebnisse deduplizieren und re-ranken
- Verbessert Recall für mehrdeutige Abfragen
Evaluierungsmetriken
Retrieval-Metriken
- Recall@k: Prozentsatz relevanter Docs in Top-k
- Precision@k: Prozentsatz abgerufener Docs, die relevant sind
- MRR (Mean Reciprocal Rank): Position des ersten relevanten Ergebnisses
- NDCG: Normalized Discounted Cumulative Gain
End-to-End-Metriken
- Antwortrelevanz: Behandelt LLM-Antwort die Abfrage?
- Treue: Ist Antwort im abgerufenen Kontext begründet?
- Kontextrelevanz: Ist abgerufener Kontext nützlich?
- Latenz: Gesamtzeit von Abfrage bis Antwort
Häufige Fallstricke
- Chunk-Größe zu groß: Verwässert Relevanz
- Chunk-Größe zu klein: Verliert Kontext
- Unzureichendes Top-k: Verpasst relevante Informationen
- Exzessives Top-k: Rauschen im Kontext
- Keine Metadaten-Filterung: Ruft irrelevanten aber ähnlichen Inhalt ab
- Kontextfenster-Limits ignorieren: Gekürzter Kontext
- Retrieval-Qualität nicht überwachen: Degradierende Leistung
Kostenverwaltung
- Embedding-Generierung in Batches zur Reduzierung von API-Aufrufen
- Embeddings für häufig zugeriffene Dokumente cachen
- Kleinere Embedding-Modelle verwenden bei akzeptabler Leistung
- Abfrageduplizierung implementieren
- Kosten pro Abfrage überwachen
- Vektordatenbank-Infrastruktur richtig dimensionieren
Sicherheitsüberlegungen
- Zugriffskontrolle: Ergebnisse basierend auf Benutzerberechtigungen filtern
- Datenisolation: Multi-Tenant-Vektortrennung
- Verschlüsselung: Vektoren im Ruhezustand und während der Übertragung schützen
- Audit-Protokollierung: Verfolgen, wer was abgerufen hat
- PII-Handhabung: Vorsichtig mit personenbezogenen Daten in Dokumenten
RAG-Systeme bieten eine praktische Möglichkeit, LLMs mit Domänenwissen zu erweitern. Ordnungsgemäße Implementierung von Vektordatenbanken, Embeddings und Retrieval-Strategien ist entscheidend für Produktionsqualität.