Learn 🧠 All Concepts (20) 🤖 What is an LLM? 📚 RAG Explained ⚡ AI Agents 💻 Run AI Locally 🇮🇳 AI in India 📖 Learn Tracks 🔧 DevOps Track ⚙️ AI Ops Track 🗺️ AI Engineer Roadmap
Tools 🔧 AI Tools Directory 🔓 Open Source AI ⭐ Top GitHub Repos ✦ Claude Skill Repos 🚀 Ready-to-Deploy Projects
Build 🏗️ Build Hub 🎯 Master Prompts 🧩 RAG Agents 🚀 App Megaprompts
Workflows ⚡ All Workflows (22) 🎥 Text to Video 🎞️ Image to Video 🔊 Text to Speech ♻️ Automation
Resources 🧪 Colab Notebooks ⚙️ n8n Workflows 📈 Algo Trading 💰 Passive Income
🗂️ Browse All Topics About AItheGuru
← RAG agents
🧠 RAG Agent · Productivity

Personal Knowledge Base Agent

Chat with all your notes, PDFs, and documents. Ask "What did I read about X last month?" and get instant answers with source citations.

Productivity Intermediate Local laptopRaspberry Pi

Quick info

CategoryProductivity
DifficultyIntermediate
Deploy onLocal laptop

Get the code

Includes install commands in comments

What it does

Indexes Obsidian / Notion exports
Local LLM — fully private
Source citations with page numbers
Supports PDF, DOCX, MD, TXT

Stack

LlamaIndexOllama (local)ChromaDBGradio UI

Deploy on

✓ Local laptop✓ Raspberry Pi✓ Any VPS

Full source code

Install commands are in the top comments. Copy and run.

# Personal Knowledge Base RAG Agent # Stack: LlamaIndex + Ollama (local, private) + ChromaDB # Deploy: runs entirely on your laptop, zero API cost # Install: pip install llama-index llama-index-llms-ollama llama-index-embeddings-ollama chromadb gradio import os from pathlib import Path from llama_index.core import VectorStoreIndex, SimpleDirectoryReader, Settings from llama_index.core.storage.storage_context import StorageContext from llama_index.llms.ollama import Ollama from llama_index.embeddings.ollama import OllamaEmbedding from llama_index.vector_stores.chroma import ChromaVectorStore import chromadb import gradio as gr # ── CONFIG ──────────────────────────────────────────────────────── DOCS_DIR = "./knowledge_base" # Put all your notes/PDFs here CHROMA_DIR = "./chroma_db" # Where the vector index is stored LLM_MODEL = "llama3.2" # Any model in Ollama EMBED_MODEL = "nomic-embed-text" # Fast local embedding model # ── SETUP LOCAL LLM + EMBEDDINGS ───────────────────────────────── Settings.llm = Ollama(model=LLM_MODEL, request_timeout=120.0) Settings.embed_model = OllamaEmbedding(model_name=EMBED_MODEL) # ── BUILD OR LOAD VECTOR INDEX ──────────────────────────────────── def get_index(): chroma_client = chromadb.PersistentClient(path=CHROMA_DIR) chroma_collection = chroma_client.get_or_create_collection("knowledge_base") vector_store = ChromaVectorStore(chroma_collection=chroma_collection) # If index exists, load it if len(chroma_collection.get()["ids"]) > 0: print(f"Loading existing index with {len(chroma_collection.get()['ids'])} chunks...") storage_context = StorageContext.from_defaults(vector_store=vector_store) return VectorStoreIndex.from_vector_store(vector_store, storage_context=storage_context) # Otherwise, build from documents print(f"Building index from {DOCS_DIR}...") os.makedirs(DOCS_DIR, exist_ok=True) if not any(Path(DOCS_DIR).iterdir()): print("⚠️ No documents found. Add files to ./knowledge_base/ and restart.") return None documents = SimpleDirectoryReader( DOCS_DIR, recursive=True, required_exts=[".pdf", ".txt", ".md", ".docx"] ).load_data() print(f"Loaded {len(documents)} documents. Building index...") storage_context = StorageContext.from_defaults(vector_store=vector_store) index = VectorStoreIndex.from_documents( documents, storage_context=storage_context, show_progress=True ) print("✅ Index built successfully!") return index # ── QUERY ENGINE WITH CITATIONS ─────────────────────────────────── def create_query_engine(index): return index.as_query_engine( similarity_top_k=5, # Retrieve top 5 most relevant chunks streaming=False, response_mode="compact", # Compact answer with citations verbose=True ) # ── CHAT FUNCTION ───────────────────────────────────────────────── index = get_index() query_engine = create_query_engine(index) if index else None def chat(message, history): if not query_engine: return "⚠️ No documents found. Add files to ./knowledge_base/ directory." try: response = query_engine.query(message) # Format response with sources answer = str(response) sources = [] for node in response.source_nodes: filename = node.metadata.get("file_name", "Unknown") page = node.metadata.get("page_label", "") score = round(node.score, 3) if node.score else "N/A" sources.append(f"📄 {filename} {f'(p.{page})' if page else ''} — relevance: {score}") if sources: answer += "\n\n**Sources:**\n" + "\n".join(sources[:3]) return answer except Exception as e: return f"Error: {str(e)}" def reindex(progress=gr.Progress()): """Re-index all documents (run when you add new files)""" import shutil if os.path.exists(CHROMA_DIR): shutil.rmtree(CHROMA_DIR) global index, query_engine index = get_index() query_engine = create_query_engine(index) if index else None return "✅ Re-indexed successfully!" # ── GRADIO UI ───────────────────────────────────────────────────── with gr.Blocks(title="Knowledge Base Agent", theme=gr.themes.Soft()) as demo: gr.Markdown("# 🧠 Personal Knowledge Base Agent") gr.Markdown("Chat with all your notes, PDFs, and documents. Fully local — no API keys needed.") with gr.Row(): with gr.Column(scale=4): chatbot = gr.ChatInterface( chat, examples=[ "Summarise the key ideas in my documents", "What did I note about machine learning?", "Find everything related to Python", "What are my action items from recent notes?" ], title="" ) with gr.Column(scale=1): gr.Markdown("### Controls") reindex_btn = gr.Button("🔄 Re-index docs", variant="secondary") status = gr.Textbox(label="Status", interactive=False) reindex_btn.click(reindex, outputs=status) doc_count = len(list(Path(DOCS_DIR).rglob("*"))) if Path(DOCS_DIR).exists() else 0 gr.Markdown(f"**Documents folder:** ./knowledge_base/\n**Files found:** {doc_count}") if __name__ == "__main__": print("\n🚀 Starting Knowledge Base Agent...") print("📁 Add your documents to: ./knowledge_base/") print("🌐 Opening at: http://localhost:7860") demo.launch(server_name="0.0.0.0", server_port=7860)