“Chat with your PDF” is one of the most requested AI features — upload a document, ask questions, get answers grounded in that document. It’s a perfect, self-contained application of RAG. This guide builds one in .NET, end to end.
The pipeline
Four steps, two of them one-time per document:
- Extract the text from the PDF.
- Chunk it into passages and embed each one.
- Retrieve the chunks most relevant to the user’s question.
- Answer using only those chunks as context.
Step 1: Extract text from the PDF
Use a PDF library (such as PdfPig or another .NET PDF reader) to pull the text out, page by page:
using UglyToad.PdfPig;
string ExtractText(string path)
{
using var doc = PdfDocument.Open(path);
return string.Join("\n", doc.GetPages().Select(p => p.Text));
}
Step 2: Chunk and embed
You can’t embed a whole document as one vector — retrieval would be too coarse. Split into overlapping passages and generate an embedding for each:
var chunks = ChunkText(pdfText, size: 500, overlap: 75); // your splitter
foreach (var chunk in chunks)
{
var vector = (await embedder.GenerateAsync(chunk)).Vector;
store.Add(chunk, vector); // in-memory list, or pgvector for scale
}
For a single uploaded PDF, an in-memory list is fine; for many documents, use pgvector.
Step 3: Retrieve the relevant chunks
When a question comes in, embed it and pull the closest chunks (semantic search):
var q = (await embedder.GenerateAsync(question)).Vector;
var top = store.NearestTo(q, k: 4); // the 4 most relevant passages
Step 4: Answer, grounded in the document
Build a prompt that gives the model only those chunks and instructs it to answer from them — and to admit when the answer isn’t there:
var context = string.Join("\n\n", top);
var prompt = $"""
Answer the question using ONLY the context below, which comes from the user's document.
If the answer isn't in the context, say you couldn't find it in the document.
Context:
{context}
Question: {question}
""";
var answer = await chatClient.GetResponseAsync(prompt);
That “answer only from the context, and say when you can’t” instruction is what keeps the app honest — it answers from the PDF instead of the model’s general knowledge, and it says “not in the document” instead of inventing something.
Making it production-grade
- Cite the source — return which chunk/page an answer came from, so users can verify.
- Cache the embeddings — embed a document once on upload, not on every question.
- Handle scanned PDFs — image-only PDFs need OCR before extraction.
- Mind the tokens — retrieving too many chunks inflates cost; tune
kdown.
Note: PDF and embedding library APIs vary; verify against your chosen packages. The pipeline — extract, chunk, embed, retrieve, answer-from-context — is the stable, provider-independent recipe behind every “chat with your documents” feature.
Takeaway
A “chat with your PDF” app is RAG applied to one document: extract the text, chunk and embed it, retrieve the passages relevant to each question, and answer using only those passages. Ground the model firmly in the retrieved context — “answer only from this, say when you can’t” — cite sources, and cache the embeddings. That’s a genuinely useful feature you can build in an afternoon in .NET.
Next: chunking strategies for RAG to improve the answers this returns.
Have a correction or a topic you want covered? Email mani.bc72@gmail.com.