LangChain Crash Course
Theme
Palette
Download
Caption (tap to copy)
📖 Deep dive (full written explanation)
This is the hands-on post, and the cover promises a real Retrieval-Augmented Generation chain in LangChain, end to end. After three posts of concept and architecture, the reader wants to build the canonical LangChain app — the one that answers questions from their own documents rather than from the model's training data.
The post is structured to run top to bottom: install, load and split documents, embed them into a vector store, build a retriever, wire it into an LCEL chain, ask a question, and stream the answer. The payoff to watch for is the model answering from your documents, grounded, in roughly thirty lines.
The install slide lists the packages the RAG app needs: the core langchain package, the OpenAI integration for the model and embeddings, the community package for the vector store, and faiss-cpu for the actual vector index. Using FAISS keeps the example fully local — no external vector database to provision.
Keeping the stack to these four packages means the reader can run the whole post with just an API key and a text file. The split across multiple packages also quietly reinforces post 1's point about the ecosystem being a family of packages, and post 5's warning that importing from the right one matters.
This slide loads a document and splits it into chunks, the first real RAG step. It reads a text file, then uses RecursiveCharacterTextSplitter with a chunk_size of 500 and chunk_overlap of 50 to break the text into overlapping pieces. The comment notes the reason: each chunk has to fit comfortably in the context window when later injected as context.
The RecursiveCharacterTextSplitter is the sensible default because it tries to split on natural boundaries — paragraphs, then sentences — before falling back to raw character counts, which preserves meaning better than a blind cut. The overlap ensures a fact that straddles a boundary isn't lost. Chunking choices made here directly determine retrieval quality, which is exactly the first mistake post 5 warns about.
This slide embeds the chunks into a vector store, turning text into searchable vectors. It creates an OpenAIEmbeddings model, then builds a FAISS store from the chunks — each chunk becomes a vector, and semantically similar text ends up near each other in the vector space. Finally it turns the store into a retriever configured to return the top 3 matches.
The k=3 setting is worth noting: it controls how many chunks the retriever pulls per query, trading completeness against token cost and noise. The retriever is itself a Runnable, which is the crucial link to the architecture posts — it can be dropped straight into an LCEL chain exactly like a prompt or parser, because it satisfies the same interface.
This slide builds the RAG chain itself, and it's where the architecture from post 3 becomes concrete. The prompt template has {context} and {question} variables and instructs the model to answer using only the provided context. The chain's input is a dict: the retriever fills the context key from the question, while RunnablePassthrough forwards the raw question to the question key. That dict flows into prompt, model, and parser.
This is precisely the fan-out-then-merge diagram from post 3, now in code. The retriever and the passthrough run on the same input question; their combined output dict feeds the prompt. Instructing the model to answer 'using only this context' is what makes the answer grounded rather than a blend of retrieved facts and training-data guesses.
The pipeline diagram summarizes what the chain does so the reader can map the code back to a mental picture. A question comes in, the retriever pulls the top-k relevant chunks, the prompt combines those chunks with the question, and the model produces a grounded answer.
Placing this after the chain-building code lets it act as a recap. The reader has now seen the dict with the retriever and passthrough, and the diagram confirms how those pieces produce the linear flow of retrieve, prompt, answer. It's the same shape as the fan-out diagram from post 3, which reinforces that this real app is just an instance of the architecture they already learned.
This slide actually runs the chain and is the payoff of the whole post. Calling invoke with a plain question string triggers the entire flow: the retriever pulls the three most relevant chunks, the prompt injects them as context, and the model answers from those chunks. The comment spells out the significance — the answer comes from your documents, not from the model guessing based on training data.
Note that the input is just a string, not a dict, because RunnablePassthrough sits at the top of the chain and accepts the question directly. This is the moment retrieval-augmented generation stops being an acronym and becomes a working feature: a question about a private handbook gets answered from that handbook.
This slide shows streaming the answer, demonstrating the free-orchestration benefit from post 2. The exact same chain, with no rewrite, is called with .stream() instead of .invoke(), and it yields tokens as the model produces them. Printing each token as it arrives gives the typewriter-style UI users now expect.
The load-bearing point is 'same chain, no rewrite.' Because every Runnable exposes invoke, batch, and stream uniformly, switching from a blocking call to a streaming one costs a single method change. This is the concrete cash value of the uniform interface that posts 2 and 3 described — streaming a UI is something you'd otherwise hand-orchestrate per project.
The tips slide walks the reader through the flow one more time as a compact recap, because seeing the five RAG steps in sequence cements the pattern. Split documents into overlapping chunks. Embed those chunks into vectors in a store. Retrieve the top-k chunks for a given question. Stuff those chunks into the prompt as context. Let the model answer, grounded in that context.
Learning to recite this sequence is also the foundation for debugging, since each step is a place RAG can go wrong — covered in post 5. When an answer is bad, you check the chain in this order: was the chunking sane, did retrieval pull relevant chunks, did the prompt include them, did the model stay grounded.
This bonus code slide shows how little it takes to add conversation memory, reinforcing how composable the framework is. It wraps the existing chain in RunnableWithMessageHistory, backed by a simple per-session ChatMessageHistory stored in a dict. With that wrapper, follow-up questions remember the prior turns, keyed by session_id.
The lesson is that memory is just another layer that clips onto the chain you already built — you don't restructure the RAG flow to add it. This is the composability-compounds claim from post 2 made tangible: a major new capability arrives as a wrapper around the existing runnable, not as a rewrite. In production you'd swap the in-memory dict for a real store like Redis.
The CTA hands off to the final post of the day. The reader can now build a working RAG chain with memory; the remaining risk is the subtle, expensive ways these chains break once they leave the demo and meet real data and real traffic.
Day 80's teaser flags the mistakes post: careless chunking that wrecks retrieval, fragile parsers that crash on imperfect output, re-embedding the corpus on every run, treating the chain as an opaque black box, and getting burned by LangChain's fast-moving versions. These are the failures that bite even when the happy-path code runs perfectly.