ChatGPT-Style Retrieval-Augmented Generation (RAG) with LangChain
Ask ChatGPT a general-knowledge question and it answers from what it learned during training. Ask a support bot "what's your refund window for a damaged item bought during the holiday sale," and there is no training run that ever saw your company's actual refund policy document. Retrieval-augmented generation is the pattern that closes that gap: fetch the relevant passage from your own documents at request time, hand it to the model as context, and generate an answer grounded in that passage instead of the model's general training.
This guide builds one such bot with LangChain: a support assistant answering questions from a refund-policy document, following one question — "what's the refund window for a damaged item?" — through chunking, embedding, retrieval, and generation. Each section adds the piece that makes the difference between a demo that looks right on the happy path and a bot that admits "I don't know" instead of confidently answering from outside the document when retrieval comes up empty. For the deeper mechanics of chunk boundaries, see RAG chunking strategies, and for how embeddings represent meaning as vectors, see Embeddings explained.
The four moving parts
A LangChain RAG chain has four pieces with distinct responsibilities: a document loader + splitter that turns a policy PDF or markdown file into chunks small enough to embed meaningfully; an embedding model that converts each chunk (and later, each query) into a vector; a vector store that indexes those vectors for fast similarity search; and a chat model that receives the retrieved chunks plus the question and composes an answer.
The critical constraint that trips up first attempts: the embedding model used to index documents and the one used to embed the query at request time must be the same model. Mixing embedding models (or even different versions of the same model) puts query and document vectors in incompatible spaces — cosine similarity between them becomes meaningless, and retrieval silently returns unrelated chunks with no error.
Quick reference
- Loader + splitter: PDF/markdown/HTML in, list of ~200–500 token chunks out, each tagged with source metadata.
- Embedding model: same model, same version, for both indexing and query time — this is a contract, not a suggestion.
- Vector store: pgvector, Chroma, or a managed service — the choice affects operational cost more than retrieval quality at small scale.
- Chat model: receives retrieved chunks + question in the prompt; its job is composition, not knowledge recall.
Remember this
Retrieval silently breaks, without any error, if the query-time embedding model differs from the indexing-time embedding model — that consistency is a hard requirement, not a tuning knob.
Chunking and indexing the policy document
Split the refund policy into chunks before embedding — a whole multi-page document embedded as one vector loses the granularity to distinguish "damaged item" refunds from "changed my mind" refunds, because one vector can only represent the average of everything in it. A few hundred tokens per chunk, with modest overlap, keeps each chunk focused enough that its embedding actually represents one coherent idea.
Source metadata attached at chunking time (which document, which section) is what lets a later answer cite where it came from — that citation is the second half of the trust story: not just "answer grounded in the docs," but "answer traceable to a specific paragraph a human can go check."
Quick reference
- Pin the embedding model name/version in one constant, not scattered across index and query code.
- Keep chunk metadata (source file, section heading) — it's what makes a cited answer verifiable.
- For a production store, replace
MemoryVectorStorewith pgvector or a managed vector DB — memory stores don't survive a restart. - Re-index whenever the source document changes; a stale index answers from last quarter's policy.
Remember this
Chunk-level metadata is what turns a RAG answer from an assertion into something a user can verify — index it at chunking time, not as an afterthought at generation time.
Retrieval, generation, and the grounding rule
The retrieval step embeds the incoming question with the same embedding model and finds the top-k most similar chunks. The generation step then "stuffs" those chunks into the prompt alongside the question and asks the chat model to answer using only that context. The instruction to use only the provided context is the entire mechanism that keeps the bot from quietly falling back to its general training when the retrieved chunks don't actually answer the question.
Without that instruction, a question outside the document's scope ("what's your policy on international shipping duties" when the refund policy never mentions duties) gets a fluent, confident, and completely made-up answer — the model fills the gap from general knowledge because nothing told it not to. The fix is a system prompt that explicitly permits "I don't know" as a valid answer.
Quick reference
- The grounding instruction is doing real work — test the out-of-scope case explicitly, don't assume the model infers it.
- Return source citations from the retrieved chunk metadata alongside the answer so a user can verify it.
- If similarity scores for all top-k chunks are low, treat that as "nothing relevant found" and skip generation entirely rather than stuffing weak matches.
- Log question → retrieved chunks → answer triples; a wrong answer is almost always a retrieval problem, not a generation problem.
Remember this
An explicit instruction to say "I don't know" when the context doesn't answer the question is the single mechanism separating a grounded RAG bot from one that quietly reverts to hallucinating from general training on any out-of-scope question.
When RAG is the right tool
RAG earns its complexity when the answer source changes independently of any model release — a refund policy edited next week, a product catalog updated daily — and when you need traceable citations a compliance or support team can verify. It's the wrong tool when the knowledge is small enough to fit permanently in a system prompt, or when the task needs reasoning over a huge document all at once rather than a handful of relevant chunks.
That second case is where long-context prompting can outperform chunked retrieval — feeding the model the whole document instead of a handful of matching passages. Treat the choice as a workload question, not a loyalty to one architecture: measure retrieval hit rate and answer faithfulness on your own labeled question set before assuming the more complex pipeline is earning its cost.
Quick reference
- Use RAG when the source document changes independently of model releases and answers need to be citable.
- Skip RAG for a small, stable knowledge base — a system prompt with the whole document may be simpler and just as accurate.
- Consider long context instead of chunked retrieval when a question needs the whole document's structure, not a handful of matching passages — see Long context vs RAG.
- Evaluate with a labeled question set: track retrieval hit rate (right chunk in top-k) and answer faithfulness (grounded vs. invented) separately — they fail independently.
Remember this
RAG is the right tool exactly when the underlying document changes on its own schedule and answers need a verifiable source — for a small, stable knowledge base, a plain system prompt is often simpler and equally accurate.
Key takeaway
Build the pipeline against a short refund policy document: chunk and index it, then run the retrieval + generation chain against "what's the refund window for a damaged item?" and confirm the answer cites the correct clause and source. That's your expected success — a grounded, citable answer.
Then break it deliberately: ask a question the policy never addresses ("what's your international shipping duties policy?") and confirm the bot returns your exact fallback string rather than an invented answer. Pass criterion: the in-scope question returns a cited, grounded answer, and the out-of-scope question returns the explicit "not in our documentation" fallback — never a confident, uncited invention either way.
Related Articles
Explore this topic