Case
Top2000.ai: a small study in AI that keeps everything on your own machine
Top2000.ai is a chat application that answers questions about the Dutch Radio 2 Top 2000, the yearly list of 2,000 songs chosen by listeners. I built it to answer one question for myself: how far can you get with AI when nothing is allowed to leave the machine?
View the code- external calls during use
- 0
- test cases with golden answers
- 31
- recall@5 on descriptive questions
- 32%
What it is
What is Top2000.ai?
The Top 2000 is the vehicle, not the point. The data is public, well known in the Netherlands and large enough to be interesting: 54,000 chart positions, 4,925 songs and 27 editions from 1999 to 2025.
You can ask two kinds of question. Questions with an exact answer, such as how often a song appeared or which artist had the most entries in 2024. And questions about meaning, such as which song is about a boxer who was wrongly convicted.
The real subject is the infrastructure underneath. Every choice is written down with its reason, and where a choice came from a measurement, the measurement is there too. For me it was also a study in applying retrieval-augmented generation (RAG) with local models only.
Architecture
The model translates, the database calculates
- The model writes one query
- The query is checked: read only, two tables
- The database does the counting
- The model words the rows it gets back
- Keyword and vector search in the local index
- Best match too weak? Then no answer is generated
- The model answers from the passages found
For exact questions, the language model does one thing: it turns the question into a single database query. This is known as text-to-SQL. The database does the counting and sorting. The model then sees only the rows that came back and writes a sentence around them. A number that is not in those rows is not allowed in the answer.
A router decides which path a question takes. It uses written rules and no model, so the same question always takes the same path and every decision can be checked by hand. When in doubt it chooses the database. A wrong database answer is an empty table, which is visibly wrong. A wrong generated answer reads well and is still wrong.
Questions about meaning take the RAG path. The application searches a local index of Wikipedia text and the model answers from the passages it finds. If the best match is too weak, no answer is generated at all. That check sits outside the model, because a small model should not be the judge of its own context.
Privacy
What stays on the machine
While the application runs, no request goes to an external model or service. The language model and the embedding model both run locally through Ollama. The data is a single file on disk, opened read-only. The interface loads no external scripts, fonts or analytics, and questions are not stored.
You do not have to take my word for it. The code is open, and the run path contains no external address.
I did use Claude to build it. Claude wrote scripts and pipelines with me, and it never sits in the path that answers a question. That line is drawn in the repository and followed as a working rule: Claude builds, Ollama runs. It also means every evaluation runs against the local model, never against the stronger model that helped to build it.
Energy
Energy use, per question
Each answer shows how much electricity it cost, measured on the machine while the model was running and converted to CO2 using the Dutch electricity mix. The interface compares the total with one washing machine cycle at 40 degrees. The machine draws about 3 watts at rest and 35 to 36 watts while generating.
Limits
Where it falls short
The RAG path is the weak spot. Questions that name a song or artist find the right song every time in my test set. Questions that only describe a song find it in the top five in 32% of cases. I set the bar at 60% before measuring. Cutting the text into smaller pieces took two hours of computing and did not move that number.
There is no personal data in this project, which makes the privacy promise easier to keep here than in a client system. The architecture is the same one I would use where the data is sensitive.
The models are open weights trained by a large technology company, and the hardware comes from another. Independence is a spectrum, not a switch.
The demo runs on my own machine. Once it is hosted publicly, the choice of host becomes part of the privacy story.
Layers
Layer by layer
Router
- Written rules, no model
- Chooses the database when in doubt
- 31 of 31 routing questions correct
Generated queries
- One read-only statement, two known tables
- At most two retries, then a plain refusal
- Stopped after five seconds
Database
- Opened read-only, so the query check is not the only lock
- One SQLite file, no database server
Retrieval (RAG)
- Keyword and vector search over a local index
- No answer below the similarity threshold
- Every answer lists the passages it used
Models
- qwen2.5-coder:32b for text-to-SQL
- qwen2.5:7b-instruct for RAG answers
- bge-m3 for embeddings
- All three run locally through Ollama
- 0 external calls during use
Measuring
- 31 test cases with answers taken from the database
- Evaluations run against the local model only
- Watt-hours and CO2 shown per answer
python · fastapi · sqlite · ollama · react