spinny:~/writing $ less rag-langchain-deep-dive.md
12Large Language Models (LLMs) wie GPT-4 und Claude sind außerordentlich leistungsfähig, leiden aber unter einer grundlegenden Einschränkung: Ihr Wissen ist zum Zeitpunkt des Trainings eingefroren. Sie können nicht auf deine internen Dokumente, deine Datenbank oder Echtzeitinformationen zugreifen. **Retrieval-Augmented Generation (RAG)** löst genau dieses Problem, indem sie die generative Kraft von LLMs mit der Fähigkeit kombiniert, Informationen aus externen Quellen abzurufen.34## Das Problem: Die Grenzen von LLMs56Bevor wir über RAG sprechen, ist es wichtig zu verstehen, warum wir sie brauchen.781. **Statisches Wissen**: Ein LLM weiß nur das, was es während des Trainings gesehen hat. Wenn du nach einem Ereignis nach seinem Cutoff fragst, kann es nicht antworten.92. **Halluzinationen**: Wenn ein LLM die Antwort nicht kennt, neigt es dazu, sie zu erfinden und plausible, aber völlig falsche Informationen zu generieren.103. **Kein Zugriff auf private Daten**: Ein generisches LLM hat keinen Zugriff auf die interne Dokumentation deines Unternehmens, Tickets oder deine Codebasis.1112RAG adressiert alle drei Probleme, indem es dem Modell **relevanten Kontext** liefert, der zum Zeitpunkt der Abfrage aus externen Quellen abgerufen wird.1314## Was ist RAG?1516Retrieval-Augmented Generation ist eine Architektur, die den an ein LLM gesendeten Prompt mit Informationen aus einer externen Wissensdatenbank anreichert. Anstatt sich ausschließlich auf das parametrische Wissen des Modells zu verlassen, **sucht** RAG zuerst nach relevanten Informationen und **injiziert** sie dann in den Prompt, wodurch das Modell genaue, fundierte Antworten generieren kann.1718```mermaid19graph LR20 User["Benutzer"] -- "Frage" --> Retriever21 Retriever -- "Suche relevante\nDokumente" --> VectorStore["Vector Store"]22 VectorStore -- "Relevante\nDokumente" --> Retriever23 Retriever -- "Kontext + Frage" --> LLM24 LLM -- "Fundierte\nAntwort" --> User25```2627## Wie RAG im Detail funktioniert2829Die RAG-Architektur besteht aus zwei Hauptphasen: **Indexierung** (offline) und **Retrieval + Generierung** (online).3031### Phase 1: Indexierung (Dokumentenaufnahme)3233Die Indexierungsphase bereitet deine Dokumente für die semantische Suche vor. Sie besteht aus vier Schritten.3435```mermaid36graph TD37 A["Dokumente\n(PDF, HTML, MD, DB)"] --> B["Document Loader"]38 B --> C["Text Splitter"]39 C --> D["Text-Chunks"]40 D --> E["Embedding-Modell"]41 E --> F["Numerische Vektoren"]42 F --> G["Vector Store\n(ChromaDB, Pinecone, FAISS)"]43```4445#### 1. Document Loading4647Dokumente können aus jeder Quelle stammen: PDF-Dateien, Webseiten, Datenbanken, Markdown-Dateien, APIs. Der **Document Loader** liest diese Dokumente und konvertiert sie in strukturierten Text.4849#### 2. Text Splitting (Chunking)5051LLMs haben ein begrenztes Kontextfenster, und Dokumente können sehr lang sein. Der **Text Splitter** teilt Dokumente in kleinere Fragmente, sogenannte *Chunks*. Die Qualität des Chunking ist entscheidend: Zu kleine Chunks verlieren Kontext, zu große verwässern die Relevanz.5253Die gängigsten Strategien sind:54- **Recursive Character Splitting**: Teilt Text rekursiv mit Trennzeichen wie `\n\n`, `\n`, `. ` und respektiert die Dokumentstruktur.55- **Semantic Splitting**: Verwendet Embeddings, um natürliche Bruchpunkte im Text zu finden.56- **Chunk Overlap**: Enthält eine Überlappung zwischen aufeinanderfolgenden Chunks, um den Kontext an den Grenzen zu bewahren.5758#### 3. Embedding5960Jeder Chunk wird über ein Embedding-Modell (wie OpenAIs `text-embedding-3-small`) in einen **numerischen Vektor** (Embedding) umgewandelt. Diese Vektoren erfassen die semantische Bedeutung des Textes: Sätze mit ähnlichen Bedeutungen haben Vektoren, die im mehrdimensionalen Raum nahe beieinander liegen.6162#### 4. Vector Store6364Die Vektoren werden in einem **Vector Store** (oder Vektordatenbank) gespeichert, wie ChromaDB, Pinecone, Weaviate oder FAISS. Diese Datenbank ist für die **Ähnlichkeitssuche** optimiert: Bei einer Abfrage findet sie die ähnlichsten Vektoren (und damit die relevantesten Text-Chunks).6566### Phase 2: Retrieval + Generierung6768Wenn der Benutzer eine Frage stellt:69701. Die Frage wird mit demselben Embedding-Modell in ein Embedding umgewandelt.712. Der Vector Store findet die ähnlichsten Chunks über **Ähnlichkeitssuche** (typischerweise Kosinusähnlichkeit oder euklidische Distanz).723. Die abgerufenen Chunks werden als Kontext in den Prompt eingefügt.734. Das LLM generiert die Antwort basierend auf dem bereitgestellten Kontext.7475## Eine RAG-Pipeline mit LangChain aufbauen7677**LangChain** ist das beliebteste Python- (und JavaScript-) Framework zum Erstellen von LLM-basierten Anwendungen. Es bietet High-Level-Abstraktionen für jede Komponente der RAG-Pipeline.7879### Installation8081```bash82pip install langchain langchain-openai langchain-community chromadb83```8485### Schritt 1: Dokumente laden8687LangChain bietet Dutzende von Document Loadern für verschiedene Datenquellen.8889```python90from langchain_community.document_loaders import (91 PyPDFLoader,92 WebBaseLoader,93 DirectoryLoader,94 TextLoader,95)9697# Ein PDF laden98pdf_loader = PyPDFLoader("docs/handbuch.pdf")99pdf_docs = pdf_loader.load()100101# Eine Webseite laden102web_loader = WebBaseLoader("https://docs.example.com/guide")103web_docs = web_loader.load()104105# Alle .md-Dateien aus einem Verzeichnis laden106dir_loader = DirectoryLoader("./knowledge_base", glob="**/*.md", loader_cls=TextLoader)107md_docs = dir_loader.load()108109all_docs = pdf_docs + web_docs + md_docs110```111112### Schritt 2: Dokumente in Chunks aufteilen113114```python115from langchain.text_splitter import RecursiveCharacterTextSplitter116117text_splitter = RecursiveCharacterTextSplitter(118 chunk_size=1000,119 chunk_overlap=200,120 separators=["\n\n", "\n", ". ", " ", ""],121)122123chunks = text_splitter.split_documents(all_docs)124print(f"Originaldokumente: {len(all_docs)}, Chunks: {len(chunks)}")125```126127Der Parameter `chunk_overlap` ist entscheidend: Er erzeugt eine Überlappung zwischen aufeinanderfolgenden Chunks, damit der Kontext an den Grenzen nicht verloren geht.128129### Schritt 3: Embeddings und Vector Store erstellen130131```python132from langchain_openai import OpenAIEmbeddings133from langchain_community.vectorstores import Chroma134135embedding_model = OpenAIEmbeddings(model="text-embedding-3-small")136137vectorstore = Chroma.from_documents(138 documents=chunks,139 embedding=embedding_model,140 persist_directory="./chroma_db",141)142```143144### Schritt 4: Den Retriever erstellen145146Der Retriever ist die Komponente, die bei einer Abfrage die relevantesten Chunks aus dem Vector Store abruft.147148```python149retriever = vectorstore.as_retriever(150 search_type="similarity",151 search_kwargs={"k": 4},152)153154relevant_docs = retriever.invoke("Wie funktioniert die Authentifizierung?")155for doc in relevant_docs:156 print(doc.page_content[:200])157 print("---")158```159160### Schritt 5: Die RAG-Chain aufbauen161162Jetzt fügen wir alles mit einem LLM und einem Prompt-Template zusammen.163164```python165from langchain_openai import ChatOpenAI166from langchain_core.prompts import ChatPromptTemplate167from langchain_core.runnables import RunnablePassthrough168from langchain_core.output_parsers import StrOutputParser169170llm = ChatOpenAI(model="gpt-4o", temperature=0)171172prompt = ChatPromptTemplate.from_template("""173Beantworte die Frage ausschließlich basierend auf dem bereitgestellten Kontext.174Wenn der Kontext nicht genügend Informationen enthält, sage, dass du es nicht weißt.175176Kontext:177{context}178179Frage: {question}180181Antwort:182""")183184def format_docs(docs):185 return "\n\n".join(doc.page_content for doc in docs)186187rag_chain = (188 {"context": retriever | format_docs, "question": RunnablePassthrough()}189 | prompt190 | llm191 | StrOutputParser()192)193194response = rag_chain.invoke("Wie funktioniert die Authentifizierung im System?")195print(response)196```197198## Fortgeschrittene RAG-Techniken199200Die grundlegende Pipeline funktioniert gut, aber es gibt verschiedene Techniken, um die Antwortqualität erheblich zu verbessern.201202### Multi-Query Retrieval203204Manchmal ist die Abfrage des Benutzers mehrdeutig oder nicht mit der in den Dokumenten verwendeten Sprache abgestimmt. Der **Multi-Query Retriever** generiert automatisch Varianten der ursprünglichen Frage, um mehrere Perspektiven zu erfassen.205206```python207from langchain.retrievers import MultiQueryRetriever208209multi_retriever = MultiQueryRetriever.from_llm(210 retriever=vectorstore.as_retriever(),211 llm=llm,212)213214docs = multi_retriever.invoke("Was sind die Sicherheits-Best-Practices?")215```216217### Contextual Compression218219Nicht der gesamte Inhalt eines Chunks ist für die Abfrage relevant. Der **Contextual Compression Retriever** verwendet ein LLM, um nur die relevanten Teile aus jedem abgerufenen Chunk zu extrahieren.220221```python222from langchain.retrievers import ContextualCompressionRetriever223from langchain.retrievers.document_compressors import LLMChainExtractor224225compressor = LLMChainExtractor.from_llm(llm)226compression_retriever = ContextualCompressionRetriever(227 base_compressor=compressor,228 base_retriever=retriever,229)230```231232### Hybrid Search233234Rein semantische Suche ist nicht immer optimal. **Hybrid Search** kombiniert semantische Suche (Embeddings) mit lexikalischer Suche (BM25, Keyword-Matching), um bessere Ergebnisse zu erzielen.235236```python237from langchain.retrievers import EnsembleRetriever238from langchain_community.retrievers import BM25Retriever239240bm25_retriever = BM25Retriever.from_documents(chunks)241bm25_retriever.k = 4242243semantic_retriever = vectorstore.as_retriever(search_kwargs={"k": 4})244245hybrid_retriever = EnsembleRetriever(246 retrievers=[bm25_retriever, semantic_retriever],247 weights=[0.4, 0.6],248)249```250251### Conversational RAG (mit Gedächtnis)252253Um einen RAG-Chatbot zu bauen, der sich an den Gesprächskontext erinnert, musst du ein Gedächtnis hinzufügen, das die Fragen des Benutzers unter Berücksichtigung des Gesprächsverlaufs umformuliert.254255```python256from langchain.chains import create_history_aware_retriever257from langchain_core.prompts import MessagesPlaceholder258259contextualize_prompt = ChatPromptTemplate.from_messages([260 ("system", "Formuliere angesichts des Chatverlaufs und der letzten Frage des Benutzers "261 "die Frage so um, dass sie ohne den Verlauf verständlich ist."),262 MessagesPlaceholder("chat_history"),263 ("human", "{input}"),264])265266history_aware_retriever = create_history_aware_retriever(267 llm, retriever, contextualize_prompt268)269```270271## Best Practices2722731. **Wähle die richtige Chunk-Größe**: Experimentiere mit verschiedenen Größen (500-1500 Token). Kleinere Chunks für präzise Antworten, größere für breiteren Kontext.2742. **Verwende Dokument-Metadaten**: Füge Quelle, Datum und Kategorie als Metadaten zu Chunks hinzu. Dies ermöglicht das Filtern von Ergebnissen beim Retrieval.2753. **Bewerte die Qualität**: Verwende Frameworks wie [RAGAS](https://docs.ragas.io/), um Metriken wie *Faithfulness*, *Relevancy* und *Context Precision* zu messen.2764. **Verwalte Dokumenten-Updates**: Implementiere eine Re-Ingestion-Pipeline, um den Vector Store mit deinen Datenquellen synchron zu halten.2775. **Füge einen Re-Ranker hinzu**: Verwende nach dem initialen Retrieval ein Re-Ranking-Modell (wie Cohere Rerank), um die Ergebnisse nach tatsächlicher Relevanz neu zu ordnen.278279## Fazit280281RAG ist zur Standardarchitektur für den Bau von KI-Anwendungen geworden, die Zugriff auf spezifisches, aktuelles Wissen benötigen. LangChain vereinfacht die Implementierung erheblich, indem es Abstraktionen für jede Komponente der Pipeline bereitstellt.282283**Nächste Schritte:**284- **Lokal experimentieren**: Beginne mit ChromaDB und wenigen Dokumenten, um dich mit der Pipeline vertraut zu machen.285- **LangSmith erkunden**: Verwende [LangSmith](https://smith.langchain.com/), um deine Chains in der Produktion zu überwachen und zu debuggen.286- **Verschiedene Embedding-Modelle testen**: Vergleiche Modelle wie `text-embedding-3-small`, `text-embedding-3-large` und Open-Source-Modelle von Sentence Transformers.287- **Dokumentation lesen**: Die [LangChain-Dokumentation](https://python.langchain.com/docs/) ist eine hervorragende und ständig aktualisierte Ressource.288
:RAG und LangChain: Ein umfassender Leitfaden zur Retrieval-Augmented Generationlines 1-288 (END) — press q to close