RAG for your documents
Answers from your documents, and never pays twice for the same question
Groundline indexes the files you upload and answers with the fragments it used. Answers are cached by the meaning of the question rather than its text, so "what is our SLA?" and "how fast must we respond under the contract?" share one cache entry - and the second one never reaches the language model.
- 439 ms to answer from cache instead of ~2.6 s through the full pipeline
- 849 tokens saved by a single cache hit, measured, not projected
- 7 pipeline steps, each shown with its own time and token cost
Before and after Jev
Three checks moved from the language model to a decision model
Jev is the first System One model from TypeSafe. It does not write text: it reads a state and typed questions and returns calibrated probabilities - yes or no, one option out of several, or a position on a scale. Groundline now asks it the yes-or-no questions a RAG pipeline used to put to its LLM.
A decision, not a paragraph
Whether the fragments are enough is one bit. Asking a chat model for it costs a full prompt with every fragment and returns text that still has to be parsed.
Fewer tokens in the answer
Jev marks which fragments the answer needs, so the LLM reads only those instead of all five.
Calibrated, so a threshold means something
Jev returns a probability. The pipeline acts on thresholds chosen from measurements.
Fails open
When Jev is slow, unavailable or out of budget, the pipeline takes its old path. No answer depends on Jev being up.
Who makes each decision
| Before | With Jev | |
|---|---|---|
| Is the context enough to answer? | The LLM on every attempt, reading the question and all five fragments | Jev, in the same call that grades every fragment; the LLM only when Jev is unsure or down. The first search uses the question as asked, the LLM rewrites it only for a second search |
| Is this the question already in the cache? | Embedding similarity alone: 0.90 or more is a hit | Jev confirms matches between 0.85 and 0.97: a paraphrase can hit below 0.90, a look-alike that asks something else misses |
| Do the sources back the answer? | Not checked: any answer with sufficient context went to the cache | Jev grades the answer after it is shown; only supported answers are cached, and the chat shows the verdict |
Live, from the questions people ask
Every answered question is measured as it happens: time, LLM calls and tokens, cache hits, the grounding verdict. Only these numbers are kept for this table, never the question or the answer. The Jev columns count only the current Jev-first pipeline; the questions without Jev were asked before Jev was turned on and before the database round trips were cut, so their time is not directly comparable.
| Without Jev, average | With Jev, average | With Jev, last question | |
|---|---|---|---|
| Time to the full answer, s (cache misses) | pending | pending | pending |
| LLM calls per question | pending | pending | pending |
| LLM tokens per question | pending | pending | pending |
| Answered from the cache | pending | pending | pending |
| Time spent waiting for Jev, s | pending | pending | pending |
| Answers backed by the sources | pending | pending | pending |
| Jev cost per question, USD | pending | pending | pending |
Measured with ragas on the GitLab Handbook
The same 29 questions in the same order with the cache cleared, judged by an independent model. Jev v1 rewrote every question with the LLM before searching; Jev-first is the current pipeline: it searches the question as asked and sends only the fragments the answer needs.
| Without Jev | Jev v1 | Jev-first (current) | |
|---|---|---|---|
| Faithfulness | pending | pending | pending |
| Answer correctness | pending | pending | pending |
| Context precision | pending | pending | pending |
| Context recall | pending | pending | pending |
| LLM calls per question | pending | pending | pending |
| LLM tokens per question | pending | pending | pending |
What changes for the person asking
A grounding badge
Under every answer: supported, partly supported, not supported or contradicted - the same verdict that decides whether the answer is cached.
An honest "not in the documents"
When the fragments do not cover the question, the answer still says so instead of guessing: Jev skips the LLM check only when it is confident the context is enough.
Fewer wrong cache hits
A question that reads like an earlier one but asks something else no longer receives the earlier answer.
How it was measured
- Corpus
- 8 pages of the GitLab Handbook on time off, leave, benefits, expenses and travel, pinned to commit f243917f, CC BY-SA 4.0 - 70 chunks.
- Dataset
- 29 questions: answered by one chunk, answered across documents, unanswerable, distractors, 5 in Russian. Plus 20 cache pairs: paraphrases and look-alikes.
- Judge
- ragas with gpt-oss-20b, max_tokens 4096: through OpenRouter for the runs without Jev and Jev v1, on free Groq for Jev-first.
- Limits
- One corpus and 29 questions on free-tier Groq. Differences smaller than the judge spread are noise. Jev is strongest in English.
How it works
One question, seven steps, all of them visible
A question is embedded and compared with earlier questions first; only a miss runs the rest. The interface shows which step is running, what the previous one took and how many tokens it spent.
- 01
check_cache
The question becomes a vector and is compared with questions already answered. Above the similarity threshold, the stored answer is returned immediately.
- 02
rewrite_query
The wording is rewritten into a precise search query: abbreviations expanded, vague terms resolved.
- 03
retrieve
Hybrid search over your chunks: vector search for paraphrases, full-text search for exact terms, merged by reciprocal rank fusion.
- 04
rerank
A cross-encoder re-reads the candidates and keeps the five that actually answer the question.
- 05
check_sufficiency
The model judges whether those fragments are enough. If not, the question is reformulated and searched again, at most twice.
- 06
generate_answer
The answer is streamed from those fragments only, citing the file and chunk each claim came from.
- 07
record
Answer, sources and per-step metrics go to the history, and to the cache for the next matching question.
Steps marked LLM cost tokens. A cache hit runs none of them.
Why sign in
The email step is part of the product, not paperwork
The demo runs on free tiers of external services. Identifying the person is how that budget is shared fairly and how one person’s documents stay out of another’s answers.
Personal quotas
Queries and documents are counted per account, not per service. One busy visitor cannot burn the day’s language-model budget for everyone else.
Data isolation
Documents, history and cached answers belong to an account. Search only ever runs over your chunks, and the cache never returns someone else’s answer - enforced by Postgres row-level security, not only by application code.
A code instead of a password
A six-digit code by email: no password to leak, no OAuth app, no extra personal data stored.
Deletion in one step
The Documents screen deletes the account with every document, answer and cache entry. No support ticket.
Cache numbers
What the semantic cache actually saved
Measured on 2026-09-18 against the demo corpus (Northwind Robotics employee handbook, 1 document). These are readings from one run, not a benchmark - the same figures appear in the interface while you use it.
| Answer from cache | 439 ms | one embedding and one indexed lookup |
| Answer through the full pipeline | 2.6 s | 3 language-model calls |
| Tokens saved per hit | 849 | 0 language-model calls |
| Similarity that produced the hit | 0.9916 | threshold 0.9 |
| Indexing the document | 2.3 s | extraction, chunking and embeddings |
Question asked twice, the second time reworded: "What is the vacation policy?" and "Whats the vacation policy?"
Limits
What Groundline does not do
Not a corporate search engine
The demo gives each account one document of up to 0.3 MB, not a company-wide archive.
Answers only from what you uploaded
When the documents do not contain the answer, the sufficiency step stops the model from inventing one.
PDF, TXT and MD only
Scans without a text layer, spreadsheets and slide decks are not parsed.
Free provider tiers
When the daily quota of a provider runs out, requests are refused until it resets. The bar at the bottom of the app shows how much is left.
FAQ
Short answers
How is a semantic cache different from a normal one?
A normal cache compares the request string. A semantic cache compares embeddings, so a reworded question finds the same answer. The similarity threshold is configurable; below it the question runs the full pipeline.
Who can see my documents?
Documents and chunks belong to your account and never enter anyone else’s search. Postgres row-level security enforces that at the database level. Text is sent to external models for embeddings and answer generation - the security notes in the repository list every service and what reaches it.
How many questions can I ask per day?
The demo allows 50 questions a day and one document of up to 0.3 MB per account, with at least 15 seconds between questions. Cache hits are free and do not count. Your current usage is visible in the app.
Can I run it myself?
Yes. The repository has a docker compose setup and documents every environment variable. Model and embedding providers are swapped by configuration, including fully local models.
What happens on a cache hit?
Steps two to six never run. The answer and its sources come from the cache entry, and the interface marks check_cache as a hit with the similarity and the tokens saved.