Large Language Models (LLM's) zoals GPT-4 en Claude zijn buitengewoon krachtig, maar ze lijden aan een fundamentele beperking: hun kennis is bevroren op het moment van training. Retrieval-Augmented Generation (RAG) lost precies dit probleem op door de generatieve kracht van LLM's te combineren met het vermogen om informatie uit externe bronnen op te halen.
Het Probleem: LLM-beperkingen
- Statische kennis: Een LLM weet alleen wat het zag tijdens training.
- Hallucinaties: Wanneer een LLM het antwoord niet weet, verzint het er een.
- Geen toegang tot privédata: Een generiek LLM heeft geen toegang tot je interne documentatie.
Wat is RAG?
RAG is een architectuur die de prompt verrijkt met informatie opgehaald uit een externe kennisbasis.
Hoe RAG Werkt
Fase 1: Indexering
Fase 2: Retrieval + Generation
- De vraag wordt omgezet in een embedding.
- De Vector Store vindt de meest vergelijkbare chunks.
- De opgehaalde chunks worden in de prompt ingevoegd als context.
- Het LLM genereert een antwoord op basis van de context.
Een RAG Pipeline Bouwen met LangChain
pip install langchain langchain-openai langchain-community chromadb
Stap 1: Documenten Laden
from langchain_community.document_loaders import ( PyPDFLoader, WebBaseLoader, DirectoryLoader, TextLoader, ) # Load a PDF pdf_loader = PyPDFLoader("docs/manual.pdf") pdf_docs = pdf_loader.load() # Load a web page web_loader = WebBaseLoader("https://docs.example.com/guide") web_docs = web_loader.load() # Load all .md files from a directory dir_loader = DirectoryLoader("./knowledge_base", glob="**/*.md", loader_cls=TextLoader) md_docs = dir_loader.load() all_docs = pdf_docs + web_docs + md_docs
Stap 2: Documenten Splitsen
from langchain.text_splitter import RecursiveCharacterTextSplitter text_splitter = RecursiveCharacterTextSplitter( chunk_size=1000, chunk_overlap=200, separators=["\n\n", "\n", ". ", " ", ""], ) chunks = text_splitter.split_documents(all_docs) print(f"Original documents: {len(all_docs)}, Chunks: {len(chunks)}")
Stap 3: Embeddings en Vector Store
from langchain_openai import OpenAIEmbeddings from langchain_community.vectorstores import Chroma embedding_model = OpenAIEmbeddings(model="text-embedding-3-small") vectorstore = Chroma.from_documents( documents=chunks, embedding=embedding_model, persist_directory="./chroma_db", )
Stap 4: De Retriever
retriever = vectorstore.as_retriever( search_type="similarity", search_kwargs={"k": 4}, ) relevant_docs = retriever.invoke("How does authentication work?") for doc in relevant_docs: print(doc.page_content[:200]) print("---")
Stap 5: De RAG Chain
from langchain_openai import ChatOpenAI from langchain_core.prompts import ChatPromptTemplate from langchain_core.runnables import RunnablePassthrough from langchain_core.output_parsers import StrOutputParser llm = ChatOpenAI(model="gpt-4o", temperature=0) prompt = ChatPromptTemplate.from_template(""" Answer the question based only on the provided context. If the context does not contain enough information, say you don't know. Context: {context} Question: {question} Answer: """) def format_docs(docs): return "\n\n".join(doc.page_content for doc in docs) rag_chain = ( {"context": retriever | format_docs, "question": RunnablePassthrough()} | prompt | llm | StrOutputParser() ) response = rag_chain.invoke("How does authentication work in the system?") print(response)
Geavanceerde RAG-technieken
Multi-Query Retrieval
from langchain.retrievers import MultiQueryRetriever multi_retriever = MultiQueryRetriever.from_llm( retriever=vectorstore.as_retriever(), llm=llm, )
Contextual Compression
from langchain.retrievers import ContextualCompressionRetriever from langchain.retrievers.document_compressors import LLMChainExtractor compressor = LLMChainExtractor.from_llm(llm) compression_retriever = ContextualCompressionRetriever( base_compressor=compressor, base_retriever=retriever, )
Hybrid Search
from langchain.retrievers import EnsembleRetriever from langchain_community.retrievers import BM25Retriever bm25_retriever = BM25Retriever.from_documents(chunks) bm25_retriever.k = 4 semantic_retriever = vectorstore.as_retriever(search_kwargs={"k": 4}) hybrid_retriever = EnsembleRetriever( retrievers=[bm25_retriever, semantic_retriever], weights=[0.4, 0.6], )
Conversational RAG (met Geheugen)
from langchain.chains import create_history_aware_retriever from langchain_core.prompts import MessagesPlaceholder contextualize_prompt = ChatPromptTemplate.from_messages([ ("system", "Given the chat history and the user's latest question, " "reformulate the question so it is understandable without the history."), MessagesPlaceholder("chat_history"), ("human", "{input}"), ]) history_aware_retriever = create_history_aware_retriever( llm, retriever, contextualize_prompt )
Best Practices
- Kies de juiste chunkgrootte: Experimenteer met 500-1500 tokens.
- Gebruik documentmetadata: Voeg bron, datum en categorie toe als metadata.
- Evalueer kwaliteit: Gebruik frameworks zoals RAGAS.
- Beheer documentupdates: Implementeer een her-ingestiepipeline.
- Voeg een re-ranker toe: Gebruik een re-ranking model na initiële retrieval.
Conclusie
RAG is de standaardarchitectuur geworden voor AI-applicaties die toegang nodig hebben tot specifieke, actuele kennis. LangChain vereenvoudigt de implementatie enorm.