Skip to main content

About

A portfolio project exploring retrieval-augmented generation with verifiable citations.

The problem

Ask a language model about your own documents and you get an answer that sounds right. Whether it is right, and which sentence in which file it came from, is usually left to you to work out — which means checking it costs about as much as not having asked.

CiteSeek answers from documents you upload, and every claim carries a numbered citation. Clicking one opens the source document at the exact passage the answer used, highlighted in place. The point is not that the answer is confident; it is that you can check it in one click.

Why not just ask ChatGPT?

For most things, do. A general assistant is better at breadth, better at reasoning across subjects, and needs no setup at all.

But it only knows what it was trained on, and it has never seen your handbook. Ask it about your expenses policy and it has three options, all bad: refuse, describe what expenses policies are like in general, or invent one that sounds right. The third is the dangerous one, because a fluent invention reads exactly like an answer.

The obvious fix is to paste the document into the question. That works for one page and falls apart for a shelf of them. There is a hard limit on how much a model can be handed at once, you pay by the length of what you send, and a model given fifty documents attends to all of them poorly.

So here a search runs first. Your files are split into passages, and each passage is stored in a way that captures what it is about rather than which words it happens to use — so “annual leave” and “time off” end up in the same neighborhood. Your question is looked up the same way, and only a shortlist of the closest passages is handed over, with an instruction to answer from those and nothing else. The model never sees your collection. It sees the shortlist — which is what makes a citation possible, because the passages were chosen before a word was written.

What this does not claim is that it always knows when it does not know. That threshold was measured rather than guessed, and the scores for answerable and unanswerable questions overlap, so some questions fall on the wrong side of it. The narrower promise is the one worth making: every citation points at text that was really retrieved, and you can open it.

The guarantee

When no passage clears the relevance threshold, the answer says so and cites nothing — and it cannot do otherwise. Passages are retrieved before the model is involved, and if none clear it no answer is generated at all. There is no prose to hallucinate a citation into, because none is written. A short follow-up is rewritten and searched again first, and that rewrite is shown to you as a search query rather than offered as an answer.

That is the difference between a rule the system enforces and an instruction a model is asked to follow. Citations are resolved against passages the server retrieved, so a marker pointing at something that was not retrieved renders as plain text rather than as a link to nowhere.

The other half is an instruction rather than a rule, and it is measured rather than assumed. When a question your documents cannot answer clears the threshold anyway — which the section above says does happen — the model still refuses, and in testing it refused every time and invented nothing. What it sometimes does is attach a citation marker to that refusal, which its own instructions forbid: wording rather than a false claim, and the difference between what is guaranteed here and what is merely reliable.

How it is built

Next.js and TypeScript, Postgres with pgvector for search, and Google’s Gemini for embeddings and generation. Everything is stored in the EU; the model is Google’s, and its standard endpoint makes no commitment about where it runs, so the text of your documents does leave. The privacy page says exactly what is stored, where, and who else sees it.

The code is public, along with the reasoning: architectural decisions are recorded as they were made, including the ones that turned out to be wrong.

Who built it

Lucian Patrascu, a senior frontend engineer, as a way of learning the parts of this stack that are not frontend — retrieval, embeddings, database design, and the operational side of running it.

It is a portfolio project rather than a company. Nothing is sold, and nothing about it is a commitment.

Try the demoRead the source