- Blog
- 07.03.2024
- Data Fundamentals, Product
Step-by-Step Guide: Building a RAG Model with open-Source LLM (Llama 2) and Vector store (FAISS)

Large Language Models (LLMs) offer new avenues for enhancing document retrieval and analysis efficiency through Retrieval Augmented Generation (RAG) applications.
This article demonstrates the Python implementation of a RAG model using open source LLM model, LLAMA2, and vector store, Facebook AI Similarity Search (FAISS). After reading this part one, check out part two, which gives a step-by-step guide on how to deploy a RAG Model in Snowpark Container Service with the Matillion Data Productivity Cloud.
Llama2
Llama 2 encompasses a series of pre-trained and fine-tuned generative text models, varying in size from 7 billion to 70 billion parameters. These models, particularly the Llama-2-Chat variants, are fine-tuned for dialogue applications, making them exceptionally suitable for RAG applications.
Facebook AI Similarity Search (FAISS)
FAISS, developed by Facebook, is an open source library designed for fast similarity search on large datasets. It excels at managing and searching through high-dimensional vectors, making it an ideal choice for projects where speed and scalability are key.
In the context of a RAG model, FAISS serves as the backbone for efficiently retrieving the documents most relevant to a given query. FAISS's efficiency lies in its ability to cluster data using an "inverted file index" system, significantly speeding up searches even with millions of vectors.
Step 1 - Document Preprocessing
Preprocessing source documents is crucial for effective analysis. This involves several key steps. First, when the documents are loaded into the Python environment, the text is cleaned to remove extraneous formatting, special characters, and irrelevant content. This step ensures that subsequent analyses are performed on consistent and clean data.
Next, the documents are segmented into smaller, more manageable chunks. Each chunk is typically sized at 1,200 characters, with a 300-character overlap to maintain context. Additionally, the title of each document is appended to each chunk to enhance contextual clarity. This segmentation facilitates easier analysis and information retrieval. The supported document formats are ".md", ".pdf", and ".txt".
The pip dependencies for the following script are:
pip install markdown pip install langchain pip install pdfminer.six
Example Python code:
import os
import re
import markdown
from pdfminer.high_level import extract_text as extract_text_from_pdf
from io import StringIO
from html.parser import HTMLParser
from langchain.text_splitter import RecursiveCharacterTextSplitter
# HTML Stripper class to clean HTML tags from the text
class MLStripper(HTMLParser):
def __init__(self):
super().__init__()
self.reset()
self.strict = False
self.convert_charrefs = True
self.text = StringIO()
def handle_data(self, d):
self.text.write(d)
def get_data(self):
return self.text.getvalue()
def strip_tags(html):
"""Remove HTML tags from a string."""
s = MLStripper()
s.feed(html)
return s.get_data()
def clean_markdown(text):
"""Clean Markdown syntax from text."""
# Remove Markdown URL links
text = re.sub(r'\[([^\]]+)\]\([^)]+\)', r'\1', text)
# Remove bold and italic text markers
text = re.sub(r'\*\*([^*]+)\*\*', r'\1', text)
text = re.sub(r'\*([^*]+)\*', r'\1', text)
text = re.sub(r'__([^_]+)__', r'\1', text)
text = re.sub(r'_([^_]+)_', r'\1', text)
# Remove images and their references
text = re.sub(r'!\[[^\]]]*]\([^)]*\)', '', text)
# Remove headers markers
text = re.sub(r'#+\s?', '', text)
# Remove other Markdown syntax as needed (e.g., tables, bullet points)
text = re.sub(r'\|', ' ', text)
text = re.sub(r'-{2,}', '', text)
text = re.sub(r'\n{2,}', '\n', text) # Remove extra newlines
return text
def extract_text_from_md(md_path):
"""Extract and clean text from a Markdown file."""
with open(md_path, "r", encoding="utf-8") as file:
md_content = file.read()
html = markdown.markdown(md_content)
text = strip_tags(html)
return clean_markdown(text)
def extract_text_from_file(file_path):
"""Extract text from a file based on its extension."""
if file_path.endswith('.pdf'):
return extract_text_from_pdf(file_path)
elif file_path.endswith('.md'):
return extract_text_from_md(file_path)
elif file_path.endswith('.txt'):
with open(file_path, 'r', encoding='utf-8') as file:
return file.read()
else:
return "Unsupported file format."
# Directory containing documents to process
directory = r'/replace/with/your/source/directory/'
# Parameters for text splitting
chunk_size = 1200
chunk_overlap = 300
# List to store all document chunks
all_docs = []
allowed_extensions = ['.md', '.pdf', '.txt']
# Process each file in the directory
for root, dirs, files in os.walk(directory):
for filename in files:
# Get the file extension
_, file_extension = os.path.splitext(filename)
if file_extension in allowed_extensions:
file_path = os.path.join(root, filename) # Full path of the file
# Remove the ".md", ".pdf" or ".txt" extension from the file name
file_name_without_extension = os.path.splitext(filename)[0]
# Open and read the file
file_content = extract_text_from_file(file_path)
# Split the text into chunks
text_splitter = RecursiveCharacterTextSplitter(chunk_size=chunk_size, chunk_overlap=chunk_overlap)
docs = text_splitter.split_text(file_content)
for i, chunk in enumerate(docs):
# Define metadata for each chunk (you can customize this)
metadata = {
"File Name": file_name_without_extension,
"Chunk Number": i + 1,
}
# Create a header with metadata and file name
header = f"File Name: {file_name_without_extension}\n"
for key, value in metadata.items():
header += f"{key}: {value}\n"
# Combine header, file name, and chunk content
chunk_with_header = header + file_name_without_extension + "\n" + chunk
all_docs.append(chunk_with_header)
print(f"Processed: {filename}")
Step 2 - Vectorisation and Indexing
Following preprocessing and chunking, the subsequent phase involves vectorization and indexing, which are essential steps in building any RAG model.
Vectorization entails transforming the preprocessed text data into numerical vectors using embedding techniques, such as those provided by Hugging Face. Each text chunk is encoded as a sequence of numbers, which enables computational comprehension and analysis of the documents. This transformation allows the model to interpret the textual content in a format that is suitable for further processing.
Next, FAISS (Facebook AI Similarity Search) is utilized for indexing the vectorized documents.
FAISS is a powerful library designed for fast similarity search on large datasets, particularly adept at managing and searching through high-dimensional vectors. By creating an index of these vector representations, FAISS enables users to efficiently retrieve relevant document chunks based on their queries. This indexed structure significantly enhances the speed and efficiency of document retrieval, allowing for swift access to pertinent information.
One notable advantage of FAISS is its capability to save the vector embeddings locally for future reference. This means that once the vector representations of the documents are computed and indexed, they can be stored locally and readily accessed whenever needed. This eliminates the need for repetitive vectorization and indexing processes, saving computational resources and streamlining the retrieval process in subsequent interactions or use cases of the RAG model.
from langchain.vectorstores import FAISS
from langchain.embeddings import HuggingFaceInstructEmbeddings
# Initialize HuggingFaceInstructEmbeddings
model_name = "hkunlp/instructor-large"
model_kwargs = {'device': 'cpu'}
encode_kwargs = {'normalize_embeddings': True}
hf_embedding = HuggingFaceInstructEmbeddings(
model_name=model_name,
model_kwargs=model_kwargs,
encode_kwargs=encode_kwargs
)
# Embed and index all the documents using FAISS
db = FAISS.from_texts(all_docs, hf_embedding)
# Save the indexed data locally
db.save_local("faiss_AiDoc")
This can be easily reloaded for future use by calling:
# Load the FAISS index from local storage
db = FAISS.load_local("faiss_AiDoc", embeddings=hf_embedding)
Step 3 - Loading the LLAMA2 Model and Querying for Information:
In this step, the LLAMA2 model is loaded into memory to facilitate response generation based on the contextual information retrieved from the indexed documents. LLAMA2 is a robust language model fine-tuned specifically for text generation tasks, making it an ideal choice for generating responses in a RAG model.
For this implementation, we utilize the "llama-2-7b-chat.Q6_K.gguf" model, which can be downloaded from Hugging Face via this link.
Querying for Information
Once the LLAMA2 model is loaded and the documents are indexed using FAISS, users can input queries. The system then utilizes FAISS to search the indexed documents and identify relevant chunks of information aligned with the query. FAISS returns the top 5 curated list of document chunks that closely match the user's query. These document chunks serve as contextual cues for response generation, providing the necessary information to formulate accurate responses.
Sample Query and Response
Leveraging the retrieved document chunks as contextual anchors, the system generates responses using the LLAMA2 model. LLAMA2 processes the contextual information and generates responses that directly address the user's query. The responses provide precise information derived directly from the contextual cues provided by the indexed documents.
from langchain.llms import LlamaCpp
from langchain import PromptTemplate, LLMChain
from langchain.callbacks.manager import CallbackManager
from langchain.callbacks.streaming_stdout import (
StreamingStdOutCallbackHandler
)
# Template for question-answer prompt
template = """Question: {question} \n\nAnswer:"""
# Initialize prompt template and callback manager
prompt = PromptTemplate(template=template, input_variables=["question"])
callback_manager = CallbackManager([StreamingStdOutCallbackHandler()])
# Define the local path to the Llama2 model download
model_path = "llama-2-7b-chat.Q6_K.gguf"
# Initialize LlamaCpp model
llm = LlamaCpp(model_path=model_path, temperature=0.2, max_tokens=4095, top_p=1, callback_manager=callback_manager, n_ctx=6000)
# Create LLMChain
llm_chain = LLMChain(prompt=prompt, llm=llm)
# Define a query to search the indexed documents
query = "<<user question goes here>>?"
# Search for semantically similar chunks and return the top 5 chunks
search = db.similarity_search(query, k=5)
# Define a template for generating a final prompt
template = '''Context: {context}
Based on the Context, please answer the following question:
Question: {question}
Provide an answer based on the context only, without using general knowledge. The answer
should be derived directly from the context provided.
Please correct any grammatical errors for improved readability.
If the context does not contain relevant information to answer the question, state that the
answer is not available in the given context.
Please include the source title of the information as a reference of how you arrive at your
answer. '''
# Create a prompt template
prompt = PromptTemplate(input_variables=["context", "question"], template=template)
# Format the final prompt with the query and search results
final_prompt = prompt.format(question=query, context=search)
# Run LLMChain to generate an answer based on the context
llm_chain.run(final_prompt)
RAG in the Matillion Data Productivity Cloud
Matillion's Data Productivity Cloud contains components that help you perform RAG queries using a no-code interface.
Below is a screenshot of a RAG-enabled AI Prompt component using an OpenAI LLM and embedding.
A full demonstration video is available in Matillion's library of AI Videos and Demos, under the heading "Automating Customer Support Part 1".
Conclusion
The deployment of a RAG model leveraging open-source technologies such as FAISS Vector Store and LLAMA2 represents a significant milestone in information retrieval. By harnessing the capabilities of these advanced tools, organizations can streamline their information retrieval processes, enabling users to promptly access pertinent data.
This approach facilitates seamless information retrieval, empowering users to make well-informed decisions based on contextualized responses. Consequently, organizations can enhance their operational efficiency and decision-making capabilities, leading to improved outcomes and better overall performance.
Look out for a future article that will use the FAISS index that was saved in this article to deploy a RAG model in Snowpark Container Service (SPCS).
Part two can be found here!
To try out the Matillion Data Productivity Cloud for free, using your own data, run a free trial.
Alex Egaji
Data Scientist
Featured Resources
Big Data London 2025: Key Takeaways and Maia Highlights
There’s no doubt about it – Maia dominated at Big Data London. Over the two-day event, word spread quickly about Maia’s ...
Learn more BlogSay Hello to Ask Matillion, Your New AI Assistant for Product Answers
We’re excited to introduce a powerful new addition to the Matillion experience: Ask Matillion.
Learn more BlogRethinking Data Pipeline Pricing
Discover how value-based data pipeline pricing improves ROI, controls costs, and scales data processing without billing surprises.
Learn more
Share: