Draw this on the whiteboard before you even open the notebook. Ask them straight: "if step 2 or 3 is broken, can step 5, the vector database, fix it for you?" Let it go quiet. Nobody answers yes, and that silence is basically the whole session.
PyPDFLoader extracts raw text. PyMuPDF4LLMLoader reconstructs layout. Same file, same table, and the table barely survives one of them.Region Shipments On-Time % Revenue ($M) Northwest 128,400 97.2% 14.6 West 204,910 98.1% ...
|Region|Shipments|On-Time %|Revenue| |---|---|---|---| |Northwest|128,400|97.2%|14.6| |West|204,910|98.1%|23.9| ...
Cover the right panel first. Show them just the naive output and ask "which number is Northwest's on-time rate?" Nobody will be sure. Rows and columns are gone, it's just a list of numbers now. Then uncover the Markdown version. That's it, that's the whole demo, don't over-explain it.
Run the OCR cell live, don't just show a screenshot. Let them find the garbage themselves, "causedby", "INTERNALMEMO", stuff like that. Point out that this noise doesn't stay contained, it flows straight into your chunks and then your embeddings. Some pipelines add a confidence gate before chunking for exactly this reason.
Tell them straight up: when someone says "our RAG is broken," the fix is almost never a bigger model. Nine times out of ten it's one step up this ladder. I say this line at least twice a session, it's worth repeating.
Cuts blindly every N tokens. Watch a boundary land mid-table.
chunk_size=80, chunk_overlap=10 → 4 chunks | tokens 58/70/78
Tries \n\n, then \n, then a plain space, in that order. Whichever one gives a natural break and still fits, wins.
chunk_size=400, chunk_overlap=60 → 3 chunks | tokens 46/83/132
semantic = SemanticChunker(embeddings, breakpoint_threshold_type="percentile") sem_chunks = semantic.create_documents([contract]) → 2 chunks | tokens 31/141/252
Be honest about the cost here. It's calling an embedding model during indexing, not just at query time, so it's slower and pricier than recursive splitting. I tell students to reach for this only when one document mixes wildly different topics, not as a default choice. Also worth mentioning: it lives in langchain_experimental still, not core LangChain, so expect API drift.
Honestly the best "aha" in the whole notebook, don't rush it. Ask the class: if we only returned the matched sentence, what fact would the model never even see? Answer: contractors aren't eligible, and that's a compliance detail you really don't want silently dropped. Worth mentioning LangChain has no built-in for this, it's a manual metadata trick. Notebook 03 turns it into a one-liner in LlamaIndex, so tease that for tomorrow.
Put this right next to strategy 4 and make them compare. Sentence-window fixes context with a fixed number of neighboring sentences. Parent-document fixes it using the document's actual structure, section boundaries. Ask which one a legal-contract system would want and why. Clause boundaries mean something, an arbitrary sentence count doesn't.
from_language)#/## headersMake sure your Markdown demo doc actually has more than one heading. If it only has one, the splitter quietly returns 1 chunk and looks completely broken. We built this exact demo against a single-heading doc the first time and spent ten minutes convinced the splitter was buggy. It wasn't, the test doc was just wrong. Tell students this story, it's a real lesson: check that your demo is actually demonstrating what you think it is before you trust it.
An LLM writes a short blurb (50 to 100 tokens), something like: "this chunk is from Northwind's Q2 report, Northeast region." That blurb gets prepended before embedding and BM25 indexing.
Yes, it's one more LLM call per chunk, but it happens at indexing time, not on every query, so it's cheap and you can cache it. I don't build this live in class, just flag that it exists. Pairs well with reranking, which we get to in Notebook 08.
| Time | Segment |
|---|---|
| 0–5 min | Recap Notebook 01, show the pipeline diagram, tell them today is parsing plus chunking |
| 5–15 min | Ingestion: run the PDF naive vs. layout-aware demo live, then a quick pass on HTML/Word/OCR |
| 15–35 min | Chunking strategies 1 through 6, live in the notebook, in order. Don't skip the ramp framing, it's the thread that ties everything together |
| 35–42 min | Contextual Retrieval as a bonus concept, no live coding needed here |
| 42–50 min | Summary table, then hand off to Notebook 03 (same ideas, different library) |