RAG starter: document Q&A with hybrid search and verified citations
We start our document Q&A and knowledge assistant builds from this starter. It covers ingestion, hybrid retrieval, an agent that answers only from your documents and a citation check that fails closed.
What's inside.
Event-driven ingestion
Uploads go to Cloudflare R2, and Celery workers on Redis parse, chunk and embed each document in the background, with retries and a status per file.
Hybrid retrieval in Postgres
Vector search with pgvector and Postgres full-text search, merged with reciprocal rank fusion and filtered by user on every query.
Answers only from documents
A PydanticAI agent with a retrieval tool and structured output. It cites the passages it used and says so when the documents do not contain an answer.
Fail-closed citation validator
Every citation must point to a passage retrieved in that turn and quote it verbatim. If a check fails, the user gets a controlled response in place of an unverified answer.
Evals harness
A test set and runner for retrieval and answer quality, so changes to chunking, prompts or models are measured before they ship.
Signed webhooks
Outgoing events for finished ingestion and answers, signed so the receiving system can verify where they came from.
How it fits together.
- 01
Upload
The Next.js front end sends a file to the FastAPI back end, which stores it in R2 and queues an ingestion job.
- 02
Ingest
A Celery worker extracts the text, splits it into chunks with their position in the document, embeds them and writes them to Postgres with a full-text index.
- 03
Retrieve and answer
For each question the agent calls hybrid search, which runs vector and keyword search in parallel, fuses the rankings and adds neighbouring chunks for context.
- 04
Validate
The validator checks every citation against the chunks retrieved in that turn before the answer is returned. Failures are logged and replaced with a safe response.
A look at the code.
A simplified citation check: any citation that cannot be traced to retrieved text rejects the whole answer.
Built with.
All technologiesHear when it is public.
We are preparing the rag starter for release. One email when it is out, and our occasional notes.