CommunityCodierung & Entwicklunggithub.com

RAG-Implementierungsmuster

Umfassender Leitfaden zur Implementierung von RAG-Systemen, einschließlich Vektordatenbank-Auswahl, Chunking-Strategien, Embedding-Modellen und Retrieval-Optimierung. Verwenden Sie dies beim Aufbau von RAG-Systemen, bei der Implementierung semantischer Suche, zur Optimierung der Retrieval-Qualität oder zur Fehlerbehebung von RAG-Performance-Problemen.

Was ist RAG-Implementierungsmuster?

Dieser Skill bietet einen detaillierten, praktischen Leitfaden zur Implementierung von Retrieval-Augmented Generation (RAG)-Systemen, der sich an Entwickler richtet, die semantische Such- und Dokumentenabruf-Pipelines aufbauen. Er behandelt die wichtigsten Designentscheidungen: die Auswahl der richtigen Vektordatenbank (Qdrant, Pinecone, Chroma, Weaviate, Milvus) auf der Grundlage von Faktoren wie Bereitstellungsmodell, Leistung und Kosten. Der Skill vergleicht verschiedene Chunking-Strategien (feste Größe, semantisch, hierarchisch, gleitendes Fenster) mit Python-Codebeispielen und erklärt deren Kompromisse in Bezug auf Qualität, Geschwindigkeit und Kontexterhaltung. Er enthält Anleitungen zur Auswahl von Embedding-Modellen (OpenAI, Sentence Transformers, Cohere) und zeigt, wie Embeddings sowohl über API als auch lokal generiert werden. Der Abschnitt zur Retrieval-Optimierung demonstriert fortgeschrittene Techniken wie Hybrid Search (dicht + dünnbesetzt), Query Expansion, Reranking und Metadaten-Filterung, die alle für die reale Produktionsnutzung konzipiert sind. Schließlich behandelt der Skill die Optimierung des abgerufenen Kontexts und die Zitierungsverfolgung, um die LLM-Kontextfenster effektiv zu verwalten. Die Anleitung ist plattformneutral, konzentriert sich aber auf die beliebten Frameworks LangChain, LlamaIndex und Haystack für die Integration.

Funktioniert mit~Claude Code✓Codex CLI~Cursor
npx skills add https://github.com/applied-artificial-intelligence/claude-code-toolkit/tree/main/skills/rag-implementation

Installed? Explore more Codierung & Entwicklung skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

Was macht RAG-Implementierungsmuster?

Umfassender Leitfaden zur Implementierung von Retrieval-Augmented Generation (RAG)-Systemen, einschließlich Vektordatenbank-Auswahl, Chunking-Strategien, Embedding-Modellen, Retrieval-Optimierung und Produktionsbereitstellungsmustern.


Kurzreferenz

Wann dieser Skill verwendet werden sollte:

  • Aufbau von RAG-/semantischen Suchsystemen
  • Implementierung von Dokumentenabruf-Pipelines
  • Optimierung der Vektordatenbank-Leistung
  • Behebung von Problemen mit der Retrieval-Qualität
  • Auswahl zwischen Vektordatenbank-Optionen
  • Entwerfen von Chunking-Strategien
  • Implementierung von Hybrid-Suche

Abgedeckte Technologien:

  • Vektor-DBs: Qdrant, Pinecone, Chroma, Weaviate, Milvus
  • Embeddings: OpenAI, Sentence Transformers, Cohere
  • Frameworks: LangChain, LlamaIndex, Haystack

Teil 1: Vektordatenbank-Auswahl

Datenbank-Vergleichsmatrix

DatenbankAm besten geeignet fürBereitstellungPerformanceKosten
QdrantSelbst gehostet, ProduktionDocker/K8sExzellent (Rust)Kostenlos (selbst gehostet)
PineconeVerwaltet, schnelles PrototypingCloudExzellentPay-per-use
ChromaLokale Entwicklung, eingebettetIn-ProzessGut (Python)Kostenlos
WeaviateKomplexe Schemata, GraphQLDocker/CloudExzellent (Go)Kostenlos + Cloud
MilvusGroß angelegt, verteiltK8sExzellent (C++)Kostenlos (selbst gehostet)

Qdrant-Setup (Empfohlen für die Produktion)

from qdrant_client import QdrantClient
from qdrant_client.models import Distance, VectorParams, PointStruct

# Client initialisieren (lokal oder Cloud)
client = QdrantClient(url="http://localhost:6333")  # oder Cloud-URL

# Sammlung erstellen
client.create_collection(
    collection_name="documents",
    vectors_config=VectorParams(
        size=1536,  # OpenAI text-embedding-3-small Dimension
        distance=Distance.COSINE  # oder DOT, EUCLID
    )
)

# Vektoren mit Nutzdaten einfügen
client.upsert(
    collection_name="documents",
    points=[
        PointStruct(
            id=1,
            vector=[0.1, 0.2, ...],  # 1536 Dimensionen
            payload={
                "text": "Dokumenteninhalt",
                "source": "doc.pdf",
                "page": 1,
                "metadata": {...}
            }
        )
    ]
)

# Suche
results = client.search(
    collection_name="documents",
    query_vector=[0.1, 0.2, ...],
    limit=5,
    score_threshold=0.7  # Minimale Ähnlichkeit
)

Pinecone-Setup (Verwalteter Dienst)

from pinecone import Pinecone, ServerlessSpec

# Initialisieren
pc = Pinecone(api_key="your-key")

# Index erstellen
pc.create_index(
    name="documents",
    dimension=1536,
    metric="cosine",
    spec=ServerlessSpec(cloud="aws", region="us-east-1")
)

# Index abrufen
index = pc.Index("documents")

# Vektoren einfügen
index.upsert(vectors=[
    ("doc1", [0.1, 0.2, ...], {"text": "...", "source": "..."})
])

# Abfrage
results = index.query(
    vector=[0.1, 0.2, ...],
    top_k=5,
    include_metadata=True
)

Teil 2: Chunking-Strategien

Strategie 1: Chunking mit fester Größe (Einfach, Schnell)

def fixed_size_chunking(text: str, chunk_size: int = 512, overlap: int = 50) -> list[str]:
    """
    Text in Blöcke fester Größe mit Überlappung aufteilen.

    Vorteile: Einfach, vorhersagbare Blockgrößen
    Nachteile: Kann Satzmitten aufbrechen, schlechte semantische Grenzen
    """
    words = text.split()
    chunks = []

    for i in range(0, len(words), chunk_size - overlap):
        chunk = ' '.join(words[i:i + chunk_size])
        chunks.append(chunk)

    return chunks

# Verwendung
chunks = fixed_size_chunking(document, chunk_size=512, overlap=50)

Wann verwenden:

  • Einfache Dokumente (Protokolle, Transkripte)
  • Prototyping/MVP
  • Gleichbleibende Token-Budgets erforderlich

Strategie 2: Semantisches Chunking (Bessere Qualität)

from langchain.text_splitter import RecursiveCharacterTextSplitter

def semantic_chunking(text: str, chunk_size: int = 1000, overlap: int = 200) -> list[str]:
    """
    Aufteilung an semantischen Grenzen (Absätze, Sätze).

    Vorteile: Bewahrt Bedeutung, bessere Retrieval-Qualität
    Nachteile: Unterschiedliche Blockgrößen, langsamerer Verarbeitung
    """
    splitter = RecursiveCharacterTextSplitter(
        chunk_size=chunk_size,
        chunk_overlap=overlap,
        separators=["\n\n", "\n", ". ", " ", ""],  # Prioritätsreihenfolge
        length_function=len
    )

    return splitter.split_text(text)

# Verwendung
chunks = semantic_chunking(document, chunk_size=1000, overlap=200)

Wann verwenden:

  • Lange Dokumente (Artikel, Bücher, Berichte)
  • Qualität > Geschwindigkeit
  • Natürlichsprachige Inhalte

Strategie 3: Hierarchisches Chunking (Am besten für strukturierte Dokumente)

def hierarchical_chunking(document: dict) -> list[dict]:
    """
    Blockbildung basierend auf Dokumentenstruktur (Abschnitte, Unterabschnitte).

    Vorteile: Bewahrt Hierarchie, ermöglicht Eltern-Kind-Retrieval
    Nachteile: Erfordert strukturierte Eingabe, komplexer
    """
    chunks = []

    for section in document['sections']:
        # Eltern-Block (Abschnittszusammenfassung)
        chunks.append({
            'text': section['title'] + '\n' + section['summary'],
            'type': 'parent',
            'section_id': section['id']
        })

        # Kinder-Blöcke (Absätze)
        for para in section['paragraphs']:
            chunks.append({
                'text': para,
                'type': 'child',
                'parent_id': section['id']
            })

    return chunks

Wann verwenden:

  • Technische Dokumentation
  • Bücher mit Inhaltsverzeichnis
  • Juristische Dokumente
  • Notwendigkeit, Kontexthierarchie zu bewahren

Strategie 4: Gleitendes Fenster (Maximale Kontexterhaltung)

def sliding_window_chunking(text: str, window_size: int = 512, stride: int = 256) -> list[str]:
    """
    Überlappende Fenster für maximalen Kontext.

    Vorteile: Kein Informationsverlust an Grenzen
    Nachteile: Speichermehraufwand (doppelte Inhalte)
    """
    words = text.split()
    chunks = []

    for i in range(0, len(words) - window_size + 1, stride):
        chunk = ' '.join(words[i:i + window_size])
        chunks.append(chunk)

    return chunks

Wann verwenden:

  • Kritische Retrieval-Genauigkeit erforderlich
  • Kurze Abfragen benötigen breiteren Kontext
  • Speicherkosten spielen keine Rolle

Teil 3: Embedding-Modelle

Modell-Auswahlhilfe

ModellDimensionenGeschwindigkeitQualitätKostenAnwendungsfall
OpenAI text-embedding-3-small1536SchnellExzellent$0,02/1M TokensProduktion, allgemeiner Zweck
OpenAI text-embedding-3-large3072MittelAm besten$0,13/1M TokensHochwertiges Retrieval
all-MiniLM-L6-v2384Sehr schnellGutKostenlosSelbst gehostet, Prototyping
all-mpnet-base-v2768SchnellSehr gutKostenlosSelbst gehostet, Qualität
Cohere embed-english-v3.01024SchnellExzellent$0,10/1M TokensFokus auf semantische Suche

OpenAI Embeddings (Empfohlen)

from openai import OpenAI

client = OpenAI(api_key="your-key")

def get_embeddings(texts: list[str], model: str = "text-embedding-3-small") -> list[list[float]]:
    """
    Embeddings mit OpenAI generieren.

    Batch-Größe: Bis zu 2048 Eingaben pro Anfrage
    Ratenbegrenzungen: Tier-Grenzen prüfen
    """
    response = client.embeddings.create(
        model=model,
        input=texts
    )

    return [item.embedding for item in response.data]

# Verwendung
chunks = ["Block 1", "Block 2", ...]
embeddings = get_embeddings(chunks)

Sentence Transformers (Selbst gehostet)

from sentence_transformers import SentenceTransformer

# Modell laden (nach erstem Download zwischengespeichert)
model = SentenceTransformer('all-MiniLM-L6-v2')

def get_embeddings_local(texts: list[str]) -> list[list[float]]:
    """
    Embeddings lokal generieren (keine API-Kosten).

    GPU für Batches > 100 empfohlen
    CPU für kleine Batches akzeptabel
    """
    return model.encode(texts, show_progress_bar=True).tolist()

# Verwendung
embeddings = get_embeddings_local(chunks)

Teil 4: Retrieval-Optimierung

Technik 1: Hybrid-Suche (Dicht + Dünnbesetzt)

from qdrant_client.models import Filter, FieldCondition, MatchValue

def hybrid_search(query: str, query_vector: list[float], top_k: int = 10):
    """
    Kombiniert dichte (Vektor) und dünnbesetzte (Schlüsselwort) Suche.

    Dicht: Semantische Ähnlichkeit
    Dünnbesetzt: Exakte Schlüsselwortübereinstimmungen
    """
    # Dichte Suche
    dense_results = client.search(
        collection_name="documents",
        query_vector=query_vector,
        limit=top_k * 2  # Mehr Kandidaten abrufen
    )

    # Dünnbesetzte Suche (BM25 über Metadaten)
    sparse_results = client.search(
        collection_name="documents",
        query_filter=Filter(
            must=[
                FieldCondition(
                    key="text",
                    match=MatchValue(value=query)
                )
            ]
        ),
        limit=top_k * 2
    )

    # Zusammenführen und neu bewerten
    combined = merge_results(dense_results, sparse_results, weights=(0.7, 0.3))
    return combined[:top_k]

Technik 2: Abfrageerweiterung

def expand_query(query: str) -> list[str]:
    """
    Abfragevarianten für bessere Abdeckung generieren.

    Techniken:
    - Synonym-Erweiterung
    - Frageumformulierung
    - Entitätsextraktion
    """
    from openai import OpenAI
    client = OpenAI()

    response = client.chat.completions.create(
        model="gpt-4",
        messages=[{
            "role": "system",
            "content": "Generiere 3 alternative Formulierungen der Benutzerabfrage."
        }, {
            "role": "user",
            "content": query
        }]
    )

    expanded = [query] + response.choices[0].message.content.split('\n')
    return expanded

# Verwendung
queries = expand_query("Wie trainiere ich neuronale Netze?")
# → ["Wie trainiere ich neuronale Netze?",
#    "Was sind Trainingstechniken für neuronale Netze?",
#    "Optimierungsmethoden für neuronale Netze",
#    "Deep-Learning-Modelltraining"]

Technik 3: Neubewertung (Reranking)

from sentence_transformers import CrossEncoder

# Cross-Encoder laden (besser als Bi-Encoder zur Neubewertung)
reranker = CrossEncoder('cross-encoder/ms-marco-MiniLM-L-6-v2')

def rerank_results(query: str, results: list[dict], top_k: int = 5) -> list[dict]:
    """
    Anfangsergebnisse mit Cross-Encoder neu bewerten.

    Genauer, aber langsamer als das anfängliche Retrieval
    Nur auf den Top-20-50 Kandidaten anwenden
    """
    # Jedes Abfrage-Dokument-Paar bewerten
    pairs = [(query, result['text']) for result in results]
    scores = reranker.predict(pairs)

    # Ergebnisse mit Bewertungen kombinieren
    for result, score in zip(results, scores):
        result['rerank_score'] = float(score)

    # Sortieren und top_k zurückgeben
    reranked = sorted(results, key=lambda x: x['rerank_score'], reverse=True)
    return reranked[:top_k]

Technik 4: Metadaten-Filterung

def filtered_search(
    query_vector: list[float],
    filters: dict,
    top_k: int = 5
):
    """
    Suche nach Metadaten filtern (Datum, Kategorie, Autor, usw.)

    Vor-Filter: Schneller, kann aber Ergebnisse übersehen
    Nach-Filter: Mehr Ergebnisse, aber langsamer
    """
    from qdrant_client.models import Filter, FieldCondition, Range

    # Filterbedingungen erstellen
    conditions = []

    if 'date_range' in filters:
        conditions.append(
            FieldCondition(
                key="date",
                range=Range(
                    gte=filters['date_range']['start'],
                    lte=filters['date_range']['end']
                )
            )
        )

    if 'category' in filters:
        conditions.append(
            FieldCondition(
                key="category",
                match=MatchValue(value=filters['category'])
            )
        )

    # Mit Filtern suchen
    results = client.search(
        collection_name="documents",
        query_vector=query_vector,
        query_filter=Filter(must=conditions) if conditions else None,
        limit=top_k
    )

    return results

Teil 5: Kontextverwaltung

Muster 1: Optimierung des abgerufenen Kontexts

def optimize_context(query: str, retrieved_docs: list[str], max_tokens: int = 4000) -> str:
    """
    Abgerufenen Kontext optimieren, um in das LLM-Kontextfenster zu passen.

    Strategien:
    1. Relevanzbasierte Kürzung
    2. Extraktive Zusammenfassung
    3. Überlappungsentfernung
    """
    # Nach Relevanz sortieren
    sorted_docs = sorted(retrieved_docs, key=lambda d: d['score'], reverse=True)

    # Kontext innerhalb des Token-Budgets aufbauen
    context_parts = []
    total_tokens = 0

    for doc in sorted_docs:
        doc_tokens = estimate_tokens(doc['text'])

        if total_tokens + doc_tokens <= max_tokens:
            context_parts.append(f"[Quelle: {doc['source']}]\n{doc['text']}")
            total_tokens += doc_tokens
        else:
            # Letztes Dokument kürzen, um zu passen
            remaining = max_tokens - total_tokens
            truncated = truncate_to_tokens(doc['text'], remaining)
            context_parts.append(f"[Quelle: {doc['source']}]\n{truncated}")
            break

    return "\n\n---\n\n".join(context_parts)

Muster 2: Zitierungsverfolgung

def generate_with_citations(query: str, context: str, sources: list[dict]) -> dict:
    """
    Antwort mit Zitierungsverfolgung generieren.

    Rückgabe:
    - answer: Generierter Text
    - citations: Liste der verwendeten Quelldokumente
    """
    from openai import OpenAI
    client = OpenAI()

    # Quellenkarte erstellen
    source_map = {i+1: source for i, source in enumerate(sources)}
    numbered_context = "\n\n".join([
        f"[{i+1}] {source['text']}"
        for i, source in enumerate(sources)
    ])

    response = client.chat.completions.create(
        model="gpt-4",
        messages=[{
            "role": "system",
            "content": "Antworten Sie unter Verwendung der bereitgestellten Quellen. Zitieren Sie Quellen als [1], [2] usw."
        }, {
            "role": "user",
            "content": f"Kontext:\n{numbered_context}\n\nFrage: {query}"
        }]
    )

    answer = res

Individual skills in this repo

This repo contains 2 individual skills — each has its own dedicated page.

Verwandte Skills