3 min read

RAG Chatbot for Markdown Files

This guide demonstrates how to build a Retrieval-Augmented Generation (RAG) chatbot. The bot loads a directory of Markdown (.md) files, splits them into chunks, creates a searchable vector index, and uses an LLM to answer questions based on that specific data. This is perfect for creating a bot that answers questions from your documentation or FAQ files.

Modules Used:

  • langchain: The framework for building LLM applications.
  • langchain-openai: Integration for OpenAI models and embeddings.
  • langchain-chroma: Integration for the Chroma vector database.
  • python-dotenv: To manage API keys.

Prerequisites

  1. OpenAI API Key: You need an API key from OpenAI.
  2. Data: A folder containing .md files (e.g., docs/ or notes/).

Installation

pip install langchain langchain-community langchain-openai langchain-chroma python-dotenv

Setup

Create a .env file:

OPENAI_API_KEY=sk-your-key-here

The Code

Save this as doc_bot.py.

import os
import argparse
import sys
from dotenv import load_dotenv

# LangChain Imports
from langchain_community.document_loaders import DirectoryLoader, TextLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_chroma import Chroma
from langchain.chains import create_retrieval_chain
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain_core.prompts import ChatPromptTemplate

# 1. Load Config
load_dotenv()
if not os.getenv("OPENAI_API_KEY"):
    print("Error: OPENAI_API_KEY not found in .env")
    sys.exit(1)

def create_knowledge_base(directory):
    print(f"Loading .md files from '{directory}'...")

    # Load documents
    # glob="**/*.md" finds all markdown files recursively
    loader = DirectoryLoader(directory, glob="**/*.md", loader_cls=TextLoader)
    docs = loader.load()

    if not docs:
        print("No markdown files found.")
        return None

    print(f"Loaded {len(docs)} documents. Splitting text...")

    # Split documents into chunks for embedding
    text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
    splits = text_splitter.split_documents(docs)

    print(f"Created {len(splits)} chunks. Building vector store...")

    # Create Vector Store (In-memory for this example)
    vectorstore = Chroma.from_documents(documents=splits, embedding=OpenAIEmbeddings())
    return vectorstore

def start_chat(vectorstore):
    # Initialize LLM
    llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)

    # Create Retrieval Chain
    # This prompt tells the LLM to use the context provided
    prompt = ChatPromptTemplate.from_template("""
    Answer the following question based only on the provided context:

    <context>
    {context}
    </context>

    Question: {input}
    """)

    document_chain = create_stuff_documents_chain(llm, prompt)
    retriever = vectorstore.as_retriever()
    retrieval_chain = create_retrieval_chain(retriever, document_chain)

    print("\n--- Doc Bot Ready (Type 'quit' to exit) ---")
    while True:
        query = input("\nQuestion: ")
        if query.lower() in ["quit", "exit"]:
            break

        response = retrieval_chain.invoke({"input": query})
        print(f"\nAnswer: {response['answer']}")

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="RAG Chatbot for Markdown Files")
    parser.add_argument("directory", help="Directory containing .md files")

    args = parser.parse_args()

    vs = create_knowledge_base(args.directory)
    if vs:
        start_chat(vs)

Usage

  1. Prepare Data: Ensure you have a folder (e.g., my_docs) with some Markdown files inside.
  2. Run the Bot:
    python doc_bot.py ./my_docs
  3. Ask Questions: The bot will answer based only on the information found in your Markdown files.

programming/python/python