Die RAG-Architektur (Retrieval-Augmented Generation) revolutioniert die Art und Weise, wie grosse Sprachmodelle mit externen Daten arbeiten. Dieser Leitfaden fuehrt Sie von grundlegenden Konzepten zur praktischen Implementierung Ihres eigenen RAG-Systems.
Grundlagen der RAG-Architektur¶
RAG (Retrieval-Augmented Generation) stellt einen revolutionaeren Ansatz zur Arbeit mit grossen Sprachmodellen dar, der die Kraft der Textgenerierung mit der Praezision des Information Retrieval kombiniert. Anstatt sich nur auf das parametrische Wissen des Modells zu verlassen, reichert RAG den Kontext dynamisch mit relevanten Informationen aus externen Datenbanken an.
Die RAG-Architektur loest ein grundlegendes Problem aktueller LLMs - eine begrenzte und oft veraltete Wissensbasis. Waehrend ein Standardmodell nur allgemeine Antworten basierend auf Trainingsdaten liefern kann, kann RAG mit aktuellen und spezifischen Informationen aus Unternehmensdokumenten, Datenbanken oder Webinhalten arbeiten.
RAG-Systemkomponenten¶
Vektordatenbank¶
Das Herzstuck jedes RAG-Systems ist eine Vektordatenbank, die Dokumente in Form von Vektordarstellungen (Embeddings) speichert. Diese Vektoren erfassen die semantische Bedeutung von Text im mehrdimensionalen Raum und ermoeglichen eine schnelle Suche nach aehnlichen Inhalten.
import chromadb
from sentence_transformers import SentenceTransformer
# RAG-Architektur von Grund auf
client = chromadb.Client()
collection = client.create_collection("documents")
# Model for creating embeddings
encoder = SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2')
def add_document(text, doc_id):
embedding = encoder.encode([text])
collection.add(
embeddings=embedding.tolist(),
documents=[text],
ids=[doc_id]
)
Embedding-Modell¶
Das Embedding-Modell transformiert sowohl Textdokumente als auch Benutzeranfragen in Vektorform. Fuer mehrsprachige Inhalte empfehle ich Modelle wie paraphrase-multilingual-MiniLM-L12-v2 oder sentence-transformers/LaBSE, die mehrsprachige Inhalte gut verarbeiten.
Implementierung des grundlegenden RAG-Workflows¶
Ingestion Pipeline¶
Der erste Schritt umfasst das Laden und Verarbeiten von Dokumenten. Es ist wichtig, lange Texte in kleinere Chunks aufzuteilen, die die semantische Kohaerenz bewahren.
from langchain.text_splitter import RecursiveCharacterTextSplitter
def process_documents(documents):
splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=50,
separators=["\n\n", "\n", " ", ""]
)
chunks = []
for doc in documents:
text_chunks = splitter.split_text(doc.content)
for i, chunk in enumerate(text_chunks):
chunks.append({
'text': chunk,
'doc_id': f"{doc.id}_{i}",
'metadata': doc.metadata
})
return chunks
def ingest_documents(chunks):
for chunk in chunks:
add_document(chunk['text'], chunk['doc_id'])
Retrieval-Mechanismus¶
Die Retrieval-Phase sucht nach den relevantesten Dokumenten basierend auf semantischer Aehnlichkeit zur Benutzeranfrage. Die richtige Einstellung von Parametern wie Anzahl der Ergebnisse und Aehnlichkeitsschwellenwert ist entscheidend.
def retrieve_documents(query, k=5):
# Create embedding for query
query_embedding = encoder.encode([query])
# Search for similar documents
results = collection.query(
query_embeddings=query_embedding.tolist(),
n_results=k
)
return [
{'text': doc, 'score': score}
for doc, score in zip(results['documents'][0], results['distances'][0])
if score < 0.8 # relevance threshold
]
Generierung mit Kontext¶
Der letzte Schritt kombiniert abgerufene Dokumente mit der urspruenglichen Anfrage zu einem Prompt fuer das LLM. Ein richtig strukturierter Prompt ist der Schluessel zur Antwortqualitaet.
import openai
def generate_answer(query, retrieved_docs):
context = "\n\n".join([doc['text'] for doc in retrieved_docs])
prompt = f"""
Answer the following question based on the provided context.
If you can't find the answer in the context, say so directly.
Context:
{context}
Question: {query}
Answer:
"""
response = openai.chat.completions.create(
model="gpt-4",
messages=[{"role": "user", "content": prompt}],
temperature=0.1
)
return response.choices[0].message.content
Fortgeschrittene Techniken und Optimierungen¶
Hybride Suche¶
Die Kombination von Dense (Vektor) und Sparse (Keyword) Suche liefert oft bessere Ergebnisse als jede Methode allein. Sparse-Suche glaenzt bei exakten Begriffen, Dense bei semantischer Aehnlichkeit.
from rank_bm25 import BM25Okapi
class HybridRetriever:
def __init__(self):
self.bm25 = None
self.documents = []
def index_documents(self, docs):
self.documents = docs
tokenized_docs = [doc.split() for doc in docs]
self.bm25 = BM25Okapi(tokenized_docs)
def hybrid_search(self, query, k=5, alpha=0.7):
# Vector search
vector_results = retrieve_documents(query, k)
# BM25 search
bm25_scores = self.bm25.get_scores(query.split())
# Score combination
final_scores = {}
for i, doc in enumerate(self.documents):
vector_score = next((r['score'] for r in vector_results
if r['text'] == doc), 1.0)
bm25_score = bm25_scores[i]
final_scores[doc] = alpha * (1 - vector_score) + (1 - alpha) * bm25_score
return sorted(final_scores.items(), key=lambda x: x[1], reverse=True)[:k]
Reranking¶
Nach dem initialen Retrieval koennen Sie ein spezialisiertes Reranking-Modell verwenden, um die Ergebnisse besser nach Relevanz zur Anfrage zu ordnen.
from sentence_transformers import CrossEncoder
reranker = CrossEncoder('cross-encoder/ms-marco-MiniLM-L-6-v2')
def rerank_documents(query, documents, top_k=3):
pairs = [(query, doc['text']) for doc in documents]
scores = reranker.predict(pairs)
# Sort by score
ranked_docs = sorted(
zip(documents, scores),
key=lambda x: x[1],
reverse=True
)
return [doc for doc, score in ranked_docs[:top_k]]
Monitoring und Evaluierung¶
RAG-Systeme erfordern ein kontinuierliches Qualitaetsmonitoring. Wichtige Metriken umfassen Retrieval Precision, Kontextrelevanz und Antworttreue.
def evaluate_rag_performance(test_queries):
metrics = {
'retrieval_precision': 0,
'answer_relevance': 0,
'response_time': 0
}
for query_data in test_queries:
start_time = time.time()
# Retrieval
retrieved = retrieve_documents(query_data['query'])
# Generation
answer = generate_answer(query_data['query'], retrieved)
end_time = time.time()
# Calculate metrics
metrics['response_time'] += end_time - start_time
# Additional evaluation logic...
return metrics
Zusammenfassung¶
Die RAG-Architektur stellt einen praktischen Weg dar, die Faehigkeiten von LLMs mit aktuellem und spezifischem Wissen zu erweitern. Eine erfolgreiche Implementierung erfordert eine sorgfaeltige Auswahl des Embedding-Modells, eine korrekte Dokumentensegmentierung und die Optimierung der Retrieval-Mechanismen. Mit fortgeschrittenen Techniken wie hybrider Suche und Reranking koennen Sie Produktionsqualitaetssysteme erreichen, die zuverlaessig Anfragen aus Ihrer Wissensbasis beantworten. Der Schluessel zum Erfolg ist ein iterativer Ansatz mit kontinuierlichem Monitoring und Verbesserung basierend auf realer Nutzung.