Retrieval-Augmented Generation (RAG)
Retrieval-Augmented Generation (RAG) connects foundation models with private corporate knowledge, eliminating hallucinations and providing verifiable source citations. Learn how to architect naive and advanced RAG pipelines using LCEL, hybrid search, and semantic re-ranking.
1. The Declarative LCEL RAG Chain
In modern LangChain, an entire end-to-end RAG pipeline can be expressed cleanly in just a few lines of code using RunnableParallel and RunnablePassthrough:
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_chroma import Chroma
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
# 1. Format retrieved document list into clean text string
def format_docs(docs):
return "\n\n".join(f"[Source: {doc.metadata.get('source', 'Unknown')}]\n{doc.page_content}" for doc in docs)
# 2. Initialize components
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = Chroma(collection_name="support_kb", embedding_function=embeddings)
retriever = vectorstore.as_retriever(search_kwargs={"k": 4})
# 3. Grounded RAG Prompt Template
rag_prompt = ChatPromptTemplate.from_template("""
You are an authoritative enterprise knowledge assistant. Answer the question based ONLY on the following context.
If the answer cannot be determined from the context, state "I do not have sufficient information in the knowledge base."
Context:
{context}
Question: {question}
Helpful & Cited Answer:
""")
llm = ChatOpenAI(model="gpt-4o", temperature=0)
# 4. Assembled LCEL RAG Chain
rag_chain = (
{"context": retriever | format_docs, "question": RunnablePassthrough()}
| rag_prompt
| llm
| StrOutputParser()
)
# 5. Execute
response = rag_chain.invoke("What are the cancellation terms for enterprise tier?")
print(response)2. Overcoming Phrasing Bias with MultiQueryRetriever
Users often ask questions using colloquialisms or ambiguous phrasing that fails to match the precise vocabulary in your documentation. MultiQueryRetriever uses an LLM to generate multiple diverse variations of the user query, retrieves candidate documents across all variations, and computes their unique union:
from langchain.retrievers.multi_query import MultiQueryRetriever
# Automatically rewrites user query from 3 distinct semantic angles
multi_query_retriever = MultiQueryRetriever.from_llm(
retriever=vectorstore.as_retriever(),
llm=ChatOpenAI(model="gpt-4o-mini", temperature=0.7)
)
# Querying with ambiguous slang
unique_docs = multi_query_retriever.invoke("How do I cancel my sub and get money back?")
print(f"Retrieved {len(unique_docs)} relevant context passages across all query variations.")3. Hybrid Search with EnsembleRetriever (BM25 + Vectors)
Pure vector embeddings excel at general concepts but often miss exact alphanumeric identifiers (e.g., error codes like ERR_503_GATEWAY or specific model numbers like RTX-4090).EnsembleRetriever combines sparse BM25 keyword matching with dense vector search:
# Install: pip install rank_bm25
from langchain_community.retrievers import BM25Retriever
from langchain.retrievers import EnsembleRetriever
# Sparse keyword retriever
bm25_retriever = BM25Retriever.from_documents(sample_docs)
bm25_retriever.k = 3
# Dense semantic vector retriever
dense_retriever = vectorstore.as_retriever(search_kwargs={"k": 3})
# Weighted Reciprocal Rank Fusion (RRF)
hybrid_retriever = EnsembleRetriever(
retrievers=[bm25_retriever, dense_retriever],
weights=[0.4, 0.6] # 40% keyword exact match, 60% semantic similarity
)Explore Related Tools
Hand-picked utilities and calculators related to this tool.
