An end-to-end Retrieval-Augmented Generation (RAG) chatbot designed for enterprise knowledge bases. The system ingests local document repositories (SharePoint substitutes), project emails, and QA records, builds a vector index, offers domain-aware semantic search with citations, and exposes an API surface for chatbot queries, data crawling, and response evaluation.
Key highlights:
- LangChain-driven RAG with domain filtering for knowledge, tender drafting, and financial queries.
- Data crawler that processes structured and unstructured files, generates synthetic Q&A pairs, and persists embeddings in ChromaDB.
- Evaluation pipeline leveraging
deepevalwhen configured, with deterministic heuristics fallback. - FastAPI service providing three composable endpoints and returning consistent metadata (IDs, timestamps, cost estimates).
- Deployment ready with Docker image and GitHub Actions CI pipeline.
- Architecture
- Directory Layout
- Prerequisites
- Configuration (.env)
- Local Development
- API Endpoints
- Evaluation Strategy
- Deployment (Docker)
- Continuous Integration
- Testing
┌──────────────────────┐ ┌─────────────────────┐
│ Data Crawler │ │ Vector Store │
│ (utils/data_ing... )│ ───────▶ │ Chroma (persistent)│
└─────────┬────────────┘ └─────────┬───────────┘
│ │
▼ ▼
┌──────────────────────┐ ┌─────────────────────┐
│ Generated Q&A cache │ │ Domain-aware RAG │
│ (generated_qa/*.json) │ (utils/rag.py) │
└─────────┬────────────┘ └─────────┬───────────┘
│ │
▼ ▼
┌────────────────────────────────────────────────────────┐
│ FastAPI Service │
│ /api/crawler /api/chatbot /api/evaluation /health │
└────────────────────────────────────────────────────────┘
- Crawls the
data/directory (proxy for SharePoint/project email storage). - Supports
.txt,.md,.csv,.json,.pdf,.eml,.logfiles. - Produces document chunks and optional synthetic Q&A pairs that are indexed into Chroma.
- Uses LangChain
RetrievalQAwith domain-aware filtering (knowledge,tender,finance). - Provides citations by returning text snippets and source paths for top-
kmatches. - Computes token-based cost estimates via a lightweight heuristic.
- Invokes
deepevalmetrics (faithfulness, contextual precision, answer relevancy) when an API key is supplied. - Falls back to deterministic overlap/coverage checks without external dependencies.
.
├── controllers/ # Request orchestration logic
├── routers/ # FastAPI routers for each endpoint
├── utils/ # Config, RAG, ingestion, evaluation, embeddings, etc.
├── data/
│ ├── knowledge/ # Sample knowledge base markdown
│ ├── tender/ # Tender drafting guidance
│ ├── finance/ # Financial FAQs (CSV)
│ └── web_samples/ # Mock website HTML pages for crawler demo
├── tests/ # Pytest suite covering API flows
├── main.py # FastAPI application factory
├── pyproject.toml # Project dependencies and build metadata
├── Dockerfile # Production-ready container image
├── .dockerignore # Docker build context exclusions
└── .github/workflows/ # GitHub Actions CI pipeline
Local knowledge sources should be placed under data/ (auto-created on first run). Generated Q&A artifacts are stored in generated_qa/, and Chroma persistence lives at .chroma_store/ by default.
- Python 3.10
uvpackage manager (pip install uv)- Git (for cloning and running CI locally)
Optional (for full-featured evaluation and LLM responses):
- Access to a GPT-compatible model (e.g., OpenAI) and API key, or
- A local open-source model server via Ollama (no API key required).
All runtime configuration is managed via environment variables loaded through Pydantic settings (utils/config.py). Create a .env file in the project root with the variables you require. The .env file is already ignored by Git via .gitignore to keep secrets out of version control.
# .env
# Application metadata
CHATBOT_APP_NAME="Enterprise RAG Chatbot"
CHATBOT_APP_ENV="development"
# LLM configuration
# Provider options: placeholder | openai | ollama
CHATBOT_LLM_PROVIDER="placeholder"
# Required only for openai provider
CHATBOT_LLM_API_KEY="sk-xxx"
# e.g. gpt-4o-mini (openai) or llama3.2 (ollama)
CHATBOT_LLM_MODEL="gpt5-mini"
CHATBOT_OLLAMA_BASE_URL="http://127.0.0.1:11434"
CHATBOT_OLLAMA_TIMEOUT_SECONDS=300
# Optional cost estimation
CHATBOT_LLM_COST_PER_1K_TOKENS=0.002
# Enable deepeval metrics when API key is present
CHATBOT_ENABLE_DEEPEVAL=true
# Chroma telemetry
ANONYMIZED_TELEMETRY=False
# Storage locations (override if needed)
CHATBOT_CHROMA_PERSIST_DIRECTORY=.chroma_store
CHATBOT_DATA_DIRECTORY=data
CHATBOT_QA_CACHE_DIRECTORY=generated_qaNote: If
CHATBOT_LLM_PROVIDER=ollama, the app uses your local open-source model endpoint and does not requireCHATBOT_LLM_API_KEY.If no provider is configured, the application uses a deterministic placeholder LLM and heuristic evaluation, allowing the stack to run fully offline.
.env.uatand.env.productionare committed as deployment templates for CI/CD. Keep real credentials out of these files (use GitHub Environment secrets for sensitive values).
-
Install dependencies
uv pip install --system .[dev]
-
Prepare data
- Place PDFs, emails (
.eml), CSVs, etc. intodata/knowledge/,data/tender/, ordata/finance/to seed domain-specific content.
- Place PDFs, emails (
-
Run the API locally
uvicorn main:app --reload
The service listens at
http://127.0.0.1:8000. -
Explore interactive docs
- Swagger UI:
http://127.0.0.1:8000/docs - Redoc:
http://127.0.0.1:8000/redoc
These documentation portals let you inspect request/response schemas and execute API calls (crawler, chatbot, evaluation, health) directly from the browser.
- Swagger UI:
-
Trigger ingestion (optional before first chatbot query)
curl -X POST http://127.0.0.1:8000/api/crawler -H "Content-Type: application/json" \ -d '{"refresh_index": true, "max_qas_per_document": 2}'
Simple readiness probe returning { "status": "ok" }.
Kicks off the ingestion pipeline.
{
"refresh_index": true,
"max_qas_per_document": 2,
"website_urls": [
"https://www.iso.org/about-us.html",
"https://support.microsoft.com/en-us/topic/purchase-approval-policy-sample-1234567890",
"https://www.un.org/en/about-us"
]
}Response includes documents indexed, QA pairs generated, and metadata with:
idcreatedAttimeTakenapiCost
{
"id": "req_f3c90b4d5a2e4f0fbf7b33d01b7b2f41",
"createdAt": "2026-02-21T07:30:00.000000+00:00",
"timeTaken": 215.37,
"apiCost": null,
"cost": {
"input_tokens": 0,
"output_tokens": 0,
"total_tokens": 0,
"estimated_cost_usd": 0.0
},
"documents_indexed": 6,
"qa_pairs_generated": 6,
"qa_output_path": "generated_qa/qa_pairs_20260221T073000Z.json"
}ℹ️ Tip: The listed URLs are public pages with stable text content, making them suitable demos for external website ingestion.
Retrieves an answer with citations from the RAG system.
{
"query": "What are the tender drafting guidelines?",
"domain": "tender",
"top_k": 4
}Returns a structure containing:
answercitations(file path, snippet, optional score)costestimation (token counts, USD estimate)idcreatedAttimeTakenapiCost
{
"id": "req_0b9ad3c6f1c8428ab6d7098a994c1df3",
"createdAt": "2026-02-21T07:31:12.120000+00:00",
"timeTaken": 342.58,
"apiCost": 0.0012,
"cost": {
"input_tokens": 180,
"output_tokens": 220,
"total_tokens": 400,
"estimated_cost_usd": 0.0012
},
"answer": "Tender drafting should highlight compliance with Policy A, including CFO approval for spends above USD 10,000...",
"citations": [
{
"source": "data/tender/tender_drafting_guide.md",
"snippet": "Tender drafting should highlight compliance with policy A...",
"score": 0.87
},
{
"source": "data/web_samples/tender_hub.html",
"snippet": "Include a compliance matrix referencing Policy A approval checkpoints...",
"score": 0.79
}
]
}Evaluates a question/answer pair against ground-truth references.
{
"question": "Summarise policy A",
"answer": "Policy A mandates CFO approval for purchases.",
"references": [{ "context": "Policy A requires CFO approval." }]
}Responds with per-metric scores (reference overlap, question coverage, or deepeval metrics) plus an average. Metadata fields include id, createdAt, timeTaken, and apiCost.
{
"id": "req_58f921de4b7a4a6693f1f4a7a4cd9151",
"createdAt": "2026-02-21T07:32:05.450000+00:00",
"timeTaken": 129.44,
"apiCost": null,
"cost": {
"input_tokens": 0,
"output_tokens": 0,
"total_tokens": 0,
"estimated_cost_usd": 0.0
},
"metrics": [
{
"metric": "ReferenceOverlap",
"score": 0.82,
"passed": true,
"feedback": "Checks for hallucination via reference overlap."
},
{
"metric": "QuestionCoverage",
"score": 0.74,
"passed": true,
"feedback": "Ensures answer addresses major terms in the question."
}
],
"average_score": 0.78
}-
deepeval metrics (default when API key & flag supplied)
- Faithfulness: detects hallucinations by comparing answer vs. retrieved context.
- Contextual Precision: ensures answer leverages relevant knowledge snippets.
- Answer Relevancy: measures direct alignment with the posed question.
-
Heuristic fallback (offline mode)
- Reference overlap ratio to flag unsupported statements.
- Question term coverage to detect incomplete answers.
These results are exposed via /api/evaluation for CI or human review workflows.
Build and run using the provided Dockerfile:
docker build -t enterprise-chatbot .
docker run -it --rm -p 8000:8000 --env-file .env enterprise-chatbot:latestThe container installs dependencies, bundles the application code, and starts Uvicorn on port 8000.
If you want open-source inference without GPT keys, run with Ollama mode enabled:
docker run -it --rm \
-p 8000:8000 \
-p 11434:11434 \
--env-file .env \
-e CHATBOT_LLM_PROVIDER=ollama \
-e CHATBOT_LLM_MODEL=llama3.2 \
-e CHATBOT_OLLAMA_BASE_URL=http://127.0.0.1:11434 \
-e CHATBOT_OLLAMA_TIMEOUT_SECONDS=600 \
enterprise-chatbot:latestIf you need live code editing via bind mount, use:
docker run -it --rm -v "$(pwd):/app" -w /app -p 8000:8000 --env-file .env enterprise-chatbot:latestOn Docker Desktop for macOS, bind mounts require the folder to be shared in Docker settings.
In Ollama mode, container startup will:
- start the local Ollama server,
- pull
CHATBOT_LLM_MODEL(for examplellama3.2), - then start the FastAPI app.
CI note: Pulling an Ollama model can significantly increase startup time on first run. For CI pipelines, prefer
CHATBOT_LLM_PROVIDER=placeholder(or a pre-warmed/cached Ollama setup) to keep test jobs fast and deterministic.
The GitHub Actions workflow (.github/workflows/ci.yml) runs on:
- Pushes and pull requests to any branch
- Manual dispatch
- Tags ending in
-uator-prod(trigger deployments)
- build-and-test: Sets up Python 3.10, installs
uv, installs dependencies, and runs pytest. - deploy-uat: Runs on tags like
0.0.1-uat; builds Docker image and performs sample UAT deployment. - deploy-prod: Runs on tags like
0.0.1-prod; builds Docker image and performs sample production deployment.
Both deploy jobs reference GitHub environments (uat, production) for protection rules and secrets. Ensure these environments exist in the repository settings before tagging releases.
Execute the automated tests locally with:
pytest --maxfail=1 --disable-warnings -qThe tests spin up the FastAPI application, seed synthetic documents, and validate crawler, chatbot, and evaluation endpoints. Use these tests as a foundation for further coverage as your project evolves.
- Integrate Azure-friendly secrets management (e.g., Key Vault) when deploying in production.
- Extend the crawler to connect directly to SharePoint APIs or email services.
- Instrument request/response logging and observability in line with enterprise compliance requirements.