A learning project
Learning RAG by building it by hand
I'm a frontend developer, curious about how AI actually works under the hood. Instead of reaching for a framework that hides everything, I wanted to build Retrieval-Augmented Generation from the ground up, so I could actually explain what embedding, retrieval and grounded generation each do, in my own words.
So every moving part here is deliberately visible: the embedding step calls a hosted model, the similarity search ranks stored vectors by cosine distance (I first wrote this by hand, then moved it to a hosted vector database), and the language model is told to answer only from the retrieved NASA texts. To check the search rather than trust it blindly, I also built a small eval by hand using the standard search metrics (Recall@K and MRR): it asks questions whose right answer I already know and grades how often, and how near the top, the right picture comes back.
It was also my chance to practise Clean Architecture in a Nuxt app: the query and ingest flows are both split into a domain, use cases with ports, and infrastructure adapters, so the core logic never depends on Gemini, Upstash or NASA directly. Arguably over-engineered for a demo this size, but that was exactly the point, to learn the pattern by building it by hand.
Local Transformers.js embeddings (Xenova/paraphrase-multilingual-MiniLM-L12-v2), in the function.Too heavy for Netlify's 250 MB function limit.
Google Gemini embeddings: hosted, tiny to ship.
Hand-written cosine over one plain JSON file.Lovely to learn on, but it will not scale.
A hosted vector database (Upstash Vector), live now.
A free-text answer with NONSENSE / NO_MATCH markers, parsed by pattern-matching.Brittle to read, and it never verified which pictures were actually cited.
A schema-constrained JSON reply, validated with Zod, with its cited sources checked.
Guessing the retrieval thresholds (how similar counts, how many to fetch) by eye.No way to tell whether a tweak actually helped or hurt.
A small offline eval that grades retrieval (Recall@K + MRR) so the knobs are set from numbers.
- Framework
- Nuxt 4 + Vue, Nitro server routes
- Embeddings
- Google Gemini: gemini-embedding-001 (multilingual, 768-dim)
- Vector store
- Upstash Vector (was hand-written cosine over JSON first)
- Language model
- Google Gemini: gemini-flash-latest (free tier), structured JSON output validated with Zod
- Data
- NASA Astronomy Picture of the Day API
- Ingest
- Manual backfill script + a daily Netlify function
- Hosting
- Netlify
This is deliberately a free-tier demo, so it can hit the daily quota. A hobby project, built to learn and to show, not to run up costs.
View the source on GitHub ↗Built by Melanie Stief, forever half-hoping for a NASA badge, or at least a window seat past the atmosphere. Until the universe sends the invite, building small things about the cosmos will have to do. · viridis.de