Designing the App
What This Capstone Combines
This final unit builds one complete application from the ground up: a document assistant that lets a user upload their own documents, then ask questions about them in an ongoing conversation, with the assistant able to call tools when a question calls for something beyond simply retrieving and summarizing text. This single project deliberately draws on nearly everything this course has covered — Unit 4's conversation state, Unit 6's structured outputs, Unit 8's tool calling, Unit 9 and Unit 10's retrieval techniques, Unit 12's production-readiness practices — combined into one coherent system rather than exercised in isolation the way each unit's own capstone did. This lesson designs the system before any of the following lessons write its backend (Lesson 2), streaming interface (Lesson 3), or deployment checklist (Lesson 4); designing deliberately before writing code is worth doing explicitly here precisely because this project is large enough that starting without a plan invites exactly the kind of structural rework a clearer upfront design avoids.
Defining the Application's Actual Behavior
Before any architecture, it's worth stating plainly and specifically what this application needs to do, since architecture decisions should follow directly from actual required behavior rather than from habit or from what's most familiar to build.
A user uploads one or more documents (text files, for this capstone's scope — PDF parsing introduces complexity deliberately set aside here). The assistant answers questions about the content of those documents, grounding its answers in what the documents actually say rather than the model's own general knowledge, following the same grounding principle Unit 9 and Unit 10 established. The conversation persists across multiple turns, so a follow-up question can refer back to an earlier answer without the user needing to restate context, following Unit 4's conversation-state patterns. And the assistant has at least one tool available beyond pure retrieval — a way to look up a specific fact that isn't well suited to semantic search, such as retrieving metadata about which documents are currently loaded — demonstrating that retrieval and tool calling aren't mutually exclusive techniques but ones that combine within a single assistant.
The System's Major Components
Breaking the application into its constituent pieces, each mapping to a specific unit's techniques, is what turns the description above into something buildable.
| Component | Responsibility | Builds On |
|---|---|---|
| Document ingestion | Chunk uploaded documents and generate embeddings for each chunk | Unit 10, Lesson 2 (generating and storing embeddings), Unit 10, Lesson 5's chunking |
| Retrieval | Given a user's question, find the most relevant chunks | Unit 10, Lesson 3 (similarity search) or Unit 9's hosted file search |
| Conversation state | Track the ongoing back-and-forth across multiple turns | Unit 4's conversation state patterns |
| Tools | Handle requests better served by a function than by retrieval | Unit 8's tool-calling loop |
| Response generation | Combine retrieved context, conversation history, and any tool results into a grounded answer | Unit 6's structured outputs where a predictable response shape matters |
| API layer | Expose the assistant over HTTP so a frontend can use it | Lesson 2 (FastAPI) |
| Streaming | Return a response incrementally rather than all at once | Lesson 3 |
Laying the system out this way before writing any code is what makes it possible to reason about how pieces connect — the retrieval component's output becomes part of what the response-generation component receives as context, which is a different (and, for this design, deliberately simpler) data flow than a hosted vector store's built-in file search tool would use if that alternative were chosen instead.
Choosing Between a Custom Retrieval Pipeline and a Hosted Vector Store
Unit 10, Lesson 4 compared rolling a custom retrieval pipeline against using a hosted vector store directly, and this capstone is a concrete point where that choice needs to actually be made rather than discussed abstractly.
# Option A: hosted vector store + built-in file search (Unit 9)
# Simpler to wire up; less visibility into and control over the retrieval step itself.
tools_with_hosted_search = [{"type": "file_search", "vector_store_ids": ["vs_..."]}]
# Option B: a custom retrieval pipeline (Unit 10)
# More code, but full visibility into chunking, similarity scoring, and what gets
# passed into the final prompt — chosen here because this capstone's purpose is
# to demonstrate the mechanics explicitly, not to build the least code possible.
def retrieve_relevant_chunks(query_embedding: list, document_chunks: list, top_k: int = 5) -> list:
pass # implemented fully in Lesson 2
This capstone deliberately builds the custom pipeline (Option B) rather than relying on the hosted file search tool, specifically because the goal here is to see every step of retrieval explicit and inspectable — a real production application without that specific pedagogical goal might reasonably choose the hosted option instead for its simplicity, exactly the trade-off Unit 10, Lesson 4's comparison table laid out.
Designing the Conversation Flow
Sketching out what actually happens across a single exchange — from a user's question arriving to an answer being returned — is what the following lessons' code will implement directly.
- A user's question arrives, along with the ongoing conversation's identifier (Unit 4).
- The question is embedded (Unit 10, Lesson 2) and compared against the stored document chunk embeddings to retrieve the most relevant chunks (Unit 10, Lesson 3).
- The retrieved chunks, the conversation history, and the available tools (Unit 8) are combined into a single request to the model.
- If the model calls a tool, the tool executes and its result is fed back in, following Unit 8, Lesson 3's loop, before a final answer is produced.
- The final answer is returned to the user, and the turn is added to the conversation's stored history for the next question to build on.
This flow is deliberately similar in shape to Unit 8's tool-calling loop and Unit 9's retrieval-augmented assistant, combined into one — nothing about combining retrieval and tool calling in the same request requires new mechanics beyond what those two units already established individually; the design work here is mostly about how the pieces are wired together, not new technique.
Scoping What This Capstone Deliberately Excludes
A capstone project this size can grow indefinitely if scope isn't bounded deliberately — being explicit about what's excluded, and why, keeps the project focused on demonstrating this course's techniques clearly rather than becoming an open-ended production system.
Excluded deliberately: user authentication and multi-user access control (a real deployment would need this, but it's a general web-application concern rather than one specific to this course); PDF or other complex document format parsing (plain text keeps the ingestion step focused on chunking and embedding rather than format parsing); a persistent database for conversation history (an in-memory store is used for clarity, with Lesson 4's deployment checklist noting where a real database would replace it); and horizontal scaling or multi-server deployment concerns beyond what Lesson 4 covers at a checklist level.
Common Mistakes
Starting to write code before deciding how the major components connect, risking significant rework once an important piece — like how retrieved context, conversation history, and tool results all combine into one prompt — turns out not to fit the initial approach.
Choosing a hosted vector store or a custom retrieval pipeline without weighing the actual trade-off, rather than an explicit decision informed by Unit 10, Lesson 4's comparison of visibility and control against implementation simplicity.
Letting a capstone's scope grow to include concerns — authentication, multi-format parsing, horizontal scaling — that dilute focus away from the actual techniques being demonstrated.
Treating retrieval and tool calling as alternative approaches rather than complementary ones, when a single assistant combining both, as this capstone does, is a common and realistic pattern.
Best Practices
Design the major components and how they connect before writing implementation code, especially for a project combining several previously-separate techniques into one system.
Make the retrieval-approach decision (hosted vs. custom) explicitly and for a stated reason, rather than defaulting to whichever was most recently covered.
Scope a learning-focused capstone deliberately, excluding concerns that would meaningfully grow the project without adding to what it demonstrates.
Reuse this course's established patterns (the tool-calling loop, conversation state, chunking and embedding) rather than inventing new ones, since the value of this capstone is in combining known techniques, not replacing them.