Technical Challenge
# Kinyarwanda Citizen-Service Message Classifier
A small tool that takes short Kinyarwanda text — citizen requests,
follow-ups, complaints, and questions about public services — and sorts
each one into a category.
## Categories
- **new_request** — wants to START a service (file/pay taxes, book a
clinic visit, request seeds/fertilizer, get a document/ID)
- **status_check** — already submitted something and is asking where it
stands ("did my application go through?", "when will my document be
ready?")
- **issue_report** — something went wrong with a service already received
(delay, wrong information, no follow-up, poor treatment)
- **general_inquiry** — general information question with no case attached
(eligibility, fees, hours, requirements), plus greetings/small talk
The `new_request` vs `status_check` split matters because in a real system
these two go to different handlers — one starts a new case, the other
looks one up.
## How it works
**LLM-first, with a deterministic fallback** (`src/classifier.py`):
1. **LLM classifier** (`src/llm_classifier.py`) — a few-shot Gemini
(`gemini-2.5-flash`) prompt that returns strict JSON with a category
and a short reasoning in Kinyarwanda. This is the primary classifier.
2. **Baseline classifier** (`src/baseline_classifier.py`) — a
deterministic, zero-cost keyword-signal classifier. Used only as a
fallback when the LLM call fails (no API key, network error, rate
limit, unparseable response), so the tool never goes fully down.
Both run through the same text normalizer (`src/normalizer.py`), which
fixes common typos and code-switched English/French/Swahili words before
classification.
## Project structure
```
kinyarwanda-classifier/
├── src/
│ ├── normalizer.py # typo/code-switch cleanup
│ ├── baseline_classifier.py # rule-based classifier (fallback)
│ ├── llm_classifier.py # few-shot Gemini classifier (primary)
│ └── classifier.py # combined entry point: LLM-first, fallback
├── data/ …