Project: ask questions about your documents
Build a retrieval-augmented chatbot over a folder of documents. It teaches embeddings, chunking, vector search and how to measure retrieval.
Words on this page (13)
- embedding
- : A list of numbers that represents the meaning of a piece of text or an image, so similar things sit close together.
- chunk
- : A small piece of a document, sized for indexing and retrieval.
- vector
- : An ordered list of numbers. Embeddings and model activations are vectors.
- retrieval
- : Finding the most relevant pieces of text for a query.
- token
- : A piece of text the model reads and writes, usually a word or part of a word. About 4 characters of English on average.
- metadata
- : Extra labels stored with a document or chunk, such as its title, date or country, which let you filter results.
- vector database
- : A database that stores embeddings and finds the nearest ones quickly.
- prompt
- : The input text you give a model, including instructions, examples and data.
- recall
- : Of the truly positive cases, the share the system found.
- reranker
- : A second model that rescores a short list of search results by reading query and passage together.
- API
- : Application programming interface: a way for one program to call another, usually over HTTP.
- dot product
- : Multiply matching entries of two vectors and add them up. Large when they point the same way.
- cosine similarity
- : A score from -1 to 1 for how much two vectors point the same way, ignoring their length.
The brief
Pick a small set of documents you know well, such as a product manual or your own notes. The user asks a question and gets an answer with citations to the passages used.
Steps
- Split the documents into of about 300 with overlap and keep the source and heading as .
- Embed each chunk and store it in a database (pgvector, Chroma or Supabase).
- Embed a question and fetch the top five chunks.
- the model to answer only from the chunks, cite them and say when the answer is not there.
- Print the retrieved chunks next to every answer so failures are easy to see.
Measure it
Write twenty questions with the document that answers each. Compute at five. Try one change at a time: chunk size, adding keyword search, adding a . Keep the changes that improve the number.
Done when
You have 20 questions, each with the passage that answers it written down. You measured how often the right passage is in the top 5 results and wrote the number down. Every answer shows its source chunk. A question the documents do not cover gets "I could not find that" instead of an invention. You can name two questions it still gets wrong and why.
Stretch
Add metadata filters, such as only documents from a chosen year. Add a question you know the documents cannot answer and check that the bot says so.
Starter
Python again. This starter chunks one text file, embeds the chunks locally (no key needed) and finds the closest ones for a question. Install with pip install sentence-transformers numpy.
import numpy as np
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("all-MiniLM-L6-v2")
def chunks(text, size=200, overlap=40):
words = text.split()
step = size - overlap
return [" ".join(words[i:i + size]) for i in range(0, max(len(words) - overlap, 1), step)]
docs = chunks(open("notes.txt").read())
vectors = model.encode(docs, normalize_embeddings=True) # unit length, so a dot product is cosine similarity
def top(question, k=5):
q = model.encode([question], normalize_embeddings=True)[0]
best = np.argsort(vectors @ q)[::-1][:k]
return [(float(vectors[i] @ q), docs[i]) for i in best]
for score, chunk in top("What is the refund window?"):
print(f"{score:.2f} {chunk[:100]}")Print the scores next to the chunks, as step 5 says. Then add the model call that answers from them, using the pattern in the Calling a model lesson. Swap the in-memory list for pgvector or Chroma once it works.
A learner builds a bot over their own course notes. The first version answers confidently and sometimes wrongly. When they print the retrieved chunks next to each answer, they see that the right paragraph was split in half. Changing the chunk size fixes it. That moment, seeing why it was wrong, is the point of the project.
Check yourself
A wrong answer appears. What do you inspect first?
Not quite. Most failures are retrieval failures.
What should the bot do with an unanswerable question?
Not quite. Test this on purpose.
Your recall at 5 is 0.6. Which one change should you try first?
Not quite. Change one thing at a time so you know what helped.
You got 0 of 3 right.
Go deeper
- Repo
Nir Diamant · Notebooks for each improvement to try.
- Docs
Supabase · Store and query embeddings with Postgres.
- Repo
Exploding Gradients · Metrics to score retrieval and answers.
Spotted a mistake or something unclear? Report it on GitHub.