Recommended resources
The freely available resources this lesson will direct you to.
Hello again. Last lesson followed a Markdown excerpt and a Python excerpt from their source lines into a retrieved prompt, an answer, and citations. This time, we will turn that trace into component boundaries: what each part of the system is responsible for, what it receives, and what it must hand to the next part.
That distinction matters when you build the project. If an answer cites the wrong line, for example, you need to know whether the source location was lost during loading, changed during chunking, omitted from a retrieval result, or mishandled after generation. By the end of this lesson, you should be able to describe all seven components without tying them to a particular Python library or service.
The boundary between preparation and answering
The diagram below shows two activities. Indexing prepares source material before a question arrives. Question answering uses that prepared material when a user asks something. The embedding model participates in both: it represents source chunks during indexing and represents a question during retrieval.

Watch the short integration segment of KodeKloud’s RAG Crash Course for Beginners. It is useful here not as an implementation prescription, but as a way to hear which work happens in advance and which work begins with a question.
RAG Crash Course for Beginners
KodeKloud’s RAG Crash Course for Beginners brings the preparation and answering stages together in one example.
Watch the pipeline overview. Focus on the point at which prepared, stored chunks become retrieved context for a new question. Treat its named models and database as examples, not required choices for your project.
A RAG system can be drawn with fewer boxes than we use here. A library might combine loading and chunking, or combine question embedding and database search in one function. Separate responsibilities remain useful even when one function performs several of them: each responsibility has a different input, output, and possible failure.
For a second perspective, read the architecture summary and five-stage walkthrough in Databricks’ End-to-End RAG Workflow. Its “integration layer” is the prompt-assembly step we will place between retrieval and generation.
End-to-End RAG Workflow: How Retrieval Augmented Generation ...
Read Databricks’ overview to compare its broader architectural groups with the more specific component boundaries in this lesson.
In “Key Components of a RAG Architecture,” read the architecture overview. Then, in “How Does RAG Work: Ingestion to Generation,” follow Stages 1–5, from the paragraph beginning “The rag pipeline begins with data ingestion” through the final paragraph of “Stage 5: Generate the Final Response,” ending with the stage walkthrough. Track what changes hands at each stage rather than the platform examples.
The indexing contracts: files become searchable records
Think of an input/output contract as a promise about a piece of data. It says not just “here is some text,” but also which source it came from and which later component can rely on it. We will use the same illustrative src/loader.py file from last lesson; these are design-level examples, not calls to a particular framework.
1. Loader: source files into documents
The loader is responsible for finding permitted source files and reading their contents. In your project, its inputs are Markdown and Python file paths and the bytes stored at those paths. Its output is a collection of documents: readable text associated with source identity, such as a path and document ID.
A minimal document contract might contain:
Document
document_id: stable identity for this source file
source_path: src/loader.py
file_type: Python
text: the file contents
The loader should not decide which passage answers a question or produce an embedding. Its immediate obligation is more basic: represent the file faithfully and identify where the text came from. Later ingestion lessons will address encodings, unreadable files, and other practical details.
2. Chunker: documents into attributable passages
The chunker receives loaded documents and divides their text into passages suitable for retrieval. Its output is a set of chunks. Each chunk contains text plus enough information to locate that text in its original document.
For the Python example, a chunk might contain the suffix set and the accepts function together:
Chunk C2
document_id: identity of src/loader.py
source_path: src/loader.py
start_line: 3
end_line: 6
text: ALLOWED_SUFFIXES = {".md", ".py"}
...
def accepts(path: Path) -> bool:
return path.suffix.lower() in ALLOWED_SUFFIXES
This boundary is especially important for your two file types. A Markdown heading can supply context for the paragraph beneath it; a Python function name and its surrounding declarations can supply context for code. The chunker decides what stays together, but it must not make the original path and line range ambiguous. We will work through chunking strategies in detail later.
3. Embedder: text into comparable vectors
The embedder accepts text and returns a numeric vector. During indexing, its input is typically each chunk’s text; its output is an embedding that can be compared with other embeddings produced by a compatible configuration.
The embedder does not return an answer, a citation, or a replacement for the chunk’s words. Its vector is a search representation. Your application must keep that vector associated with the chunk text and source metadata. It should also record which embedding configuration produced the vectors: question-time retrieval must use a compatible one.
4. Vector store: indexed records into searchable matches
The vector store accepts records to store or update. A useful record connects a chunk ID, its embedding, its text—or a dependable means of retrieving that text—and its source metadata. When given a query vector, the store searches those records and returns matching stored chunks, usually with a similarity score or distance.
Its two interfaces are worth separating:
- Indexing input: an ID, vector, text or text reference, and citation metadata. Output: a stored, searchable record.
- Search input: a query vector and search settings such as how many matches to return. Output: candidate matches with their IDs, text or text references, metadata, and scores.
The store answers a search request; it does not decide whether a returned passage proves a claim. Likewise, a stored vector by itself is not enough for generation: the generator will need the recovered words.
The question-time contracts: matches become an answer
Suppose the user asks, “Does accepts permit .txt files?” The system no longer starts by reading every source file. It consults the index already built from those files.
5. Retriever: a question into candidate evidence
The retriever accepts the user’s question and access to the prepared index. For vector retrieval, it asks the embedder to represent the question with a configuration compatible with the indexed chunks, then asks the vector store for relevant matches. It returns ranked candidate evidence: chunk text, source metadata, IDs, and often scores.
The retriever is more than another name for the vector store. The store performs its search operation; the retriever decides how to use it for this question and which results to pass onward. A retrieved C2 could be strong evidence about what accepts returns for .txt. It would not, by itself, establish every file type handled by an entire application.
The retriever’s output should make that distinction inspectable. If it returns only a filename, the next component may lack the actual passage. If it returns only passage text, the citation layer may lack a checkable source location.
The handoff: assemble a prompt
Before generation, application code assembles the user’s question, relevant chunk text, and instructions into a prompt. This is sometimes called an integration or augmentation layer. We are keeping it separate from the seven named components so that “retriever output” does not silently turn into “LLM input.”
For our question, the assembler might assign a prompt-local label [S1] to retrieved chunk C2, include its text, and retain a lookup from [S1] to src/loader.py:3–6. The generator sees the question and the labeled passage—not the similarity vector. Prompt construction will receive its own treatment later in the course.
6. Generator: an augmented prompt into a draft answer
The generator is the LLM acting on the assembled prompt. Its input is the question plus supplied context and instructions. Its output is draft answer text, which may include requested citation labels.
With the C2 excerpt, an appropriately scoped draft might say:
No. The
acceptsfunction compares the lowercased suffix against a set containing.mdand.py, so.txtdoes not pass that check. [S1]
This answer reasons from supplied code. The generator did not read src/loader.py directly or search the database; it received selected text from earlier components. It also should not extend the claim to “the whole project never handles .txt,” because that broader claim is not established by this function alone.
7. Citation layer: cited draft into checkable output
The citation layer receives the draft answer and the lookup connecting prompt labels to retrieved chunks and their source locations. Its output is a user-facing answer with citations that resolve to source paths and line ranges. It should check that emitted labels actually exist and that the cited passages support the associated claims; unsupported claims need to be corrected, qualified, or withheld.
For [S1], the lookup might identify src/loader.py, lines 3–6. Merely appending that location beneath the answer is not proof that every sentence is supported. The citation layer has a distinct responsibility from the generator: make a claim’s provenance checkable and catch citation failures where possible. We will examine how to do that reliably in the citations module.
A compact contract you can explain to a teammate
The seven boundaries can be summarized without naming a provider:
| Component | Responsibility | Receives | Produces |
|---|---|---|---|
| Loader | Read permitted source files | Markdown/Python paths and file contents | Documents with text and source identity |
| Chunker | Select retrievable passages | Documents | Chunks with text and original locations |
| Embedder | Represent text for similarity search | Chunk text or question text | Comparable numeric vectors |
| Vector store | Store records and search vectors | Indexed records; later, a query vector | Stored records; later, candidate matches |
| Retriever | Select evidence for a question | Question and searchable index | Ranked chunks with text and metadata |
| Generator | Compose a response using supplied context | Augmented prompt | Draft answer, possibly with citation labels |
| Citation layer | Resolve and check source references | Draft answer and label-to-source lookup | Answer with checkable citations, or a flagged gap |
Notice the two uses of the embedder and vector store. The embedder operates both before questions and when a question arrives. The vector store accepts records during indexing and serves matches during answering. The loader and chunker usually run during indexing; the retriever, generator, and citation layer operate for each question.
A useful design test is to ask what would be lost if one component returned less information than its contract requires. If the chunker drops original line numbers, neither a capable database nor a careful LLM can reconstruct reliable line citations from a vector. If retrieval drops chunk text, the generator has no evidence to read. If generation invents [S9] when only [S1] was supplied, the citation layer should expose that mismatch rather than display a convincing-looking reference.
These are boundary failures, not interchangeable “RAG errors.” Describing them precisely will make both debugging and your eventual presentation much clearer.
Key takeaways
A loader produces identifiable documents; a chunker produces attributable passages; an embedder produces searchable vectors; and a vector store preserves and finds the records connecting those pieces. For each question, a retriever selects candidate evidence, the application places it into a prompt, a generator drafts an answer, and a citation layer ties supported claims back to checkable locations. No single component guarantees a grounded answer on its own.
Next, you will run a small synchronous reference pipeline and inspect the actual objects crossing these boundaries—documents, chunks, retrieval results, prompt, answer, and citations.