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.

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

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.

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.

Full method and per-question results

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.

  1. 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.

  2. 02

    rewrite_query

    The wording is rewritten into a precise search query: abbreviations expanded, vague terms resolved.

  3. 03

    retrieve

    Hybrid search over your chunks: vector search for paraphrases, full-text search for exact terms, merged by reciprocal rank fusion.

  4. 04

    rerank

    A cross-encoder re-reads the candidates and keeps the five that actually answer the question.

  5. 05

    check_sufficiency

    The model judges whether those fragments are enough. If not, the question is reformulated and searched again, at most twice.

  6. 06

    generate_answer

    The answer is streamed from those fragments only, citing the file and chunk each claim came from.

  7. 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.

The chat screen: a streamed answer with its sources, and the pipeline panel showing each step with its time and token cost.

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.

The documents screen: the upload area, the indexing queue and the list of indexed files.

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?"

The service limits bar expanded: every external quota with how much is left and where the number came from.

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.