A lightweight yet powerful conversational assistant tailored exclusively to answer questions about the Republic of Kosovo.
It uses a custom-trained intent classifier to route user messages, a retrieval-augmented generation (RAG) pipeline backed by Hugging Face sentence embeddings, and Google Gemini 2 Flash for grounded and informative response generation. Built with Streamlit for an interactive, browser-based UI.
β
Kosovo-Only Focus
β
Intent classification with ~94% accuracy
β
Retrieval-Augmented Generation (RAG) pipeline for fact-grounded answers
β
Google Gemini 2 Flash for high-quality responses
β
Graceful fallback for out-of-scope queries
β
Interactive UI built with Streamlit
β
Lightweight and fast with local embeddings + Chroma DB
python -m venv venv- On Windows:
venv\Scripts\activate
- On macOS/Linux:
source venv/bin/activate
pip install -r requirements.txtCreate a .env file in the project root with the following content:
GEMINI_API_KEY=your_google_gemini_api_key
python models/train_intent_classifier.pyThis script uses TF-IDF + LogisticRegression and is trained on data/chatbot_dataset.csv.
- Example intents:
greetinggoodbyekosovo_infoout_of_scope
Training output is saved to models/intent_classifier.joblib.
python models/classifier_stats.pyThis prints a full classification_report and shows a confusion matrix.
π‘ Achieves ~94% overall accuracy.
Sample output:
precision recall f1-score support
goodbye 0.99 0.97 0.98 80
greeting 0.99 0.94 0.96 87
kosovo_info 0.96 0.93 0.94 143
out_of_scope 0.85 0.93 0.89 92
accuracy 0.94 402
This indexes all .txt files inside the data/ directory using HuggingFaceEmbeddings:
python chroma/create_vector_store.pyDetails:
- Embedding model:
all-MiniLM-L6-v2 - Chunk size: 500 characters
- Overlap: 50 characters
- Persisted to:
data/chroma_store/
streamlit run --server.fileWatcherType=none app/streamlit_app.pyThen open the Streamlit UI in your browser.
Uses a pipeline of:
TfidfVectorizer() + LogisticRegression(max_iter=1000)Intent labels:
greeting: e.g. "hello", "hi there"goodbye: e.g. "bye", "see you"kosovo_info: Kosovo-related factual queriesout_of_scope: Anything not about Kosovo
For kosovo_info intents:
- Top-3 relevant document chunks retrieved using cosine similarity in
Chromavector store - Context + question passed to Gemini 2 Flash
- Gemini is instructed to:
- Prefer factual Kosovo context
- Use own knowledge if context is insufficient
- Reject out-of-scope questions
You are a helpful assistant who answers questions only about the Republic of Kosovo.
Use the following context to answer the question. It's important to clarify that you are not limited by the context.
Don't be afraid to use your own knowledge if the context is not sufficient.
If the question is unrelated to Kosovo, say you can't answer.
Context:
[retrieved content]
Question:
[user input]
A bar chart showing the number of examples per intent class in the training data. This helps identify class imbalance.
These are the most influential keywords the classifier learned for each intent:
Top features for goodbye
- goodbye: 3.128
- bye: 2.761
- thanks: 2.324
- now: 2.322
- later: 2.202
- out: 2.054
- that: 1.893
- see: 1.819
- time: 1.697
- done: 1.657
Top features for greeting
- hi: 4.374
- hello: 3.842
- hey: 3.734
- yo: 2.208
- let: 2.206
- good: 2.188
- again: 1.861
- greetings: 1.664
- back: 1.591
- to: 1.475
Top features for kosovo_info
- kosovo: 6.929
- is: 2.874
- in: 2.755
- where: 2.307
- are: 2.222
- what: 1.973
- does: 1.955
- about: 1.633
- there: 1.436
- can: 1.302
Top features for out_of_scope
- do: 1.867
- what: 1.696
- can: 1.495
- write: 1.410
- me: 1.346
- the: 1.158
- turn: 1.141
- book: 1.116
- make: 1.095
- who: 1.066
These help interpret model behavior and explain decision boundaries.
A version of the confusion matrix that shows percentages (per row), useful for identifying which intents are frequently misclassified.
A t-SNE projection of TF-IDF vectors from the test set into 2D space. Each point is colored by its true label.
Clusters indicate well-separated classes. Overlap between classes (e.g., greeting and out_of_scope) may signal ambiguity in the data or intent phrasing.
Examples where the classifier predicted the wrong intent. Useful for reviewing edge cases or ambiguous inputs.
| Text | True Label | Predicted Label |
|---|---|---|
| How do people get healthcare? | kosovo_info | out_of_scope |
| End of line. | goodbye | out_of_scope |
| Have a good one! | goodbye | greeting |
| Whatβs the latest? | greeting | out_of_scope |
| Howdy! | greeting | out_of_scope |
kosovo_chatbot/
βββ app/
β βββ streamlit_app.py # Streamlit app
βββ chroma/
β βββ create_vector_store.py # Build vector store
βββ data/
β βββ chroma_store/ # Persisted Chroma vector store
β βββ chatbot_dataset.csv # Intent classification dataset
β βββ kosovo_*.txt # Knowledge base documents
βββ images/
β βββ *.png # Demo visuals
βββ models/
β βββ train_intent_classifier.py
β βββ classifier_stats.py
β βββ intent_classifier.joblib # Trained model
βββ .env # API key
βββ requirements.txt
βββ README.md
| Type | Input | Response Behavior |
|---|---|---|
| Greeting | "Hi there!" | Friendly welcome message |
| Kosovo Info | "What is Kosovoβs population?" | Pulls RAG + Gemini for answer |
| Out-of-Scope | "Whatβs the weather in Paris?" | Graceful rejection: not about Kosovo |
| Goodbye | "See you later" | Farewell message |
- Only single-turn interactions (no chat memory)
- Kosovo-only knowledge domain
- Requires Gemini API access
Want to improve it? PRs and issues are welcome!
To contribute:
- Fork this repo
- Create a new branch
- Make your changes
- Submit a pull request
This project is licensed under the MIT License.
- Google Gemini
- Hugging Face
- ChromaDB
- LangChain
- π½π° Inspired by the people and beauty of Kosovo.





