ChromaDB Einsteiger
Vektordatenbanken und ChromaDB für RAG-Systeme
Inhaltsverzeichnis
- Kurzüberblick: Warum ChromaDB Vektordatenbanken?
- Was sind Embeddings?
- ChromaDB Basics
- Embeddings mit OpenAI
- Dokumente indexieren
- Similarity Search
- LangChain-Integration
- Troubleshooting
- Best Practices
- Zusammenfassung
Kurzüberblick: Warum ChromaDB Vektordatenbanken?
Large Language Models haben trotz ihrer beeindruckenden Fähigkeiten klare Limitierungen:
- Wissens-Cutoff: Das Modell kennt nur Informationen bis zum Trainingszeitpunkt
- Kein Firmenwissen: Interne Dokumente, Richtlinien oder aktuelle Daten sind unbekannt
- Halluzination: Bei fehlendem Wissen werden plausibel klingende, aber falsche Antworten generiert
- Kontextlimit: Nicht alle relevanten Dokumente passen in den Prompt
[!WARNING] Ohne Retrieval riskant
Bei wissensintensiven Aufgaben ohne RAG steigt das Risiko für Halluzinationen und veraltete Antworten deutlich.
Vektordatenbanken lösen diese Probleme durch semantische Suche:
| Aspekt | Keyword-Suche | Semantische Suche |
|---|---|---|
| Funktionsweise | Exakte Wortübereinstimmung | Bedeutungsähnlichkeit |
| Beispiel | “Auto” findet nicht “Fahrzeug” | “Auto” findet auch “Fahrzeug”, “PKW”, “Wagen” |
| Synonyme | ❌ Nicht erkannt | ✅ Erkannt |
| Tippfehler | ❌ Keine Treffer | ✅ Oft trotzdem Treffer |
| Kontext | ❌ Ignoriert | ✅ Berücksichtigt |
Kernidee: Texte werden in Vektoren umgewandelt. Ähnliche Bedeutungen führen zu ähnlichen Vektoren. Die Suche arbeitet dann nicht primär mit exakten Wörtern, sondern mit Nähe im Vektorraum.
Gerade bei RAG-Systemen ist das oft der entscheidende Unterschied. Wer nur auf Keywords setzt, findet vor allem exakte Treffer. Semantische Suche wird dann interessant, wenn Formulierungen variieren, Synonyme vorkommen oder Fragen den Wortlaut der Quelle nicht direkt wiederholen.
[!NOTE] Merksatz
Semantische Suche findet Bedeutung, nicht nur exakte Wörter. Das ermöglicht Treffer für Synonyme, verwandte Konzepte und paraphrasierte Satzstrukturen — selbst wenn kein Wort übereinstimmt.
Was sind Embeddings?
Embeddings sind numerische Repräsentationen von Text, die semantische Bedeutung erfassen.
Konzept: Text → Vektor
"Der Hund spielt im Park" → [0.12, -0.45, 0.78, ..., 0.33] (1536 Dimensionen)
"Die Katze liegt im Garten" → [0.15, -0.42, 0.71, ..., 0.29] (ähnlich!)
"Quantenmechanik ist komplex" → [-0.89, 0.23, -0.11, ..., 0.67] (anders!)
Wichtige Eigenschaften:
- Jeder Text wird zu einem Vektor fester Länge (z.B. 1536 Dimensionen bei OpenAI)
- Semantisch ähnliche Texte haben ähnliche Vektoren
- Die “Ähnlichkeit” wird über mathematische Distanzmaße berechnet
Ähnlichkeit im Vektorraum
Die gebräuchlichsten Distanzmaße:
| Maß | Beschreibung | Wertebereich | ChromaDB Default |
|---|---|---|---|
| Cosine Similarity | Winkel zwischen Vektoren | -1 bis 1 | ✅ Ja |
| Euclidean Distance | Geometrischer Abstand | 0 bis ∞ | Nein |
| Dot Product | Skalarprodukt | -∞ bis ∞ | Nein |
Cosine Similarity ist der Standard, da sie unabhängig von der Vektorlänge funktioniert und nur die “Richtung” (= Bedeutung) vergleicht.
Vektorraum-Visualisierung (konzeptionell)
graph TB
subgraph "Semantischer Vektorraum (vereinfacht 2D)"
A["'Hund'<br/>[0.8, 0.3]"]
B["'Katze'<br/>[0.75, 0.35]"]
C["'Tier'<br/>[0.7, 0.4]"]
D["'Auto'<br/>[0.2, 0.9]"]
E["'Fahrzeug'<br/>[0.25, 0.85]"]
F["'Quantenmechanik'<br/>[-0.5, -0.8]"]
end
A -.ähnlich.- B
B -.ähnlich.- C
A -.ähnlich.- C
D -.ähnlich.- E
style A fill:#e1f5ff
style B fill:#e1f5ff
style C fill:#e1f5ff
style D fill:#ffe6cc
style E fill:#ffe6cc
style F fill:#f8cecc
Hinweis: Semantisch verwandte Konzepte (“Hund”, “Katze”, “Tier”) bilden Cluster im Vektorraum, während unverwandte Konzepte (“Quantenmechanik”) weiter entfernt liegen.
Visualisierung
Für ein intuitives Verständnis von Embeddings:
Embedding Projector (Google):
https://projector.tensorflow.org/?hl=de
Damit lassen sich hochdimensionale Vektoren auf 2D oder 3D projizieren und anschaulich erkunden. Sichtbar wird vor allem, dass semantisch verwandte Begriffe häufig Cluster bilden, auch wenn ihre Wortoberfläche voneinander abweicht.
Beispiel: Embedding erzeugen
from langchain_openai import OpenAIEmbeddings
# Embedding-Modell initialisieren
embedding_model = OpenAIEmbeddings(model="text-embedding-3-small")
# Einzelnen Text embedden
text = "KI-Agenten können autonom Aufgaben erledigen."
vector = embedding_model.embed_query(text)
print(f"Dimensionen: {len(vector)}") # 1536
print(f"Erste 5 Werte: {vector[:5]}")
ChromaDB Basics
ChromaDB ist eine leichtgewichtige, Open-Source-Vektordatenbank, die sich ideal für Entwicklung und Prototyping eignet.
Im Kurs ist ChromaDB vor allem deshalb nützlich, weil sich damit die Mechanik von Retrieval-Systemen gut nachvollziehen lässt. Für kleine bis mittlere Datenbestände funktioniert das meist unkompliziert. Die eigentlichen Qualitätsfragen entstehen nicht bei der Datenbank selbst, sondern bei Embeddings, Chunking und Retrieval-Parametern.
Installation
Standard-Installation:
!pip install chromadb
Google Colab – SQLite-Patch (WICHTIG!):
In Colab kann es zu SQLite-Versionskonflikten kommen. Folgender Patch löst das Problem:
# NUR in Google Colab erforderlich!
!pip install pysqlite3-binary
import sys
__import__('pysqlite3')
sys.modules['sqlite3'] = sys.modules.pop('pysqlite3')
Dieser Patch muss vor dem Import von ChromaDB ausgeführt werden.
[!DANGER] Reihenfolge beachten
Wird ChromaDB vor dem Patch importiert, schlägt die Initialisierung in Colab mit einem SQLite-Fehler fehl — und ChromaDB-Daten gehen ggf. unbemerkt verloren. Der Patch muss zwingend vor jedemimport chromadbstehen.
Client erstellen
import chromadb
# In-Memory Client (Daten gehen bei Neustart verloren)
client = chromadb.Client()
# Persistenter Client (Daten werden gespeichert)
client = chromadb.PersistentClient(path="./chroma_db")
| Modus | Verwendung | Vorteil |
|---|---|---|
| In-Memory | Entwicklung, Tests | Schnell, kein Aufräumen nötig |
| Persistent | Produktion, große Datenmengen | Daten überleben Neustart |
Collections
Eine Collection ist vergleichbar mit einer Tabelle in relationalen Datenbanken.
# Collection erstellen
collection = client.create_collection(
name="meine_dokumente",
metadata={"description": "Firmendokumente für RAG"}
)
# Oder: Collection holen/erstellen (idempotent)
collection = client.get_or_create_collection(name="meine_dokumente")
# Bestehende Collection holen
collection = client.get_collection(name="meine_dokumente")
# Collection löschen
client.delete_collection(name="meine_dokumente")
# Alle Collections auflisten
print(client.list_collections())
Best Practice: get_or_create_collection() verwenden, um Fehler bei wiederholter Ausführung zu vermeiden.
Gerade in Notebooks zahlt sich das sofort aus. Viele Probleme entstehen nicht durch ChromaDB selbst, sondern durch wiederholt ausgeführte Setup-Zellen, inkonsistente Collection-Namen oder wechselnde Embedding-Konfigurationen.
[!SUCCESS] Idempotenz im Alltag
get_or_create_collection()macht Notebooks robuster bei mehrfacher Ausführung und reduziert Setup-Fehler.
Idempotenz = Operation kann mehrfach hintereinander ausgeführt werden kann, ohne dass sich das Ergebnis verändert
Dokumente hinzufügen
collection.add(
documents=["Erster Text", "Zweiter Text", "Dritter Text"],
ids=["doc1", "doc2", "doc3"],
metadatas=[
{"source": "handbuch.pdf", "seite": 1},
{"source": "handbuch.pdf", "seite": 2},
{"source": "richtlinie.pdf", "seite": 1}
]
)
Parameter:
| Parameter | Pflicht | Beschreibung |
|---|---|---|
documents | ✅ | Liste der Texte |
ids | ✅ | Eindeutige IDs (Strings) |
metadatas | ❌ | Zusätzliche Informationen pro Dokument |
embeddings | ❌ | Vorgefertigte Vektoren (sonst automatisch) |
[!TIP] Embedding-Konsistenz
Ohne expliziteembeddingsverwendet ChromaDB ein internes Embedding-Modell. Für Konsistenz mit LangChain immer dasselbe Embedding-Modell für Indexierung und Query verwenden — ein Wechsel des Modells führt zu Dimensionsmismatch und fehlerhaften Suchergebnissen.
Embeddings mit OpenAI
Für produktive RAG-Systeme werden typischerweise OpenAI-Embeddings verwendet.
Verfügbare Modelle
| Modell | Dimensionen | Kosten | Empfehlung |
|---|---|---|---|
text-embedding-3-small | 1536 | Günstig | ✅ Standard für Kurs |
text-embedding-3-large | 3072 | Mittel | Höhere Qualität |
text-embedding-ada-002 | 1536 | Günstig | Legacy, nicht empfohlen |
Embeddings erzeugen
from langchain_openai import OpenAIEmbeddings
# Modell initialisieren
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
# Einzelner Text (für Queries)
query_vector = embeddings.embed_query("Was ist ein KI-Agent?")
# Mehrere Texte (für Dokumente)
texts = ["Text 1", "Text 2", "Text 3"]
doc_vectors = embeddings.embed_documents(texts)
print(f"Query-Vektor: {len(query_vector)} Dimensionen")
print(f"Dokument-Vektoren: {len(doc_vectors)} Stück")
Alternative: Sentence Transformers (lokal)
Für Szenarien ohne API-Zugriff:
from langchain_huggingface import HuggingFaceEmbeddings
# Lokales Modell (kein API-Key nötig)
embeddings = HuggingFaceEmbeddings(
model_name="sentence-transformers/all-MiniLM-L6-v2"
)
Vorteile: Kostenlos, offline nutzbar, DSGVO-konform
Nachteile: Geringere Qualität als OpenAI, benötigt mehr Speicher
Dokumente indexieren
Bevor Dokumente durchsucht werden können, müssen sie in Chunks aufgeteilt und indexiert werden.
Warum Chunking?
- Kontextlimit: LLMs haben begrenzte Eingabelänge
- Präzision: Kleinere Chunks ermöglichen gezieltere Treffer
- Relevanz: Nur relevante Teile werden dem LLM übergeben
Chunking-Strategien
[!TIP] Praktischer Startwert
Für viele deutschsprachige Wissensdokumente funktionierenchunk_size=500undchunk_overlap=100als stabiler Ausgangspunkt. Für FAQ und Kurztexte eher 200–300, für Rechtsdokumente 800–1000 (vollständige Paragraphen). Kurs-Referenz: Tabelle in Kapitel 9.1.
from langchain_text_splitters import RecursiveCharacterTextSplitter
# Standard-Splitter
splitter = RecursiveCharacterTextSplitter(
chunk_size=500, # Maximale Chunk-Größe in Zeichen
chunk_overlap=100, # Überlappung zwischen Chunks
separators=["\n\n", "\n", ". ", " ", ""] # Trennzeichen-Hierarchie
)
text = """Langer Dokumenttext hier...
Mit mehreren Absätzen...
Und viel Inhalt..."""
chunks = splitter.split_text(text)
print(f"Anzahl Chunks: {len(chunks)}")
Parameter erklärt:
| Parameter | Beschreibung | Empfehlung |
|---|---|---|
chunk_size | Maximale Zeichen pro Chunk | 500–1000 |
chunk_overlap | Überlappung (verhindert Informationsverlust) | 10–20% von chunk_size |
separators | Bevorzugte Trennstellen | Standard belassen |
Overlap visualisiert
flowchart TB
subgraph "Ohne Overlap - Informationsverlust"
DOC1[Dokument: AAAA BBBB CCCC DDDD]
C1[Chunk 1: AAAA]
C2[Chunk 2: BBBB]
C3[Chunk 3: CCCC]
C4[Chunk 4: DDDD]
DOC1 --> C1
DOC1 --> C2
DOC1 --> C3
DOC1 --> C4
WARN1[⚠️ Sätze werden zerrissen!]
end
subgraph "Mit 25% Overlap - Kontext erhalten"
DOC2[Dokument: AAAA BBBB CCCC DDDD]
O1[Chunk 1: AAAA BB]
O2[Chunk 2: BB CCCC CC]
O3[Chunk 3: CC DDDD]
DOC2 --> O1
DOC2 --> O2
DOC2 --> O3
OK1[✅ Kontext bleibt erhalten!]
end
style WARN1 fill:#f8cecc
style OK1 fill:#d5e8d4
ASCII-Darstellung:
Dokument: [AAAA|BBBB|CCCC|DDDD]
Ohne Overlap:
Chunk 1: [AAAA]
Chunk 2: [BBBB]
Chunk 3: [CCCC]
→ Sätze an Grenzen werden zerrissen!
Mit Overlap (25%):
Chunk 1: [AAAA|BB]
Chunk 2: [BB|CCCC]
Chunk 3: [CC|DDDD]
→ Kontext bleibt erhalten!
Vollständiger Indexierungs-Workflow
flowchart LR
START([Dokument<br/>dokument.txt])
LOAD[Document Loader<br/>TextLoader]
SPLIT[Text Splitter<br/>RecursiveCharacterTextSplitter]
EMBED[Embedding Model<br/>OpenAIEmbeddings]
STORE[Vector Store<br/>ChromaDB]
END([Indexed & Ready<br/>for Search])
START --> LOAD
LOAD -->|documents| SPLIT
SPLIT -->|chunks<br/>chunk_size=500<br/>overlap=100| EMBED
EMBED -->|vectors<br/>1536 dimensions| STORE
STORE --> END
style LOAD fill:#e1f5ff
style SPLIT fill:#ffe6cc
style EMBED fill:#d5e8d4
style STORE fill:#dae8fc
from langchain_community.document_loaders import TextLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
## 1. Dokument laden
loader = TextLoader("dokument.txt", encoding="utf-8")
documents = loader.load()
## 2. In Chunks aufteilen
splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=100
)
chunks = splitter.split_documents(documents)
print(f"Dokument in {len(chunks)} Chunks aufgeteilt")
## 3. Embeddings erstellen und in ChromaDB speichern
embedding_model = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = Chroma.from_documents(
documents=chunks,
embedding=embedding_model,
collection_name="meine_dokumente",
persist_directory="./chroma_db" # Optional: Persistenz
)
print("✅ Indexierung abgeschlossen!")
Batch-Indexierung (große Datenmengen)
[!WARNING] Hohe API-Kosten bei großen Datenmengen
10.000 Dokumente × API-Calls für Embedding-Erzeugung können in einer Sitzung erhebliche Kosten verursachen. Batch-Größe und Gesamtvolumen vorher abschätzen, Fortschrittsanzeige (tqdm) verwenden.
Bei vielen Dokumenten sollte in Batches indexiert werden:
from tqdm import tqdm
batch_size = 100
all_chunks = [...] # Große Liste von Chunks
for i in tqdm(range(0, len(all_chunks), batch_size)):
batch = all_chunks[i:i+batch_size]
vectorstore.add_documents(batch)
Similarity Search
Die Suche in der Vektordatenbank findet die semantisch ähnlichsten Dokumente.
Similarity Search Workflow
sequenceDiagram
autonumber
participant User
participant App
participant Embeddings as Embedding Model
participant ChromaDB as Vector Store
User->>App: Query: "Was sind KI-Agenten?"
App->>Embeddings: embed_query(query)
Embeddings-->>App: query_vector [1536 dims]
App->>ChromaDB: similarity_search(query_vector, k=3)
ChromaDB->>ChromaDB: Cosine Similarity<br/>mit allen Vektoren
ChromaDB-->>App: Top-3 Documents<br/>with scores
App-->>User: Relevante Dokumente
Note over ChromaDB: Nur Vektoren vergleichen,<br/>kein Text-Matching
Grundlegende Suche
# Einfache Suche
results = vectorstore.similarity_search(
query="Was sind die Vorteile von KI-Agenten?",
k=3 # Anzahl der Ergebnisse
)
for i, doc in enumerate(results, 1):
print(f"\n--- Treffer {i} ---")
print(f"Inhalt: {doc.page_content[:200]}...")
print(f"Metadaten: {doc.metadata}")
Suche mit Scores
# Suche mit Ähnlichkeits-Scores
results_with_scores = vectorstore.similarity_search_with_score(
query="Was sind die Vorteile von KI-Agenten?",
k=3
)
for doc, score in results_with_scores:
print(f"Score: {score:.4f} | {doc.page_content[:100]}...")
Score-Interpretation (Cosine Distance):
- 0.0 = Perfekte Übereinstimmung
- < 0.3 = Sehr relevant
- 0.3–0.5 = Relevant
- > 0.5 = Weniger relevant
Metadaten-Filtering
# Nur Dokumente aus bestimmter Quelle
results = vectorstore.similarity_search(
query="Sicherheitsrichtlinien",
k=5,
filter={"source": "sicherheit.pdf"}
)
# Komplexere Filter
results = vectorstore.similarity_search(
query="Umsatzzahlen",
k=5,
filter={
"$and": [
{"jahr": {"$gte": 2023}},
{"abteilung": "Vertrieb"}
]
}
)
Verfügbare Filter-Operatoren:
| Operator | Beschreibung | Beispiel |
|---|---|---|
$eq | Gleich | {"status": {"$eq": "aktiv"}} |
$ne | Ungleich | {"status": {"$ne": "archiviert"}} |
$gt, $gte | Größer (gleich) | {"jahr": {"$gte": 2023}} |
$lt, $lte | Kleiner (gleich) | {"seite": {"$lt": 10}} |
$in | In Liste | {"typ": {"$in": ["pdf", "docx"]}} |
$and, $or | Logische Verknüpfung | Siehe Beispiel oben |
Retriever erstellen
Für die Integration in LangChain-Chains wird ein Retriever benötigt:
# Standard-Retriever
retriever = vectorstore.as_retriever(
search_kwargs={"k": 3}
)
# Retriever mit Filter
retriever = vectorstore.as_retriever(
search_kwargs={
"k": 5,
"filter": {"source": "handbuch.pdf"}
}
)
# Retriever mit Score-Threshold
retriever = vectorstore.as_retriever(
search_type="similarity_score_threshold",
search_kwargs={
"score_threshold": 0.5, # Nur relevante Treffer
"k": 10
}
)
LangChain-Integration
ChromaDB integriert sich nahtlos in LangChain für RAG-Systeme.
Vectorstore erstellen (Zusammenfassung)
from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings
# Variante 1: Aus Texten
vectorstore = Chroma.from_texts(
texts=["Text 1", "Text 2", "Text 3"],
embedding=OpenAIEmbeddings(model="text-embedding-3-small"),
collection_name="demo",
persist_directory="./chroma_db"
)
# Variante 2: Aus Documents (mit Metadaten)
vectorstore = Chroma.from_documents(
documents=chunks, # Liste von Document-Objekten
embedding=OpenAIEmbeddings(model="text-embedding-3-small"),
collection_name="demo"
)
# Variante 3: Bestehende Collection laden
vectorstore = Chroma(
collection_name="demo",
embedding_function=OpenAIEmbeddings(model="text-embedding-3-small"),
persist_directory="./chroma_db"
)
RAG-Chain mit LCEL
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
from langchain.chat_models import init_chat_model
# Komponenten vorbereiten
llm = init_chat_model("openai:gpt-4o-mini", temperature=0.0)
retriever = vectorstore.as_retriever(search_kwargs={"k": 3})
# Hilfsfunktion: Dokumente formatieren
def format_docs(docs):
return "\n\n".join(doc.page_content for doc in docs)
# RAG-Prompt
rag_prompt = ChatPromptTemplate.from_template(
"""Beantworte die Frage basierend auf dem folgenden Kontext.
Wenn die Antwort nicht im Kontext steht, sage ehrlich, dass keine Information vorliegt.
Kontext:
{context}
Frage: {question}
Antwort:"""
)
# LCEL Chain
rag_chain = (
{
"context": retriever | format_docs,
"question": RunnablePassthrough()
}
| rag_prompt
| llm
| StrOutputParser()
)
# Aufruf
antwort = rag_chain.invoke("Was sind die wichtigsten Sicherheitsrichtlinien?")
print(antwort)
RAG als Agent-Tool
from langchain_core.tools import tool
@tool
def firmenwissen_suchen(frage: str) -> str:
"""🔍 FIRMENWISSEN – Durchsucht interne Dokumente nach relevanten Informationen.
Verwende dieses Tool für Fragen zu:
- Unternehmensrichtlinien
- Internen Prozessen
- Produktinformationen
Args:
frage: Die Suchanfrage in natürlicher Sprache
Returns:
Relevante Informationen aus den Firmendokumenten
"""
try:
antwort = rag_chain.invoke(frage)
return antwort
except Exception as e:
return f"Fehler bei der Suche: {str(e)}"
Für die vollständige RAG-Chain-Implementierung siehe Einsteiger_LangChain.md, Kapitel 11.
Troubleshooting
[!TIP] Diagnose-Reihenfolge
Zuerst Embedding-Modell, Chunking undk-Wert prüfen. Erst danach lohnt ein Umbau der gesamten Pipeline.
Häufige Probleme und deren Lösungen:
SQLite-Versionsfehler (Google Colab)
Fehlermeldung:
sqlite3.OperationalError: database is locked
RuntimeError: Your system has an unsupported version of sqlite3
Lösung:
# Am ANFANG des Notebooks ausführen (vor chromadb import!)
!pip install pysqlite3-binary
import sys
__import__('pysqlite3')
sys.modules['sqlite3'] = sys.modules.pop('pysqlite3')
# Erst JETZT chromadb importieren
import chromadb
Collection existiert bereits
Fehlermeldung:
ValueError: Collection meine_dokumente already exists
Lösung:
# Option A: get_or_create verwenden (empfohlen)
collection = client.get_or_create_collection(name="meine_dokumente")
# Option B: Bestehende Collection löschen
client.delete_collection(name="meine_dokumente")
collection = client.create_collection(name="meine_dokumente")
Keine Ergebnisse bei Suche
Mögliche Ursachen und Lösungen:
| Ursache | Diagnose | Lösung |
|---|---|---|
| Collection leer | collection.count() prüfen | Dokumente hinzufügen |
| Falsches Embedding-Modell | Dimensionen vergleichen | Gleiches Modell für Index und Query |
| Query zu spezifisch | Mit breiterem Begriff testen | Query umformulieren |
| k zu klein | k erhöhen | search_kwargs={"k": 10} |
Diagnose-Code:
# Collection-Status prüfen
print(f"Anzahl Dokumente: {vectorstore._collection.count()}")
# Test-Query ohne Filter
results = vectorstore.similarity_search("test", k=1)
print(f"Test-Ergebnis: {results}")
Langsame Queries
Optimierungen:
## 1. Batch-Größe bei Indexierung anpassen
vectorstore.add_documents(chunks, batch_size=100)
## 2. Persistenten Client verwenden
client = chromadb.PersistentClient(path="./chroma_db")
## 3. k reduzieren
retriever = vectorstore.as_retriever(search_kwargs={"k": 3})
## 4. Metadaten-Filter nutzen (reduziert Suchraum)
results = vectorstore.similarity_search(
query="Frage",
k=5,
filter={"kategorie": "relevant"}
)
Speicherprobleme
Bei großen Datenmengen:
# In-Memory vermeiden
client = chromadb.PersistentClient(path="./chroma_db")
# Alte Collections aufräumen
for coll in client.list_collections():
if "test" in coll.name:
client.delete_collection(coll.name)
Best Practices
Chunk-Size Empfehlungen
| Dokumenttyp | chunk_size | chunk_overlap | Begründung |
|---|---|---|---|
| FAQ / kurze Texte | 200–300 | 50 | Präzise Antworten |
| Handbücher | 500–800 | 100–150 | Kontext erhalten |
| Rechtsdokumente | 800–1000 | 200 | Vollständige Paragraphen |
| Code-Dokumentation | 300–500 | 100 | Funktionen zusammenhalten |
Embedding-Modell-Auswahl
| Kriterium | OpenAI | Sentence Transformers |
|---|---|---|
| Qualität | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
| Kosten | ~$0.02 / 1M Tokens | Kostenlos |
| Geschwindigkeit | Mittel (API) | Schnell (lokal) |
| Offline | ❌ | ✅ |
| DSGVO | ⚠️ Prüfen | ✅ |
Empfehlung für Kurs: text-embedding-3-small (beste Balance)
Persistenz-Strategie
# Entwicklung: In-Memory (schnell, sauber)
client = chromadb.Client()
# Produktion: Persistent
client = chromadb.PersistentClient(path="./chroma_db")
# Wichtig: Pfad in .gitignore aufnehmen!
# .gitignore:
# chroma_db/
Metadaten sinnvoll nutzen
# Gute Metadaten ermöglichen präzises Filtering
collection.add(
documents=[chunk.page_content],
ids=[f"doc_{i}"],
metadatas=[{
"source": "handbuch_v2.pdf",
"seite": 42,
"kapitel": "Sicherheit",
"version": "2.0",
"erstellt": "2024-01-15",
"sprache": "de"
}]
)
# Spätere Suche mit Filter
results = vectorstore.similarity_search(
query="Passwortrichtlinien",
filter={
"$and": [
{"kapitel": "Sicherheit"},
{"version": "2.0"}
]
}
)
Qualitätssicherung
# Test-Queries vor Produktion
test_queries = [
"Was ist die Passwort-Policy?",
"Wie beantrage ich Urlaub?",
"Wer ist mein Ansprechpartner für IT-Probleme?"
]
for query in test_queries:
results = vectorstore.similarity_search_with_score(query, k=3)
print(f"\nQuery: {query}")
for doc, score in results:
print(f" Score: {score:.3f} | {doc.page_content[:50]}...")
Zusammenfassung
Kernkonzepte
| Konzept | Beschreibung |
|---|---|
| Embedding | Numerische Repräsentation von Text (Vektor) |
| Vektordatenbank | Speichert Vektoren und ermöglicht Ähnlichkeitssuche |
| Similarity Search | Findet semantisch ähnliche Dokumente |
| Chunking | Aufteilung großer Dokumente in kleinere Teile |
| RAG | Retrieval-Augmented Generation – LLM + externes Wissen |
Typischer RAG-Workflow
flowchart TB
LOAD[1. Dokumente laden<br/>TextLoader, PyPDFLoader]
CHUNK[2. Chunking<br/>RecursiveCharacterTextSplitter<br/>chunk_size=500, overlap=100]
EMBED[3. Embeddings erzeugen<br/>OpenAIEmbeddings<br/>text-embedding-3-small]
STORE[4. In ChromaDB speichern<br/>Chroma.from_documents]
RETRIEVE[5. Retriever erstellen<br/>vectorstore.as_retriever]
CHAIN[6. RAG-Chain bauen<br/>LCEL Pipeline]
AGENT[7. Agent mit RAG-Tool ausstatten<br/>create_agent + @tool]
LOAD --> CHUNK
CHUNK --> EMBED
EMBED --> STORE
STORE --> RETRIEVE
RETRIEVE --> CHAIN
CHAIN --> AGENT
style LOAD fill:#e1f5ff
style CHUNK fill:#ffe6cc
style EMBED fill:#d5e8d4
style STORE fill:#dae8fc
style RETRIEVE fill:#e1f5ff
style CHAIN fill:#ffe6cc
style AGENT fill:#d5e8d4
Text-Version:
1. Dokumente laden
↓
2. Chunking (RecursiveCharacterTextSplitter)
↓
3. Embeddings erzeugen (OpenAIEmbeddings)
↓
4. In ChromaDB speichern
↓
5. Retriever erstellen
↓
6. RAG-Chain bauen (LCEL)
↓
7. Agent mit RAG-Tool ausstatten
Quick Reference
# Setup (Colab)
!pip install chromadb pysqlite3-binary
import sys; __import__('pysqlite3'); sys.modules['sqlite3'] = sys.modules.pop('pysqlite3')
# Imports
from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter
# Indexieren
splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=100)
chunks = splitter.split_documents(documents)
vectorstore = Chroma.from_documents(chunks, OpenAIEmbeddings())
# Suchen
retriever = vectorstore.as_retriever(search_kwargs={"k": 3})
results = retriever.invoke("Meine Frage")
💡 Tipp: Für die vollständige RAG-Chain-Implementierung siehe Einsteiger_LangChain.md, Kapitel 11!
🔗 Weiterführend:
Version: 1.0
Stand: November 2025
Kurs: Generative KI. Verstehen. Anwenden. Gestalten.