An end-to-end RAG (Retrieval-Augmented Generation) knowledge assistant with built-in evaluation harness to iterate on chunking and retrieval quality.
- FastAPI Web App with simple HTML/JS UI for document upload and chat
- SSE Streaming chat endpoint for real-time responses
- Kafka-based ingestion pipeline for scalable document processing
- PostgreSQL + pgvector for efficient vector similarity search
- Configurable embedding models (default:
intfloat/multilingual-e5-small) - Local inference via vLLM with CPU support (configurable model)
- Dynamic chunking strategies with profile management
- Evaluation harness with Golden Set (50 items) and comprehensive metrics
- Docker Compose setup for easy local development
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β User Interface β
β (Browser / FastAPI UI) β
ββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β FastAPI Web API β
β β’ Document Upload β’ Chat (SSE Stream) β’ Chunk Profiles β
β β’ Reindex Trigger β’ Health Check β
ββββββββ¬βββββββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββ
β β
β Kafka Events β Query
β β
βΌ βΌ
βββββββββββββββββββ ββββββββββββββββββββββββ
β Kafka Topics β β PostgreSQL β
β β’ ingest β β + pgvector β
β β’ reindex β β β’ Documents β
ββββββββββ¬βββββββββ β β’ Chunks β
β β β’ Embeddings β
β β β’ Profiles β
βΌ ββββββββββββ¬ββββββββββββ
βββββββββββββββββββ β
β Ingestion β β Vector Search
β Worker βββββββββββββββββββββββββββ
β β’ Load Docs β
β β’ Extract β ββββββββββββββββββββββββ
β β’ Chunk β β vLLM (OpenAI API) β
β β’ Embed β β β’ Qwen2.5-0.5B β
βββββββββββββββββββ β β’ CPU Inference β
β β’ Streaming β
ββββββββββββββββββββββββ
- Docker & Docker Compose
- Python 3.11+ (for local development)
- 8GB+ RAM recommended
- For vLLM: additional compute resources (can run separately)
git clone https://github.com/IGabriel/ai-knowledge-bench.git
cd ai-knowledge-bench
# Copy environment file
cp .env.example .env
# Optional: Adjust settings in .envcd deploy
docker compose up -d
# Check status
docker compose ps
# View logs
docker compose logs -f web_api
docker compose logs -f worker_ingestServices will be available at:
- Web UI: http://localhost:8080
- API Docs: http://localhost:8080/docs
- PostgreSQL: localhost:5432
- Kafka: localhost:9092
- Redis: localhost:6379
The vLLM service is commented out in docker-compose.yml as it can be resource-intensive. You have two options:
Option A: Run vLLM in Docker (uncomment in docker-compose.yml)
Option B: Run vLLM Separately (Recommended for development)
# Install vLLM
pip install vllm
# Run vLLM server with CPU
vllm serve Qwen/Qwen2.5-0.5B-Instruct \
--dtype auto \
--device cpu \
--max-model-len 2048 \
--port 8000
# Or use a slightly larger model if you have resources
vllm serve Qwen/Qwen2.5-1.5B-Instruct \
--dtype auto \
--device cpu \
--max-model-len 2048 \
--port 8000Update .env with vLLM URL:
VLLM_BASE_URL=http://localhost:8000/v1
The database will be automatically initialized with migrations when the web_api container starts. To run migrations manually:
# Inside web_api container
docker compose exec web_api alembic upgrade head
# Or locally
pip install -r requirements.txt
alembic upgrade headVia Web UI:
- Open http://localhost:8080
- Upload a document (PDF, DOCX, PPTX, XLSX, HTML, MD, TXT)
- Wait for ingestion (check worker logs:
docker compose logs -f worker_ingest) - Ask questions in the chat interface
Via API:
# Upload document
curl -X POST "http://localhost:8080/v1/documents" \
-F "file=@/path/to/document.pdf"
# List documents
curl "http://localhost:8080/v1/documents"
# Chat (SSE stream)
curl -N "http://localhost:8080/v1/chat/stream?query=What%20is%20this%20document%20about?"Chunk profiles allow you to experiment with different chunking strategies:
# List profiles
curl "http://localhost:8080/v1/chunk-profiles"
# Create new profile
curl -X POST "http://localhost:8080/v1/chunk-profiles" \
-H "Content-Type: application/json" \
-d '{
"name": "large-chunks",
"description": "Larger chunks for broader context",
"chunk_size": 1024,
"chunk_overlap": 256
}'
# Activate profile
curl -X POST "http://localhost:8080/v1/chunk-profiles/{profile_id}/activate"
# Reindex documents with new profile
curl -X POST "http://localhost:8080/v1/reindex" \
-H "Content-Type: application/json" \
-d '{
"chunk_profile_id": "{profile_id}"
}'Run evaluations to measure RAG system quality:
# Get active chunk profile ID first
PROFILE_ID=$(curl -s "http://localhost:8080/v1/chunk-profiles" | jq -r '.[] | select(.is_active==true) | .id')
# Run evaluation
docker compose exec web_api python -m packages.eval.run \
--dataset eval/golden_set_v1.jsonl \
--profile "$PROFILE_ID" \
--topk 5 \
--embedding intfloat/multilingual-e5-small \
--llm Qwen/Qwen2.5-0.5B-Instruct
# Or locally (with all dependencies installed)
python -m packages.eval.run \
--dataset eval/golden_set_v1.jsonl \
--profile YOUR_PROFILE_ID \
--topk 5The evaluation harness computes:
- Embedding Coverage: Ratio of retrieved chunks (should be β₯ 0.99)
- Recall@K: Fraction of expected sources found in top-K results (strict match)
- MRR (Mean Reciprocal Rank): Position of first correct result
- Semantic Similarity: Cosine similarity between expected and generated answers
- Semantic Correct Rate: % of answers above similarity threshold
- Citation Hit Rate: % of questions with at least one correct source cited
- Composite Score: Weighted combination (30% recall, 20% MRR, 30% semantic, 20% citation)
The golden set (eval/golden_set_v1.jsonl) contains 50 test questions:
- 25 from PDFs
- 10 from PPTX
- 5 from XLSX
- 5 from DOCX
- 5 from MD/HTML/TXT
Before running evaluation, replace UUID placeholders with actual document IDs from your uploaded documents.
# Install dependencies
pip install -r requirements.txt
# Install dev dependencies
pip install -e ".[dev]"
# Run linters
black .
ruff check .
# Run tests (if implemented)
pytestai-knowledge-bench/
βββ apps/
β βββ web_api/ # FastAPI application
β βββ worker_ingest/ # Kafka consumer for ingestion
βββ packages/
β βββ core/ # Shared core functionality
β β βββ config.py # Configuration management
β β βββ database.py # SQLAlchemy models
β β βββ loaders.py # Document loaders
β β βββ chunking.py # Chunking strategies
β β βββ embeddings.py # Embedding generation
β β βββ retrieval.py # Vector search & retrieval
β β βββ vllm_client.py# vLLM client wrapper
β β βββ kafka_utils.py# Kafka utilities
β βββ eval/ # Evaluation harness
βββ deploy/
β βββ docker-compose.yml
β βββ init-db.sh
βββ docs/ # Documentation
βββ eval/ # Golden set and test data
βββ alembic/ # Database migrations
βββ data/ # Runtime data (uploads, etc.)
Key environment variables (see .env.example):
# Database
DATABASE_URL=postgresql://bench_user:bench_pass@localhost:5432/ai_knowledge_bench
# Kafka
KAFKA_BOOTSTRAP_SERVERS=localhost:9092
KAFKA_TOPIC_INGEST=document.ingest.requested
KAFKA_TOPIC_REINDEX=document.reindex.requested
# Embeddings
EMBEDDING_MODEL=intfloat/multilingual-e5-small
EMBEDDING_DIMENSION=384
EMBEDDING_DEVICE=cpu
# vLLM
VLLM_BASE_URL=http://localhost:8000/v1
VLLM_MODEL=Qwen/Qwen2.5-0.5B-Instruct
# Chunking
DEFAULT_CHUNK_SIZE=512
DEFAULT_CHUNK_OVERLAP=128
# Retrieval
DEFAULT_TOP_K=5
DEFAULT_SIMILARITY_THRESHOLD=0.7- PDF (.pdf) - Page-based sections
- Word (.docx) - Heading-based sections
- PowerPoint (.pptx) - Slide-based sections
- Excel (.xlsx, .xls) - Sheet-based sections
- HTML (.html, .htm) - Heading-based sections
- Markdown (.md) - Heading-based sections
- Text (.txt) - Single section
1. vLLM connection errors
- Ensure vLLM is running and accessible at
VLLM_BASE_URL - Check vLLM logs for model loading issues
- For CPU inference, expect slower performance
2. Kafka connection issues
# Check Kafka health
docker compose exec kafka kafka-broker-api-versions.sh --bootstrap-server localhost:90923. Database migration errors
# Reset database (WARNING: deletes all data)
docker compose down -v
docker compose up -d postgres
docker compose exec web_api alembic upgrade head4. Worker not processing documents
# Check worker logs
docker compose logs -f worker_ingest
# Check Kafka topics
docker compose exec kafka kafka-topics.sh --list --bootstrap-server localhost:9092
# Check messages in topic
docker compose exec kafka kafka-console-consumer.sh \
--bootstrap-server localhost:9092 \
--topic document.ingest.requested \
--from-beginning5. Out of memory errors
- Reduce
EMBEDDING_BATCH_SIZEin.env - Use a smaller vLLM model
- Increase Docker memory limits
# All services
docker compose logs -f
# Specific service
docker compose logs -f web_api
docker compose logs -f worker_ingest
# Database logs
docker compose logs -f postgresSee docs/design.md for detailed information about:
- Chunk profile system
- Embedding strategy (separate tables per model)
- Strict source_ref matching rules
- Retrieval and ranking
- Embedding Generation: CPU-based, ~100-500 chunks/minute (varies by hardware)
- Vector Search: ivfflat index, ~10-50ms query time for 10K documents
- vLLM Inference: CPU mode, ~5-20 tokens/second (depends on model size)
- Scalability: Target ~10K documents, ~300 pages each, ~3M chunks
For production deployments:
- Use GPU for embeddings and inference
- Switch to HNSW index for better query performance
- Add load balancing for web_api
- Scale worker_ingest horizontally
- Use managed Kafka (e.g., Confluent Cloud, AWS MSK)
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests (if applicable)
- Run linters:
black . && ruff check . - Submit a pull request
See LICENSE file for details.
- pgvector for PostgreSQL vector extension
- sentence-transformers for embeddings
- vLLM for fast LLM inference
- FastAPI for the web framework
- Apache Kafka for event streaming