Retrieval without generation: how the Ask widget on this site works
This site has an Ask widget. Type a question about my work and it answers in a few milliseconds, with citations, or it refuses. People assume it is RAG. It is not, and that is the interesting part.
There is no LLM anywhere in the loop. No embeddings, no vector database, no API call. It is the R of RAG with the AG deliberately removed: pure retrieval over a hand-reviewed index, running entirely in your browser.
The index is the contract
The whole system sits on top of one file: a list of 35 passages, each a single self-contained, citable fact about my work, each tagged with the page it comes from. Every passage is reviewed by me before it ships. That review is the contract: if a sentence is in the index, it is true, so the widget can show retrieved passages verbatim and never needs a model to rephrase them.
This is the inversion that makes generation unnecessary. RAG systems need an LLM because their chunks are fragments cut from larger documents, so something has to synthesize an answer. If you curate the corpus into atomic facts up front, the retrieved passage already is the answer.
Scoring in 30 lines
Retrieval is classic lexical scoring, precomputed at module load:
- Queries and passages are tokenized: lowercased, punctuation stripped, stopwords and single characters dropped.
- Each query term that appears in a passage scores twice its inverse document frequency, so rare terms like "Kubernetes" count far more than common ones like "systems".
- A prefix match (at least 4 shared leading characters, in either direction) scores half as much. That is the entire stemming strategy: "evals" finds "eval" without a stemming library.
Passages are ranked by total score and the top 3 are shown, each with its score, its source page, and the matched terms highlighted. The retrieval log you see animating is real output, including the actual elapsed time.
The refusal threshold is the feature
The most important line in the widget is the one that returns nothing. If the best passage scores below 3.5, the widget refuses outright. Results that clear the threshold but score under 55 percent of the top hit are dropped too, so one strong match is not padded out with two weak ones.
Ask it about Kubernetes or my salary expectations and you get a grounded refusal, because the index says nothing about either. The widget says so instead of stretching a weak match into an answer. Every change to the index has to pass a fixed set of retrieval eval questions, including the ones that must refuse. Retrieval quality regressions are caught the same way code regressions are: by a failing test.
What carries over to production RAG
The widget is small, but the principles are the ones my production RAG systems ship with. Citations on every answer, because an unsourced answer is an unverifiable one. A confidence floor below which the honest output is "I do not know". And evals as a gate, not a dashboard: if the refusal cases stop refusing, the change does not ship.
The part that does not carry over is the curation. A 400-page contract cannot be hand-distilled into atomic facts, which is exactly why real RAG needs chunking, embeddings, hybrid search, and a generation step. But when your corpus is 35 facts about one person, adding an LLM would only add latency, cost, and a hallucination surface. The strongest engineering decision in the widget is everything it leaves out.
Try it on the homepage. Ask something the index covers, then ask something it does not, and watch it decline.