From b1405f3b84782dd39bb47f0eda1d25e4f683a6f9 Mon Sep 17 00:00:00 2001 From: Philipp Mock Date: Mon, 26 Jan 2026 19:49:02 +0100 Subject: [PATCH] test RAG with local Ollama models initial commit --- .gitignore | 25 ++++++++ README.md | 65 ++++++++++++++++++++ local_rag.py | 155 +++++++++++++++++++++++++++++++++++++++++++++++ requirements.txt | 5 ++ 4 files changed, 250 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 local_rag.py create mode 100644 requirements.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dcfadde --- /dev/null +++ b/.gitignore @@ -0,0 +1,25 @@ +# Python +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +venv/ +env/ +ENV/ + +# Vector store +vectorstore/ + +# Uploads +uploads/ + +# IDE +.vscode/ +.idea/ +*.swp +*.swo + +# OS +.DS_Store +Thumbs.db diff --git a/README.md b/README.md new file mode 100644 index 0000000..dda3a87 --- /dev/null +++ b/README.md @@ -0,0 +1,65 @@ +# Local RAG Setup + +Minimal RAG implementation with LangChain, Ollama, and FAISS. + +## Dependencies + +Only 5 packages: +- `langchain` - Core framework +- `langchain-ollama` - Ollama integration +- `faiss-cpu` - Vector search +- `sentence-transformers` - Embeddings +- `pypdf` - PDF loading + +## Installation + +```bash +# Create conda environment +conda create -n local_rag python=3.10 -y +conda activate local_rag + +# Install dependencies +pip install -r requirements.txt +``` + +## Setup Ollama + +```bash +# Make sure Ollama is running +ollama serve + +# Pull a model (in another terminal) +ollama pull llama2 +``` + +## Usage + +Edit `local_rag.py` and uncomment the example code: + +```python +# Add documents +rag.add_documents([ + "path/to/document1.pdf", + "path/to/document2.txt" +]) + +# Query +question = "What is this document about?" +answer = rag.query(question) +print(f"Answer: {answer}") +``` + +Run: +```bash +python local_rag.py +``` + +## How it works + +1. **Load documents** - PDFs or text files +2. **Split into chunks** - 1000 chars with 200 overlap +3. **Create embeddings** - Using sentence-transformers +4. **Store in FAISS** - Fast similarity search +5. **Query** - Retrieve relevant chunks and generate answer with Ollama + +That's it! Simple and minimal. diff --git a/local_rag.py b/local_rag.py new file mode 100644 index 0000000..e53d6f1 --- /dev/null +++ b/local_rag.py @@ -0,0 +1,155 @@ +""" +Local RAG setup with LangChain, Ollama, and FAISS +Minimal dependencies, simple code +""" +import os +from pathlib import Path +from langchain_community.document_loaders import PyPDFLoader, TextLoader +from langchain.text_splitter import RecursiveCharacterTextSplitter +from langchain_community.embeddings import HuggingFaceEmbeddings +from langchain_community.vectorstores import FAISS +from langchain_ollama import ChatOllama + + +class LocalRAG: + def __init__(self, vectorstore_path="./vectorstore", ollama_model="llama2"): + """Initialize local RAG system""" + self.vectorstore_path = vectorstore_path + self.ollama_model = ollama_model + + # Embeddings + print("Loading embeddings model...") + self.embeddings = HuggingFaceEmbeddings( + model_name="sentence-transformers/all-MiniLM-L6-v2" + ) + + # Text splitter + self.text_splitter = RecursiveCharacterTextSplitter( + chunk_size=1000, + chunk_overlap=200 + ) + + # Ollama LLM + print(f"Connecting to Ollama (model: {ollama_model})...") + self.llm = ChatOllama( + model=ollama_model, + base_url="http://localhost:11434" + ) + + # Vector store (load if exists, otherwise None) + self.vectorstore = None + self._load_vectorstore() + + def _load_vectorstore(self): + """Load existing vector store if available""" + index_file = os.path.join(self.vectorstore_path, "index.faiss") + if os.path.exists(index_file): + try: + self.vectorstore = FAISS.load_local( + self.vectorstore_path, + self.embeddings, + allow_dangerous_deserialization=True + ) + print(f"Loaded existing vector store from {self.vectorstore_path}") + except Exception as e: + print(f"Could not load vector store: {e}") + self.vectorstore = None + + def add_documents(self, file_paths): + """Add documents to the vector store""" + print(f"\nLoading {len(file_paths)} document(s)...") + all_docs = [] + + for file_path in file_paths: + path = Path(file_path) + if not path.exists(): + print(f"Warning: {file_path} not found, skipping") + continue + + # Load document + if path.suffix.lower() == '.pdf': + loader = PyPDFLoader(str(path)) + elif path.suffix.lower() in ['.txt', '.md']: + loader = TextLoader(str(path)) + else: + print(f"Warning: Unsupported file type {path.suffix}, skipping") + continue + + docs = loader.load() + chunks = self.text_splitter.split_documents(docs) + all_docs.extend(chunks) + print(f" - {path.name}: {len(chunks)} chunks") + + if not all_docs: + print("No documents loaded!") + return + + # Create or update vector store + print(f"\nCreating embeddings for {len(all_docs)} chunks...") + if self.vectorstore is None: + self.vectorstore = FAISS.from_documents(all_docs, self.embeddings) + else: + new_store = FAISS.from_documents(all_docs, self.embeddings) + self.vectorstore.merge_from(new_store) + + # Save + os.makedirs(self.vectorstore_path, exist_ok=True) + self.vectorstore.save_local(self.vectorstore_path) + print(f"Vector store saved to {self.vectorstore_path}") + + def query(self, question, k=4): + """Query the RAG system""" + if self.vectorstore is None: + return "Error: No documents loaded. Please add documents first." + + print(f"\nSearching for relevant documents...") + docs = self.vectorstore.similarity_search(question, k=k) + print(f"Found {len(docs)} relevant documents") + + # Combine context from documents + context = "\n\n".join([doc.page_content for doc in docs]) + + # Create prompt + prompt = f"""Use the following context to answer the question. +If you don't know the answer, say that you don't know instead of making up an answer. + +Context: +{context} + +Question: {question} + +Answer:""" + + print("Generating answer with Ollama...") + response = self.llm.invoke(prompt) + answer = response.content if hasattr(response, 'content') else str(response) + + return answer + + +def main(): + """Example usage""" + print("=" * 60) + print("Local RAG with LangChain, Ollama, and FAISS") + print("=" * 60) + + # Initialize + rag = LocalRAG(ollama_model="llama2") + + # Add documents (uncomment and add your file paths) + rag.add_documents([ + "diverses/local_rag/test1.pdf", + "diverses/local_rag/test2.txt" + ]) + + # Query + # question = "What is this document about?" + # answer = rag.query(question) + # print(f"\nQuestion: {question}") + # print(f"Answer: {answer}") + + print("\nSetup complete! Uncomment the code above to add documents and query.") + + +if __name__ == "__main__": + main() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..70300bf --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +langchain +langchain-ollama +faiss-cpu +sentence-transformers +pypdf