← Back to DevBytes

Building a Knowledge Base Chatbot with CrewAI: Complete Guide

Building a Knowledge Base Chatbot with CrewAI: Complete Guide

Knowledge base chatbots have become essential tools for businesses looking to automate customer support, internal documentation access, and information retrieval. CrewAI, a powerful multi-agent framework built on top of LangChain, provides an elegant way to build intelligent chatbots that can reason, search, and respond accurately using your custom knowledge base. In this complete guide, you will learn how to build a production-ready knowledge base chatbot using CrewAI from scratch.

What Is CrewAI?

CrewAI is an open-source Python framework designed to orchestrate role-playing autonomous AI agents. Each agent has a specific role, goal, and backstory, and they work together as a "crew" to accomplish complex tasks. Unlike single-prompt chatbot solutions, CrewAI enables you to build collaborative agent systems where multiple specialized agents can share information, delegate tasks, and produce higher-quality outputs.

For knowledge base chatbots, CrewAI shines because it allows you to separate concerns: one agent can handle document retrieval, another can synthesize answers, and a third can verify accuracy. This modular approach leads to more reliable and trustworthy responses.

Why Use CrewAI for Knowledge Base Chatbots?

Traditional retrieval-augmented generation (RAG) pipelines often struggle with complex queries that require multi-step reasoning or cross-referencing multiple documents. CrewAI addresses these limitations through several key advantages:

Prerequisites and Setup

Before building the chatbot, ensure you have Python 3.10 or higher installed. You will also need an OpenAI API key or another supported LLM provider. Let us start by setting up the project environment.

Installing Dependencies

Create a new project directory and install the required packages:

mkdir crewai-kb-chatbot
cd crewai-kb-chatbot
python -m venv venv
source venv/bin/activate  # On Windows: venv\Scripts\activate

pip install crewai crewai-tools langchain chromadb sentence-transformers pypdf

Next, set your API keys as environment variables:

export OPENAI_API_KEY="your-openai-api-key-here"

Project Structure

Organize your project with the following structure:

crewai-kb-chatbot/
├── data/
│   └── knowledge_base/
│       ├── product_docs.pdf
│       ├── faq.md
│       └── policies.txt
├── crew/
│   ├── __init__.py
│   ├── agents.py
│   ├── tasks.py
│   ├── tools.py
│   └── crew.py
├── app.py
└── requirements.txt

Building the Knowledge Base

The foundation of any knowledge base chatbot is the data it can access. We will use ChromaDB as our vector store and sentence-transformers for embeddings. This combination is free, local, and performs well for most use cases.

Creating the Document Search Tool

CrewAI provides a built-in PDFSearchTool and DirectoryReadTool, but for maximum flexibility, we will build a custom tool that searches across multiple file types in a directory.

Create crew/tools.py:

import os
from crewai.tools import BaseTool
from langchain_community.document_loaders import (
    DirectoryLoader,
    TextLoader,
    PyPDFLoader,
    UnstructuredMarkdownLoader
)
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_community.vectorstores import Chroma

class KnowledgeBaseTool(BaseTool):
    name: str = "knowledge_base_search"
    description: str = (
        "Search the company knowledge base for relevant information. "
        "Input should be a search query string. Returns relevant "
        "document excerpts that can be used to answer user questions."
    )

    def _run(self, query: str) -> str:
        persist_dir = os.getenv("CHROMA_PERSIST_DIR", "./chroma_db")
        embeddings = HuggingFaceEmbeddings(
            model_name="all-MiniLM-L6-v2"
        )

        vectorstore = Chroma(
            persist_directory=persist_dir,
            embedding_function=embeddings
        )

        results = vectorstore.similarity_search_with_relevance_scores(
            query, k=5
        )

        if not results:
            return "No relevant documents found in the knowledge base."

        output = []
        for i, (doc, score) in enumerate(results, 1):
            source = doc.metadata.get("source", "unknown")
            output.append(
                f"--- Result {i} (Relevance: {score:.2f}) ---\n"
                f"Source: {source}\n"
                f"Content: {doc.page_content}\n"
            )

        return "\n".join(output)

def build_vectorstore(data_dir: str = "./data/knowledge_base"):
    """Build and persist the vector store from documents."""
    loaders = {
        ".txt": lambda p: TextLoader(p),
        ".md": lambda p: UnstructuredMarkdownLoader(p),
        ".pdf": lambda p: PyPDFLoader(p),
    }

    documents = []
    for root, _, files in os.walk(data_dir):
        for file in files:
            ext = os.path.splitext(file)[1].lower()
            if ext in loaders:
                path = os.path.join(root, file)
                try:
                    docs = loaders[ext](path).load()
                    documents.extend(docs)
                except Exception as e:
                    print(f"Error loading {path}: {e}")

    text_splitter = RecursiveCharacterTextSplitter(
        chunk_size=1000,
        chunk_overlap=200,
        separators=["\n\n", "\n", " ", ""]
    )

    chunks = text_splitter.split_documents(documents)

    embeddings = HuggingFaceEmbeddings(
        model_name="all-MiniLM-L6-v2"
    )

    vectorstore = Chroma.from_documents(
        documents=chunks,
        embedding=embeddings,
        persist_directory="./chroma_db"
    )

    print(f"Indexed {len(chunks)} chunks from {len(documents)} documents.")
    return vectorstore

Defining the Agents

Now we define the agents that will form our crew. For a knowledge base chatbot, we will create three specialized agents: a researcher, an answer synthesizer, and a quality reviewer.

Create crew/agents.py:

from crewai import Agent, LLM

# Define the LLM
llm = LLM(
    model="gpt-4o-mini",
    temperature=0.3,
    max_tokens=2000
)

researcher = Agent(
    role="Knowledge Base Researcher",
    goal="Find the most relevant and accurate information from the "
         "knowledge base to answer the user's question",
    backstory=(
        "You are an expert research analyst with years of experience "
        "in information retrieval. You have a keen eye for finding "
        "the most relevant documents and extracting key information. "
        "You never fabricate information and always cite your sources."
    ),
    tools=[],  # Tools will be injected at crew creation
    llm=llm,
    verbose=True,
    allow_delegation=False
)

synthesizer = Agent(
    role="Answer Synthesizer",
    goal="Create clear, accurate, and helpful answers based on the "
         "research findings",
    backstory=(
        "You are a skilled technical writer who specializes in "
        "explaining complex topics in simple terms. You always "
        "structure your answers logically and include relevant "
        "details without overwhelming the reader."
    ),
    llm=llm,
    verbose=True,
    allow_delegation=False
)

reviewer = Agent(
    role="Quality Reviewer",
    goal="Ensure the answer is accurate, complete, and does not "
         "contain hallucinated information",
    backstory=(
        "You are a meticulous quality assurance specialist. You "
        "cross-check every claim against the source material and "
        "flag any inconsistencies or unsupported statements. Your "
        "standards are uncompromising."
    ),
    llm=llm,
    verbose=True,
    allow_delegation=False
)

Defining the Tasks

Each agent needs a task to perform. Tasks define what each agent should accomplish and what output they should produce.

Create crew/tasks.py:

from crewai import Task

def create_research_task(question: str):
    return Task(
        description=(
            f"Research the following question using the knowledge "
            f"base search tool: '{question}'\n\n"
            f"Steps:\n"
            f"1. Search the knowledge base with the most relevant query\n"
            f"2. If initial results are insufficient, try alternative "
            f"search terms\n"
            f"3. Collect all relevant excerpts and organize them by topic\n"
            f"4. Note the source of each piece of information\n\n"
            f"Return a structured summary of all findings with source "
            f"references."
        ),
        expected_output=(
            "A structured research report containing relevant excerpts "
            "from the knowledge base, organized by topic, with source "
            "citations for each piece of information."
        ),
        agent=None  # Will be assigned at crew creation
    )

def create_synthesis_task(question: str):
    return Task(
        description=(
            f"Using the research findings, write a comprehensive "
            f"answer to the user's question: '{question}'\n\n"
            f"Requirements:\n"
            f"- Base your answer ONLY on the research findings\n"
            f"- Do not include information not present in the sources\n"
            f"- Structure the answer with clear paragraphs\n"
            f"- Include relevant details and examples from the sources\n"
            f"- If the sources do not contain enough information, "
            f"explicitly state what is missing\n\n"
            f"Write the answer as if speaking directly to the user."
        ),
        expected_output=(
            "A clear, well-structured answer to the user's question, "
            "based exclusively on the research findings, with "
            "appropriate detail and context."
        ),
        agent=None
    )

def create_review_task(question: str):
    return Task(
        description=(
            f"Review the synthesized answer for the question: "
            f"'{question}'\n\n"
            f"Check for:\n"
            f"- Factual accuracy against the research findings\n"
            f"- Completeness (does it fully address the question?)\n"
            f"- Hallucinations (any claims not supported by sources?)\n"
            f"- Clarity and readability\n\n"
            f"If issues are found, provide a corrected version. "
            f"If the answer is satisfactory, return it as-is with "
            f"a brief approval note."
        ),
        expected_output=(
            "The final reviewed and approved answer, or a corrected "
            "version if issues were found, along with a brief note "
            "on any changes made."
        ),
        agent=None
    )

Assembling the Crew

Now we bring everything together by creating the crew that orchestrates the agents and tasks.

Create crew/crew.py:

from crewai import Crew, Process
from crew.agents import researcher, synthesizer, reviewer
from crew.tasks import (
    create_research_task,
    create_synthesis_task,
    create_review_task
)
from crew.tools import KnowledgeBaseTool

def create_kb_crew(question: str) -> Crew:
    # Instantiate the knowledge base tool
    kb_tool = KnowledgeBaseTool()

    # Assign the tool to the researcher agent
    researcher.tools = [kb_tool]

    # Create tasks and assign agents
    research_task = create_research_task(question)
    research_task.agent = researcher

    synthesis_task = create_synthesis_task(question)
    synthesis_task.agent = synthesizer

    review_task = create_review_task(question)
    review_task.agent = reviewer

    # Build the crew
    crew = Crew(
        agents=[researcher, synthesizer, reviewer],
        tasks=[research_task, synthesis_task, review_task],
        process=Process.sequential,
        verbose=True
    )

    return crew

def ask_question(question: str) -> str:
    """Ask a question to the knowledge base chatbot."""
    crew = create_kb_crew(question)
    result = crew.kickoff()
    return str(result)

Building the Chatbot Interface

Now let us create a simple command-line interface that allows users to interact with the chatbot. We will also include a setup step to build the vector store on first run.

Create app.py:

import os
import sys

# Add project root to path
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))

from crew.tools import build_vectorstore
from crew.crew import ask_question

def initialize_knowledge_base():
    """Initialize the vector store if it does not exist."""
    chroma_dir = "./chroma_db"
    if not os.path.exists(chroma_dir) or not os.listdir(chroma_dir):
        print("Building knowledge base index for the first time...")
        print("This may take a few minutes depending on your data.\n")
        build_vectorstore("./data/knowledge_base")
        print("Knowledge base ready!\n")
    else:
        print("Knowledge base already indexed.\n")

def main():
    print("=" * 60)
    print("  Knowledge Base Chatbot powered by CrewAI")
    print("=" * 60)
    print("Type your question and press Enter.")
    print("Type 'quit' or 'exit' to stop.\n")

    initialize_knowledge_base()

    while True:
        try:
            question = input("\nYou: ").strip()

            if question.lower() in ["quit", "exit"]:
                print("Goodbye!")
                break

            if not question:
                continue

            print("\nBot: Thinking...")
            answer = ask_question(question)
            print(f"\nBot: {answer}")

        except KeyboardInterrupt:
            print("\n\nGoodbye!")
            break
        except Exception as e:
            print(f"\nError: {e}")

if __name__ == "__main__":
    main()

Running the Chatbot

To run the chatbot, first place your documents in the data/knowledge_base/ directory. These can be PDF files, Markdown files, or plain text files. Then execute:

python app.py

On the first run, the application will index your documents into ChromaDB. Subsequent runs will load the existing index directly. Here is an example interaction:

============================================================
  Knowledge Base Chatbot powered by CrewAI
============================================================

Knowledge base already indexed.

You: What is our refund policy?

Bot: Thinking...

Bot: According to the company policies document, our refund policy
states that customers can request a full refund within 30 days of
purchase. After 30 days, partial refunds may be available on a
case-by-case basis. To request a refund, customers should contact
support@company.com with their order number and reason for the
refund request. Refunds are typically processed within 5-7
business days.

Adding a Web Interface with FastAPI

For production use, a web API is often more practical than a CLI. Let us add a FastAPI endpoint so the chatbot can be integrated into web applications.

Install FastAPI and Uvicorn:

pip install fastapi uvicorn

Create api.py:

from fastapi import FastAPI
from pydantic import BaseModel
from crew.crew import ask_question
from crew.tools import build_vectorstore
import os

app = FastAPI(title="Knowledge Base Chatbot API")

class Question(BaseModel):
    query: str

class Answer(BaseModel):
    response: str
    status: str

@app.on_event("startup")
async def startup_event():
    chroma_dir = "./chroma_db"
    if not os.path.exists(chroma_dir) or not os.listdir(chroma_dir):
        build_vectorstore("./data/knowledge_base")

@app.post("/ask", response_model=Answer)
async def ask(question: Question):
    try:
        result = ask_question(question.query)
        return Answer(response=result, status="success")
    except Exception as e:
        return Answer(response=str(e), status="error")

@app.get("/health")
async def health():
    return {"status": "healthy"}

Run the API server:

uvicorn api:app --reload --port 8000

You can now send questions via HTTP:

curl -X POST http://localhost:8000/ask \
  -H "Content-Type: application/json" \
  -d '{"query": "What is the refund policy?"}'

Best Practices

To get the most out of your CrewAI knowledge base chatbot, follow these best practices:

Document Preparation

Chunking Strategy

Agent Configuration

Performance Optimization

Handling Edge Cases

Advanced: Adding Conversation Memory

By default, each question is processed independently. To support follow-up questions, you can add conversation memory by maintaining chat history and passing it as context.

Update crew/crew.py with memory support:

from crewai import Crew, Process
from crew.agents import researcher, synthesizer, reviewer
from crew.tasks import (
    create_research_task,
    create_synthesis_task,
    create_review_task
)
from crew.tools import KnowledgeBaseTool

conversation_history = []

def create_kb_crew(question: str, history: list = None) -> Crew:
    kb_tool = KnowledgeBaseTool()
    researcher.tools = [kb_tool]

    # Include conversation context in the question
    context = ""
    if history:
        recent = history[-4:]  # Keep last 4 exchanges
        context = "\n\nPrevious conversation:\n"
        for exchange in recent:
            context += f"User: {exchange['question']}\n"
            context += f"Bot: {exchange['answer']}\n"

    full_question = f"{question}{context}" if context else question

    research_task = create_research_task(full_question)
    research_task.agent = researcher

    synthesis_task = create_synthesis_task(full_question)
    synthesis_task.agent = synthesizer

    review_task = create_review_task(full_question)
    review_task.agent = reviewer

    crew = Crew(
        agents=[researcher, synthesizer, reviewer],
        tasks=[research_task, synthesis_task, review_task],
        process=Process.sequential,
        verbose=True
    )

    return crew

def ask_question(question: str) -> str:
    """Ask a question with conversation memory."""
    crew = create_kb_crew(question, conversation_history)
    result = crew.kickoff()
    answer = str(result)

    # Store in history
    conversation_history.append({
        "question": question,
        "answer": answer
    })

    return answer

def clear_history():
    """Clear conversation history."""
    conversation_history.clear()

Monitoring and Debugging

CrewAI provides verbose logging that shows each agent's thought process, tool usage, and output. Enable verbose mode by setting verbose=True on both agents and the crew. For deeper observability, you can integrate LangSmith or Phoenix to trace the full execution pipeline.

Here is a simple logging wrapper you can add to track performance:

import time
import logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

def ask_question_with_logging(question: str) -> str:
    start_time = time.time()
    logger.info(f"Processing question: {question}")

    try:
        answer = ask_question(question)
        elapsed = time.time() - start_time
        logger.info(f"Answer generated in {elapsed:.2f}s")
        logger.info(f"Answer length: {len(answer)} characters")
        return answer
    except Exception as e:
        logger.error(f"Error processing question: {e}")
        raise

Conclusion

Building a knowledge base chatbot with CrewAI gives you a powerful, modular, and extensible system that goes beyond simple retrieval. By leveraging multiple specialized agents, you can ensure that answers are well-researched, clearly synthesized, and quality-checked before reaching the user. The combination of ChromaDB for vector storage, custom CrewAI tools for retrieval, and a sequential agent workflow creates a robust foundation that you can adapt to any domain. Start with the basic setup described in this guide, then iterate on your document collection, chunking strategy, and agent configurations to fine-tune performance for your specific use case. As your needs grow, consider adding hierarchical processes, additional agents for specialized tasks, and integration with external APIs to create an even more capable knowledge base assistant.

— Ad —

Google AdSense will appear here after approval

← Back to all articles